Appearance
飞书 CLI Agent 工具链
基于自研 lark-cli 搭的飞书 Agent(Daniel / MergeBot):在群聊里用自然语言发起 Git merge、推 Gerrit Review、跑 HCrash 崩溃分析,共 17 类指令。核心不是"远程执行 Git 命令",而是每一步都有权限、路由、状态机、日志和回滚边界。
指令覆盖17 类
权限模型三层
失败处理自动回滚
日报整理30min → 5min
背景
日常研发里有两类高频重复劳动。一类是 merge:人工切分支、清理仓库、执行 merge、处理冲突、推 Gerrit、再挨个通知评审人,大仓合并前还必须把本地状态清干净,漏一步就是事故。另一类是崩溃分析:拿到日志包、解密、定位案发窗口、翻源码行号,全靠手工。
这个 Agent 的目标是把这两类流程收敛到飞书对话里。它同时还是个人知识生产的资料入口——群消息、文档、附件经审计脱敏后沉淀为日报草稿和案例素材,日报整理从 30 分钟级降到约 5 分钟。
问题
| 问题 | 表现 | 后果 |
|---|---|---|
| merge 步骤多 | 切分支、clean、merge、解冲突、push、通知评审 | 漏步骤直接污染远端 |
| 高风险操作无边界 | 谁都能在群里喊机器人跑 Git | 权限和分支必须硬控 |
| 机器人串台 | 多群多机器人,@错人就乱响应 | 错误上下文里执行危险操作 |
| 失败留烂摊子 | merge 失败本地 target 已偏移 | 下一次合并从脏状态开始 |
| 日志分析重 | 解密、定位窗口、查符号全手工 | 一次分析大半天 |
方案
整体分六个域:飞书事件接入、消息路由、权限与策略、任务队列、Git merge 引擎、卡片回调。FastAPI 只绑定 loopback(127.0.0.1),事件入口快速返回,真正的合并在常驻任务队列里串行执行。
mermaid
flowchart TD
A["飞书群聊 / 单聊"] --> B["lark-cli 事件消费者"]
B --> C["FastAPI loopback 服务<br>仅绑定 127.0.0.1"]
C --> D["消息路由<br>群聊必须 @机器人"]
D --> E["意图三层安全路由"]
E --> F["权限与分支策略校验"]
F --> G["任务队列<br>merge 串行执行"]
G --> H["MergeEngine<br>专用 clone 仓库"]
H --> I["Gerrit refs/for/target"]
E --> J["飞书交互卡片<br>状态 / 冲突 / 审批"]
J --> C关键实现
消息路由:先解决串台
单聊直接处理;群聊必须显式 @daniel 或 @MergeBot;消息以其他用户的 @ 开头直接静默忽略——防止机器人在多群里被 @别人 的消息误触发。未 @ 的普通消息不回复、不占用任何队列。
意图三层安全路由
merge 是高风险动作,MVP 阶段的原则是高风险操作先用确定性模型兜住,LLM 只做意图归一:
- 第一层:确定性解析。正则和规则直接识别中英文 merge 语法、查询、取消、授权等指令,不经过任何模型。
- 第二层:模糊筛选 + LLM 归一。无法识别时,先从完整指令目录按关键词模糊筛选候选,再让模型只返回结构化意图(意图、命令表单、槽位、缺项、置信度)。模型不直接生成可执行命令——可执行命令由本地代码拼装,并必须重新通过确定性解析器。
- 第三层:用户确认。高置信度生成确认卡,用户点击或发确认码后才执行;中置信度只追问缺项;低置信度直接回能力导航。确认码绑定当前用户和会话,一次有效。
LLM 在任何一层都不能绕过权限、分支白名单和 Git 安全校验。
三层权限模型
超级管理员 > merge owner > 普通 merge 用户。超级管理员管理 owner;owner 管理 merge 权限、分支策略和流程配置;普通用户只能发起授权范围内的合并。无权限用户发起 merge 时提示去申请,不会自动代申请、更不会轰炸 owner。一个细节:取消某人的普通 merge 权限不会取消其 owner 身份——两个维度独立。
Merge 引擎与失败自动回滚
这是整条链路里最关键的部分。所有 Git 操作只在专用 clone 仓库进行,绝不碰开发工作区。每次 merge 前,source 和 target 都走同一套准备流程:checkout → clean → pull --rebase → reset 到远端 → 再 clean,保证从干净的远端状态开始。失败时的边界:
- clean / pull / reset 失败:job 进 failed,记录失败命令摘要和日志路径。
- merge 冲突:记录冲突文件和双方提交者,然后
merge --abort回到干净状态;冲突卡片支持分页查看、逐文件选择 source/target,所有冲突都有选择才恢复执行,非文本冲突标记需人工处理,不提供危险自动选择。 - verify 失败:不 push,把 target 重置回远端状态。
- push Gerrit 失败:同样重置 target,不留本地偏移。
推送固定走 git push --no-thin origin HEAD:refs/for/target,即只进 Gerrit Review,不直接推目标分支。同一 repo + target 有活跃 job 时,新请求返回已有 job 状态,不创建并发合并。
HCrash 综合分析
材料集齐(案发时间、主仓库分支、问题描述、ZIP 日志、Skill)后,固定各仓库到 40 位 commit SHA,进单槽 FIFO 队列。实测一次分析的扫描窗口覆盖 101,779 行日志。模型输入只含 Bug 信息、HCrash 确定性结论、Skill、选中日志的覆盖范围和有限原始证据——不整包投喂日志,也绝不读取源码。首轮结论一产出就释放重型槽位,每张分析单另有独立 5 分钟追问窗口,追问不占队列。
Gerrit 后续处理
merge 成功拿到 Review 链接后,同一张卡片进入 +2 / submit 后续处理区。每个角色可配多个候选人,任一人点击即结算该角色,卡片显示完成者和时间;非候选人点击被拒绝或静默。关键设计:merge job 此时已是终态,Gerrit 后续流程不再占用 target 锁。
指标
| 指标 | 数据 |
|---|---|
| 指令覆盖 | 17 类(HCrash 分析、merge、权限、分支策略、卡片风格、日志解密等) |
| 单次分析扫描窗口 | 101,779 行日志(案发前后对称扫描) |
| 崩溃分析结论验证 | 样例单经证据链复核输出"已证明",置信度 100% |
| 日报整理 | 30 分钟级 → 约 5 分钟 |
| merge 推送方式 | 只进 Gerrit Review(refs/for/),不直推目标分支 |
复盘
- 这类 Agent 的工程难点不在"调 LLM",而在权限、状态机、回滚边界。自然语言只是入口,真正值钱的是每一条指令背后的确定性校验和失败兜底。
- LLM 的输出必须落成结构化意图,再过一遍确定性解析器。让模型直接生成 Git 命令等于把生产仓库交给概率。
- 串台和越权比"没功能"更可怕。静默忽略、权限提示、确认卡这些"不做的事",设计成本不低于功能本身。
- 一个 job 一张卡片。状态实时更新在同一张卡片上,冲突选择、取消、+2/submit 全在卡片里完成,聊天上下文就是操作界面,不需要另开系统。
- 资料入口侧的红线不变:飞书资料先过审计脱敏,再进公开站点,user 身份的灵活性必须配更严格的发布边界。