
如何建立持续工作的AGENTS系统
最近越来越多人开始真正使用 Agent。今天听了硅谷创业者LingHua Jin的分享,觉得很有用,记在这里。
这里说的已经不只是让 AI 写一段代码、查一次资料,而是让 Codex、Claude Code 这样的 Agent 在一个项目里持续工作几个小时,甚至更长时间。它要读代码、修改文件、调用工具、跑测试、查看 Git、继续修改,然后再处理下一个任务。
Agent 开始从一次性的 AI 助手,变成一个持续工作的执行系统。
但一个很容易被忽略的问题也随之出现:
Agent 跑得越久,它掌握的“事实”可能反而越不可靠。
原因很简单。
Agent 在上午十点读取了一个文件,十点半这个文件被另一个 Agent 或人修改了。十一点它继续根据十点钟看到的内容做判断。
它没有撒谎。
它只是活在十点钟的世界里。
这可能是 Long Running Agent 最重要,同时也最容易被低估的问题之一。
一、Agent 最大的问题之一,是它看到的世界会过期
传统的大语言模型有一个天然倾向:把已经进入 Context 的东西继续当成事实。
例如一个 Coding Agent 曾经读到:
config.ts MAX_RETRY = 3
后来开发者已经把它改成:
MAX_RETRY = 5
但如果 Agent 没有重新读取文件,它脑子里的世界仍然是:
MAX_RETRY = 3
类似的问题可以发生在几乎所有动态数据上:
代码文件 Git branch 数据库 API 返回值 Calendar 任务状态 账户余额 部署状态 测试结果 服务器日志 另一个 Agent 刚刚完成的工作
因此,一个 Agent “读过某个东西”和“它现在知道这个东西是什么”,其实是两件完全不同的事情。
时间越长,两者之间的距离越大。
这可以叫做 State Staleness,也就是状态陈旧。
真正危险的地方在于,这种错误通常看起来非常合理。
Agent 的推理过程可能完全正确,只是推理的前提已经过期。
于是我们会遇到一种很典型的 Agent Failure:
Reasoning 正确,State 错误,最后的 Action 当然也错。
二、另一个问题,是 Context 会腐烂
长期运行还有第二个问题。
Context 越积越多。
Agent 最开始读了一版需求。
后来产品经理修改需求。
Agent 又读了一版。
开发过程中发现数据库结构不同,又产生新的解释。
测试失败之后 Agent 又形成第三套判断。
几十轮以后,Context 里面可能同时存在:
旧需求 新需求 旧代码 新代码 旧测试结果 新测试结果 已经解决的问题 仍未解决的问题 Agent 曾经做过但后来推翻的推理
这些东西全部存在于同一个上下文窗口。
这就是所谓的 Context Rot。
问题并不单纯是 token 太多。
更麻烦的是,Context 中开始同时存在多个版本的“事实”。
例如:
上午 10:00:
API endpoint = /v1/payment
上午 11:00:
API endpoint = /v2/payment
下午 1:00:
Agent 在处理 payment bug。
此时它的 Context 里两个 endpoint 都存在。
模型必须自己判断哪个仍然有效。
随着历史越来越长,这种判断会越来越困难。
所以 Long Running Agent 的问题,本质上同时存在两个方向:
外面的世界不断变化。
里面的 Context 不断膨胀。
一个越来越旧,一个越来越乱。
三、解决这个问题的第一原则:State 和 Context 必须分开
这是整个方法论最核心的一步。
真正的事实应该存在外部。
Context 只是 Agent 当前完成任务所需要的工作空间。
可以简单理解为:
External State ↓ Retrieve ↓ Working Context ↓ Reason ↓ Action ↓ Verify ↓ External State
代码真正存在 Git repository。
项目状态存在项目管理系统。
账户余额存在数据库。
日历存在 Calendar。
部署状态存在服务器。
任务完成情况存在经过验证的状态记录。
这些才是 Source of Truth。
Agent 的 Context 只是它此刻从这些 Source of Truth 中抽取出来的一份 Working View。
这样一来,我们对 Agent 的要求就发生了一个非常重要的变化:
不要要求 Agent 永远记住正确的信息。
要让 Agent 在需要的时候重新获得正确的信息。
这是两种完全不同的 Agent Architecture。
四、Mutable State 必须在使用时重新读取
接下来就可以建立第二条规则:
Read mutable state at point of use.
凡是可能变化的东西,在真正使用之前重新读取。
例如 Codex 准备修改:
src/auth/session.ts
即使它二十分钟前读过这个文件,只要期间可能发生变化,在修改之前就应该重新读。
准备 commit:
重新运行:
git status git diff
准备判断测试是否通过:
重新跑测试。
准备处理一个 API:
重新获取 API 当前状态。
准备根据 Calendar 安排任务:
重新读取 Calendar。
这个原则甚至可以进一步做成 Freshness Policy。
不同数据有不同的有效期。
例如:
代码文件,修改前重新读取。
Git 状态,commit 前重新读取。
测试结果,最终提交前重新运行。
服务器状态,操作前重新查询。
Calendar,涉及实际安排时重新读取。
金融数据,原则上使用时重新获取。
产品规范,如果版本没有改变,可以允许更长时间缓存。
这样 Agent 开始拥有一个非常重要的能力:
它知道自己的知识会过期。
五、长期任务不要依赖一条无限延长的 Conversation
这对 Codex 和 Claude Code 的实际使用尤其重要。
很多人的自然使用方式是:
开一个 Agent。
然后不断告诉它:
继续。
再改这里。
再看看数据库。
顺便把 payment 修一下。
然后优化 checkout。
再跑测试。
三小时之后,这条 Conversation 已经极其庞大。
一种更稳定的方式是:
一个 Task 对应一个相对干净的 Context。
例如:
Task 1 Investigate payment retry bug Task 2 Implement retry fix Task 3 Review implementation Task 4 Run integration tests
每一个新的 Agent 都从当前真实状态重新建立自己的 Working Context。
它需要继承的,是经过确认的结果。
无需继承前一个 Agent 的全部思考过程。
六、跨 Agent 应该传递 Verified State
这是 Multi Agent 系统里非常关键的一步。
假设 Agent A 调查一个 Bug。
它可能做了大量探索:
读了 20 个文件 执行了 15 个命令 产生了 8 个假设 否定了 7 个 最后确认一个原因
Agent B 没必要重新阅读这一整段过程。
真正应该传过去的是:
Finding: Race condition occurs in refreshToken() Evidence: auth/session.ts:142 integration test #184 Verified: Yes Timestamp: 14:32 Relevant commit: a82f31
也就是说:
跨 Agent 传播 Evidence 和 Verified Conclusion。
历史轨迹可以保存,但不应该默认进入下一轮 Context。
这会极大减少 Context Rot。
七、解决方法:拆分Manager、Executor、Auditor
复杂 Agent Workflow 可以进一步拆成三个角色。
Manager 负责决定下一步做什么。
Executor 负责执行。
Auditor 负责确认世界究竟有没有发生预期变化。
完整流程可以变成:
Current State Manager ↓ Intent Executor ↓ Action Auditor ↓ Re-read State ↓ Verify State Delta Commit ↓ New Current State
这里有一个非常重要的工程原则:
Agent 说“完成了”,不能直接等于完成。
例如 Executor 说:
I fixed the authentication bug.
这只是一个 Claim。
Auditor 应该重新检查:
代码是否真的改变?
测试是否通过?
有没有新的 regression?
Git diff 是否包含意外修改?
Acceptance Criteria 是否满足?
只有这些重新验证之后,系统才能把:
Authentication bug fixed
写入 Verified State。
于是整个 Agent 系统从“相信 Agent 的叙述”,升级成“相信可验证的世界状态”。
八、还要给 Context 建立垃圾回收机制
仅仅不断 Retrieve 新信息也不够。
旧信息还需要被淘汰。
可以借鉴计算机系统里的 Garbage Collection 思路。
Agent Context 里的信息可以分成:
Current
Verified
Temporary
Stale
Archived
例如:
balance = $18,000 timestamp = 10:00
下午重新查询:
balance = $14,200 timestamp = 15:00
旧的 $18,000 就应该进入 Stale 状态。
它仍然可以保留用于审计和历史分析,但不能继续参与当前决策。
这比单纯做 Summary 更重要。
Summary 解决的是 Context 太长的问题。
Invalidation 解决的是 Context 里什么东西已经不能相信的问题。
Long Running Agent 两者都需要。
九、具体到 Codex,AGENTS.md 应该承担什么角色
到了实际 Coding Agent,这套思想就很好用了。
很多人会把越来越多的信息塞进 AGENTS.md:
架构 产品规则 数据库 历史问题 Coding Style API 部署规则 测试说明
最后 AGENTS.md 自己变成一个巨大的 Context。
更好的设计是让它成为地图。
例如:
AGENTS.md
Architecture:
See docs/ARCHITECTURE.md
Product specifications:
See docs/product/
Database:
See docs/generated/schema.md
Active plans:
See docs/exec-plans/active/
Testing:
npm test
npm run lint
Freshness Rules:
Re-read mutable state before use.
Verification Rules:
Never claim completion before tests and diff review.
也就是说:
AGENTS.md 告诉 Codex“去哪里找”。
真正的事实仍然存在 Repo 里面。
这样新开的 Agent 可以迅速重新构建当前世界,而不用依赖之前 Conversation 留下来的记忆。
十、把 Prompt 写成 Issue,而不是一句聊天指令
这套方法还会改变我们给 Codex 下任务的方式。
例如一句:
Improve login.
给 Agent 留下了太大的解释空间。
更好的任务结构是:
Goal
Fix login token refresh race condition.
Relevant files
src/auth/session.ts
src/auth/refresh.ts
Constraints
Do not change public API.
Before implementation
Read latest relevant files.
Check current git status.
Acceptance criteria
Concurrent refresh calls collapse into one request.
Existing tests pass.
Regression test added.
Before completion
Run tests.
Inspect git diff.
Verify acceptance criteria.
这样一个 Prompt 实际上已经承担了一部分 Agent Orchestration 的功能。
它定义:
目标 边界 数据源 执行规则 验证条件
Agent 的自由度依然存在,但自由度被限制在一个可验证的空间里。
十一、不要让 Chat History 成为 Project Management System
这是长期使用 Coding Agent 时特别值得注意的一点。
如果一个项目做到哪里,只存在于:
“昨天我和 Codex 聊到那里”
那么这个项目其实非常脆弱。
真正重要的进度应该落盘。
例如:
docs/
exec-plans/
active/
auth-refactor.md
completed/
payment-migration.md
每个 Active Plan 可以非常简单:
Goal Current Verified State Completed Remaining Known Issues Relevant Commits Last Verified
今天 Agent A 工作到一半。
明天完全可以启动一个新的 Agent B。
Agent B 读取:
AGENTS.md Active Plan 相关代码 Git Status
然后重新构建当前 Context。
Conversation 可以消失。
Project State 仍然存在。
这才是真正适合 Long Running Agent 的结构。
十二、Multi Agent 也不能无限拆
当然,这套方法还有一个容易走向另一个极端的地方。
看到 Context Rot 之后,很容易认为 Agent 越多越好。
其实未必。
每增加一个 Agent,就增加一次:
状态传递 任务解释 结果验证 协调成本
所以 Multi Agent 更适合边界明确的任务。
例如:
Agent A 调查 Bug。
Agent B 修改代码。
Agent C Review。
或者:
Agent A 处理 frontend。
Agent B 处理 backend。
Agent C 做 integration verification。
如果一个任务只有十分钟工作量,把它拆成五个 Agent,系统复杂度可能比任务本身还高。
所以应该遵循一个原则:
Context isolation 要有价值,才值得拆 Agent。
十三、还有一个风险:Summary 本身也可能制造错误
长期 Agent 很依赖 Summary。
但 Summary 是有损压缩。
假设原始记录是:
Payment API occasionally fails when retry > 3, but only under concurrent token refresh.
经过几轮 Summary,可能变成:
Payment API fails when retry > 3.
关键条件消失了。
再经过一轮:
Retry limit should be 3.
一个观察结果逐渐变成了规则。
所以跨 Agent 的 Summary 最好不要只保存自然语言结论。
重要结论应该同时保存:
Claim Evidence Source Timestamp Confidence Verification Status
这样未来 Agent 可以重新验证。
十四、最终目标,是把 Agent 从 Conversation Machine 变成 State Machine
如果沿着这条路继续走,会发现一个更大的变化。
今天我们使用 AI 的基本单位还是 Conversation。
我们习惯问:
“它刚才说了什么?”
“它还记得前面吗?”
“我要不要继续这个 Thread?”
但 Agent 真正成熟之后,这些问题的重要性会越来越低。
更重要的问题会变成:
Before State A Requested Change X Action Y After State B Evidence Tests Diff Logs Verified Yes
这时候 Agent 的基本工作循环就非常清楚:
Retrieve ↓ Think ↓ Act ↓ Verify ↓ Commit ↓ Forget
这里最后的 Forget 非常重要。
一个好的 Agent 系统,不需要把所有经历永远背在身上。
已经失效的信息应该退出工作 Context。
已经验证的结果应该进入 State。
需要审计的历史进入 Archive。
新的任务重新 Retrieve 当前事实。
于是 Agent 可以运行很久,同时保持 Context 相对干净。
十五、如果今天开始使用 Codex或Claude,请遵守这七条规则
第一,一个 Task 尽量对应一个清楚的问题。
第二,长项目主动开启 Fresh Agent,不迷信超长 Conversation。
第三,AGENTS.md 做地图,Repo 做事实库。
第四,任何 Mutable State 在真正使用之前重新读取。
第五,Agent 完成任务之后必须重新验证。
第六,跨 Agent 传递 Verified State 和 Evidence,减少传递完整轨迹。
第七,Project Progress 写进 Repo,不依赖 Chat History 保存。
这七条背后的逻辑其实只有一个:
Agent 的长期可靠性,并不来自它拥有多长的记忆,而来自它有没有能力持续重新确认现实。
未来 Agent 真正困难的问题,很可能不再只是让模型变得更聪明。
模型已经足够聪明之后,新的瓶颈会变成:
它现在看到的数据是真的吗?
这个事实是什么时候读取的?
期间有没有发生变化?
这个结论是谁验证的?
这一步真的执行成功了吗?
旧的信息什么时候应该失效?
下一个 Agent 应该继承什么?
这些问题听起来已经不像传统 Prompt Engineering。
它们越来越接近数据库、操作系统、分布式系统和软件工程长期面对的问题。
这也意味着 Agent Engineering 正在进入下一个阶段。
我们开始需要设计的,已经不只是“怎样让 AI 想得更好”。
我们还需要设计:
它怎样读取世界,怎样更新世界,怎样确认世界已经改变,以及怎样及时忘掉那个已经不存在的世界。
对于一个真正能够工作几小时、几天,未来甚至几个月的 Agent 来说,最重要的能力之一,可以浓缩成一句话:
不要试图让 Agent 记住一切。
让它随时知道,什么仍然是真的。
再次感谢LingHua Jin, 一并感谢@FounderCoHo @yichenlicoho