把 Agent 从能跑到可靠关键不在模型神准而在状态、上下文和协作工程。原文链接AI 小老六聊 Agent 时很多讨论容易落到模型能力上模型会不会推理代码写得准不准能不能理解复杂需求。这些当然重要但真正把 Agent 做到可用以后会发现麻烦往往不在“想不明白”而在“执行过程中会散”。模型可以给出不错的判断却不会天然知道自己刚才读过哪些文件它可以规划十步任务却可能在第五步被一段日志带跑它能调用工具但每一次工具返回的内容都会塞进上下文越做越重。再往后如果要让多个 Agent 一起做事还会冒出通信、审批、退出、状态同步这些更琐碎的问题。所以我更愿意把 Agent 看成一个状态工程问题。模型负责判断系统负责让判断落地并且让落地过程可追踪、可恢复、可约束。下面不是按功能清单罗列而是按一个 Agent 从“能跑起来”到“能长期干活”的过程把几个关键设计放在一起讲。图可靠 Agent 的核心不是单次推理而是可追踪、可恢复的状态工程。Tool-Calling Runtime把推理闭环接入真实执行环境最原始的模型只是一个文本系统。你把代码贴进去它能分析你把日志贴进去它能猜根因。但它不会自己打开src/auth.js不会自己跑npm test也不会知道当前目录下到底有什么。Agent Loop 解决的是这个入口问题模型决定要不要调用工具Harness 执行工具再把结果交回给模型。这个循环一直持续到模型不再请求工具。图Agent Loop 将模型判断、工具执行和结果回写串成闭环一个极简实现大概长这样messages[{role:user,content:query}]whileTrue:responseclient.messages.create(modelMODEL,systemSYSTEM,messagesmessages,toolsTOOLS,max_tokens8000,)messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:returnresponse.content results[]forblockinresponse.content:ifblock.type!tool_use:continueoutputrun_tool(nameblock.name,inputblock.input)results.append({type:tool_result,tool_use_id:block.id,content:output,})messages.append({role:user,content:results})这里有个分工很重要模型只做选择Harness 负责执行。工具层必须有自己的安全边界不能指望模型每次都谨慎。比如一个 Bash 工具至少要拦掉明显危险的命令限制路径逃逸避免shellTrue带来的注入风险。importosimportsubprocessdefrun_bash_simple_secure(command:str)-str:dangerous[rm,sudo,shutdown,reboot,,|,,;]ifany(partindangerousforpartincommand.split()):returnError: dangerous command blocked.if../incommandorcommand.startswith(/):returnError: path must stay inside workspace.try:argscommand.split()resultsubprocess.run(args,cwdos.getcwd(),capture_outputTrue,textTrue,timeout10,shellFalse,)text(result.stdoutresult.stderr).strip()returntext[:50000]iftextelse(no output)exceptFileNotFoundError:returnfError: command {args[0]} not found.exceptExceptionase:returnfError:{e}工具也别做成一个大杂烩。读文件、写文件、编辑文件、跑命令最好各管各的。Loop 里只需要一个分发表把模型说出的工具名映射到真实处理函数。TOOL_HANDLERS{read_file:lambda**kw:run_read(kw[path],kw.get(limit)),write_file:lambda**kw:run_write(kw[path],kw[content]),edit_file:lambda**kw:run_edit(kw[path],kw[old_text],kw[new_text]),}到这一步Agent 算是有了眼睛和手。但能动手不代表能把长任务做稳。Progress Externalization用外部状态锁住长任务进度短任务里模型的表现通常不错。让它读一个文件、解释一个报错、改一个小函数来回几轮就能收尾。麻烦出现在多步任务里。比如一次重构要做十件事先读模块再设计接口再改实现再补测试再跑验证。前几步通常没问题后面就开始漂。它可能重复读已经读过的文件也可能跳过前置设计直接改代码更常见的是被某个局部问题吸住忘了用户真正要的是什么。这不是“模型笨”而是任务状态只放在上下文里太脆弱。上下文会被日志、代码、报错和解释不断稀释。TodoManager 的作用很朴素把进度写到外面。不要让模型靠记忆维护计划。设计点为什么需要pending / in_progress / completed让任务进度显式化同一时间只允许一个in_progress避免模型同时推进多件互相冲突的事最多 20 个任务防止拆任务本身变成噪声可渲染文本让模型每次都能快速读懂当前局面一个简化版实现如下fromtypingimportDict,ListclassTodoManager:MAX_ITEMS20VALID_STATUSES(pending,in_progress,completed)MARKERS{pending:[ ],in_progress:[],completed:[x],}def__init__(self)-None:self.items:List[Dict[str,str]][]defupdate(self,items:List[Dict[str,str]])-str:iflen(items)self.MAX_ITEMS:raiseValueError(fMax{self.MAX_ITEMS}todos allowed)in_progress0checked:List[Dict[str,str]][]fori,iteminenumerate(items):textstr(item.get(text,)).strip()statusstr(item.get(status,pending)).lower()item_idstr(item.get(id,i1))ifnottext:raiseValueError(fItem{item_id}: text required)ifstatusnotinself.VALID_STATUSES:raiseValueError(fItem{item_id}: invalid status {status})ifstatusin_progress:in_progress1checked.append({id:item_id,text:text,status:status})ifin_progress1:raiseValueError(Only one task can be in_progress at a time)self.itemscheckedreturnself.render()defrender(self)-str:ifnotself.items:returnNo todos.lines[]foriteminself.items:lines.append(f{self.MARKERS[item[status]]}#{item[id]}:{item[text]})donesum(1foriteminself.itemsifitem[status]completed)lines.append(f\n({done}/{len(self.items)}completed))return\n.join(lines)光有 todo 还不够。模型有时会连续几轮不看清单。Nag Reminder 就是一个轻量提醒器如果模型太久没碰 todo就在下一次工具结果里塞一行提醒。它不强行改模型动作只是把注意力拽回来一下。NAG_THRESHOLD3NAG_TEXTreminderUpdate your todos./reminderTODO_TOOLtododefagent_loop(messages):rounds_since_todo0whileTrue:responseclient.messages.create(modelMODEL,systemSYSTEM,messagesmessages,toolsTOOLS,max_tokens8000,)messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:returntool_uses[bforbinresponse.contentifb.typetool_use]results[]forblockintool_uses:outputdispatch_tool(block.name,block.input)results.append({type:tool_result,tool_use_id:block.id,content:output,})used_todoany(block.nameTODO_TOOLforblockintool_uses)rounds_since_todo0ifused_todoelserounds_since_todo1ifrounds_since_todoNAG_THRESHOLD:results.append({type:text,text:NAG_TEXT})rounds_since_todo0messages.append({role:user,content:results})DAG Task System从待办清单升级到依赖调度TodoManager 解决的是“别忘事”。但复杂工程不只是很多事项并列放着它们之间有先后关系。接口没定调用方就不该改数据结构没定存储层就不该写死基础能力没完成集成测试跑了也只是浪费时间。扁平清单没法表达这些约束。这时任务系统要从 list 变成 DAG。图复杂工程任务从扁平清单升级为依赖图一种直接的做法是把每个任务存成独立 JSON 文件放在.tasks/目录里。.tasks/ ├── task_1.json ├── task_2.json └── task_3.json任务字段不用复杂但要足够表达依赖。字段含义id任务唯一标识title任务标题description更完整的任务说明statuspending / in_progress / completedblockedBy当前任务依赖哪些前置任务createdAt创建时间completedAt完成时间Agent 每次只需要回答三个问题。问题判断方式现在能做什么statuspending且blockedBy[]什么还不能做blockedBy非空完成后影响谁找出依赖当前任务的后续任务任务完成时系统把它的 ID 从其他任务的blockedBy里删掉。如果某个任务的blockedBy变成空数组它就解锁了。图前置任务完成后系统自动释放可执行任务这个变化不显眼但很关键。它把“模型凭感觉挑下一步”改成了“系统告诉模型哪些步骤已经具备前置条件”。Context Compaction控制 Token 膨胀和信息半衰期图把长历史压缩成摘要让主上下文只保留当前真正需要的信息。Agent Loop 有个副作用工具结果会持续进入上下文。读文件、跑命令、查日志、搜索代码每一次都可能带来几千 tokens。几轮下来主上下文很快就变成垃圾堆。这时会出现两个问题。第一模型注意力被旧信息稀释第二真正有用的远端信息可能被截断或召回不到。粗略看一次工程任务的上下文消耗操作数量估算消耗读取源码文件30 个约 60,000 tokens执行 Shell 命令20 条约 30,000 tokens工具调用往返15 次约 20,000 tokens模型输出多轮持续生成约 15,000 tokens这就是为什么需要 compact。压缩不是删除历史而是把历史从“模型当前必须看见的内容”里移出去。层次做什么类比micro_compact把几轮前的工具结果换成占位符页面置换auto_compact上下文超阈值后保存全文并摘要替换Swapcompact工具模型主动触发压缩手动 GCmicro_compact最简单。超过 3 轮的旧工具结果保留调用痕迹删掉大块输出。[Previous: used read_file] [Previous: used bash]压缩收益很大一次 2000 tokens 的文件读取结果可能只剩十来个 tokens。模型仍知道自己“读过文件”但不会每轮都背着全文走。auto_compact再激进一点。上下文超阈值时系统先把完整 transcript 写入磁盘再让模型生成任务摘要用摘要替代活跃历史。.transcripts/ ├── 2026-05-14_task-refactor-auth-module.md ├── 2026-05-14_task-fix-api-endpoint.md └── 2026-05-13_task-setup-ci-pipeline.md摘要应该保留用户目标、关键决策、当前状态和剩余事项。日志全文、试错过程、已经过期的文件内容都不该继续占着窗口。还有一种情况适合让模型自己调用compact任务进入新阶段或者它预判接下来要读很多文件。这时主动清理比等系统阈值触发更稳。图compact 将完整历史归档并把摘要留在活跃上下文Subagent Isolation隔离高噪声探索链路并不是所有工作都适合主 Agent 亲自做。比如排查一个测试失败可能要读很多文件、跑很多命令、试几个假设。主流程真正需要的是最后的结论问题在哪证据是什么建议怎么修。Subagent 就是为这种场景准备的。它拿到一个子任务在独立上下文里完成探索然后只把结论交还给主 Agent。中间那些日志、搜索结果、错误尝试不进入主上下文。图子 Agent 在独立上下文中探索只把结论交还主流程实现上有几个约束值得保留子 Agent 不继承主对话子 Agent 没有再派生子 Agent 的工具最多运行固定轮次返回值只取最终文本。CHILD_TOOLS[bash,read_file,write_file,edit_file]PARENT_TOOLSCHILD_TOOLS[task]defrun_subagent(prompt:str)-str:sub_messages[{role:user,content:prompt}]for_inrange(30):responseclient.messages.create(modelMODEL,systemSUBAGENT_SYSTEM,messagessub_messages,toolsCHILD_TOOLS,max_tokens8000,)sub_messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:breakresults[]forblockinresponse.content:ifblock.type!tool_use:continuehandlerTOOL_HANDLERS.get(block.name)outputhandler(**block.input)ifhandlerelsefunknown tool:{block.name}results.append({type:tool_result,tool_use_id:block.id,content:str(output)[:50000],})sub_messages.append({role:user,content:results})return.join(block.textforblockinresponse.contentifhasattr(block,text))or(no summary)这个设计不是为了“多一个智能体显得高级”只是为了保护主上下文。主 Agent 像负责人子 Agent 像临时调查员。负责人不需要看完整侦查录像只需要拿到结论和证据。Skill Injection让领域工作流按需进入上下文Agent 往往要遵守很多工作流Git 提交规范、单测规范、Code Review 清单、接口设计约定、文档写作模板。把这些都写进系统提示看起来省事实际很浪费。假设一个 Skill 完整内容约 2000 tokens10 个 Skill 全量注入就是 20000 tokens。而一次任务通常只会用到 1 到 2 个。更好的方式是两层加载。第一层只放索引让模型知道有哪些 Skill。Available Skills: - git-conventions: Git 提交和分支命名规范 - test-patterns: 单元测试编写规范与最佳实践 - code-review: 代码审查清单与安全检查项 - api-design: RESTful API 设计规范 - docs-writing: 技术文档写作模板第二层按需加载完整内容。图常驻索引与按需加载正文的两层 Skill 机制这和工具类似索引常驻正文临时进入。上下文里只放当前有用的东西。Background Execution把阻塞式工具调用改成异步任务很多命令本身不需要模型参与。npm install是下载依赖pytest是跑测试docker build是按 Dockerfile 构建镜像。它们可能跑很久但中间不需要 Agent 做判断。命令典型耗时主要工作npm install30-120 秒下载和安装依赖pytest60-300 秒执行测试套件docker build60-600 秒构建镜像层如果 Agent Loop 阻塞等待模型的推理时间就被浪费了。后台任务管理器的做法是慢命令交给子进程跑Agent 立刻继续做别的事命令结束后结果进入队列在下一次合适的调用边界注入。图慢命令进入后台执行结果通过队列回到主循环组件其实很少。组件作用Agent 主循环保持推理串行避免并发推理混乱守护线程或进程盯着子进程结束子进程真正执行慢命令通知队列让执行和消费解耦这个机制让 Agent 不必把时间花在“等”上。它可以一边跑测试一边读下一组文件或者把已经确定的代码先写掉。Persistent Agent Teams从临时子任务到稳定协作单元图稳定身份、消息通道和审批协议让多个 Agent 像团队一样协同。Subagent 适合临时探索但它不是团队。真正的大任务需要长期协作前端、后端、测试、文档各自有角色每个人有自己的上下文和工具任务可以异步分发和交付。这时需要两个东西稳定身份和异步通信。一种够简单的通信方式是 JSONL 邮箱。每个 Agent 有一个 inbox 文件发消息就是追加一行 JSON收消息就是读取并清空自己的 inbox。.messages/ ├── frontend-agent.inbox.jsonl ├── backend-agent.inbox.jsonl └── test-agent.inbox.jsonl消息格式保持结构化。{from:leader,to:frontend-agent,timestamp:2026-05-14T10:00:00Z,type:task,content:请实现登录页面的表单验证}{from:backend-agent,to:frontend-agent,timestamp:2026-05-14T10:05:00Z,type:info,content:API 接口已就绪端点是 POST /api/login参数为 {email, password}}JSONL 不花哨但在这个场景里很合适。消息频率不高文件足够快内容可读方便审计进程挂了消息也还在磁盘上。方案好处问题数据库有事务太重要维护 schema内存共享快进程崩溃就丢JSONL 文件简单、持久、人能看性能不是最优团队成员由 TeammateManager 管。它维护一个config.json记录每个成员的角色、技能和系统提示。{teammates:[{name:frontend-agent,role:前端开发,skills:[react,typescript,css],system_prompt:你是一个专注于前端开发的工程师...},{name:backend-agent,role:后端开发,skills:[python,fastapi,postgresql],system_prompt:你是一个专注于后端开发的工程师...},{name:test-agent,role:质量保障,skills:[pytest,e2e-testing,ci-cd],system_prompt:你是一个专注于测试的 QA 工程师...}]}spawn()一个队友后它有自己的上下文窗口、工具集和执行循环。主 Agent 不需要盯着它每一步只要通过消息收发任务和结果。Team Protocols用请求-响应协议约束多 Agent 协作多 Agent 之间能发消息以后很快会遇到新的问题。比如领导 Agent 想停掉一个队友如果直接杀线程可能文件写到一半、数据库连接没关、任务状态没更新。再比如某个队友想删除旧认证模块重写这种高风险动作不应该没有确认就执行。还有消息格式如果一会儿自然语言、一会儿 JSON、一会儿只发个ok接收方也会浪费推理能力猜意思。所以团队需要协议。最小可用协议其实很简单request-response 加一个三态状态机。A 发起请求B 批准或拒绝。状态只有三个pending、approved、rejected。图多 Agent 协作中的请求审批三态协议它比 2PC、Raft、Pub/Sub 都轻得多也更贴近 Agent 之间的真实交互。协调方式复杂度适配度两阶段提交高需要 prepare 和 commit对 Agent 协作太重Raft/Paxos极高需要投票和日志复制不是这个问题Pub/Sub中等需要 topic 管理一对一请求用不上广播Request-Response 三态 FSM低两条消息就够适合请求和审批后续要加新协议也不难。只要定义新的 request type状态机可以复用。shutdown_request# pending - approved/rejectedplan_request# pending - approved/rejectedresource_request# pending - approved/rejectedmerge_request# pending - approved/rejected如果一次交互需要补充信息再加一个needs_info状态即可。[pending] - [needs_info] - [pending] - [approved/rejected]工程化收束可靠 Agent 本质上是状态系统Agent 工程没有想象中那么玄。很多设计看起来像“智能体架构”拆开以后其实都是老问题状态怎么存任务怎么排日志怎么处理慢任务怎么异步团队怎么通信高风险动作怎么审批。模型确实带来了新的执行方式但工程上的账不会消失。你不把状态显式化状态就会散在上下文里你不压缩历史历史就会拖慢每一轮推理你不定义协议多 Agent 协作就会变成一群模型互相发散。能用的 Agent不是因为模型每一步都神准而是因为系统允许它犯小错、能把它拉回任务、能记录它做过什么也能在必要时让它停下来等一个确认。
Agent Runtime 九个关键设计:状态外化、上下文压缩与多智能体协同
把 Agent 从能跑到可靠关键不在模型神准而在状态、上下文和协作工程。原文链接AI 小老六聊 Agent 时很多讨论容易落到模型能力上模型会不会推理代码写得准不准能不能理解复杂需求。这些当然重要但真正把 Agent 做到可用以后会发现麻烦往往不在“想不明白”而在“执行过程中会散”。模型可以给出不错的判断却不会天然知道自己刚才读过哪些文件它可以规划十步任务却可能在第五步被一段日志带跑它能调用工具但每一次工具返回的内容都会塞进上下文越做越重。再往后如果要让多个 Agent 一起做事还会冒出通信、审批、退出、状态同步这些更琐碎的问题。所以我更愿意把 Agent 看成一个状态工程问题。模型负责判断系统负责让判断落地并且让落地过程可追踪、可恢复、可约束。下面不是按功能清单罗列而是按一个 Agent 从“能跑起来”到“能长期干活”的过程把几个关键设计放在一起讲。图可靠 Agent 的核心不是单次推理而是可追踪、可恢复的状态工程。Tool-Calling Runtime把推理闭环接入真实执行环境最原始的模型只是一个文本系统。你把代码贴进去它能分析你把日志贴进去它能猜根因。但它不会自己打开src/auth.js不会自己跑npm test也不会知道当前目录下到底有什么。Agent Loop 解决的是这个入口问题模型决定要不要调用工具Harness 执行工具再把结果交回给模型。这个循环一直持续到模型不再请求工具。图Agent Loop 将模型判断、工具执行和结果回写串成闭环一个极简实现大概长这样messages[{role:user,content:query}]whileTrue:responseclient.messages.create(modelMODEL,systemSYSTEM,messagesmessages,toolsTOOLS,max_tokens8000,)messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:returnresponse.content results[]forblockinresponse.content:ifblock.type!tool_use:continueoutputrun_tool(nameblock.name,inputblock.input)results.append({type:tool_result,tool_use_id:block.id,content:output,})messages.append({role:user,content:results})这里有个分工很重要模型只做选择Harness 负责执行。工具层必须有自己的安全边界不能指望模型每次都谨慎。比如一个 Bash 工具至少要拦掉明显危险的命令限制路径逃逸避免shellTrue带来的注入风险。importosimportsubprocessdefrun_bash_simple_secure(command:str)-str:dangerous[rm,sudo,shutdown,reboot,,|,,;]ifany(partindangerousforpartincommand.split()):returnError: dangerous command blocked.if../incommandorcommand.startswith(/):returnError: path must stay inside workspace.try:argscommand.split()resultsubprocess.run(args,cwdos.getcwd(),capture_outputTrue,textTrue,timeout10,shellFalse,)text(result.stdoutresult.stderr).strip()returntext[:50000]iftextelse(no output)exceptFileNotFoundError:returnfError: command {args[0]} not found.exceptExceptionase:returnfError:{e}工具也别做成一个大杂烩。读文件、写文件、编辑文件、跑命令最好各管各的。Loop 里只需要一个分发表把模型说出的工具名映射到真实处理函数。TOOL_HANDLERS{read_file:lambda**kw:run_read(kw[path],kw.get(limit)),write_file:lambda**kw:run_write(kw[path],kw[content]),edit_file:lambda**kw:run_edit(kw[path],kw[old_text],kw[new_text]),}到这一步Agent 算是有了眼睛和手。但能动手不代表能把长任务做稳。Progress Externalization用外部状态锁住长任务进度短任务里模型的表现通常不错。让它读一个文件、解释一个报错、改一个小函数来回几轮就能收尾。麻烦出现在多步任务里。比如一次重构要做十件事先读模块再设计接口再改实现再补测试再跑验证。前几步通常没问题后面就开始漂。它可能重复读已经读过的文件也可能跳过前置设计直接改代码更常见的是被某个局部问题吸住忘了用户真正要的是什么。这不是“模型笨”而是任务状态只放在上下文里太脆弱。上下文会被日志、代码、报错和解释不断稀释。TodoManager 的作用很朴素把进度写到外面。不要让模型靠记忆维护计划。设计点为什么需要pending / in_progress / completed让任务进度显式化同一时间只允许一个in_progress避免模型同时推进多件互相冲突的事最多 20 个任务防止拆任务本身变成噪声可渲染文本让模型每次都能快速读懂当前局面一个简化版实现如下fromtypingimportDict,ListclassTodoManager:MAX_ITEMS20VALID_STATUSES(pending,in_progress,completed)MARKERS{pending:[ ],in_progress:[],completed:[x],}def__init__(self)-None:self.items:List[Dict[str,str]][]defupdate(self,items:List[Dict[str,str]])-str:iflen(items)self.MAX_ITEMS:raiseValueError(fMax{self.MAX_ITEMS}todos allowed)in_progress0checked:List[Dict[str,str]][]fori,iteminenumerate(items):textstr(item.get(text,)).strip()statusstr(item.get(status,pending)).lower()item_idstr(item.get(id,i1))ifnottext:raiseValueError(fItem{item_id}: text required)ifstatusnotinself.VALID_STATUSES:raiseValueError(fItem{item_id}: invalid status {status})ifstatusin_progress:in_progress1checked.append({id:item_id,text:text,status:status})ifin_progress1:raiseValueError(Only one task can be in_progress at a time)self.itemscheckedreturnself.render()defrender(self)-str:ifnotself.items:returnNo todos.lines[]foriteminself.items:lines.append(f{self.MARKERS[item[status]]}#{item[id]}:{item[text]})donesum(1foriteminself.itemsifitem[status]completed)lines.append(f\n({done}/{len(self.items)}completed))return\n.join(lines)光有 todo 还不够。模型有时会连续几轮不看清单。Nag Reminder 就是一个轻量提醒器如果模型太久没碰 todo就在下一次工具结果里塞一行提醒。它不强行改模型动作只是把注意力拽回来一下。NAG_THRESHOLD3NAG_TEXTreminderUpdate your todos./reminderTODO_TOOLtododefagent_loop(messages):rounds_since_todo0whileTrue:responseclient.messages.create(modelMODEL,systemSYSTEM,messagesmessages,toolsTOOLS,max_tokens8000,)messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:returntool_uses[bforbinresponse.contentifb.typetool_use]results[]forblockintool_uses:outputdispatch_tool(block.name,block.input)results.append({type:tool_result,tool_use_id:block.id,content:output,})used_todoany(block.nameTODO_TOOLforblockintool_uses)rounds_since_todo0ifused_todoelserounds_since_todo1ifrounds_since_todoNAG_THRESHOLD:results.append({type:text,text:NAG_TEXT})rounds_since_todo0messages.append({role:user,content:results})DAG Task System从待办清单升级到依赖调度TodoManager 解决的是“别忘事”。但复杂工程不只是很多事项并列放着它们之间有先后关系。接口没定调用方就不该改数据结构没定存储层就不该写死基础能力没完成集成测试跑了也只是浪费时间。扁平清单没法表达这些约束。这时任务系统要从 list 变成 DAG。图复杂工程任务从扁平清单升级为依赖图一种直接的做法是把每个任务存成独立 JSON 文件放在.tasks/目录里。.tasks/ ├── task_1.json ├── task_2.json └── task_3.json任务字段不用复杂但要足够表达依赖。字段含义id任务唯一标识title任务标题description更完整的任务说明statuspending / in_progress / completedblockedBy当前任务依赖哪些前置任务createdAt创建时间completedAt完成时间Agent 每次只需要回答三个问题。问题判断方式现在能做什么statuspending且blockedBy[]什么还不能做blockedBy非空完成后影响谁找出依赖当前任务的后续任务任务完成时系统把它的 ID 从其他任务的blockedBy里删掉。如果某个任务的blockedBy变成空数组它就解锁了。图前置任务完成后系统自动释放可执行任务这个变化不显眼但很关键。它把“模型凭感觉挑下一步”改成了“系统告诉模型哪些步骤已经具备前置条件”。Context Compaction控制 Token 膨胀和信息半衰期图把长历史压缩成摘要让主上下文只保留当前真正需要的信息。Agent Loop 有个副作用工具结果会持续进入上下文。读文件、跑命令、查日志、搜索代码每一次都可能带来几千 tokens。几轮下来主上下文很快就变成垃圾堆。这时会出现两个问题。第一模型注意力被旧信息稀释第二真正有用的远端信息可能被截断或召回不到。粗略看一次工程任务的上下文消耗操作数量估算消耗读取源码文件30 个约 60,000 tokens执行 Shell 命令20 条约 30,000 tokens工具调用往返15 次约 20,000 tokens模型输出多轮持续生成约 15,000 tokens这就是为什么需要 compact。压缩不是删除历史而是把历史从“模型当前必须看见的内容”里移出去。层次做什么类比micro_compact把几轮前的工具结果换成占位符页面置换auto_compact上下文超阈值后保存全文并摘要替换Swapcompact工具模型主动触发压缩手动 GCmicro_compact最简单。超过 3 轮的旧工具结果保留调用痕迹删掉大块输出。[Previous: used read_file] [Previous: used bash]压缩收益很大一次 2000 tokens 的文件读取结果可能只剩十来个 tokens。模型仍知道自己“读过文件”但不会每轮都背着全文走。auto_compact再激进一点。上下文超阈值时系统先把完整 transcript 写入磁盘再让模型生成任务摘要用摘要替代活跃历史。.transcripts/ ├── 2026-05-14_task-refactor-auth-module.md ├── 2026-05-14_task-fix-api-endpoint.md └── 2026-05-13_task-setup-ci-pipeline.md摘要应该保留用户目标、关键决策、当前状态和剩余事项。日志全文、试错过程、已经过期的文件内容都不该继续占着窗口。还有一种情况适合让模型自己调用compact任务进入新阶段或者它预判接下来要读很多文件。这时主动清理比等系统阈值触发更稳。图compact 将完整历史归档并把摘要留在活跃上下文Subagent Isolation隔离高噪声探索链路并不是所有工作都适合主 Agent 亲自做。比如排查一个测试失败可能要读很多文件、跑很多命令、试几个假设。主流程真正需要的是最后的结论问题在哪证据是什么建议怎么修。Subagent 就是为这种场景准备的。它拿到一个子任务在独立上下文里完成探索然后只把结论交还给主 Agent。中间那些日志、搜索结果、错误尝试不进入主上下文。图子 Agent 在独立上下文中探索只把结论交还主流程实现上有几个约束值得保留子 Agent 不继承主对话子 Agent 没有再派生子 Agent 的工具最多运行固定轮次返回值只取最终文本。CHILD_TOOLS[bash,read_file,write_file,edit_file]PARENT_TOOLSCHILD_TOOLS[task]defrun_subagent(prompt:str)-str:sub_messages[{role:user,content:prompt}]for_inrange(30):responseclient.messages.create(modelMODEL,systemSUBAGENT_SYSTEM,messagessub_messages,toolsCHILD_TOOLS,max_tokens8000,)sub_messages.append({role:assistant,content:response.content})ifresponse.stop_reason!tool_use:breakresults[]forblockinresponse.content:ifblock.type!tool_use:continuehandlerTOOL_HANDLERS.get(block.name)outputhandler(**block.input)ifhandlerelsefunknown tool:{block.name}results.append({type:tool_result,tool_use_id:block.id,content:str(output)[:50000],})sub_messages.append({role:user,content:results})return.join(block.textforblockinresponse.contentifhasattr(block,text))or(no summary)这个设计不是为了“多一个智能体显得高级”只是为了保护主上下文。主 Agent 像负责人子 Agent 像临时调查员。负责人不需要看完整侦查录像只需要拿到结论和证据。Skill Injection让领域工作流按需进入上下文Agent 往往要遵守很多工作流Git 提交规范、单测规范、Code Review 清单、接口设计约定、文档写作模板。把这些都写进系统提示看起来省事实际很浪费。假设一个 Skill 完整内容约 2000 tokens10 个 Skill 全量注入就是 20000 tokens。而一次任务通常只会用到 1 到 2 个。更好的方式是两层加载。第一层只放索引让模型知道有哪些 Skill。Available Skills: - git-conventions: Git 提交和分支命名规范 - test-patterns: 单元测试编写规范与最佳实践 - code-review: 代码审查清单与安全检查项 - api-design: RESTful API 设计规范 - docs-writing: 技术文档写作模板第二层按需加载完整内容。图常驻索引与按需加载正文的两层 Skill 机制这和工具类似索引常驻正文临时进入。上下文里只放当前有用的东西。Background Execution把阻塞式工具调用改成异步任务很多命令本身不需要模型参与。npm install是下载依赖pytest是跑测试docker build是按 Dockerfile 构建镜像。它们可能跑很久但中间不需要 Agent 做判断。命令典型耗时主要工作npm install30-120 秒下载和安装依赖pytest60-300 秒执行测试套件docker build60-600 秒构建镜像层如果 Agent Loop 阻塞等待模型的推理时间就被浪费了。后台任务管理器的做法是慢命令交给子进程跑Agent 立刻继续做别的事命令结束后结果进入队列在下一次合适的调用边界注入。图慢命令进入后台执行结果通过队列回到主循环组件其实很少。组件作用Agent 主循环保持推理串行避免并发推理混乱守护线程或进程盯着子进程结束子进程真正执行慢命令通知队列让执行和消费解耦这个机制让 Agent 不必把时间花在“等”上。它可以一边跑测试一边读下一组文件或者把已经确定的代码先写掉。Persistent Agent Teams从临时子任务到稳定协作单元图稳定身份、消息通道和审批协议让多个 Agent 像团队一样协同。Subagent 适合临时探索但它不是团队。真正的大任务需要长期协作前端、后端、测试、文档各自有角色每个人有自己的上下文和工具任务可以异步分发和交付。这时需要两个东西稳定身份和异步通信。一种够简单的通信方式是 JSONL 邮箱。每个 Agent 有一个 inbox 文件发消息就是追加一行 JSON收消息就是读取并清空自己的 inbox。.messages/ ├── frontend-agent.inbox.jsonl ├── backend-agent.inbox.jsonl └── test-agent.inbox.jsonl消息格式保持结构化。{from:leader,to:frontend-agent,timestamp:2026-05-14T10:00:00Z,type:task,content:请实现登录页面的表单验证}{from:backend-agent,to:frontend-agent,timestamp:2026-05-14T10:05:00Z,type:info,content:API 接口已就绪端点是 POST /api/login参数为 {email, password}}JSONL 不花哨但在这个场景里很合适。消息频率不高文件足够快内容可读方便审计进程挂了消息也还在磁盘上。方案好处问题数据库有事务太重要维护 schema内存共享快进程崩溃就丢JSONL 文件简单、持久、人能看性能不是最优团队成员由 TeammateManager 管。它维护一个config.json记录每个成员的角色、技能和系统提示。{teammates:[{name:frontend-agent,role:前端开发,skills:[react,typescript,css],system_prompt:你是一个专注于前端开发的工程师...},{name:backend-agent,role:后端开发,skills:[python,fastapi,postgresql],system_prompt:你是一个专注于后端开发的工程师...},{name:test-agent,role:质量保障,skills:[pytest,e2e-testing,ci-cd],system_prompt:你是一个专注于测试的 QA 工程师...}]}spawn()一个队友后它有自己的上下文窗口、工具集和执行循环。主 Agent 不需要盯着它每一步只要通过消息收发任务和结果。Team Protocols用请求-响应协议约束多 Agent 协作多 Agent 之间能发消息以后很快会遇到新的问题。比如领导 Agent 想停掉一个队友如果直接杀线程可能文件写到一半、数据库连接没关、任务状态没更新。再比如某个队友想删除旧认证模块重写这种高风险动作不应该没有确认就执行。还有消息格式如果一会儿自然语言、一会儿 JSON、一会儿只发个ok接收方也会浪费推理能力猜意思。所以团队需要协议。最小可用协议其实很简单request-response 加一个三态状态机。A 发起请求B 批准或拒绝。状态只有三个pending、approved、rejected。图多 Agent 协作中的请求审批三态协议它比 2PC、Raft、Pub/Sub 都轻得多也更贴近 Agent 之间的真实交互。协调方式复杂度适配度两阶段提交高需要 prepare 和 commit对 Agent 协作太重Raft/Paxos极高需要投票和日志复制不是这个问题Pub/Sub中等需要 topic 管理一对一请求用不上广播Request-Response 三态 FSM低两条消息就够适合请求和审批后续要加新协议也不难。只要定义新的 request type状态机可以复用。shutdown_request# pending - approved/rejectedplan_request# pending - approved/rejectedresource_request# pending - approved/rejectedmerge_request# pending - approved/rejected如果一次交互需要补充信息再加一个needs_info状态即可。[pending] - [needs_info] - [pending] - [approved/rejected]工程化收束可靠 Agent 本质上是状态系统Agent 工程没有想象中那么玄。很多设计看起来像“智能体架构”拆开以后其实都是老问题状态怎么存任务怎么排日志怎么处理慢任务怎么异步团队怎么通信高风险动作怎么审批。模型确实带来了新的执行方式但工程上的账不会消失。你不把状态显式化状态就会散在上下文里你不压缩历史历史就会拖慢每一轮推理你不定义协议多 Agent 协作就会变成一群模型互相发散。能用的 Agent不是因为模型每一步都神准而是因为系统允许它犯小错、能把它拉回任务、能记录它做过什么也能在必要时让它停下来等一个确认。