Agent Harness:从 Demo 到工程系统的 10 层架构
很多人第一次做 AI Agent,容易把问题想简单:
“接一个 LLM,再给它几个工具,不就能干活了吗?”
做 Demo 的时候确实可以。一个 system prompt,一个 tool calling,一个文件读写工具,一个搜索工具,就能跑出很漂亮的视频。
但只要你让它进入真实任务,问题马上出现:上下文爆了怎么办?工具报错怎么办?模型限流怎么办?它要写文件时怎么拦?它记住了什么、忘掉了什么?用户中断后怎么恢复?多任务能不能拆给子 Agent?一次危险命令是不是要人确认?
这时候你会发现:Agent 的核心不是模型,而是模型外面的 Harness。
LLM 提供推理和生成能力,Harness 负责把它变成一个可执行、可恢复、可扩展、可治理的工程系统。
一句话定义:Harness 是 Agent 的操作系统
如果把 LLM 看成“大脑”,那 Agent Harness 更像一个小型操作系统:
- 给模型提供工具和技能;
- 管理上下文、记忆和会话;
- 控制权限、风险和人工确认;
- 处理模型 API 的流式响应、重试、限流和缓存;
- 支持命令、Hook 和子 Agent 编排;
- 把一次聊天变成一个可持续运行的任务系统。
可以把它理解成下面这张分层图:
flowchart TD
User[用户 / 任务入口] --> Command[Command 命令层]
User --> Session[Sessions 会话层]
Command --> Runtime[Agent Runtime]
Session --> Runtime
Runtime --> Context[Context 上下文管理]
Runtime --> Memory[Memory 记忆系统]
Runtime --> Permission[Permission 权限治理]
Runtime --> Skills[Skills 任务级能力]
Runtime --> Tools[Tools 原子工具]
Runtime --> SubAgent[Sub-agent 多 Agent 编排]
Runtime --> Hooks[Hooks 执行扩展点]
Runtime --> Query[Query Engine 模型调用层]
Query --> LLM[LLM Providers]
Tools --> Files[文件 / 命令 / 网络]
Skills --> Workflows[PDF / 搜索 / 发布 / 评测等流程]
Memory --> Store[(SQLite FTS / 向量库 / Profile)]
真正成熟的 Agent,不是“一个 prompt 加几个函数”,而是这些层的组合。
第 1 层:Tools,Agent 的原子行动能力
Tools 是 Agent 最底层的行动接口。它们通常很小,只做一件明确的事:
read_file:读取文件;write_file:写入文件;exec:执行 shell 命令;run_python:运行 Python 脚本;web_search:搜索网页;web_fetch:抓取网页内容。
Tools 的设计重点不是“多”,而是三个字:可控、可审计、可组合。
一个好的工具层至少要回答这些问题:
- 参数 schema 是否明确?
- 返回值是否稳定、结构化?
- 错误是否可恢复,而不是只抛一段日志?
- 是否能被权限系统拦截?
- 是否记录调用历史,方便 debug?
很多 Agent 项目做不稳,是因为 tool 只是“把系统能力裸露给模型”。模型一旦传错参数、路径、命令,整个流程就变成玄学。
更好的做法是:工具要像内部 API,而不是像随便暴露的脚本。
第 2 层:Skills,把工具组合成任务能力
Tools 偏原子操作,Skills 偏任务级能力。
举个例子:
read_file是 Tool;- “读取 PDF,抽取摘要,生成 Markdown 笔记”是 Skill。
Skills 往往包含一段可复用流程:什么时候用、先调用什么工具、失败怎么处理、结果怎么验证、有什么坑。
graph LR
T1[read_file] --> S[PDF 阅读技能]
T2[web_fetch] --> S
T3[run_python] --> S
S --> O[结构化笔记 / 摘要 / 引用]
A[原子工具] --> B[任务流程]
B --> C[可复用能力]
工程上,Skills 层至少需要四个能力:
get_skills:知道系统里有哪些技能;find-skills:按任务语义检索相关技能;load_skill:把技能说明加载进当前上下文;skill-creator:在完成复杂任务后沉淀新技能。
这层的价值很大:它让 Agent 不只是“每次从零思考”,而是能复用过去验证过的流程。
Tools 让 Agent 能动手,Skills 让 Agent 会做事。
第 3 层:Query Engine,稳定性和成本控制的核心
很多人会把模型调用写成一行:
const result = await model.invoke(messages)
这对 Demo 没问题,但生产环境不够。
真实的 Query Engine 至少要处理:
API:多模型、多供应商接入;stream:流式输出;cache:缓存重复请求或中间结果;error:统一错误分类;retry:可配置重试策略;rate-limit:限流与排队;token-limit:上下文长度与输出长度控制。
sequenceDiagram
participant R as Agent Runtime
participant Q as Query Engine
participant C as Cache
participant L as LLM Provider
R->>Q: messages + tools + config
Q->>C: 查询缓存
alt 命中缓存
C-->>Q: cached response
Q-->>R: 返回结果
else 未命中
Q->>L: stream request
L-->>Q: tokens / tool call / error
alt 可重试错误
Q->>L: retry with backoff
end
Q->>C: 写入缓存或日志
Q-->>R: normalized response
end
Query Engine 是 Agent 的可靠性边界。没有这一层,模型 API 的任何抖动都会直接变成用户体验问题。
而且它还决定成本。一次任务如果需要 30 次模型调用,缓存、压缩、截断、模型路由都会直接影响账单。
第 4 层:Context,不是把所有东西都塞进去
Context Engineering 是 Agent 工程里最容易被低估的一层。
很多失败不是模型不聪明,而是上下文被污染了:
- 系统提示太长,关键约束被淹没;
- 工具输出太多,占满窗口;
- 历史对话无差别塞入,导致注意力漂移;
- 长任务中间状态丢失,模型开始瞎编;
- 成本不可控,越跑越贵。
所以 Context 层要做的不只是“拼 messages”,而是持续管理信息密度:
system prompt:稳定行为边界;micro-compact:小范围压缩当前推理链路;tool-compact:压缩工具输出,只保留关键证据;auto-compact:上下文接近阈值时自动总结;token & cost:实时追踪 token 与成本。
一个实用原则是:上下文里不应该放“所有信息”,只应该放“当前决策需要的信息”。
第 5 层:Memory,让 Agent 不再每次失忆
Memory 解决的是跨轮、跨任务、跨会话的连续性问题。
它通常可以分成三类:
short-term:当前任务中的工作记忆;long-term:长期可复用事实、偏好、环境信息;profile:用户画像和稳定偏好。
底层可以很简单,比如 sqlite FTS 全文检索;也可以更复杂,比如向量库、知识图谱、时间线索引。
关键不在数据库选型,而在两个动作:
m-create:什么时候写入记忆;m-retrieve:什么时候召回记忆。
flowchart LR
Event[任务事件] --> Judge{是否值得长期保存?}
Judge -->|否| Drop[不写入]
Judge -->|是| Create[m-create]
Create --> Store[(Memory Store)]
NewTask[新任务] --> Retrieve[m-retrieve]
Store --> Retrieve
Retrieve --> Context[注入当前上下文]
我倾向于把 Memory 设计得克制一点。不要把每个过程日志都塞进去,否则长期记忆会变成噪声仓库。真正应该保存的是稳定偏好、环境约定、项目惯例和反复会用到的事实。
第 6 层:Permission,Agent 工程的安全底座
Agent 越能干,越危险。
它能读文件、写文件、执行命令、访问网络、调用外部 API,就必须有权限系统。
Permission 层至少要覆盖:
read:哪些路径可读;write:哪些路径可写;exec:哪些命令可执行;network:哪些网络请求允许;dangerous-path:敏感路径识别;safe-cmd:安全命令白名单或风险分级;human-in-the-loop:高风险动作需要人确认。
这层不是为了限制 Agent,而是为了让 Agent 可以放心地做更多事。
flowchart TD
Action[Agent 请求执行动作] --> Classify{风险分类}
Classify -->|低风险 read| Allow[直接允许]
Classify -->|中风险 write| Policy[策略检查]
Classify -->|高风险 exec / network| Human[人工确认]
Policy -->|通过| Allow
Policy -->|拒绝| Block[阻断并解释]
Human -->|确认| Allow
Human -->|拒绝| Block
Allow --> Audit[记录审计日志]
Block --> Audit
没有 Permission 的 Agent,只适合玩具场景;有 Permission 的 Agent,才有机会进入真实电脑、真实仓库和真实业务流程。
第 7 层:Sessions,让任务可恢复、可回滚
聊天记录不是 Session 管理。
真正的 Session 至少需要支持:
create:创建任务会话;list:查看历史会话;rewind:回滚到某个状态;- 保存消息、工具调用、文件变更、模型配置和关键中间结果。
这对长任务尤其重要。
比如一个 Agent 正在改代码,跑到一半发现方向错了,如果没有 Session 快照,只能靠人肉回忆;如果有 rewind,就可以回到某个工具调用前重新分支。
这也是 Agent 从“聊天界面”走向“工作台”的关键。
第 8 层:Command,给自然语言配一条确定性通道
自然语言很灵活,但不适合所有操作。
很多 Agent 产品都会引入命令层:
slash-command:例如/reset、/compact、/status、/model;system-cmd:系统内部命令,例如切换模式、导出日志、清理缓存。
Command 的价值是确定性。
用户不需要解释“请帮我把上下文压缩一下”,直接 /compact;开发者也不需要让模型猜“用户是不是要清空会话”,直接把命令解析成明确动作。
好的 Agent 交互应该是:自然语言负责表达意图,命令负责执行确定性操作。
第 9 层:Hook,把治理和扩展插进执行链
Hook 是 Harness 的扩展点。
最常见的是:
pre-tool:工具调用前执行;post-tool:工具调用后执行。
它能做很多事:
- 权限检查;
- 参数改写;
- 敏感信息过滤;
- 调用日志记录;
- 结果摘要压缩;
- 自动写入记忆;
- 失败重试或降级;
- 指标统计和 tracing。
flowchart LR
A[模型产生 tool call] --> B[pre-tool hooks]
B --> C[权限 / 参数 / 审计]
C --> D[执行工具]
D --> E[post-tool hooks]
E --> F[压缩 / 记忆 / 指标 / 错误处理]
F --> G[返回 Agent Runtime]
如果没有 Hook,所有治理逻辑都会散落在业务代码里;有了 Hook,Harness 才能变成可插拔平台。
第 10 层:Sub-agent,从单 Agent 到 Agent Team
Sub-agent 是 Agent 架构从单体走向协作的关键。
一个主 Agent 不一定要自己做所有事。它可以把任务拆给不同子 Agent:
- 代码审查 Agent;
- 搜索研究 Agent;
- 测试验证 Agent;
- 文档整理 Agent;
- 数据分析 Agent。
Sub-agent 需要的不只是“再开一个模型调用”,而是一套完整协议:
agent-tool:子 Agent 可以被包装成工具;invoke:主 Agent 调用子 Agent;tools:子 Agent 拥有哪些工具;skills:子 Agent 加载哪些技能;context:传给子 Agent 的上下文边界;agent team:多个 Agent 的协作关系。
graph TD
Lead[主 Agent / Orchestrator] --> Research[研究 Agent]
Lead --> Coder[编码 Agent]
Lead --> Reviewer[审查 Agent]
Lead --> Tester[验证 Agent]
Research --> ROut[资料摘要]
Coder --> COut[代码补丁]
Reviewer --> VOut[风险意见]
Tester --> TOut[测试结果]
ROut --> Lead
COut --> Lead
VOut --> Lead
TOut --> Lead
Lead --> Final[汇总决策 / 最终交付]
这里最容易犯的错是:给子 Agent 太多上下文,导致它和主 Agent 一样混乱。
更好的做法是:子 Agent 只拿完成子任务需要的信息,产出可验证的结果。
技术栈:Node.js + TypeScript 是很自然的选择
如果从工程实现角度看,一个 Agent Harness 可以用很多技术栈做。
截图里的组合很典型:
Nodejs:运行时;pnpm:包管理;Typescript:类型系统和工程约束;LangChain:模型调用、工具抽象、链式流程;LangGraph:状态图、循环、分支、多 Agent 编排;Commanderjs:CLI 命令入口;sqlite:本地会话、记忆、索引和审计日志;kimi/deepseek:模型供应商接入。
但技术栈不是重点。重点是边界要清楚:
graph TD
CLI[Commander.js CLI] --> Runtime[Agent Runtime]
Runtime --> Graph[LangGraph 状态图]
Runtime --> LC[LangChain 工具 / 模型抽象]
Runtime --> DB[(SQLite: sessions / memory / audit)]
Runtime --> Providers[LLM Providers: Kimi / DeepSeek / Others]
Runtime --> FS[Filesystem / Shell / Network]
如果模块边界设计得好,底层模型、数据库、工具框架都可以替换;如果边界设计得差,换一个模型都要重写半个系统。
一个最小可用 Harness 应该先做什么?
如果你不是要一口气做完整平台,我建议按这个顺序迭代:
- Query Engine:先把模型调用、stream、retry、error 处理稳定下来;
- Tools:提供少量高质量工具,不要一开始暴露太多能力;
- Permission:读写执行网络都要有风险分级;
- Context:实现工具输出压缩和自动 compact;
- Sessions:保存任务过程,支持恢复和回滚;
- Skills:把反复做的任务沉淀成可加载流程;
- Memory:只保存稳定事实,不保存流水账;
- Hooks:把权限、审计、压缩、记忆写入做成扩展点;
- Command:提供确定性控制入口;
- Sub-agent:最后再做多 Agent 编排。
顺序很重要。
很多团队一上来就做 multi-agent,结果基础的错误处理、权限控制、上下文压缩都没有,最后只是多个不稳定 Agent 互相放大噪声。
先把单 Agent 做稳,再让它长出团队。
常见坑:Agent Demo 为什么难进生产?
我见过不少 Agent Demo,演示时很惊艳,上线后很痛苦。常见问题基本逃不出这几类:
1. 工具太自由
模型可以随便写路径、随便执行命令、随便联网。短期看起来能力强,长期一定出事故。
2. 上下文无治理
所有历史、所有工具输出、所有错误日志都塞给模型。上下文越来越长,质量越来越差。
3. 没有可恢复状态
任务跑到一半失败,只能重来。用户不知道 Agent 做到哪一步,开发者也不知道怎么复盘。
4. 记忆变成垃圾桶
什么都记,最后召回的都是噪声。Memory 的难点不是存储,而是判断“什么值得存”。
5. 没有权限分层
读文件、写文件、执行命令、发消息、调用外部 API 都是同一个风险级别。最后只能在“完全放开”和“完全禁用”之间摇摆。
6. 过早 multi-agent
主 Agent 自己还不稳,就开始拆子 Agent。结果上下文传递、职责边界、结果验证都失控。
结语:Agent 工程的主战场在 Harness
未来模型会越来越强,但这不意味着 Harness 会变得不重要。
恰恰相反:模型越强,能执行的动作越复杂,越需要一个可靠的工程外壳来管理它。
一个真正可用的 Agent 系统,至少要同时回答四个问题:
- 能做什么? Tools 和 Skills。
- 知道什么? Context、Memory 和 Sessions。
- 能不能安全地做? Permission 和 human-in-the-loop。
- 能不能长期稳定扩展? Query Engine、Hook、Command 和 Sub-agent。
所以我更愿意把 Agent Harness 看成 AI 时代的新型运行时:它不只是调用模型,而是把模型放进一个能工作、能恢复、能审计、能演进的系统里。
如果你想从 0 开发一个 AI Agent,不要先问“用哪个模型”。
先问:我的 Harness 准备好了吗?