这是“远程智能体工作流”系列的第四篇。

我的远程 AI 开发环境,第一层解决连接问题。

手机 -> Tailscale -> SSH -> Mac -> tmux -> Codex

有了这条链路,我可以用手机访问 Mac,也能让耗时较长的本地任务持续运行。

第二层解决控制问题。

手机 -> Telegram Bot -> 本地 Codex 运行时 -> 选定的仓库

这样一来,手机就成了控制入口,而不再是一台屏幕很小的终端。

但这两层都没有触及更深的问题。

远程访问让我能连上机器。

远程控制让我能启动任务、查看进展。

而长时间运行的智能体开发还需要记忆。

智能体可能在我离开后继续运行,也可能从中断处恢复、换一个会话接着做,或者由我通过手机操控。既然如此,整个项目就不能只存在于一条聊天线程里。

于是,我给自己定了一条规则:

聊天用来传达指令。
仓库用来保存记忆。

更准确地说,仓库不只是存放代码的地方。它还承载着智能体的记忆和操作约定,并为智能体提供持续的反馈机制。

作为 Agent Harness 的仓库

为什么聊天记录不能充当项目状态 链接到标题

聊天很适合表达意图。

我可以在聊天里说明目标、提出问题、纠正方向,或者做出产品决策。

但要长期保存项目状态,聊天并不可靠。

聊天内容难以比较差异,也不便验证;它依附于某一条线程,后续会话中的智能体很容易误解,甚至根本看不到。想法、日志、猜测、决定和已经过时的背景信息,往往全都混在同一条信息流里。

如果只是一次性的小改动,这样做未尝不可。

可一旦智能体需要长时间工作,问题就会暴露出来。

项目必须能稳定回答以下问题:

  • 这个系统应该做什么?
  • 哪些内容已经实现?
  • 以前在哪里失败过?
  • 什么证据能证明这些行为现在仍然正常?
  • 下一个智能体在修改文件之前,应该先读什么?

这些答案应该和代码放在一起。

让仓库同时承担记忆与 Agent Harness 链接到标题

在我的项目里,智能体需要长期保留的状态通常分散在以下文件和记录中:

  • AGENTS.md
  • SPEC.md
  • feature_list.json
  • progress.md
  • test_plan.md
  • init.sh
  • orchestrator.py
  • git 历史

文件具体叫什么并不重要,重要的是分清职责。

每个文件为智能体保存一种不同的记忆:

AGENTS.md          -> 智能体应该如何做事
SPEC.md            -> 项目应该实现什么
feature_list.json  -> 有哪些工作,以及各自处于什么状态
progress.md        -> 当前已经确认的事实
test_plan.md       -> 用什么证明已完成的工作仍然正常
init.sh            -> 如何检查项目是否健康
orchestrator.py    -> 如何执行有边界的智能体轮次
git 历史           -> 项目随时间究竟发生了哪些变化

这样做不是为了增加仪式感。

而是为了让智能体的工作可以恢复,也可以接续。

不过,仓库不只负责保存记忆。

它也是围绕智能体建立的一套运行环境、约束与反馈机制。

我的做法受到几种相近思路的影响。OpenAI 在 Harness Engineering 相关文章中谈到一种转变:人负责设计环境、明确意图并建立反馈回路,让智能体可靠地完成工作。Anthropic 对长时间运行 Agent Harness 的研究则强调结构化的交接材料、拆分后的工作单元,以及相互分离的生成者与评估者角色。Ralph 循环又补充了一个重要观念:循环失败时,应该改进循环本身。

这些思路和我的实际经验一致。

如果智能体因为缺少背景、工具或验证手段而失败,我不想只是亲手补完眼前的任务。我更希望改进仓库里的工作环境,让下一个智能体具备更好的条件。

这可能意味着增加:

  • 一个脚本
  • 一项测试
  • 一项契约检查
  • 一份人工验证清单
  • 更清楚的 AGENTS.md 指引
  • 更有用的 progress.md 记录
  • 更精确的功能条目

每一次失败,都暴露出这套运行机制缺了什么。

所以,更值得问的不只是:

怎样才能把这个任务做完?

还应该问:

环境里缺少了什么,才让智能体觉得这个任务如此困难?

仓库里分别保存什么 链接到标题

AGENTS.md 保存操作约定。它应该说明先读什么、智能体当前扮演什么角色、可以修改哪些内容、必须如何验证,以及绝不能依赖什么。

其中最重要的规则很简单:

绝不依赖聊天记录。
始终以项目状态为准。

SPEC.md 让需求在对话结束后继续留存,包括目标、范围、约束、预期行为、验收标准,以及明确不做什么。

feature_list.json 把规格说明转化为可以执行的工作。

每个可交付单元都有稳定的 ID 和状态。这样,工作流就可以提出明确要求:

实现 F043。
评估 F043。
只有通过验证后,才能把 F043 标记为完成。

这比下面这种说法精确得多:

继续做我们之前讨论的那件事。

progress.md 是当前的交接记录,用来回答:项目现在究竟进展到哪里?

test_plan.md 保存验证证据。

如果某项功能被标记为完成,仓库里就应该写清楚它通过了哪些验证。

init.sh 提供一项朴素的健康检查。它让人和智能体能用同一套标准回答:

这个仓库目前是否足够健康,可以继续工作?

具体执行哪些命令,项目之间会有差异,但原则不变。

验证把记忆变成规则 链接到标题

智能体完成的工作必须有验证证据。

否则,“完成”就只是一种感觉。

在我的 Obsidian 插件项目中,有些行为可以自动验证:

  • 构建检查
  • 单元测试
  • Harness 测试
  • 契约测试
  • 冒烟检查

还有一些 Obsidian 内部的行为,仍然需要像用户一样进入应用检查:

  • 打开一个临时保管库
  • 启用插件
  • 从命令面板运行一条命令
  • 点击 Next
  • 确认预期的记忆内容已经出现
  • 启用 AI 后,检查 AI 请求的行为

但这并不意味着验证应该一直是非正式的。

人工检查同样属于项目状态。

应该把它写成明确的准备条件、操作步骤和预期结果。

这样才能把模糊的判断变成一份可以检查、可以照着执行,并且将来可能由另一个智能体自动化的记录。

让循环在失败后变得更好 链接到标题

在一些项目中,我会用 orchestrator.py 执行有边界的智能体轮次。

这个脚本可以读取项目状态,选择一项尚未完成的功能,启动编码智能体,再启动评估智能体。只有验证通过后,它才会更新功能状态,并提交已经完成或受阻的工作。

但编排器不是记忆层。

它只是循环本身。

真正的记忆仍然保存在仓库文件和 git 历史里。

循环的关键不在于能否反复运行。

关键在于,每次失败都能沉淀为更好的规则、工具或检查。

如果智能体无法验证界面行为,就增加 Harness 测试、Playwright 检查或人工验证方案,也可以在规格说明中把预期行为写得更清楚。

如果智能体忘记了某条架构规则,就把规则写进 AGENTS.md,将它变成契约测试或 lint 检查,或者直接写进报错信息,让下一个智能体知道该怎么修复。

如果智能体无法判断某项功能是否已经完成,就改进功能条目、验收标准、评估者提示词、测试计划或健康检查。

我希望得到的是这样的循环:

智能体尝试完成工作
  -> 验证发现缺口
  -> 仓库记录缺口
  -> 智能体改进运行环境与约束机制
  -> 下一轮从更好的环境开始

目标不是为了自动化而追求全自动。

真正的目标,是减少那些藏在流程背后、每次都不得不由人临时救场的工作。

智能体缺少某项能力时,我希望这项缺失最终能转化为仓库中明确的项目状态。

运行时状态与项目状态 链接到标题

搭建 Telegram 控制面之后,这两类状态的区别变得更加清楚。

机器人有自己的运行时状态:

  • 当前选定的仓库
  • 任务 ID
  • 日志路径
  • Codex 会话绑定
  • 审批请求
  • 轮询偏移量

这些状态用于操作控制面。

但它们不是项目记忆。

项目记忆包含的是另一类内容:

  • 需求
  • 功能状态
  • 当前进展
  • 验证证据
  • 提交记录

Telegram 机器人不应该决定某项功能是否已经完成。

它不应该掌管产品规格。

也不应该把聊天消息当成长期有效的事实来源。

机器人只负责把工作路由到选定的仓库。

这项工作究竟意味着什么,由仓库决定。

我的角色发生了什么变化 链接到标题

这套工作流也改变了我的角色。

我不再手动推动每一条命令,而是给出方向:

加入这项需求。
先更新规格说明和功能列表。
然后执行一轮经过验证的实现。
最后总结改动内容和尚未消除的风险。

这条指令可以来自笔记本电脑或 SSH,也可以来自手机上的 Telegram。相比使用哪种界面,状态模型更重要。

智能体在开始行动之前,应该能根据仓库自行还原上下文。

我的职责是设定目标、做出产品决策、审查结果,并在需要判断时介入。在这些时刻之间,记忆由仓库负责传递。

模式 链接到标题

我反复采用的分工模式是:

聊天负责表达意图。
仓库文件负责保存记忆。
测试负责判定事实。
Git 负责记录历史。
控制面负责路由。
智能体负责执行工作。

SSH 让我能够访问。

Telegram 让我能通过手机进行控制。

Codex 提供负责干活的智能体。

仓库则让工作有一个能够留存的地方。

工作越趋向异步,下面这条原则就越重要:

在仓库里,不在聊天里。

参考资料 链接到标题