资讯动态

MiroFish:根因清单自动生成Miro鱼骨图

发布时间:2026/9/18 6:46:43 来源:尧图企业网站定制
做质量和故障复盘的人对这一幕应该都不陌生会议室订好了Miro 画板开好了六个人围着一块屏幕主持人先在白板上画一条横线再斜着拉出六根大骨写上人、机、料、法、环、测然后大家开始沉默。等真正讨论到第三层原因的时候四十分钟已经过去了而画板上的内容还停留在骨架阶段。MiroFish 这个项目就是冲着这个断点去的——它把一个已经结构化的根因清单自动铺成一张排布整齐、间距均匀、带连线的鱼骨图直接落在 Miro 画板上。人一进会议室看到的是一张已经长出枝干的图剩下的时间全部用来往上面补细节和吵架而不是画线。说白了MiroFish 干的是结构到图形的翻译工作。输入是一份人写起来不费劲的清单谁、哪个环节、什么现象、可能是哪类原因输出是 Miro 上可以直接拖、可以直接改的原生图元。它不算什么大模型应用也不做什么智能推理它解决的是一件特别土但特别耗时间的事把脑子里想清楚的结构手工搬到白板上。用过协作白板做复盘的人都知道手工摆放三十个文本框并拉好连线是一个纯粹的体力活而且一旦要调整分类整个布局就崩了得重来。这个项目适合三类人参考一是经常主持复盘会、评审会的团队负责人和敏捷教练可以直接拿去用把会议前二十分钟省下来二是做 Miro 插件、白板类工具的开发者里面关于 Web SDK 与 REST API 的边界划分、OAuth 授权链路、图元批量写入和限流处理的部分是可以直接抄的三是对图形自动布局感兴趣的人鱼骨图虽然简单但它是一个很好的约束求解入门案例比力导向图好上手得多。1. 项目整体设计与思路拆解1.1 先把场景钉死MiroFish 到底解决哪一段动手写代码之前我把这件事拆成了三段收集、布局、呈现。收集阶段是人输入结构布局阶段是算坐标呈现阶段是往画板上写。当时的第一反应是用大模型直接把一段会议录音转成鱼骨图听起来很酷但我很快否掉了这个方向。原因是复盘会最值钱的部分恰恰是人一起讨论出结构的过程机器猜出来的结构没人认反而会变成新的争论点。所以 MiroFish 只做后两段收集那段老老实实交给人用一份 YAML 或者一个简单的表单来填。这个取舍带来了一个很实际的好处整个系统的确定性极高。同样的输入永远得到同样的输出坐标不会出现今天生成的图好看、明天生成的图挤成一团的情况。布局是纯函数输入是树形结构输出是图元列表中间没有随机数、没有网络请求、没有模型幻觉。调试的时候你可以直接把输出打印出来一眼就能看出问题出在哪一层。第二个取舍是不做双向同步。最早我确实想过监听画板的改变事件让人在 Miro 上手动挪动图元之后能回写到 YAML。做了两天就放弃了用户在画板上的操作是高度非结构化的——挪一点位置、改一下字体、拆一个框、加一段便签这些操作很难反向映射回原来的树。强行做同步最后的结果是系统每隔几秒就把人刚调好的布局冲掉一次比不做还糟。现在的做法是单向生成生成完就撒手后面怎么改都是人的自由。1.2 为什么选择鱼骨图而不是思维导图有人会问同样是结构化展示为什么不直接生成思维导图Miro 上画思维导图更简单一个中心节点往外发散就行。这里的关键区别在于鱼骨图带有一个隐含的因果方向。思维导图表达的是从属关系而鱼骨图的每一根骨都是指向结果的大骨是小骨的原因小骨是大骨的原因整张图从左到右汇集到最右侧的结果头。这个方向性对复盘场景是刚需它逼着参与者在写每一条的时候想清楚这一条是原因还是现象。另外从布局角度讲鱼骨图其实比思维导图更规整。思维导图的节点数量和层级一变化整张图的形状就完全不一样很难做统一的美化。鱼骨图的骨架是固定的一条主骨、几根斜骨、斜骨上挂几排水平的小骨。骨架固定意味着坐标可以提前算好剩下的事情就是往格子里填内容。这一点对自动生成极其友好——我们不是在算一个通用布局问题而是在填一个已知形状的模板。具体到 Miro 平台还有一层考虑。Miro 的画板坐标系是无限大的图元之间没有自动避让你放哪儿就是哪儿重叠了也不会有人提醒你。所以算好坐标再写比边写边调整重要得多。如果布局引擎在本地就把所有冲突解决掉写进画板的就是一份已经排好的结果用户看到的画板是干净的。1.3 三层架构与各自的分工边界MiroFish 的整体结构分三层边界划得很清楚描述层一份 YAML 文件描述结果头、分类骨、每根骨头下的子原因。这一层是人唯一需要接触的东西格式要尽量宽容。布局层一个纯 Python 模块把描述层的树转成一组带坐标的图元对象矩形、文本、连线。这一层没有任何外部依赖可以单独跑单元测试。写入层一个 Miro 应用负责拿到画板上下文把布局层产出的图元批量写到画板上。这一层处理鉴权、限流、错误重试。分层的价值在调试的时候体现得特别明显。画板上的图歪了先看布局层输出的坐标表坐标是对的但画板上没东西那问题在后面两层画板上东西多了或者少了多半是写入层的批量分片出了岔子。每一层都有独立的验证手段不用每次都从头跑一遍。注意布局层一定要做成纯函数。我一开始贪方便在布局函数里直接调了 Miro API 去读画板尺寸结果单元测试根本没法写每次调试都要连真实的画板。后来改成把画板尺寸当参数传进来测试数据直接写死调试效率提升了不止一个档次。2. 核心技术点逐层拆解2.1 Web SDK 和 REST API 的边界怎么划Miro 提供了两套写入能力用错了会很别扭。简单说Web SDK 跑在画板里REST API 跑在你的服务器上。Web SDK 是一段跑在 Miro 画板 iframe 里的 JavaScript它通过 postMessage 和宿主页面通信。它的优势是有上下文——miro.board.getInfo()直接告诉你当前是哪个画板、用户是谁、视口在哪不需要任何额外鉴权参数。它还有一个 REST API 做不到的能力控制视口。图生成完了调一次miro.board.viewport.zoomTo(items)画板会自动缩放到刚好装下所有图元用户体验上差别很大。REST API 则适合服务端批处理。它有一个批量创建图元的接口一次请求可以塞进去多个图元网络往返次数能压下来。如果一次要写两百个图元用 Web SDK 一个个创建就是两百次 postMessage 往返用 REST 批量接口可能十几次请求就完事了。MiroFish 的选择是混用静态骨架走 REST 批量接口收尾操作走 Web SDK。服务端先把所有矩形、文本、连线批量写完最后在画板里通过 SDK 调一次视口自适应并弹一个提示告诉用户生成完成。这样既有批量写入的效率又有 SDK 的交互体验。2.2 授权链路两套令牌别搞混这是最容易卡住新手的地方。Miro 应用有两种用途不同的令牌刚开始我混着用报了一下午的 401。第一种是OAuth 授权码流程拿到的访问令牌代表某个用户授权了这个应用。它的典型用法是用户点一下安装跳转到授权页同意之后回调拿 code换 access_token 和 refresh_token。这个令牌适合服务端长时间跑任务比如定时把某个系统的数据同步到画板。第二种是画板级令牌board token它是用应用密钥对当前会话信息签出来的 JWT有效期很短。Web SDK 的初始化就需要这个。它的作用范围被限制在当前打开的这一块画板上权限最小。实操上我建议这样分工Web SDK 侧走画板级令牌服务端批处理走 OAuth 令牌两套密钥分开管理别共用。令牌的存储也要注意access_token 和 refresh_token 放在服务端的环境变量或者密钥管理服务里绝对不要写进前端代码。前端只需要拿到一个短期的画板令牌就够了。权限声明也要克制。Miro 应用在注册时需要声明 scopes比如读画板、写画板。我当时顺手把团队级的权限也勾上了结果审核的时候被问了一堆你为什么需要这个权限的问题。后来全部砍掉只留boards:read和boards:write审核顺畅得多。原则是最小权限用不到的一律不勾。2.3 鱼骨图布局算法把坐标一个个算出来这是整个项目最核心也最有意思的部分。先把几何关系说清楚设定一条水平主骨从左侧起点向右延伸到结果头长度为 L。主骨上均匀分布 n 根分类骨第 i 根分类骨的基点在主骨上的位置是x_i x0 L * (i 1) / (n 1)为什么用(i1)/(n1)而不是i/(n-1)因为前者让首尾两根骨头跟起点、终点之间各留出一段等长的空隙整张图看起来是居中的后者会让第一根骨头正好压在起点上视觉上很挤。这个细节很土但改完之后图确实好看了一个档次。分类骨从基点斜向上或斜向下伸出交替排列避免同一侧挤成一堆。倾角取 60 度这是一个经验值角度太小接近水平骨头和主骨区分不开看着像一条线。角度太大接近垂直骨头占的纵向空间太多整张图会被拉得很高。60 度在视觉上最接近经典教材里的画法横向占位是B * cos60° 0.5B纵向占位是B * sin60° ≈ 0.866B比例舒服。假设骨长 B 420Miro 坐标单位大致对应 100% 缩放下的像素那么一根向上的分类骨端点坐标是终点 x x_i B * cos(60°) x_i 210 终点 y y_spine - B * sin(60°) y_spine - 364子原因挂在分类骨上每条子原因是一条水平线起点落在分类骨上向右延伸。第 j 条子原因在骨头上的位置按比例 t 取t t_start j * Δ / B 点 x x_i t * B * cos(60°) 点 y y_spine - t * B * sin(60°) 线长 s 180这里的 Δ 是相邻两条子原因沿骨头的间隔它的取值不是随便定的要从文本不重叠倒推。水平文本的行高按 h 48 算Miro 默认字号大约在这个量级两条相邻子原因在垂直方向上的间距是垂直间距 Δ * sin(60°) 0.866 * Δ要不重叠垂直间距必须大于行高0.866 * Δ ≥ 48 → Δ ≥ 55.4取 Δ 60留一点余量。反过来说如果某一根骨下面有 k 条子原因骨长至少要满足可用段长 B - B * t_start k 条子原因需要的长度 (k - 1) * Δ 所以 B ≥ t_start * B (k - 1) * Δ取 t_start 0.15让第一根子原因离主骨稍微远一点避免贴着主骨看不清。整理一下就得到自适应骨长公式B max(B_min, (k - 1) * Δ / (1 - t_start)) max(320, (k - 1) * 60 / 0.85)k 5 时B max(320, 282) 320。k 8 时B max(320, 494) 494。也就是说骨头会随着内容变多自动变长不会出现子原因挤成一坨的情况。而整张图的高度则由最长的两根上、下骨决定主骨位置需要相应下移上方最高点 y_spine - B_up_max * sin(60°) 主骨 y 坐标 画板基准 y B_up_max * 0.866 边距这一步很容易忘。如果主骨还是按固定位置画子原因多的一侧就会冲出画板边界或者被结果头的文本框盖住。2.4 输入结构设计让人写得下的格式描述层我用的是 YAML理由很简单它支持缩进表达层级写起来比 JSON 少一半的括号和引号非程序员也能改。一份典型输入长这样effect: 线上订单支付成功率下降 meta: angle: 60 branch_gap: 60 sub_line_len: 180 categories: - name: 客户端 bones: - 部分机型 SDK 初始化超时 - 版本灰度期间旧版逻辑未回退 - 本地缓存写入失败 - name: 服务端 bones: - 支付网关连接池打满 - 下游回调超时重试放大 - name: 依赖方 bones: - 渠道侧维护窗口重叠有几个设计决定值得说一下。effect单独拎出来当一个字段而不是混在分类里因为它是整张图唯一的结果头位置和样式都跟其他节点不一样需要单独渲染。meta里放布局参数这样可以针对不同长度的内容微调而不用改代码。子原因用纯字符串数组不搞嵌套对象——试过让它支持原因 证据 责任人的多字段结构结果填的人嫌麻烦一半的字段留空还不如就一行字。真要记录责任人让他写在画板便签上更自然。实操心得YAML 的缩进对新手极不友好一个 tab 和四个空格的混用就能让解析直接报错而且报错信息往往指向错误的行号。我的处理方式是在加载后立刻做一次 schema 校验把第几根骨、缺少什么字段这种人类看得懂的错误抛出来而不是把底层的解析异常直接甩给用户。3. 从零跑通 MiroFish实操步骤3.1 环境准备与项目初始化本地需要准备的东西不多我列一下我的实际环境python3 --version # 3.11.x node --version # 20.xPython 端只用到两个库pyyaml负责解析输入requests负责调 REST API。布局部分我刻意不引第三方几何库因为鱼骨图的数学太简单了引库反而是负担而且多一个依赖就多一个版本冲突的可能。mkdir mirofish cd mirofish python3 -m venv .venv source .venv/bin/activate pip install pyyaml requests前端部分要建一个 Miro 应用工程。Miro 官方提供脚手架但我的习惯是自己建因为脚手架会带一堆用不上的示例代码反而干扰。最小结构是这样mirofish/ ├── layout/ │ ├── engine.py 布局核心 │ └── models.py 图元数据类 ├── writer/ │ ├── api_client.py REST 写入 │ └── auth.py 令牌管理 ├── webapp/ │ ├── index.html 画板内 iframe 页面 │ └── app.js Web SDK 逻辑 ├── examples/ │ └── payment.yaml 示例输入 └── config.yaml注意把layout目录和writer目录严格分开。我最初混在一个包里结果只要 import 布局模块就会连带把requests和令牌配置一起加载进来写单元测试的时候环境变量缺失直接报错。拆开之后布局模块零外部依赖跑测试飞快。3.2 应用注册与权限声明在 Miro 的开发者后台新建一个应用需要填的几项配置项建议值说明应用类型画板内应用决定入口在画板工具栏回调地址你服务端的/oauth/callback必须是公网可访问的地址权限范围boards:read, boards:write不要多勾Webhook暂时不开单向生成用不到注册完会拿到三样东西client_id、client_secret、以及一个用于签名画板令牌的密钥。这三样都要放进服务端的环境变量用.env管理并且把.env写进.gitignore。我见过太多次密钥被推上公开仓库的事故加一行忽略规则的成本几乎为零。权限这块再强调一次Miro 的权限是分层的画板级、团队级、组织级各有对应的 scope。你要做的事情如果只是往某一块画板写图元boards:write就够了。团队级的 scope 意味着你的应用可能被用来遍历整个团队的所有画板审核会严格得多。同理webhook 不开就不需要相应的订阅权限能省一步是一步。3.3 布局引擎的具体实现布局引擎的核心就是前面推导的那几个公式。我把它写成纯函数输入是解析好的结构加一组布局参数输出是图元对象列表import math def build_layout(spec, board_w3200, board_h2000): angle math.radians(spec.meta.get(angle, 60)) cos_a, sin_a math.cos(angle), math.sin(angle) gap spec.meta.get(branch_gap, 60) t_start 0.15 line_len spec.meta.get(sub_line_len, 180) cats spec.categories n len(cats) # 第一步先算出每根骨需要的长度取最大值决定整体高度 branch_len [] for c in cats: k max(len(c.bones), 1) need (k - 1) * gap / (1 - t_start) if k 1 else 0 branch_len.append(max(320, need)) up_max max([branch_len[i] for i in range(n) if i % 2 0] or [320]) dn_max max([branch_len[i] for i in range(n) if i % 2 1] or [320]) margin_x, margin_y 200, 120 spine_y margin_y up_max * sin_a 80 spine_x0 margin_x spine_len board_w - 2 * margin_x - 320 # 右侧给结果头留位置 items [] items.append({ type: spine, x1: spine_x0, y1: spine_y, x2: spine_x0 spine_len, y2: spine_y, }) for i, c in enumerate(cats): base_x spine_x0 spine_len * (i 1) / (n 1) direction -1 if i % 2 0 else 1 # -1 向上1 向下 b_len branch_len[i] end_x base_x b_len * cos_a end_y spine_y direction * b_len * sin_a items.append({ type: branch, x1: base_x, y1: spine_y, x2: end_x, y2: end_y, label: c.name, }) for j, bone in enumerate(c.bones): t t_start j * gap / b_len px base_x t * b_len * cos_a py spine_y direction * t * b_len * sin_a items.append({ type: sub, x1: px, y1: py, x2: px line_len, y2: py, text: bone, }) return { items: items, spine_y: spine_y, head: {x: spine_x0 spine_len 60, y: spine_y}, bounds: {w: board_w, h: spine_y dn_max * sin_a margin_y}, }几个容易写错的地方。一是t的取值范围如果gap / b_len算出来让t超过 1说明骨长估计不足子原因会跑到骨头外面去。稳妥的做法是在循环里加一句t min(t, 0.92)把最后一条压回骨头范围内。二是方向交替用的是i % 2这意味着分类的顺序会直接影响哪根骨头朝上、哪根朝下。如果你希望某一类固定朝上可以在 YAML 里加一个显式的side字段覆盖掉自动交替。三是布局完成后一定要算一遍边界。上面返回值里的bounds就是干这个的它能告诉你整张图实际占了多大地方。后面写进画板、调视口自适应都靠这个值。没有它的话你只能靠肉眼看画板是不是被切掉了。3.4 把图元写进 Miro 画板写入分两步。第一步用 REST 批量接口创建矩形和文本第二步用 Web SDK 补连线并调整视口。REST 侧的关键是分片。批量接口单次能提交的图元数量是有限的我的做法是固定按 20 个一批切分import requests, time BATCH 20 def push_items(board_id, token, items): url fhttps://api.miro.com/v2/boards/{board_id}/items headers { Authorization: fBearer {token}, Content-Type: application/json, } created [] for i in range(0, len(items), BATCH): chunk items[i:i BATCH] payload {data: [to_miro_item(x) for x in chunk]} for attempt in range(3): r requests.post(url, jsonpayload, headersheaders, timeout15) if r.status_code in (200, 201): created.extend(r.json().get(data, [])) break if r.status_code 429: wait int(r.headers.get(Retry-After, 2)) time.sleep(wait * (attempt 1)) continue raise RuntimeError(f写入失败 {r.status_code}: {r.text[:200]}) else: raise RuntimeError(连续三次失败中止) time.sleep(0.15) # 主动降速别把限流撞满 return created这里的重试逻辑有几个细节值得说。只对 429 和 5xx 重试4xx 里的参数错误重试一百次也没用直接抛出来让人看。退避要乘上尝试次数第一次等 2 秒第二次等 4 秒而不是每次都等固定值否则容易把服务端刚恢复的窗口又打满。成功之后主动 sleep 一小会儿这个看起来傻但实测比不限速硬冲要稳得多尤其是图元数量上百的时候。图元映射这一层要做转换。Miro 的矩形图元用shape类型文本用text类型连线用connector。鱼骨图的骨头本身既要有线又要有标签我的做法是用矩形做一个非常扁的形状来代替线高度设成 3 像素这样它会跟着画板一起缩放样式也能统一控制。真正需要连线的只有主骨和结果头之间那一段。Web SDK 侧的收尾代码很短async function finish(boardItems, bounds) { await miro.board.viewport.zoomTo(boardItems); await miro.board.notifications.showInfo( 已生成 ${boardItems.length} 个图元可直接编辑 ); }zoomTo传图元数组画板会自动算出包围盒并缩放到合适比例。这个体验比手动setViewport好太多因为包围盒是它自己算的你不用操心。提示如果你生成的图元数量超过一百个zoomTo之后画板可能会比较拥挤。这时候可以按分类分组生成完之后只zoomTo第一根分类骨所在的区域再给用户一个查看全图的按钮。人看图的注意力是有限的一次给他一根骨头比一次给他整张图更好用。3.5 联调与验收怎么判断生成对了验收不是看画板上有没有东西而是看几个具体指标。我固定检查这几项检查项判定标准常见异常图元总数等于2 * 骨数 2 * 子原因数 3数量偏少说明批量分片丢包子原因垂直间距40 ~ 60 之间过小说明 gap 参数被改小最外侧骨头是否出界距离画板可视边界 ≥ 100骨长估计不足连线完整性每根分类骨都有连线指向主骨后处理阶段跳过了文本是否被裁切文本框宽度 ≥ 实际文字宽度字号估小了文本裁切这一项是踩过坑之后加的。Miro 的文本图元有个宽度属性如果设得比实际文字窄它会自动换行换行之后行高变大就跟上一条子原因撞在一起了。中文字符的宽度估算比英文麻烦我的做法是按中文字符算 1 个单位英文和数字算 0.55 个单位来估再乘上字号最后加 20% 的余量。估算肯定不准但宁可框子大一点也不要让它换行。4. 踩坑实录与排查手册4.1 授权与权限类问题401 报错最常见的原因是把两套令牌用串了。服务端调 REST API 用的是 OAuth 拿到的访问令牌画板内 Web SDK 用的是画板级令牌两者不能互换。判断方法很直接如果 401 出现在服务端日志里检查你传进去的是不是 OAuth 令牌如果 401 出现在浏览器控制台检查画板令牌是不是过期了。画板令牌的有效期通常很短过期了需要重新签发。403 报错基本就是权限没声明。有时候代码完全正确但应用注册的时候少勾了一个 scope写入就会被拒。改完权限声明后记得重新走一遍安装授权流程因为令牌里携带的是授权时刻的权限快照旧令牌不会自动获得新权限。这一点很容易忽略改完配置直接跑还是 403然后开始怀疑代码实际上只是令牌没刷新。回调地址不匹配也是一个高频问题。Miro 会严格比对回调地址协议、域名、端口、路径任何一处不一致都会拒绝。开发阶段如果用本地地址调试记得把地址和端口固定下来不要今天 3000 明天 8080。4.2 图元写入与连线类问题图元写进去了但连线连不上这是新手最容易遇到的。Miro 的连线需要引用两端的图元 ID而这个 ID 是创建图元之后服务端返回的。所以流程必然是先建图元、拿到 ID、再建连线不能一次性把连线和图元混在一个请求里。我一开始就是图省事混着提交结果连线全部悬空只能全部删掉重来。图元位置偏移也遇到过。原因是 Miro 的坐标系原点和我们想象的左上角不完全一致而且矩形图元创建时传的x, y默认是中心点不是左上角。这两个概念搞混了整张图会往右下偏半个矩形的距离。解决办法很土先写一个图元到画板上目测它落在哪然后用这个偏移量校正整个布局。比翻半天文档快。文本换行导致布局崩坏前面提过。补充一个排查技巧如果发现某一条子原因的位置特别奇怪先看它的文本长度是不是明显超过了其他条目。中文没有天然的分词边界长句很容易触发换行换行之后整个堆叠关系就乱了。4.3 大数据量与限流问题图元数量上去之后两类问题会同时出现请求被限流和画板变卡。限流的表现是 429前面给了重试逻辑。这里补充一个经验主动降速比被动重试划算得多。我实测过两种策略一种是每批之间不加延迟直接冲撞到 429 再退避另一种是每批之间固定等一小会儿。前者总耗时反而更长因为退避的等待时间往往比你主动停顿的时间久。而且被动重试会让日志里全是 429真正的问题反而被淹没。画板变卡是另一个维度的限制。Miro 画板在几百个图元的量级上还是流畅的上千之后缩放和拖动就会明显掉帧。MiroFish 的应对策略是控制单次生成的规模分类骨最多八根每根骨下面的子原因最多十条超出的部分截断并给一个提示。八十个左右的子原因已经足够覆盖绝大多数复盘场景了硬塞更多只会让图变得没人看。4.4 常见问题速查表现象最可能的原因处理方式画板上什么都没有权限 scope 缺失补齐 boards:write 并重新授权服务端 401令牌类型用错或已过期区分 OAuth 令牌与画板令牌连线悬空图元与连线在同一批提交拆成先建图元、再建连线整张图偏右下矩形坐标用了左上角语义按中心点重新计算子原因挤成一坨gap 参数被调小恢复 60 并检查骨长自适应部分图元丢失批量分片边界处理错误检查切片步长与总数是否一致请求持续 429没有主动降速每批之间加固定延迟骨头冲出画板未计算整图边界用 bounds 结果反推主骨位置文字被裁断文本框宽度估小宽度估算加 20% 余量5. 会议现场怎么用以及后面还能长成什么样5.1 实战中的几个使用心得这套东西上线之后我用了小半年攒了几条文档里不会写的心得。别在会前就把子原因填满。我一开始的思路是把所有已知原因都写进 YAML会前生成一张完整的图。结果会议变成了审阅机器的结论大家只会说这个不对那个要改讨论的深度反而不如从前。后来改成只填分类骨和大方向子原因留空现场边讨论边往上加。这才是鱼骨图本来的用法——它是讨论的载体不是结论的展示板。分类骨的命名最好现场统一。最常见的六分类是人、机、料、法、环、测但软件场景下这套不太合适用客户端、服务端、依赖方、数据、流程、变更更贴。关键是让所有人对分类的含义有共识否则会出现同一条原因被两个人分别写在两个分类下的情况。我通常在生成图之前先花两分钟跟大家确认分类这两分钟能省掉后面二十分钟的重复讨论。生成的图别急着美化。有人会花时间调字色、加图标、换主题这些对复盘结论的产出没有任何帮助。MiroFish 生成的默认样式我刻意做得比较素就是不想让人把注意力放在外观上。保留每次生成的快照。复盘常常跨多次会议把每次的 YAML 存一份下次接着改。Miro 的画板历史可以回退但回退到某个时间点不如直接拿一份 YAML 重新生成来得干净。5.2 三个值得往下做的方向第一个是本地预览。现在每次调整都要真的写到 Miro 上才能看到效果来回一轮要十几秒。用 SVG 在本地把同一份坐标渲染出来改参数就能即时看到布局变化调试效率会高很多。布局层已经是纯函数加一个渲染器是顺理成章的事。第二个是分类的自动归并。不是用模型猜原因而是在人已经写好子原因之后用简单的文本相似度把重复的条目提示出来。复盘现场经常出现两个人写了同一条原因的情况人工去重很费神。这个功能不需要多聪明能用编辑距离把连接池打满和连接池耗尽标出来就够了。第三个是把布局引擎抽出来做通用化。现在它只会画鱼骨图但里面那套约束推导间距、边界反推基准位置的思路用在树形图、层级图上是通用的。把几何部分抽象成一个独立的库输入是节点和层级约束输出是坐标后面想支持别的图形就只用写新的模板不用重写布局逻辑。我个人在这套东西上的体会是自动化最该介入的地方永远是结构已经清楚、只是搬起来费劲的环节而不是还没想清楚、指望工具帮你想的环节。MiroFish 之所以好用恰恰是因为它对前者做得足够扎实对后者一点都不碰。至于后面会不会加上更多花哨的功能我的态度是先看用的人有没有真的提需求没人提就说明现在这样正好。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价