cat agent-harness-guide.md
我如何制作 AI Agent,
以及如何给 Agent 加 Harness
让它稳定跑起来
Agent 不只是 Prompt。真正让一个 AI Agent 从"能跑一次的脚本"变成"可稳定运行的系统"的,是它外围那一整套 Harness——权限、事件流、状态、Workspace、上下文管理、Middleware、测试。这篇文章是我重新梳理后的完整方法论。
目录
两层结构:Agent + Harness
最近我在重新梳理自己制作 AI Agent 的方法。
这里的 AI Agent,可以理解为一个"能理解任务、自己规划步骤、调用工具并产出结果的智能程序"。它不只是普通聊天机器人。聊天机器人主要负责回答问题,而 Agent 还可以读文件、写代码、运行命令、调用 API、等待人类确认,甚至在多轮任务中持续推进目标。
过去我很容易把 Agent 想成一个"会调用大语言模型的脚本":写一段 Prompt,接一个模型,再塞几个工具,就让它开始干活。
但真正做久了之后,我发现 Agent 能不能跑起来不是最难的,难的是它能不能稳定、可控、可恢复地跑下去。
所以我现在会把一个 Agent 拆成两层来看:
第一层是 Agent 本身:它负责理解任务、推理、调用工具、产出结果。
第二层是 Harness:这个词可以理解为"运行支架"或"工程护栏"。它不是某一个具体函数,而是一整套让 Agent 稳定运行的外围系统,包括权限、状态、上下文、事件流、日志、恢复机制和测试。
定义 Agent 到底要做什么
我不会一上来就写代码。我通常先问自己几个问题:
- 这个 Agent 的职责是什么?
- 它只是聊天,还是要真的操作文件、调用命令、访问数据库、执行外部动作?
- 它是人在旁边看着跑,还是要无人值守地跑?
- 它出错之后应该停下来、重试,还是请求人类确认?
- 它需要记住历史状态吗?
- 它会不会运行很久,导致上下文爆掉?
这些问题决定了 Agent 的复杂度。
| 场景 | 需要的组件 | 复杂度 |
|---|---|---|
| 问答助手 | 模型 + 基础 Agent | 低 |
| 读写文件 | 模型 + Agent + Toolkit + 权限系统 | 中 |
| 长期执行任务 | 模型 + Agent + Toolkit + 状态持久化 + 上下文压缩 | 中高 |
| 多人线上使用 | 模型 + Agent + Toolkit + 权限 + Workspace + Session + 日志 + 追踪 + 服务层 | 高 |
Session 则可以理解为"一次会话"或"一条任务上下文"。同一个 Agent 可以服务很多用户或很多任务,每个任务都需要有自己的 Session,避免状态混在一起。
我的原则是:先用最小结构解决问题,不提前堆架构。
制作 Agent 的基本结构
以 AgentScope 2.0 为例,我现在会把 Agent 理解为一个统一的推理和行动引擎。
一个最小可行动的 Agent 通常包含:
| 组件 | 英文名 | 说明 |
|---|---|---|
| 模型 | Model | 负责理解和推理,比如 OpenAI、Qwen、Claude、DeepSeek 等大语言模型 |
| 凭证 | Credential | 保存模型 API 所需的认证信息,比如 API Key |
| 系统提示词 | System Prompt | 定义 Agent 的角色、边界和行为风格 |
| 工具箱 | Toolkit | 定义 Agent 能使用什么工具 |
| 状态 | State | 保存上下文、权限和会话状态 |
| 中间件 | Middleware | 用于日志、追踪、拦截和增强行为 |
我现在不会把所有能力都写进 Prompt。
具体能力应该放进工具、Skill、MCP 或 Workspace 里。
MCP 是 Model Context Protocol 的缩写,可以理解为一种"让 Agent 连接外部工具和服务的标准协议"。通过 MCP,Agent 可以接入地图、数据库、浏览器、代码仓库等外部能力。
优先用事件流,而不是只等最终结果
很多人做 Agent 时,只关心最后一句回答。但一个真正可控的 Agent,我更关心它运行中发生了什么。
AgentScope 2.0 里有两个主要入口:
| 入口 | 行为 | 适合场景 |
|---|---|---|
| reply | Agent 跑完,拿最终回复 | 简单任务 |
| reply_stream | Agent 边跑边返回事件 | 观察过程、展示进度、处理工具调用、人类确认 |
我更偏向在正式场景用 reply_stream,因为它能看到:
- 模型什么时候开始生成
- 工具什么时候被调用
- 工具参数是什么
- 是否需要用户确认
- 是否需要外部系统执行
- 中间是否出错
- 最终结果是什么
这就是 Harness 的入口。
Harness 第一件事:权限
Agent 一旦能调用工具,就必须有权限系统。尤其是文件写入、Shell 命令、数据库变更、部署、删除、转账、发消息这类动作,不能只靠模型"自觉"。
ls、git commit、npm run build、rm -rf 等。
我会把权限分成几种模式:
| 场景 | 权限模式 | 说明 |
|---|---|---|
| 开发探索 | 只读 | 只允许读文件、搜索,不能写 |
| 有人监督写代码 | 受限写入 | 只允许编辑指定目录 |
| 无人值守运行 | 白名单 | 只允许明确列出的命令 |
| 危险动作 | 确认/禁止 | 永远需要确认或直接禁止 |
在 AgentScope 中,这类权限通常由 Permission System(权限系统)来控制。它会判断某个工具调用应该允许、拒绝,还是先问人。
比如一个代码 Agent,可以允许它读项目、搜索文件、修改工作目录里的代码,但不能随便 git push,不能删除任意目录,不能执行高风险命令。
Harness 第二件事:Human-in-the-Loop
我不认为所有动作都应该自动化。
有些事情,Agent 应该停下来问我:
- 要不要删除文件?
- 要不要执行数据库迁移?
- 要不要提交代码?
- 要不要调用外部服务?
- 要不要接受某条权限规则?
这类动作适合做成人类确认事件。
AgentScope 的思路是:当权限系统判断某个工具调用需要确认时,Agent 会暂停,抛出一个确认事件。Harness 收到事件后,把它展示给我。我确认之后,再把确认结果传回 Agent,让它继续跑。
Harness 第三件事:状态持久化
如果 Agent 只跑一轮,状态不重要。但只要它涉及多轮任务、长任务、用户会话、排队执行,状态就非常关键。
我会把 Agent 的状态当成一等公民,而不是把所有东西塞进聊天历史。
状态里应该包含:
- 当前上下文
- 权限规则
- 工具状态
- 当前回复跑到哪里
- 压缩后的摘要
- 会话信息
这样 Agent 中途停了,服务重启了,或者任务需要等待外部确认时,它都能继续。
Harness 第四件事:上下文管理
Agent 跑久了,一定会遇到上下文窗口问题。
它读了太多文件,跑了太多工具,聊了太多轮,模型上下文迟早会满。
我的做法是提前设计上下文策略:
- 保留最近的关键消息
- 压缩早期上下文
- 截断超长工具结果
- 把大文件、大结果 offload 到外部存储
- 让 Agent 只在需要时重新读取
这也是 Harness 的工作。
Harness 第五件事:Workspace
当 Agent 需要真正执行任务时,我会给它一个 Workspace。
Workspace 可以是本地目录,也可以是 Docker 容器,还可以是云端沙箱。
Docker 是一种容器技术,可以给 Agent 一个隔离环境,避免它直接污染主机系统。
云端沙箱 则是运行在云上的隔离环境,适合托管执行或多人使用。
Workspace 的作用是给 Agent 一个明确的工作空间:
- 工具在哪里运行
- 文件在哪里读写
- Skill 从哪里加载
- MCP 怎么注册
- 上下文和工具结果怎么 offload
- 会话数据怎么保存
| Workspace 类型 | 适用场景 |
|---|---|
| LocalWorkspace | 本地开发 |
| DockerWorkspace | 隔离执行 |
| E2BWorkspace | 云端沙箱 |
Harness 第六件事:Middleware
Middleware,也就是中间件,是我给 Agent 加可观测性和控制逻辑的地方。
我不会把日志、追踪、策略检查都写进 Agent 主逻辑里。那样很快会变乱。Middleware 更适合做这些事:
- 记录每次 reply
- 记录模型调用
- 记录工具调用
- 注入或改写 System Prompt
- 做审计
- 做 tracing
- 统计 token 和耗时
Token 则是模型处理文本时的基本计量单位,可以粗略理解为文字被切分后的片段。模型调用成本和上下文长度通常都和 token 数有关。
判断一个 Agent 是否可靠:检查清单
我现在判断一个 Agent 是否可靠,会看这份清单:
Agent 不是 Prompt,Agent 是系统
我现在越来越觉得,制作 Agent 的关键不是写一个聪明 Prompt。Prompt 当然重要,但它只是其中一部分。
真正让 Agent 稳定的是它周围的 Harness:
所以我现在做 Agent,会先问:这个 Agent 要完成什么任务?
然后再问:我要给它什么 Harness,才能让它稳定地完成这个任务?
当我开始这样思考时,Agent 就不再只是一个会说话的模型,而是一个可以被设计、运行、观察和迭代的工程系统。