我们遇到了什么问题
如果你做过 AI 辅助分析工具,大概率踩过这两个坑:
坑一:AI 结论忽好忽坏。任务流程一长,AI 就开始"飘"------用词精准、语气自信,但结论经不起推敲。更坑的是,你问它"你确定吗",它会更加笃定地告诉你"是的,完全正确"。
坑二:Token 越烧越多。上下文越堆越厚,每次启动都要把历史上下文全部重新喂给模型,Token 消耗线性增长,但分析质量不升反降。
这正是我们团队在开发 CarPlay bug 分析工具时遇到的真实困境。AI 在多项目、长链路的 bug 分析中,分析报告质量严重不稳定;引入多 Agent 架构后,Token 消耗和运行时间又大幅增加。
这两个问题,促使我们去找一套系统性的解决方案。
一、Harness Engineering:给 AI 套上"缰绳"
问题:不确定性是 Agent 的原罪
大模型天生有随机性。在单轮对话里,这点随机性是创意的来源;但在长链路任务里,它是灾难的根源。
一个典型的失控场景:你让 Agent 完成一个分 10 步的开发任务。前 7 步一切正常,第 8 步 Agent 稍微偏了一下,第 9 步继续往偏了的方向走,第 10 步你收到了一个看起来完整、实则驴唇不对马嘴的结果。你还没意识到问题,因为 Agent 写了一段很有说服力的总结。
当 AI 开始连续执行时,如何监督并及时纠正,成了核心工程问题。
方案:Agent = Model + Harness
2026 年 2 月,Martin Fowler 团队(作者 Birgitta Böckeler)提出了 Harness Engineering 这个概念:
ini
Agent = Model(模型)+ Harness(约束框架)
Harness 是 Agent 里除了模型之外的一切东西------提示词、工具定义、规则约束、上下文管理、校验机制、反馈循环,全部统称为 Harness。
这个定义乍看平淡,但背后有个重要的思维转变:要提升 Agent 的稳定性,不是换更好的模型,而是设计更好的 Harness。
Harness 由两部分组成:
- Guides(前馈):在 AI 行动前给它正确的输入------清晰的指令、相关的上下文、结构化的任务描述
- Sensors(反馈):在 AI 行动后检验输出------独立的验证器、质量评估、异常检测
LangChain 团队的实验很直观:在不换底层模型的情况下,仅靠 Harness Engineering,他们的 Agent 排名从 30 名以外提升到了前五名。
解决:在问题到达人眼之前自动修正
一句话概括 Harness Engineering 的目标:
要让 AI Agent 在更少人工监督下工作,需要系统化地构建一套"外部控制框架"------Harness。它由前馈的 Guides 和反馈的 Sensors 组成,目标是在问题到达人眼之前就自动修正。
二、单 Agent 的两个致命失败模式
有了框架方向,我们来看单 Agent 具体会在哪里失控。Anthropic 在《Harness design for long-running application development》中识别了两类核心失败模式:
失败模式一:上下文焦虑(Context Anxiety)
任务太长,Agent 接近上下文窗口上限时会"焦虑",开始草草收尾------把没完成的工作标记为完成,把模糊的分析写成确定的结论。
这不是模型的 bug,是它在用"自信的收尾"来应对"不确定的延续"。
解决方案 :不是 Compact(压缩上下文),而是 Context Reset------完全清空上下文,带结构化的交接文件启动新 Agent,让新 Agent 接棒继续工作。
失败模式二:自我评估失灵
让 Agent 评估自己的输出,它会"病态乐观"------即使结果很糟糕,它也会自我点赞,因为它的评估基于生成该结果时的同一套认知框架。
就像让一个人同时做卷子又改自己的卷子,几乎必然高分。
解决方案 :引入独立的 Evaluator Agent,专门调教成"严格挑剔模式",与 Generator Agent 完全隔离。
这两个发现,直接引出了多 Agent 架构的第一个也是最核心的模式。
三、多 Agent 架构:五种协作模式
有些团队选模式时,依据的是听起来是否高深,而不是是否适合手头的问题。建议从可能奏效的最简单模式开始,观察它在哪里遇到困难,然后逐步演进。
模式一:生成-验证(Generator-Validator)
适合什么问题:输出质量至关重要,且评估标准能明确表述。
工作原理:Generator 生成输出 → Validator 按明确标准评估 → 不通过则附带具体反馈退回 → 循环直到通过或达到最大迭代次数。
javascript
Generator → 输出 → Validator ──通过──→ 完成
│
不通过 + 反馈
│
└─→ Generator(下一轮)
典型场景:
- 代码生成(Generator 写代码,Validator 写并运行测试)
- 客服回复(Validator 检查准确性、语气、完整性)
- 合规性审查(Validator 按规则逐条核查)
关键注意 :Validator 必须有具体的评估标准,而不是"检查是否良好"。没有标准的 Validator 只会走形式盖章。另外要设置最大迭代次数上限,防止 Generator 和 Validator 陷入无限来回振荡。
模式二:编排器-子智能体(Orchestrator-Subagent)
适合什么问题:任务可以清晰分解为相互独立的子任务。
工作原理:Orchestrator 负责全局规划和任务分配,Subagents 各自处理具体职责并返回结果,Orchestrator 整合后输出。
css
Orchestrator ──分配任务──→ Subagent A(安全检查)
──分配任务──→ Subagent B(代码风格)
──分配任务──→ Subagent C(测试覆盖率)
←─── 收集所有结果 ───
→ 整合输出最终报告
Claude Code 就用了这个模式:主 Agent 处理主流程,同时派发子 Agent 在后台搜索代码库或研究独立问题,让 Orchestrator 的上下文始终聚焦在主任务上。
局限:Orchestrator 是信息瓶颈------子 Agent 之间的关联信息必须经 Orchestrator 中转,多次传递后细节容易丢失。串行执行时也享受不到速度收益。
模式三:智能体团队(Agent Team)
适合什么问题:任务可以分解为长时间运行的独立子任务,且每个子任务能从持续积累的上下文中获益。
工作原理 :Coordinator 分发工作,多个 Worker 从共享队列领取任务,各自持续运行完成多步工作,完成后发出信号。与模式二的关键区别:Worker 跨任务持续存在,不断积累领域上下文。
css
Coordinator ──→ 任务队列
Worker A ←── 领取任务 ──→ 完成 ──→ 发信号
Worker B ←── 领取任务 ──→ 完成 ──→ 发信号
Worker C ←── 领取任务 ──→ 完成 ──→ 发信号
↓
Coordinator 收集结果 → 集成测试
典型场景:将大型代码库从一个框架迁移到另一个框架,每个 Worker 独立迁移一项服务。
局限:独立性是前提,Worker 之间无法轻松共享中间结果,需要仔细划分任务边界和设计冲突解决机制。
模式四:消息总线(Message Bus)
适合什么问题:事件驱动的流水线,且 Agent 生态在不断扩展。
工作原理:Agent 通过发布/订阅事件进行解耦通信,路由器负责分发匹配消息,新 Agent 可以随时加入订阅而无需修改已有连接。
markdown
警报来源 → 分类 Agent → 路由器
├──→ 网络调查 Agent
├──→ 身份分析 Agent
└──→ 上下文收集 Agent
↓
响应协调 Agent → 处置动作
适用条件:工作流源于事件本身而非预定顺序,且团队需要独立开发和部署各个 Agent。
局限:事件链路越长,追踪和调试越困难。路由器分类错误会导致系统静默失效。
模式五:共享状态(Shared State)
适合什么问题:Agent 需要基于彼此的发现进行协同构建,且无需中央协调器。
工作原理:Agent 自主运行,通过共享的持久化存储(数据库/文件系统)相互协调。没有中央 Orchestrator,每个 Agent 读取共享存储中的发现,基于此继续工作,并将自己的发现写回。
css
Agent A(学术文献)─┐
Agent B(行业报告)─┤──→ 共享知识库 ──→ 各 Agent 读取彼此发现
Agent C(专利文件)─┘ → 不断迭代深化
局限 :缺乏显式协调,Agent 可能重复劳动,甚至陷入"反应式循环"(A 写 → B 回应 → A 继续回应...无限循环消耗 Token)。必须设计一等公民级别的终止条件:时间预算、收敛阈值(N 个周期无新发现),或专门的判断 Agent。
四、解决"扭头就忘":Claude Code 记忆机制
问题:上下文税
Claude Code 有个致命缺陷:无状态。关掉对话窗口,记忆清零。
每次开新对话,你都要把历史背景重新塞进去------上次的分析结论、项目架构、踩过的坑、沟通风格偏好......全部重新传一遍。
更糟的是,这些重复传输是按 Token 计费的。为了唤醒 AI 的记忆,你在持续缴纳"上下文税"。
方案一:Claude Code 原生记忆(CLAUDE.md + Auto Memory)
Claude Code 提供两种跨会话传递知识的机制:
| CLAUDE.md 文件 | Auto Memory | |
|---|---|---|
| 由谁编写 | 你 | Claude 自动 |
| 包含内容 | 指令和规则 | 学习到的规律 |
| 适用场景 | 编码规范、架构约定、工作流 | 构建命令、调试发现、行为偏好 |
| 加载方式 | 每次会话加载前 200 行 | 每次会话加载前 200 行 |
CLAUDE.md 的位置决定作用范围,优先级从高到低:
.claude/CLAUDE.md(项目级,通过版本控制共享)~/.claude/CLAUDE.md(用户级,跨所有项目生效)
对于大型项目,可以用 .claude/rules/ 目录按主题拆分规则文件,还可以用 YAML 前置信息将规则路径绑定到特定文件:
yaml
---
paths:
- "src/api/**/*.ts"
---
# API 开发规则
- 所有 API 端点必须包含输入验证
- 使用标准错误响应格式
路径绑定的好处:只在处理匹配文件时加载相关规则,减少上下文噪音。
Auto Memory 是 Claude 自动积累的笔记,存储在 ~/.claude/projects/<project>/memory/ 下:
bash
memory/
├── MEMORY.md # 索引文件,每次会话加载
├── debugging.md # 调试规律详细笔记
├── api-conventions.md # API 设计决策
└── ...
运行 /init 可以自动生成初始 CLAUDE.md;Claude 后续会根据纠正和偏好自动更新 Auto Memory。
方案二:claude-mem ------ 社区的"上下文税反制方案"
官方方案偏被动(你纠正 → Claude 更新)。社区开源项目 claude-mem 则更激进:
bash
npx claude-mem install
核心机制:在 Claude Code 外部挂载本地记忆库,利用 Hooks 机制截获每次工具调用,自动压缩成摘要存入 SQLite 数据库,下次会话按需语义检索注入,而不是全量加载。
数据流:
markdown
工具调用 → PostToolUse Hook 捕获
→ 调用 Claude API(Observer 角色)
→ 压缩为 XML 格式 observation
→ 存入 SQLite + Chroma 向量库
↓
新会话 SessionStart
→ 查询最近 50 条 observation + 10 个摘要
→ 注入 Claude 上下文
↓
用户提交 prompt(UserPromptSubmit)
→ 语义搜索最相关的 5 条 observation
→ 精准注入相关历史
效果:95% 的 Token 节省(基于该项目自身数据:6 条 observation 读取了 2,911 Token,完成了原本需要 56,291 Token 的工作)。
claude-mem 的 Observer 使用专门设计的 prompt,格式化输出可解析的 XML:
xml
<observation>
<type>bugfix</type>
<title>CarPlay 启动时断连根因定位</title>
<narrative>排查发现是 IOKit 初始化时序问题,修复方案是...</narrative>
<facts>
<fact>连接断开发生在 kIOMessageServiceIsTerminated 事件后 200ms</fact>
<fact>根因:驱动初始化完成前 CarPlay 框架已开始握手</fact>
</facts>
</observation>
五、让 AI 从你身上学习:持续学习架构
问题:AI 没有"肌肉记忆"
你用了 Claude Code 三个月,它对你的编码风格一无所知。每次开新对话,它都是个对你一无所知的新员工------不知道你偏好函数式还是面向对象,不知道你的项目有什么奇怪的约定,不记得你上次是怎么解决同类问题的。
方案:Hooks + Instinct 系统
这套架构来自 claude-code-everything 项目,由两个独立子系统组成:
arduino
子系统 A:会话记忆(Memory Persistence)
→ 解决"上次做了什么"------短期记忆,跨会话恢复工作状态
子系统 B:Instinct 学习(Continuous Learning)
→ 解决"用户有什么习惯"------长期学习,积累行为偏好
两者都通过 Hook 机制触发,都在会话开始时将结果注入 Claude 上下文。
Hook 是什么?
Hook 是 Claude Code 工具执行前后的事件驱动钩子:
用户请求 → Claude 选择工具 → PreToolUse hook → 工具执行 → PostToolUse hook
不同 Hook 的触发时机和输入数据:
| Hook 类型 | 触发时机 | 关键输入 |
|---|---|---|
| PreToolUse | 工具执行前 | tool_name、tool_input |
| PostToolUse | 工具执行后 | tool_name、tool_input、tool_output |
| Stop | 每次 Claude 响应后 | transcript_path(完整会话 JSONL) |
| SessionStart | 会话开始 | session_id、cwd(可注入 additionalContext) |
| SessionEnd | 会话结束 | session_id |
PreToolUse 可以通过退出码控制工具是否执行:exit 0 继续,exit 2 中止并把错误展示给 Claude。
Instinct(本能)是什么样的?
一个 Instinct 就是一条原子化的行为偏好,存成 YAML 文件:
yaml
---
id: grep-before-edit
trigger: "when modifying existing code"
confidence: 0.7
domain: workflow
scope: project
---
# Grep Before Edit
## Action
Use Grep to locate code before Edit to confirm exact location.
## Evidence
- Observed 8 times across sessions
- Pattern: Grep → Read → Edit sequence repeated consistently
confidence:0.3~0.9,越高越确定,控制是否注入到下次会话(阈值 ≥ 0.7)scope:project(本项目生效)或global(所有项目生效)trigger + action:这是注入时 Claude 实际看到的内容
完整学习流程
markdown
工具调用
→ observe.sh(异步,不阻塞)
→ 写入 observations.jsonl
→ 计数器 +1,每 20 次发 SIGUSR1
observer-loop.sh(后台常驻进程)
→ 收到 SIGUSR1
→ 取最近 500 条 observations
→ 启动 Claude Haiku 分析(claude --model haiku --print)
→ Haiku 识别行为模式
→ 按规则写 instinct YAML:
3~5 次 → confidence 0.5
6~10 次 → confidence 0.7
11+ 次 → confidence 0.85
→ 归档已分析的 observations
新会话 SessionStart
→ session-start.js 读取 instinct YAML
→ 过滤 confidence ≥ 0.7,取前 6 个
→ 注入 additionalContext:
"Active instincts:
- [project 70%] Use Grep to locate code before Edit
- [global 85%] Grep before Edit, Read before Write"
用 Haiku 而不是 Sonnet 做分析,是因为模式识别任务不需要最强的模型,而控制成本很重要------这个过程每 20 次工具调用就触发一次。
总结
这四套机制,对应着 AI Agent 工程化落地的四个层次:
| 层次 | 问题 | 方案 |
|---|---|---|
| 稳定性 | AI 在长链路任务中偏轨 | Harness Engineering(Guides + Sensors) |
| 可靠性 | 单 Agent 失控、自我评估失灵 | 多 Agent 架构(按场景选模式) |
| 连续性 | 每次对话从零开始 | CLAUDE.md + Auto Memory + claude-mem |
| 成长性 | AI 无法积累行为习惯 | Hooks + Instinct 持续学习 |
从"每次都要从头教"到"越用越懂你",是 AI Agent 工程化的核心演进方向。上面这些方案,都不是新奇的技术噱头,而是在实际项目中摸索出来的、真正能解决问题的工程实践。
本文整理自手车互联团队 CarPlay bug 分析工具的开发经验,内容用于交流学习。