这是“远程智能体工作流”系列的第四篇。
我的远程 AI 开发环境,第一层解决连接问题。
手机 -> Tailscale -> SSH -> Mac -> tmux -> Codex
有了这条链路,我可以用手机访问 Mac,也能让耗时较长的本地任务持续运行。
第二层解决控制问题。
手机 -> Telegram Bot -> 本地 Codex 运行时 -> 选定的仓库
这样一来,手机就成了控制入口,而不再是一台屏幕很小的终端。
但这两层都没有触及更深的问题。
远程访问让我能连上机器。
远程控制让我能启动任务、查看进展。
而长时间运行的智能体开发还需要记忆。
智能体可能在我离开后继续运行,也可能从中断处恢复、换一个会话接着做,或者由我通过手机操控。既然如此,整个项目就不能只存在于一条聊天线程里。
于是,我给自己定了一条规则:
聊天用来传达指令。
仓库用来保存记忆。
更准确地说,仓库不只是存放代码的地方。它还承载着智能体的记忆和操作约定,并为智能体提供持续的反馈机制。
为什么聊天记录不能充当项目状态 链接到标题
聊天很适合表达意图。
我可以在聊天里说明目标、提出问题、纠正方向,或者做出产品决策。
但要长期保存项目状态,聊天并不可靠。
聊天内容难以比较差异,也不便验证;它依附于某一条线程,后续会话中的智能体很容易误解,甚至根本看不到。想法、日志、猜测、决定和已经过时的背景信息,往往全都混在同一条信息流里。
如果只是一次性的小改动,这样做未尝不可。
可一旦智能体需要长时间工作,问题就会暴露出来。
项目必须能稳定回答以下问题:
- 这个系统应该做什么?
- 哪些内容已经实现?
- 以前在哪里失败过?
- 什么证据能证明这些行为现在仍然正常?
- 下一个智能体在修改文件之前,应该先读什么?
这些答案应该和代码放在一起。
让仓库同时承担记忆与 Agent Harness 链接到标题
在我的项目里,智能体需要长期保留的状态通常分散在以下文件和记录中:
AGENTS.mdSPEC.mdfeature_list.jsonprogress.mdtest_plan.mdinit.shorchestrator.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 提供负责干活的智能体。
仓库则让工作有一个能够留存的地方。
工作越趋向异步,下面这条原则就越重要:
在仓库里,不在聊天里。