在 Sharkly,人们很少手动修复常规 bug。大多数 bug 会直接交给 AI 智能体处理。

Sharkly是我们团队正在打造的产品,我们用它来运行自己的产品和工程工作流。

我们专门负责 bug 修复和小型工程任务的智能体叫做Sherlock。它可以讨论问题、检查相关代码仓库、追踪实现、在真实开发环境中运行测试,并在浏览器中验证 UI 变更。当视觉行为发生变化时,它还会截图作为证据。

根据我们的内部数据,在最近 15 天里,Sherlock 完成了所有 Sharkly 任务的 13%。在最近分配给它的 100 个任务中,有 90 个成功完成。

Sherlock 运行在一台共享的开发 Computer 上,使用 oh-my-pi Runtime 和 GPT 6 Astra 模型。我们每天都会分配给它几十个 bug 和小改进。更重要的是,我们信任它能在没有持续监督的情况下处理这些任务。

这种信任并非来自假设模型永远正确,而是来自把智能体放进一个严格、可检查的工作流中。

一次真实的 bug 修复全过程

来看 Sharkly 最近的一个问题。清除构建缓存对话框中的临时目录列表可能会超出可用空间,但该列表无法滚动。

Oliver 发现了这个问题,并把它发到了我们的内部 Slack 频道。随后 Emma 提到了 Sharkly Task Assistant,并让它把 Oliver 的报告转成一个结构化任务。

Oliver's bug report and Emma's request to the Sharkly Task Assistant

Emma 把新任务分配给了 Sherlock。Sherlock 花了 14 分钟复现问题、追踪实现并定位根因。

Sherlock's diagnosis after investigating the task

随后 Sherlock 提出了三个需要人工输入才能开始开发的实现决策。

Emma 选择了推荐选项,并发送了明确的批准令牌:

1A 2A 3A [START DEVELOPMENT]

直到这时,Sherlock 才开始修改产品代码。

又过了 37 分钟,它报告实现和验证均已完成,并提供了三项具体内容供审查:

  1. 一个代码拉取请求
  2. 一个预览 URL
  3. 修复后行为的截图

The visual evidence attached to Sherlock's testing package

Emma 审查了变更及其证据。确认修复无误后,Emma 发送了第二个批准令牌:

[SUBMIT FOR TESTING]

该 token 授权 Sherlock 将变更合并到开发分支,并将任务移入自动化测试工作流。

Emma 只在两个节点做决策:

  1. 批准提议的解决方案,并允许修改产品代码。
  2. 批准已完成的变更,并允许其进入测试流水线。

这两个关口之间的一切都由系统自主处理。无需就每个文件、命令或实现细节进行冗长的来回沟通。

这样一来,一名工程师就能监督数十个小修复,而不会成为瓶颈。

Sherlock 背后的平台

Sharkly是面向 AI 编码智能体的团队级项目管理和执行层。人们在 Tasks 和 Sprints 中规划工作,将其分配给 Agents 或 Crews,并在他们自己的 Computers 上安全地并行运行这些智能体。

Sharkly 将 Agent 与其 Runtime 分离。Agent 承载其身份、指令、Skills 和操作规则。Runtime 是在选定 Computer 上执行工作的编码工具,例如 Claude Code、Codex 或其他兼容工具。这种分离让团队可以选择自己的执行工具,同时将任务上下文、审批、运行历史和证据保留在一个共享工作区中。

Sharkly 还将 Agents 连接到团队的操作流程。自动化可以在任务状态变化时启动工作,而明确的审批点则将高影响操作置于人类控制之下。Agents 负责研究、执行、测试和报告。人们负责设定方向、授予权限并验收结果。

对于 Sherlock,Sharkly 提供任务、对话、执行历史和审批关口。共享的开发 Computer 提供代码仓库、依赖、浏览器和其他工具,而配置好的 Runtime 执行 Sherlock 的指令。

Sherlock 的 10 步工作流

Sherlock 遵循一个包含十个阶段的固定工作流。它不能仅仅因为某个解决方案看起来显而易见就跳过步骤。

1. 初始化任务

Sherlock 检查任务 ID、标题、描述、标签和优先级。它确认自己运行在 Sharkly 提供的任务工作区内,并选择正确的基线分支。

紧急任务可能会使用main。高优先级任务可能会使用staging. 其他任务通常遵循仓库的默认集成分支。

2. 诊断问题

诊断阶段仅限于复现问题、收集证据和确定根本原因。在此阶段,Sherlock 不得修改产品代码,即使修复看起来很简单。

其发现必须写入diagnosis.md。后续的每个提案都必须能够追溯到该文件中记录的证据。

3. 起草解决方案

在确定根本原因后,Sherlock 会创建一个建议的解决方案。它说明应该更改什么、哪些行为必须保持不变,以及哪些未解决的决策需要人工输入。

如果更改涉及面向用户的文案,Sherlock 会先阅读仓库的术语和本地化规则。然后它会准备几个候选措辞,而不是在没有上下文的情况下凭空编写文本。

提案存储在idea.md中,并随着反馈的到来而不断完善。

4. 等待开发批准

这是第一个人工关卡。

Sherlock 可能已经理解了问题并拥有完整的解决方案,但它仍然没有修改产品代码的权限。它必须等待确切的[START DEVELOPMENT]令牌。

诸如“继续”、“看起来不错”或“开始吧”之类的消息不会解锁下一阶段。在授权至关重要时,自然语言太容易被误解。

5. 探索与实现

获得批准后,Sherlock 会执行一次聚焦的实现探索。它读取相关文件、追踪调用点、检查现有测试,并了解仓库既有的约定。

探索会产出findings.md和plan.md。然后 Sherlock 实现已批准的解决方案。任何必要的偏离计划之处都会记录在plan-delta.md中。

6. 运行洁净室审查

完成代码并不意味着任务已经完成。

Sherlock 首先与权威基线分支同步。然后它执行洁净室审查,暂时搁置实现期间使用的假设,像独立审查者那样评估更改。

审查会提出如下问题:

  • 更改是否解决了实际的根因?
  • 所有受影响的调用点是否都已迁移?
  • 测试是否覆盖了有意义的风险?
  • 是否还残留任何过时的代码路径?
  • 实现是否引入了无关的更改?

7. 补丁阻塞问题

如果审查发现阻塞性问题,Sherlock 会进入补丁阶段。它修复该问题,再次与基线同步,并重复洁净室审查。

该循环最多运行六次。如果第六次审查后仍存在阻塞性问题,自动化工作流将停止并请求人工介入。

该限制可防止智能体在编写、审查并反复修复自身代码的循环中无限运行。

8. 验证真实行为

审查通过后,Sherlock 会运行仓库声明的测试套件。它还会验证实际发生变更的行为。

测试命令成功只是一项证据。UI 变更需要来自真实界面的证明。后端变更需要来自 API、日志、查询或反映运行时行为的已执行命令的证据。

验证标准很简单:证明功能可用,而不仅仅是命令以状态码零退出。

9. 等待测试批准

Sherlock 会组装一个测试包,其中包含实现摘要、代码差异、自动化决策、测试结果、预览链接以及视觉或运行时证据。

然后它停在第二个人工关口,等待[SUBMIT FOR TESTING]。

该令牌授权 Sherlock 创建最终提交、推送代码、执行目标分支操作,并更新 Sharkly 任务状态。

10. 提交变更以供测试

最后,Sherlock 将变更合并到开发分支,并将任务移至准备测试。随后测试智能体可以继续该工作流。

核心设计:状态机,而非人格

Sherlock 的指令不是那种“你是一名资深软件工程师”的传统角色提示词。

它们定义了一个经过工程设计的单智能体缺陷解决状态机。其结果更接近可执行的开发流程,而非聊天机器人的人格。

Sherlock 可以在任务的大部分时间里独立工作,但它不能决定何时开始修改产品代码。它也不能决定何时推送或合并结果。这些状态转换属于人类。

每个阶段都记录在一个持久化的state.json文件。Sherlock 必须在采取行动前读取并验证该文件,这使得当前状态显式且可恢复。

该设计包含一些重要部分。

1. 狭窄的角色与清晰的边界

Sherlock 只被允许处理当前分配给它的 Sharkly 任务。

它不得搜索无关工作、创建替代任务、将任务委派给其他智能体,或执行属于后续阶段的操作。

这些约束减少了歧义。智能体无需决定自己应该做哪类工作,只需在允许的工作流内完成当前任务。

2. 明确的权限顺序

当信息冲突时,Sherlock 遵循既定的优先级顺序:

  1. 智能体指令与明确的人类决策
  2. 仓库级指导,例如AGENTS.md、文档和分支策略
  3. 任务描述与已批准的提案
  4. 源代码与观察到的运行时证据

重要原则是:仓库事实优先于通用模型知识。

模型可能记得某个框架通常如何配置,但这并不能证明当前仓库遵循同样的约定。如果仓库声明了自己的测试命令或分支策略,Sherlock 必须遵循它们。

当文档、代码和运行时行为不一致时,Sherlock 必须暴露该冲突,不能悄悄选择最方便的解释。

3. 持久化状态

初始化之后,state.json是工作流当前阶段的唯一事实来源。

该文件记录:

  • 任务信息
  • 当前活动阶段
  • 待处理的人工审批
  • 已批准的决策
  • 阶段产物
  • 验证状态
  • 转换历史

每次有效的状态转换都会记录前一阶段、下一阶段、转换原因以及相关文件和哈希值。需要审批的转换还会存储审批令牌以及被批准提案的确切版本。

这种持久化使 Sherlock 能够在会话中断后恢复。新的运行不会从聊天历史中猜测发生了什么,而是从磁盘重新加载状态和阶段产物。

如果state.json缺失、损坏或内部不一致,Sherlock 就会停止并报告状态错误。它不能手动推进阶段,也不能根据一条非正式的聊天消息重建缺失的审批。

4. 两道人工审批关卡

两个审批令牌控制两类不同的高影响操作。

[START DEVELOPMENT]授权对产品代码的写入。它确认有人已审查并接受了所提出的解决方案。

[SUBMIT FOR TESTING]授权提交、推送、目标分支操作和任务状态变更。它确认有人已审查了实现及其证据。

这将风险分为两部分:

  1. 更改代码的风险
  2. 将该变更送入团队共享交付流程的风险

Sherlock 可以在两道关卡之间完成大量自主工作,但它不能自行跨越任一边界。

5. 必需的阶段产物

每个阶段都必须产出一个具体的产物:

  • diagnosis.md说明问题是如何复现的,识别根本原因,并记录支撑证据。
  • idea.md描述所提出的解决方案以及用户批准的选择。
  • findings.md记录实现探索过程。
  • plan.md说明将更改什么以及如何验证该更改。
  • plan-delta.md记录与已批准计划的偏差。
  • review-report.md包含洁净室审查结果。
  • ship-summary.md包含最终交付和验证信息。

当必需的产物缺失时,Sherlock 不能声称某个阶段已完成。一条写着“诊断完成”的聊天消息本身不具备任何效力。

只有当状态记录和必需产物同时存在时,状态转换才有效。

6. 阶段专属技能

Sherlock 除主指令外还使用七个专门技能:

  • lattice-diagnose
  • lattice-idea
  • lattice-explore
  • lattice-implement
  • lattice-review
  • lattice-verify
  • lattice-deliver

主指令集控制阶段、权限和转换规则。每个技能定义在特定阶段内应如何执行工作。

在进入某个阶段之前,Sherlock 必须完整阅读相关技能,并在状态历史中记录该操作。产出的产物也必须证明该技能得到了遵循。仅记录“已阅读技能”而未产出合规工作是不够的。

这种分离使中央状态机保持专注,同时允许每个阶段拥有详细、可测试的操作规则。

7. 基于证据的验证与交付

验证阶段刻意采用了很高的证据标准。

测试通过并不能证明完整功能在真实环境中可用。用户可见的变更必须在实际界面中检查。后端变更必须有 API 响应、日志、查询或命令输出作为支撑。

提交前,Sherlock 会将其工作分支推送到专用的远程审查分支,并提供对比链接。审查者可以同时检查代码差异和相关的验证证据。

8. 一致的响应格式

每条 Sherlock 响应都必须包含:

  • 实际完成的工作
  • 当前阶段
  • 需要人工执行的下一步操作

每条响应还以状态页脚结尾,其中包含任务、当前阶段、待处理的审批令牌、审查次数和产物路径。

这使得含糊的进度更新难以掩盖真实的工作流状态。

我们的经验总结

构建 Sherlock 需要若干架构选择和大量迭代。有几条经验带来的影响远超我们的预期。

单智能体还是多智能体?

Sharkly 通过 Crews 支持多智能体协作,因此我们认真考虑过将工作流拆分到多个专用智能体上。

我们最终选择了单智能体状态机。

对于常规的缺陷修复,当今的模型已足够强大,可以在一个受控上下文中完成诊断、实现、审查和验证。多智能体设计会增加协调开销、延迟和成本,却无法持续改善结果。

当流程包含多个明确分离的专业领域、且各步骤之间耦合较松时,Crews 更有用。缺陷修复往往恰恰相反。诊断、实现和验证都高度依赖相同的仓库上下文和证据。

我们的实用规则是:从一个智能体加强大的 Skills 开始。只有当工作确实具有可分离的职责时,我们才引入多个智能体。

选择运行时

关键的架构选择是让 Sherlock 与执行其工作的 Runtime 保持分离。我们针对相同的真实任务测试了 Claude Code、Codex CLI、OpenCode 和 oh-my-pi。

对于 Sherlock 的无人值守状态机工作流,我们选择了 oh-my-pi,因为它能直接控制指令和工具、会话可回放且可恢复,并且能在共享开发 Computer 上实现轻量级并发。

这是一个针对特定工作负载的决定,而非通用排名。交互式编码助手或不同的 Agent 可能更适合其他 Runtime,而 Sharkly 则保持周围的任务、审批和证据工作流一致。

迭代指令过程中的经验教训

Sherlock 的工作流并非一蹴而就。在它变得可靠之前,我们发现了许多失败模式。

使用已经过验证的 Skill

智能体的可靠性受限于其操作流程的质量。

未经测试的指令在每次运行时可能产生不同的路径,这使得故障难以诊断。我们将每个阶段转化为经过反复演练的 Skill。Sherlock 的职责就是精确遵循这些 Skill。

新的 Skill 首先在低风险任务上运行。只有当其行为变得可预测后,才会进入生产工作流。

将人工审批关卡保持在最低限度

早期版本的 Sherlock 有七个需要人工审批的关卡。智能体每隔几步就停下来,反复打断监督它的人。

我们逐步将工作流缩减为两个关卡:修改产品代码的权限,以及提交结果进行测试的权限。

更少的关卡让剩余的关卡更有意义。现在人们只需在少数信息丰富的时刻做出决策,而不是在整个过程中对零散的工作片段进行审批。

绝不将随意语言视为授权

诸如“继续”、“我没意见”和“做吧”之类的短语不能推进状态机。

自然语言的含义会随上下文变化,智能体可能将随意的确认误认为批准。显式令牌使授权明确无误。它们还创建了可审计的记录,说明谁批准了哪个版本的提案。

将截图放在决策发生的地方

对于 UI 变更,截图是强制性的。它们还必须直接出现在对话中,而不仅仅是作为需要手动查找的文件路径。

用于做出决策的证据应当在决策点可见。当审查者能够立即比较修复前后的行为时,审批会变得更快、更可靠。

藏在几个目录之外的证据,实际上等同于缺失的证据。

信任来自流程

我们为什么要信任一个 AI 智能体来修复 bug?

Sherlock 的答案并不是这个模型格外聪明。答案是这套流程格外严格。

状态机控制智能体可以走到哪里。必需的产物保留它做过什么的证据。两道人工关卡保护影响最大的操作。洁净室审查和真实环境验证提供质量底线。每个阶段都可检查、可中断、可恢复。

这套工作流也改变了工程师的角色。人们不再需要盯着智能体运行的每一条命令。他们专注于两个高价值的决策:所提出的方案是否正确,以及已完成的改动是否已准备好测试。

人的注意力从监督活动转向行使判断。这正是让一名工程师能够同时监管数十个小任务变得可行的原因。

修复 bug 只是开始。我们正在把同样的原则,包括狭窄边界、持久状态、人工审批关卡和证据链,应用到功能开发、测试和发布工作流中。

随着这些 AI 原生工程实践逐渐成熟,我们会分享更多。