~/blog · ai-agent-harness · 2026-06-02

cat agent-harness-guide.md

我如何制作 AI Agent,
以及如何给 Agent 加 Harness
让它稳定跑起来

Agent 不只是 Prompt。真正让一个 AI Agent 从"能跑一次的脚本"变成"可稳定运行的系统"的,是它外围那一整套 Harness——权限、事件流、状态、Workspace、上下文管理、Middleware、测试。这篇文章是我重新梳理后的完整方法论。

ai-agent agentscope harness engineering hitl middleware workspace state

目录

两层结构 定义职责 基本结构 事件流 权限 HITL 状态持久化 上下文管理 Workspace Middleware 检查清单 总结

两层结构:Agent + Harness

最近我在重新梳理自己制作 AI Agent 的方法。

这里的 AI Agent,可以理解为一个"能理解任务、自己规划步骤、调用工具并产出结果的智能程序"。它不只是普通聊天机器人。聊天机器人主要负责回答问题,而 Agent 还可以读文件、写代码、运行命令、调用 API、等待人类确认,甚至在多轮任务中持续推进目标。

过去我很容易把 Agent 想成一个"会调用大语言模型的脚本":写一段 Prompt,接一个模型,再塞几个工具,就让它开始干活。

但真正做久了之后,我发现 Agent 能不能跑起来不是最难的,难的是它能不能稳定、可控、可恢复地跑下去。

所以我现在会把一个 Agent 拆成两层来看:

🧠
Agent 本身推理 / 决策 / 工具调用 / 产出结果
🦺
Harness权限 / 事件流 / 状态 / Workspace / 上下文 / Middleware / 测试

第一层是 Agent 本身:它负责理解任务、推理、调用工具、产出结果。

第二层是 Harness:这个词可以理解为"运行支架"或"工程护栏"。它不是某一个具体函数,而是一整套让 Agent 稳定运行的外围系统,包括权限、状态、上下文、事件流、日志、恢复机制和测试。

核心观点
没有 Harness 的 Agent,更像一个聪明但脆弱的临时脚本。加上 Harness 之后,它才开始接近一个可以长期使用的系统。

定义 Agent 到底要做什么

我不会一上来就写代码。我通常先问自己几个问题:

这些问题决定了 Agent 的复杂度。

场景 需要的组件 复杂度
问答助手 模型 + 基础 Agent
读写文件 模型 + Agent + Toolkit + 权限系统
长期执行任务 模型 + Agent + Toolkit + 状态持久化 + 上下文压缩 中高
多人线上使用 模型 + Agent + Toolkit + 权限 + Workspace + Session + 日志 + 追踪 + 服务层
术语 · Toolkit
Toolkit,可以理解为"工具箱"。在 AgentScope 里,Toolkit 用来管理 Agent 可以使用的工具,比如读取文件、写入文件、搜索文件、执行命令、调用外部服务等。Agent 本身负责思考,Toolkit 负责告诉它"你能用哪些工具来行动"。
术语 · Workspace / Session
Workspace,可以理解为"Agent 的工作空间"。它规定 Agent 在哪里读写文件、在哪里运行工具、哪里保存中间结果。
Session 则可以理解为"一次会话"或"一条任务上下文"。同一个 Agent 可以服务很多用户或很多任务,每个任务都需要有自己的 Session,避免状态混在一起。

我的原则是:先用最小结构解决问题,不提前堆架构

制作 Agent 的基本结构

AgentScope 2.0 为例,我现在会把 Agent 理解为一个统一的推理和行动引擎。

术语 · AgentScope
AgentScope 是一个用于构建 AI Agent 的开源框架。它提供了 Agent、模型、工具、权限、事件流、工作空间等基础组件,让我们不需要从零实现整套 Agent 运行系统。

一个最小可行动的 Agent 通常包含:

组件 英文名 说明
模型 Model 负责理解和推理,比如 OpenAI、Qwen、Claude、DeepSeek 等大语言模型
凭证 Credential 保存模型 API 所需的认证信息,比如 API Key
系统提示词 System Prompt 定义 Agent 的角色、边界和行为风格
工具箱 Toolkit 定义 Agent 能使用什么工具
状态 State 保存上下文、权限和会话状态
中间件 Middleware 用于日志、追踪、拦截和增强行为

我现在不会把所有能力都写进 Prompt。

术语 · Prompt
Prompt 可以理解为"给大模型的文字指令"。它适合描述 Agent 的角色、目标和行为原则,但不适合承载所有业务逻辑。

具体能力应该放进工具、Skill、MCP 或 Workspace 里。

术语 · Skill / MCP
Skill 可以理解为"可复用的专业能力包"。比如一个代码审查 Skill、一个写作 Skill、一个数据库分析 Skill。它通常包含说明、流程、参考资料,告诉 Agent 在某个领域应该怎么做。

MCP 是 Model Context Protocol 的缩写,可以理解为一种"让 Agent 连接外部工具和服务的标准协议"。通过 MCP,Agent 可以接入地图、数据库、浏览器、代码仓库等外部能力。
原则
Prompt 太大,Agent 会变得飘;工具和状态清晰,Agent 才更像一个工程系统。

优先用事件流,而不是只等最终结果

很多人做 Agent 时,只关心最后一句回答。但一个真正可控的 Agent,我更关心它运行中发生了什么。

AgentScope 2.0 里有两个主要入口:

入口 行为 适合场景
reply Agent 跑完,拿最终回复 简单任务
reply_stream Agent 边跑边返回事件 观察过程、展示进度、处理工具调用、人类确认
术语 · 事件流
事件流,可以理解为 Agent 运行过程中不断抛出的状态消息。比如"开始回复了""模型开始调用了""正在生成文本""准备调用工具""需要用户确认""工具执行完成"等。

我更偏向在正式场景用 reply_stream,因为它能看到:

这就是 Harness 的入口。

对比
如果只用最终结果,Agent 出错时你只能看到"它错了"。如果使用事件流,你能知道它是在哪一步错的。

Harness 第一件事:权限

Agent 一旦能调用工具,就必须有权限系统。尤其是文件写入、Shell 命令、数据库变更、部署、删除、转账、发消息这类动作,不能只靠模型"自觉"。

术语 · Shell 命令
Shell 命令,就是在命令行里执行的系统命令,比如 lsgit commitnpm run buildrm -rf 等。

我会把权限分成几种模式:

场景 权限模式 说明
开发探索 只读 只允许读文件、搜索,不能写
有人监督写代码 受限写入 只允许编辑指定目录
无人值守运行 白名单 只允许明确列出的命令
危险动作 确认/禁止 永远需要确认或直接禁止

在 AgentScope 中,这类权限通常由 Permission System(权限系统)来控制。它会判断某个工具调用应该允许、拒绝,还是先问人。

比如一个代码 Agent,可以允许它读项目、搜索文件、修改工作目录里的代码,但不能随便 git push,不能删除任意目录,不能执行高风险命令。

核心原则
我希望 Agent 聪明,但我不希望它"自由"。稳定的 Agent,不是无限自主的 Agent,而是在正确边界内自主的 Agent。

Harness 第二件事:Human-in-the-Loop

我不认为所有动作都应该自动化。

术语 · Human-in-the-Loop (HITL)
Human-in-the-Loop,简称 HITL,可以理解为"人在回路中"。意思是 Agent 执行到某些关键动作时,不是自动继续,而是暂停下来请求人类确认。

有些事情,Agent 应该停下来问我:

这类动作适合做成人类确认事件。

AgentScope 的思路是:当权限系统判断某个工具调用需要确认时,Agent 会暂停,抛出一个确认事件。Harness 收到事件后,把它展示给我。我确认之后,再把确认结果传回 Agent,让它继续跑。

效果
这让 Agent 不再是一个黑盒,而是一个可以被驾驶的系统。

Harness 第三件事:状态持久化

如果 Agent 只跑一轮,状态不重要。但只要它涉及多轮任务、长任务、用户会话、排队执行,状态就非常关键。

术语 · 状态持久化
状态持久化,意思是把 Agent 的运行状态保存到外部存储里,比如文件、数据库、Redis。这样即使程序重启、服务中断,Agent 也能从之前的位置继续。

我会把 Agent 的状态当成一等公民,而不是把所有东西塞进聊天历史。

状态里应该包含:

术语 · Redis
Redis 是一种常见的内存数据库,常用于保存会话、缓存和任务状态。

这样 Agent 中途停了,服务重启了,或者任务需要等待外部确认时,它都能继续。

判断标准
没有状态持久化的 Agent,只适合 Demo。有状态持久化的 Agent,才适合产品。

Harness 第四件事:上下文管理

Agent 跑久了,一定会遇到上下文窗口问题。

术语 · 上下文窗口
上下文窗口,指的是大语言模型一次能"看见"的文本长度上限。模型不是无限记忆的,超过这个窗口后,早期信息可能会被截断或遗忘。

它读了太多文件,跑了太多工具,聊了太多轮,模型上下文迟早会满。

我的做法是提前设计上下文策略:

术语 · Offload
Offload,可以理解为"卸载到外部存储"。也就是说,不把所有内容都塞进模型上下文,而是把大块数据保存到文件、数据库或 Workspace 里,只在需要时引用或重新读取。

这也是 Harness 的工作。

原则
一个稳定的 Agent 不应该把所有东西都塞进模型上下文。它应该知道什么放进上下文,什么放进文件,什么放进状态,什么等需要时再取。

Harness 第五件事:Workspace

当 Agent 需要真正执行任务时,我会给它一个 Workspace

术语 · Workspace
Workspace 可以理解为 Agent 的工作目录和运行环境。它不仅是一个文件夹,还可以包含工具、Skill、MCP、会话数据、上下文缓存和执行环境。

Workspace 可以是本地目录,也可以是 Docker 容器,还可以是云端沙箱。

Docker 是一种容器技术,可以给 Agent 一个隔离环境,避免它直接污染主机系统。
云端沙箱 则是运行在云上的隔离环境,适合托管执行或多人使用。

Workspace 的作用是给 Agent 一个明确的工作空间:

Workspace 类型 适用场景
LocalWorkspace 本地开发
DockerWorkspace 隔离执行
E2BWorkspace 云端沙箱
设计思路
我特别喜欢 Workspace 这个概念,因为它把"Agent 能做什么"和"Agent 在哪里做"分开了。这比直接让 Agent 在主机环境里乱跑安全得多。

Harness 第六件事:Middleware

Middleware,也就是中间件,是我给 Agent 加可观测性和控制逻辑的地方。

术语 · Middleware
Middleware 可以理解为"包在 Agent 运行流程外面的一层逻辑"。它不直接决定 Agent 要做什么,但可以在 Agent 执行前、执行中、执行后记录信息、修改输入、拦截行为或增加追踪。

我不会把日志、追踪、策略检查都写进 Agent 主逻辑里。那样很快会变乱。Middleware 更适合做这些事:

术语 · Tracing / Token
Tracing,可以理解为"链路追踪"。它会记录一次 Agent 任务从开始到结束经过了哪些步骤,每一步花了多久、调用了什么模型、用了什么工具。

Token 则是模型处理文本时的基本计量单位,可以粗略理解为文字被切分后的片段。模型调用成本和上下文长度通常都和 token 数有关。
分层
Agent 负责思考和行动,Middleware 负责观察和包裹。这是一种很干净的分层。

判断一个 Agent 是否可靠:检查清单

我现在判断一个 Agent 是否可靠,会看这份清单:

# Agent 可靠性检查清单 1. 是否使用 Agent / reply / reply_stream 标准接口 2. 密钥是否放在环境变量或配置里,而不是写死在代码里 3. 是否只加载必要工具(不多不少) 4. 工具是否标明只读并发安全副作用 5. 是否有权限系统 6. 是否支持人类确认(HITL) 7. 是否能保存和恢复状态 8. 是否有上下文压缩或 offload 策略 9. 是否有最小smoke test 10. 是否有日志事件流或 tracing 11. 是否避免混用旧版本 API
术语 · Smoke Test
Smoke Test,可以理解为"冒烟测试",也就是最小可运行测试。它不验证所有细节,只验证系统是否能基本启动、跑通核心流程,不是一运行就崩。
底线
如果这些都没有,只是"能跑一次",我不会把它当成稳定 Agent。

Agent 不是 Prompt,Agent 是系统

我现在越来越觉得,制作 Agent 的关键不是写一个聪明 Prompt。Prompt 当然重要,但它只是其中一部分。

真正让 Agent 稳定的是它周围的 Harness:

权限
让它可控
事件流
让它可见
状态
让它可恢复
Workspace
让它有边界
上下文管理
让它能跑长任务
Middleware
让它可观测
测试
让它不只在演示里能跑

所以我现在做 Agent,会先问:这个 Agent 要完成什么任务?

然后再问:我要给它什么 Harness,才能让它稳定地完成这个任务?

结论

当我开始这样思考时,Agent 就不再只是一个会说话的模型,而是一个可以被设计、运行、观察和迭代的工程系统。