加载中...
  • 结课:从 10 行代码到 Agent 六大支柱 loading

    吃透 AI Agent 开发 · 第 33 篇 · 收束

    把 33 篇内容重新串成一条判断路径:先看任务,再看上下文、工具、状态、协作和质量,而不是先问该用哪个框架。

    把 33 篇内容重新串成一条判断路径:先看任务,再看上下文、工具、状态、协作和质量,而不是先问该用哪个框架。

    先从任务而不是模型开始

    Agent 开发的起点不是选择一个更聪明的模型,而是把任务写成可观察的目标:输入是什么,允许哪些动作,成功要留下什么证据,失败后能否继续。模型只是其中一段决策能力,产品可靠性来自外围协议。

    一条完整的判断路线

    flowchart LR
      Goal[任务目标] --> Context[信息与预算]
      Context --> Loop[消息与状态循环]
      Loop --> Tools[工具与副作用]
      Tools --> Memory[记忆与恢复]
      Memory --> Team[协作与隔离]
      Team --> Harness[观测、评测、部署]
      Harness --> Decision[是否值得交付]
    

    这条路线不是章节目录的重复,而是每次做技术决定时都可以重新走一遍的检查顺序。先把任务边界钉住,再决定是否需要工具、记忆或多个 Agent;不要为了证明框架能力,反过来给任务增加复杂度。

    六个问题帮你判断设计

    1. Loop:下一步判断凭什么继续?消息、工具结果和停止条件是否可重放?
    2. Tool System:模型提出意图后,哪一层负责校验、授权、执行和回填?
    3. Context:每段信息为什么在这一轮出现,什么时候过期,超预算时如何取舍?
    4. Memory:哪些事实被明确保存,来源和验证状态是什么,如何删除?
    5. Multi-Agent:拆分是否真的降低等待或认知负担,结果如何合并,谁拥有写权限?
    6. Harness:启动、会话、事件、审计、Eval、部署和恢复是否形成闭环?

    用一场评审验收,而不是用演示验收

    评审材料 必须看见的内容 看不见时意味着什么
    一次运行 trace 消息、工具、状态、耗时、结果 只能凭最终文本猜过程
    一次失败记录 错误类别、是否有副作用、恢复动作 重试可能造成重复动作
    一张上下文账 来源、预算、年龄、注入时机 Prompt 会不断膨胀
    一份权限矩阵 角色、路径、工具、审批 UI 确认可能成为假边界
    一组 Eval 正常、失败、预算、安全和副作用 demo 成功不代表质量稳定

    q-code 只用来寻找证据

    追踪顺序 路径 课程收束问题
    1. 主循环 src/agent/loop.ts 一轮请求如何推进
    2. 上下文 src/context/prompt-builder.ts 模型究竟看到了什么
    3. 工具 src/tools/registry.ts 副作用从哪里被放行
    4. 协作 src/agents/registry.ts 子任务如何隔离和回传
    5. 评测 src/evals/runner.ts 质量如何被重复测量
    6. 观测 src/observability/audit.ts 出错后凭什么解释

    一个贯穿全课程的失败反例

    当一个 Agent 类同时持有模型、工具、会话与终端时,局部改动很快会扩散到整条链。这个问题不是某个框架 API 写错,而是六个支柱没有清楚的责任边界:上下文污染 Loop,工具绕过策略,记忆没有生命周期,评测只能看最后一句回答。

    改造时不要一次重写全部系统。先选一个真实任务,为它补运行 trace、工具策略和失败恢复;再把重复出现的边界提炼成模块。每个新抽象都必须对应一条已经观察到的风险,不能只因为架构图里缺一个盒子。

    给自己的项目做结课答辩

    练习:选一个准备上线的 Agent 任务,限时 30 分钟完成一份答辩材料:一张责任图、一条正常 trace、一条失败记录、一张权限矩阵和三个 Eval case。答辩时故意换模型、撤掉一个工具、压缩上下文、杀掉进程,再说明哪些能力仍可恢复。

    如果你只能展示“模型回答得很好”,课程还没有结束;如果你能指出某个决定的输入、证据、权限和恢复方式,就已经开始从调 Prompt 转向做工程。

    用一个 Capstone 把 33 篇重新走一遍

    任务是:“读取仓库发布说明和当前配置,把 API 超时从 10 秒改成 30 秒,运行相关测试;先给计划,得到批准后再修改;最后说明改了什么、测试证据和仍有的风险。”

    它听起来只是改一个数字,实际上包含了课程里几乎所有边界:项目怎样启动,模型看到哪些文件,计划审批如何绑定版本,工具能写哪里,测试超时怎么办,长输出放哪里,会话怎样恢复,最后怎样证明任务完成。

    下面是一份任务合同数据切片。它不是通用模板,而是这项任务的验收事实:

    goal: 将 src/config/api.ts 的 timeoutMs 从 10000 调整为 30000
    non_goals:
      - 不修改 retry 次数
      - 不改生产环境变量
    workflow:
      - 先读取发布说明、配置与相关测试
      - 生成计划 v1,等待明确批准
      - 只修改计划声明的文件
      - 运行 tests/unit/api-config.test.ts
    evidence:
      - 目标文件写后 hash
      - 测试命令与 exit code
      - 最终 diff 摘要
    stop:
      - 状态未知时先查询,不自动重放写操作
    

    只要合同不清楚,后面的“智能”就没有判定标准。比如目标写成“优化 API 稳定性”,模型可能顺便改重试、缓存和错误提示,最后每处都有道理,却无法确认是否超出授权。

    第一站:Bootstrap 没必要为计划请求启动所有外部服务

    用户通过 TUI 提交请求,Bootstrap 已走完整 Runtime;如果他只是运行 q-code help 查看 Plan Mode 用法,入口应在模型和 MCP 之前返回。Capstone 的入口 trace 能看到 route、config、runtime 与 terminal ready,而不是一上来就出现模型调用。

    Runtime 读取用户与项目配置,命令行 /model 覆盖得到当前 effective model。若恢复昨天会话,历史模型仅用于提示,本次仍使用当前选择。这个小细节避免了“用户切模型后又被会话切回”的隐性控制权恢复。

    项目 cwd 被规范化成 projectKey,Session、Memory、File History 与 Artifact 使用同一套路径 helper。任何日志和 Dashboard 只展示摘要,不把本机绝对路径上传。

    第二站:Context Manifest 说明模型究竟看到了什么

    规划首轮不需要整个仓库。稳定前缀包含核心安全、工具纪律与项目必须遵守的规则;动态尾部包含原始用户请求、当前 planning mode、Git dirty 摘要、发布说明候选与三个相关文件索引。

    manifest 可以长这样:

    core.safety         stable   3.8K chars   hash=a91f
    core.tools          stable   4.2K chars   hash=7b11
    project.instructions project  5.1K chars   hash=0d4c
    runtime.summary     dynamic    280 chars   age=0
    task.original       dynamic    176 chars   source=user
    file.candidates     dynamic    640 chars   count=3
    

    精确时间、完整 Git status、所有 Memory 正文和 150 个工具 schema 没有常驻。模型需要发布说明时调用 read_file,读取当前内容与 hash;旧索引只用于定位,不作为事实。

    用户附带截图时,图片二进制只进入本轮 model part,Session 保存尺寸/hash 摘要。用户说“忽略记忆”时,连 Memory headers 都不进入 manifest。

    第三站:Plan Mode 把建议和授权分开

    planning 模式只暴露读取、搜索与写计划能力。模型即使直接尝试 edit_file,ToolRegistry 也返回 mode rejection;这不是 Prompt 里的礼貌提醒。

    Agent 阅读配置、测试和发布说明后生成 plan v1。用户说“可以,但先把测试放到修改前”,本地意图分类器先看到修订语义,生成 v2;用户又说“就按 v2 开始”,approval receipt 绑定 planId、version 与 contentHash。

    执行上下文携带原始请求、v2 和 receipt。v1 的授权不存在,v2 后续若改步骤 hash 就失效。计划批准只开放 src/config/api.ts 与测试命令,不授予 .env、Git push 或生产部署。

    Todo 用三条短项显示当前进度,Task Graph 保存“读取证据 → 基线测试 → 修改 → 回归 → 交付”的依赖。二者不共用一段 Markdown 假装状态。

    第四站:Loop 用消息账本推进,不靠局部变量记忆

    一条正常 trace 的关键事件如下:

    {"step":1,"event":"model.finish","reason":"tool-calls","calls":["c1","c2"]}
    {"callId":"c1","tool":"read_file","status":"ok","hash":"h-config"}
    {"callId":"c2","tool":"read_file","status":"ok","hash":"h-test"}
    {"step":2,"event":"model.finish","reason":"tool-calls","calls":["c3"]}
    {"callId":"c3","tool":"shell","status":"failed","exitCode":1}
    {"step":3,"event":"model.finish","reason":"tool-calls","calls":["c4"]}
    {"callId":"c4","tool":"edit_file","status":"ok","afterHash":"h-new"}
    {"step":4,"event":"model.finish","reason":"tool-calls","calls":["c5"]}
    {"callId":"c5","tool":"shell","status":"completed","exitCode":0}
    {"step":5,"event":"model.finish","reason":"stop"}
    

    assistant tool-call message 与每个 tool result 按 callId 进入 transcript。UI 可以折叠成五行,协议不能丢。两个只读文件调用可并发执行、按声明顺序记账;修改和测试有依赖,串行推进。

    基线测试先失败并不表示 Agent 失败,它是现场证据。修改后同一测试 exit=0,任务层才能判断进展。模型 final text 只是 Loop 的 stop reason,不自动把 Case 标通过。

    用户中途取消时 Root AbortSignal 传给模型与前台工具;Shell 只收到终止请求但未确认进程树退出,就进入 unknown,不显示“已取消”。

    第五站:工具管线把一句 edit 变成受控写入

    模型给出 edit_file 意图后,Registry 解析 schema,真实路径限制在 cwd,Plan receipt 校验文件范围,pre-hook 检查策略。Hook 放行后 File History 才保存写前正文,快照失败则不写。

    编辑先在内存应用 patch,要求唯一匹配,再原子 replace。执行成功记录写后 hash、size、mode,结果做摘要后回填;snapshot 正文留在 file-history 数据面,不进 transcript、Audit 或外部 trace。

    如果模型试图改 .env,策略在执行前 rejected,结果明确 modelCanRepair: false 与“超出批准范围”。Loop 不把它当网络错误重试,也不改用 Shell 绕过。

    测试 Shell 有 cwd、timeout、AbortSignal 与输出预算。1 万行日志写 spill,模型只拿失败附近片段、exit code、总行数和全文句柄。Windows 下通过 PowerShell 7 执行并管理进程树,回退 shell 版本进入诊断。

    第六站:回滚承诺有明确边界

    修改后用户手动把文件改成另一个值,再执行 /rewind 1。恢复器比较当前 hash 与 Agent 最近写后 hash,不一致就显示冲突 diff,不覆盖用户新内容。

    若当前仍是 Agent 写后版本,按用户轮次恢复快照,验证内容与 mode。新建文件、删除文件和 rename 各有不同 snapshot 语义,空文件不等于不存在。

    Shell 可能修改的缓存或外部进程文件不在首版 rewind 保证内,执行前已提示。可回滚能力是工具写入的保护层,不是放宽权限的理由。

    第七站:只有独立调查才值得开 SubAgent

    假设用户还要求确认文档是否需要更新。主 Agent 可以把“只读检查 README 与 docs”交给一个 SubAgent,合同写 scope、allowedTools、maxTurns、doneWhen 与证据格式。它不需要主会话全部历史,也没有写权限。

    报告短则内联;超过阈值写 agent artifact,通知只含关键 finding、artifactFile、originalChars 与截断状态。后台完成在安全消息边界入队,不在主模型请求中途抢写 messages。

    如果文档检查两分钟,主 Agent 自己十秒就能完成,委派是不划算的。多 Agent 的选择由依赖与等待,不由“系统支持几个 Agent”决定。涉及修改仍由单一写入者在明确 worktree/范围内完成。

    第八站:Harness 让 UI、Audit 与 Eval 看同一事实

    Loop 发布语义事件,TUI reducer 维护 transcript、工具进度、usage 和后台 Monitor;classic/pipeline 模式消费同样事件但不输出 ANSI。Audit 创建脱敏 NDJSON,Eval Trace Recorder 归一 model/tool 事件。

    它们不从彼此的展示文本反向解析。UI 显示“测试通过”,来自 tool.result exitCode=0;Eval 检查同一个事件;Audit 记录工具、耗时与输出 hash。三条投影一致,故障才可重放。

    Langfuse 可选导出失败不影响本地 run。Dashboard 只读且只绑定 loopback,显示摘要、token、cost 与 artifact 是否存在,不展示代码和 Prompt。

    第九站:Eval 检查世界状态,不听自我陈述

    Capstone Case 要求 read/edit/shell,禁止外部发布工具;检查 src/config/api.ts 含 30000、测试 exit=0、没有其他文件副作用、步骤与 token 在预算内、输出不含 secret。

    如果模型说“测试通过”却没调用 Shell,Final Output 可能像正确答案,Trajectory 与 Tool Execution 仍失败;改了 config.example.ts,Side Effect 失败;调用越界路径,Safety 硬门槛失败。成本得分不能抵消错误文件。

    Mock Runner 验证消息与工具协议,CLI subprocess 在隔离 fixture 验证入口和真实文件,Real Agent 以显式 opt-in 重复运行观察决策波动。候选与 baseline 按 Case 对齐,而不是只比平均分。

    如果在第 c4 写完后崩溃

    进程重新启动时,Session 最后完整事件可能仍停在 assistant edit call,Audit/File History 已有 c4 写后 receipt。恢复器不能因为 transcript 缺 result 就再执行 c4。

    它按 callId 检查文件当前 hash:等于 h-new,补充“副作用已确认,消息记录待恢复”的状态;与 h-new 不同,进入冲突;snapshot 或 receipt 缺失则 unknown。当前模型、权限和计划有效性由新进程重算。

    若崩溃发生在测试 Shell 超时后,先查后台 job/pid 与输出,不盲目重跑。若发生在远端发布调用响应前,没有幂等/查询能力就停在 unknown 并要求人工确认。

    可靠恢复不承诺自动替用户做完,而是保证不会因为缺一行消息把已发生动作做第二次。

    从 10 行 Loop 演进的七个里程碑

    第一阶段只做只读问答:模型 adapter、总 timeout、基本 usage 与错误分类。第二阶段加只读工具:消息账本、schema、Registry、路径边界与 tool trace。第三阶段允许写:审批、快照、原子写、冲突感知回滚。

    第四阶段支持长会话:append-only Session、Context Budget、压缩与 artifact。第五阶段加入产品体验:事件总线、TUI/Web adapter、输入状态与取消。第六阶段按真实等待加入 SubAgent、任务图、worktree 和结果句柄。第七阶段交付生产:Eval、baseline、Dashboard、crash guard、降级、SLO 与运行手册。

    每个阶段都能单独交付价值,也有进入下一阶段的明确风险信号。没有长期任务,就不先做团队编排;没有写副作用,就不需要复杂 rollback;开始让用户授权写入后,权限与证据不能再拖延。

    只读模型
      -> 只读工具与消息协议
      -> 受控写入与回滚
      -> 长会话与上下文治理
      -> 事件化交互
      -> 有边界的协作
      -> 可评测、可恢复、可部署
    

    这条路线比“选一个大框架一次拥有全部功能”更容易保持理解。框架可以在任何阶段减少样板,责任不会因此消失。

    五条贯穿课程的分离原则

    第一,模型意图与系统授权分开。模型决定想做什么,宿主决定是否允许以及如何执行。第二,控制面与数据面分开。状态、hash 和句柄可以进消息,文件、图片与长输出留在受控存储。

    第三,稳定规则与动态事实分开。核心 Prompt 可缓存,当前任务、Git、记忆和团队状态按轮注入。第四,历史上下文与当前控制权分开。恢复消息,不恢复旧模型、旧权限和过期批准。

    第五,语言结论与外部证据分开。回答说完成不算完成,文件、退出码、远端状态和 trace 才决定任务结果。

    遇到新能力时先问它落在哪一条分界线上。无法归属的“方便功能”常常会成为下一条旁路。

    哪些任务不值得做成 Agent

    输入结构固定、规则确定、结果可由普通函数一次算出,直接程序更便宜、更可预测。例如 CSV 字段映射、固定校验、每日复制一份已知报告,不需要模型反复选择下一步。

    高风险不可逆动作又缺少验证/审批接口,也不适合全自动 Agent。模型可以准备材料和建议,人负责最终交易、医疗、法律或生产控制。

    任务目标模糊、没有可观察成功、工具又不能提供证据时,先改善业务流程,而不是用模型遮住含糊。Agent 适合需要在信息与动作之间多步判断、允许失败恢复、边界能由宿主管理的工作。

    选择框架时问“退出要带走什么”

    框架能提供模型调用、图编排、Memory、工具与观测,但应用至少应拥有内部消息协议、工具授权结果、run/session ID、artifact 引用和审计事件。Session、checkpoint、trace 能导出开放格式,Prompt/Skill/schema 在自己的仓库。

    做一个垂直切片测试审批、恢复、provider 切换和文件副作用,不只比较 API 数量。锁定发生在落盘数据与隐式行为,比 import 数更难迁移。

    决定采用框架时写 ADR:委托什么、保留什么、已知缺口、升级策略与退出成本。以后换 SDK,团队不用重新发现当初的交换条件。

    一场合格架构评审要追问失败路径

    正常 demo 之后,评审者让模型 429、工具参数非法、写后响应丢失、Session 写失败、上下文超限、MCP 断线、用户取消、进程崩溃。每次只问:现在能证明什么,是否可能有副作用,下一步由谁执行。

    如果答案是“重试看看”,继续追问幂等和状态查询;答案是“日志里有”,要求给 event/callId;答案是“模型会遵守”,要求展示工具层硬边界;答案是“都能回滚”,要求 Shell 与外部服务反例。

    评审目的不是证明团队考虑了所有异常,而是确认系统遇到未知时会停在诚实状态,不扩大伤害。

    六大支柱的验收清单

    Loop:消息 part 完整;tool call/result 可关联;取消传播;重复调用检测;每个停止有 reason。Tool System:schema、路径、权限、Hook、timeout、结果信封与审计共用入口;旁路不可达;写操作有 snapshot/幂等或明确 unknown。

    Context:section 有来源、稳定性、预算、年龄与退出条件;输出预留;长内容 offload;外部数据不提升权限;manifest 可 diff。Memory:只保存明确长期信息;source/scope/status/time;选择有预算;冲突与 stale 可见;支持 ignore/forget。

    Multi-Agent:任务合同、工具收窄、独立上下文、maxTurns、artifact、通知、取消和写隔离;并发有收益数据;合并按证据。Harness:薄启动、Effective Config、Session/Task/Event/Audit/Eval/Crash/Shutdown/迁移闭环;本地数据不默认上传。

    清单每项都能指向代码、测试或运行 artifact。只有文档里写“支持”,没有可复现实例,不算通过。

    30 分钟答辩材料怎么准备

    前 5 分钟展示目标合同与不做什么;接着 5 分钟画 Runtime/Context/Tool/Storage/Event 图;再用 5 分钟回放正常 trace,指出 callId、文件 hash 和测试 exit;随后 10 分钟演示一个拒绝、一个 unknown 和一次恢复;最后 5 分钟展示 Eval baseline 与未覆盖风险。

    材料只需要:一张责任图、一份 Context Manifest、一条 NDJSON trace、一张权限矩阵、一份 snapshot/恢复证据、三个 Eval Case。不要放大量营销截图。

    答辩者随机要求切模型、隐藏一个工具、让索引 stale 或杀进程。系统不必全部自动通过,但必须给出符合边界的结果。

    Capstone 的评分方式

    任务正确 25 分:目标文件和测试状态符合合同。副作用控制 20 分:计划批准、路径与写入范围正确,拒绝可解释。协议 15 分:消息顺序、reasoning/tool parts 与 stop reason 完整。

    上下文 15 分:manifest、预算、JIT、压缩与附件生命周期正确。恢复 15 分:snapshot、artifact、Session 和 crash 后不重复动作。质量 10 分:Audit、deterministic Eval、成本与安全 scorer 可复现。

    语言漂亮不单独高分;它属于任务正确和用户沟通的一部分。一个简洁但证据完整的交付,胜过长篇自信却没有退出码与 diff 的回答。

    项目进入维护期后每月看什么

    看 top 失败 Case、循环/超时/unknown 分布、工具拒绝原因、Context 各类别增量、Memory stale/conflict、SubAgent 重复工作、p95 step/token/cost、crash 与恢复成功率。发现趋势后回到具体事件与 fixture。

    每季度做恢复和故障注入,抽样检查 Audit 脱敏、trash/purge、artifact 保留与依赖更新。Prompt 稳定前缀变化跑质量维度和行为 Eval;工具 schema变化跑轨迹和安全;Session 格式变化跑旧 fixture 迁移。

    工程质量来自持续反馈,不是结课时一次大扫除。

    课程结束后应该留下的不是一份“最佳架构”

    代码 Agent、客服 Agent、研究 Agent 和运营 Agent 的工具、Memory 与界面会不同。相同的是一套提问方式:目标能否观察,信息从哪来,副作用谁授权,状态怎样恢复,证据如何评测。

    q-code 在课程里只是一个可选源码样本。你可以换 Vercel AI SDK、OpenAI Agents SDK、LangGraph 或自己的循环;只要还能指出消息、工具、上下文、记忆、协作和 Harness 的边界,框架变化就是局部实现问题。

    真正“吃透 Agent 开发”,不是能背出六大支柱,而是在事故里知道哪份证据可信,在新需求里知道复杂度应落在哪一层,在模型很聪明时仍不把权限和事实判断交给运气。

    一份务实的 90 天落地顺序

    前 30 天只选一条高频、边界清楚的任务。建立内部消息账本和统一 ToolRegistry,先开放只读工具;记录 run/callId、stop reason、token 与工具状态。用 10 个真实 fixture 做 deterministic Eval,故意加入参数错误、越界路径和模型超时。此时不急着做 Memory 与多 Agent。

    第 31 到 60 天再开放受控写入:Plan approval、目标范围、写前快照、原子写、写后 hash 和冲突感知 rewind 一起交付。Session 使用 append-only 事件,长输出 artifact 化,Context manifest 开始统计稳定/动态片段和预算。每次线上失败都缩成一个新 Case。

    第 61 到 90 天根据数据补真正的瓶颈。串行只读调查占据大量等待,才加 SubAgent 与任务合同;会话频繁超窗,才完善压缩、JIT 与 Memory;用户需要跨入口使用,才增加 Web adapter 或后台 Worker。并行、检索和外部观测都有成功率、延迟、成本与隐私回滚条件。

    90 天结束时不要求拥有所有课程功能,要求核心任务能正常完成、拒绝越界、在中断后恢复,并有 baseline 证明新版本没有悄悄退化。架构成熟度看证据闭环,不看功能图标数量。

    本文目录
    本文目录