《Harness工程:从上下文管理到Agent系统构建》笔记
《Harness工程:从上下文管理到Agent系统构建》读书笔记。本书从上下文工程出发,依次讨论意图识别、规划、执行、反思、记忆、Skills、多 Agent 协作,以及 Claude Code、Claw 类产品背后的 Harness 设计思想。
但我始终坚信,无论模型如何迭代,Harness 工程作为连接模型与真实世界的桥梁,都将是未来 AI 应用工程师的核心竞争力。
一、核心观点:Agent = LLM + Harness
大语言模型提供推理与生成能力,但仅有模型并不足以构成一个能够稳定完成真实任务的 Agent。模型之外还需要一套工程系统,为它提供上下文、工具、记忆、权限、状态管理、任务调度和运行环境,这套系统就是 Harness。
可以用一个简单的公式概括三者之间的关系:
Agent = LLM + Harness
Harness 工程不直接干预模型内部如何思考,而是围绕模型构建一套可靠的外部支撑环境,使模型能够:
- 理解用户真正想做什么;
- 将复杂目标拆分为可执行步骤;
- 调用工具与真实世界交互;
- 根据执行结果调整后续行动;
- 在长任务中保持状态和目标一致;
- 在必要时引入人工审核;
- 控制上下文规模,减少噪声和 Token 消耗。
因此,构建 Agent 的重点正在从“如何写出一个更复杂的 Prompt”,逐渐转向“如何为模型设计一个更好的工作环境”。
二、从提示词工程到上下文工程
传统的“提示—响应”模式在简单任务中已经足够,但进入复杂业务场景后会逐渐暴露问题:
- 意图偏移:单条指令很难完整表达复杂业务背后的真实目标;
- 长程记忆缺失:多轮交互后,模型容易遗忘最初的任务目标;
- 逻辑断层:处理多步骤、跨领域任务时,缺少结构化引导容易导致执行失控。
上下文工程不只是编写 Prompt,而是管理所有进入模型上下文窗口的信息,包括:
- 用户当前输入;
- 业务意图与任务约束;
- 历史对话和用户偏好;
- 可使用的工具及其调用方式;
- 工具调用返回的观察结果;
- 当前计划、执行进度和中间状态;
- 外部检索获得的知识;
- 反思、审核与纠错信息。
目标不是把尽可能多的信息交给模型,而是确保进入上下文的信息准确、相关、结构清晰且足以支撑当前决策。
2.1 ReAct 本质上是动态构建上下文
ReAct(Reasoning and Acting)通过“推理—行动—观察”循环驱动任务执行:
- 理解问题并进行推理;
- 判断是否需要调用外部工具;
- 选择工具并生成参数;
- 获取工具执行结果;
- 将观察结果加入上下文;
- 基于更新后的上下文继续推理,或者输出最终答案。
从上下文工程的视角看,ReAct 的每一次行动都在产生新的观察,每一次观察又会成为下一轮决策的依据。Agent 的运行过程,本质上就是上下文不断构建、更新和收敛的过程。
2.2 Agent 的四个核心模块
无论 Agent 产品形态如何变化,其核心逻辑通常都可以归纳为四个模块。
感知:识别用户意图
感知模块是系统入口,负责把非结构化的自然语言转换为结构化任务,并将请求路由到合适的模型、工具或工作流。
规划:构建执行骨架
规划模块决定任务应当如何推进,常见模式包括:
- ReAct:在推理、行动和观察之间循环,边执行边规划;
- Plan-Act:先生成完整计划,或由人工注入 SOP,再按步骤执行;
- Sequential Thinking:通过多轮思考、调整与反思处理复杂任务。
执行:获取实时信息
执行模块通过 API、代码执行器、文件系统或其他工具与外部世界交互。工具调用的结果会形成新的上下文片段,推动下一轮推理。
反思:检查并修正结果
反思模块检查规划和执行中是否存在逻辑错误、信息遗漏、工具误用或结果不完整,并在必要时触发重新规划和执行。
除上述四个模块外,一个完整的 Agent 系统还需要记忆管理,以维持多轮对话和长周期任务的连续性。
2.3 上下文不是越多越好
过多信息会带来上下文污染:工具描述、中间日志、历史对话、失败尝试和不同子任务的推理轨迹混在一起,模型反而难以抓住当前重点。
上下文管理的关键是卸载与按需加载:
- 详细分析写入外部 Markdown 文件,主上下文只保留摘要和文件路径;
- 暂时不需要的工具说明不进入上下文;
- 历史工具调用结果经过压缩后再保留;
- 专业知识和长期记忆在需要时检索;
- 子任务放入独立上下文执行,只将最终结论返回主 Agent。
这是一种“引用而非堆砌”的设计思路。
三、Agent 设计模式的演进
3.1 Function Calling:让模型学会调用工具
Function Calling 通过结构化的工具交互数据,让模型学会:
- 识别调用工具的时机;
- 选择正确的工具;
- 生成符合规范的参数;
- 根据工具结果继续完成任务。
它让 LLM 从纯文本生成器迈向了可以采取行动的 Agent。不过,Function Calling 往往依赖模型训练、工具协议和推理引擎的协同,并非所有模型都原生支持。
3.2 ReAct:用提示词构造行动循环
ReAct 不依赖专门训练,只需要通过提示词描述任务目标、工具列表和交互格式,就可以让模型交替进行推理和行动。
它的优点是简单、灵活和容易实现,但也有明显局限:
- 执行效果高度依赖模型的推理和指令遵循能力;
- 复杂任务需要多次串行调用模型,延迟和成本较高;
- 对提示词格式较为敏感;
- 缺少全局视角时,容易陷入局部最优或重复循环。
3.3 CodeAct:代码即工具
CodeAct 是 ReAct 的重要衍生模式。它不再把工具限制为预先注册的业务 API,而是把 Python、Shell 等编程语言作为通用工具空间。
模型可以根据当前任务临时生成代码,再交给代码执行器运行。这样一来,工具不再是固定的黑盒函数,而是可以即时构造的行动。该模式适合:
- 运维自动化;
- 数据分析与清洗;
- 文件批量处理;
- 系统诊断;
- 软件工程任务。
CodeAct 的核心思想可以概括为:将代码视为动作,而不是上下文中的普通文本。
3.4 Plan-Act:先谋而后动
Plan-Act 将规划和执行拆为两个阶段:规划 Agent 先对复杂目标进行全局拆解,执行 Agent 再严格按照计划推进。
真正的工程难点不只是生成计划,而是保证模型能够:
- 知道当前执行到了哪一步;
- 不跳过必要步骤;
- 根据结果更新任务状态;
- 明确判断任务是继续执行还是已经完成。
可以在每轮执行时注入结构化的历史步骤,例如 past_steps,并使用严格的结构化输出区分状态:
1 | {"actions": {"steps": ["下一步操作"]}} |
或者:
1 | {"actions": {"response": "最终结果"}} |
结构化输出使下游系统不必猜测模型意图,是工作流稳定运行的重要基础。
3.5 Reflection:让 Agent 检查自己的结果
反思模式通常由两个角色组成:
- 执行器 LLM:生成初步结果;
- 评判器 LLM:检查正确性、完整性、安全性和合理性。
系统通过“生成—评估—改进”的闭环不断提高结果质量。典型流程包括:
- 生成节点产生结果;
- 反思节点进行多维度审查;
- 条件分支判断是否需要再次迭代;
- 达到质量要求或迭代上限后结束。
反思不应是无限循环,工程实现中需要明确终止条件、重试次数和评价标准。
3.6 Human-in-the-loop:在关键节点保留人工控制
资金操作、生产变更、信息发布等高风险任务不适合完全自动执行。Human-in-the-loop 模式允许工作流在关键节点暂停,等待人工确认后继续。
以 LangGraph 为例,暂停与恢复依赖四个核心要素:
interrupt:主动挂起工作流;memory:保存中断时的上下文状态;checkpointer:记录执行进度和中断位置;thread_id:标识并隔离不同会话或任务。
人机协作的关键不是简单地“弹出确认框”,而是保证暂停前的状态能够持久化,并在确认后从准确位置恢复。
3.7 多 Agent:分工背后是上下文隔离
多 Agent 系统通常由一个协调者 Agent 和多个专家 Agent 组成:
- 协调者负责任务分解、调度、冲突处理和结果整合;
- 专家 Agent 专注于某个领域或子任务。
多 Agent 的表面价值是专业分工和工具分类,更深层的价值则是隔离上下文窗口。子任务中的多轮试错、工具日志和中间推理都留在子 Agent 内部,主 Agent 只接收最终结果,从而减少上下文污染。
因此,多 Agent 系统不只是组织架构的变化,也是复杂任务下上下文工程的自然延伸。
四、意图识别与任务路由
成熟的 Agent 不能只理解用户字面上的问题,还需要结合当前对话、历史交互、用户画像和系统状态,推断背后的真实需求。
从工程角度看,意图识别通常是一个分类与路由问题:判断请求属于哪个领域、需要执行哪类操作,以及应该交给哪个模型、工具或流程。
4.1 领域与操作
路由系统可以将请求拆分为两个维度:
- 领域(Domain):法律、医疗、编程、旅游等高层主题;
- 操作(Operation):摘要、翻译、代码编写、图片编辑、预约等任务类型。
推理阶段根据输入的语义、关键词和上下文线索判断领域及操作,再按照预设规则选择下游能力。
可靠的路由输出需要满足三个条件:
- 支持兜底:无法准确匹配时返回
other,不要强行分类; - 精准匹配:依据选项描述和用户最新输入进行判断;
- 严格格式:输出稳定、可直接解析的 JSON。
例如:
1 | {"route": "other"} |
4.2 小模型分类,大模型兜底
通用路由模型面对金融、医疗、工业运维等垂直领域时,可能无法准确识别业务边界。书中给出了一种小模型与大模型并行的思路:
- 使用企业自有标注数据微调小模型,处理高频、明确的业务意图;
- 对低置信度、长尾或未覆盖请求,使用大模型通过零样本或少样本提示进行二次判断。
小模型负责低成本、高吞吐的常规分类,大模型负责复杂语义和长尾场景。
训练数据应覆盖:
- 正样本;
- 负样本;
- 长尾意图样本。
训练方式可根据数据和算力条件选择全量微调或 LoRA。相比全量训练,LoRA 冻结原模型参数,仅训练额外的低秩模块,更适合资源有限和需要快速迭代的场景。
五、Deep Research:主动构建研究上下文
检索增强能力经历了从静态到动态、从被动到主动的演进:
- RAG:从静态知识库检索资料并生成答案;
- WAG:通过搜索引擎获取动态网络信息;
- Deep Research Agent:主动规划研究路径,执行多轮搜索、交叉验证和迭代总结。
Deep Research 的关键不是“搜索一次然后总结”,而是让 Agent 根据当前证据判断信息缺口,再决定下一轮应搜索什么。研究过程本身就是一个持续构建和修正上下文的过程。
六、Agent 的长期记忆
上下文窗口中的历史信息属于短期记忆,但随着对话增长,不可能把全部历史永久保留在窗口中。
常见的记忆管理方式包括:
- 滑动窗口:只保留最近若干轮对话;
- 上下文压缩:将较早内容总结后继续保留;
- RAG 长期记忆:将历史对话和用户画像向量化,存入外部数据库,需要时按语义召回。
6.1 Mem0 的记忆生命周期
Mem0 并非简单保存全部对话,而是通过提取、仲裁和持久化管理记忆。
第一步:提取结构化事实
利用 LLM 从原始对话中剔除闲聊,重点提取:
- 个人偏好;
- 姓名、关系、重要日期等基本信息;
- 计划、目标和未来意图;
- 餐饮、旅游、兴趣等服务偏好;
- 健康与饮食限制;
- 职业、工作习惯和专业目标;
- 书籍、电影、品牌等其他长期偏好。
第二步:执行冲突仲裁
新事实进入记忆库前,需要和已有记忆比较,并执行四种原子操作之一:
ADD:新增有效信息;UPDATE:更新已经变化的信息;DELETE:删除矛盾或过期信息;NONE:忽略重复或无效信息。
这一步决定了记忆系统能否长期保持一致,而不是不断积累互相冲突的数据。
第三步:混合存储
- 向量数据库解决语义相似性检索,适合模糊召回;
- 图数据库保存实体关系,适合沿关系链进行逻辑推理。
向量检索回答“哪些记忆和当前问题相似”,图检索则回答“这些事实之间有什么关系”。两者结合可以提升复杂记忆查询的质量。
6.2 高并发记忆服务的缓存策略
在高频对话场景中,可以在 Qdrant 等向量数据库前增加 Redis 缓存热点记忆,并采用“写时失效、读时回填”的策略:
- 写入记忆后,立即清除对应用户的 Redis 缓存;
- 查询时优先读取 Redis;
- 缓存未命中时查询向量数据库,并把结果回填到 Redis。
这种方式既减少热点读取压力,也避免返回已经过期的记忆。
七、Skills:上下文卸载的艺术
Skill 可以理解为一个标准化的专家经验包。它将完成特定任务需要的思考路径、执行流程、工具、参考资料和输出规范封装在独立目录中。
典型目录结构如下:
1 | skill-name/ |
各部分职责为:
SKILL.md:必选,描述触发条件、目标、输入输出、执行步骤和示例;scripts/:可选,保存 Python、Shell 等可执行脚本;references/:可选,保存领域资料和参考文档;assets/:可选,保存图片、配置模板和输出样例等静态资源。
7.1 渐进式加载
传统 Agent 初始化时如果加载全部工具描述,很容易浪费上下文窗口。Skills 通过三级加载降低信息噪声:
- L1:元数据。初始阶段只加载名称和描述,让 Agent 知道有哪些 Skill;
- L2:使用说明。确认某个 Skill 可能适用后,再读取
SKILL.md正文; - L3:执行资源。任务真正需要时,才读取脚本、参考资料或模板。
没有被使用的资源始终留在上下文之外。尤其是 scripts/ 中的代码,可以由 Bash 等工具直接执行,只将执行结果返回给模型,而不必将整个脚本内容放入上下文。
7.2 如何写好 SKILL.md
高质量的 SKILL.md 应当回答五个问题:
- 定时机:什么情况下应该触发这个 Skill?
- 立目标:它要解决的具体问题是什么?
- 理规则:输入如何处理,任务按什么步骤执行,输出遵循什么格式?
- 给示例:正确用法和常见错误分别是什么?
- 划边界:哪些任务不处理,信息不足或发生异常时如何兜底?
Skill 的质量主要不取决于目录里堆了多少材料,而取决于是否把专家经验转化成了模型能够稳定遵循的操作手册。
八、Claude Code 中的 Harness 设计
书中将 Claude Code 作为编程领域 Harness 工程的典型案例。它的核心不是不断增加专用工具和复杂工作流,而是围绕模型的需求构建一个适合持续工作的环境。
主要体现在四个维度:
- Agent Loop:让模型能够持续执行“观察—思考—行动”循环;
- 上下文工程:通过压缩、筛选和隔离控制上下文噪声;
- 持久化与异步:让长任务能够跨会话继续执行;
- 多 Agent 系统:通过不同 Agent 实例实现协作和上下文隔离。
在本地编程场景中,只提供 Read、Write、Bash 等少量通用能力,也可以让模型组合完成复杂任务。相比堆砌大量业务工具,这种设计降低了中间层损耗,给模型保留了更大的自主决策空间。
8.1 SubAgent:隔离执行细节
在 SubAgent 模式中,Agent 本身也可以被视为一种工具:
- 主 Agent 通过一次工具调用唤起 SubAgent;
- SubAgent 在独立上下文中处理具体任务;
- 任务完成后只返回结论和必要产物;
- 子任务中的日志、试错和中间推理不会回流到主上下文;
- 生命周期结束后,SubAgent 的临时上下文随之销毁。
主 Agent 因而可以专注于任务下发、全局协调和结果验收。
8.2 Compact:三级上下文压缩
书中给出了一种三级压缩思路:
- 每轮对话开始前,压缩较早的工具调用及返回结果;
- Token 超过阈值时,将完整历史保存到磁盘,再由 LLM 生成摘要替代原始历史;
- 为 Agent 提供 Compact 工具,使模型在判断上下文过长时可以主动触发压缩。
压缩的目标不是简单删除历史,而是在减少 Token 的同时保留继续完成任务所需的目标、约束、关键决策和当前状态。
8.3 从 Todo List 到 Task Graph
简单的 Todo 模式可以让模型生成待办列表,并保证同一时间只执行一个步骤。但扁平列表存在两个问题:
- 无法表达任务之间的依赖和并行关系;
- 状态只保存在内存中,压缩上下文或进程退出后可能丢失。
Task System 将列表升级为有状态的任务图:
- 节点表示任务;
- 边表示前置依赖;
- 状态持久化到磁盘;
- 支持串行、并行和跨会话恢复。
这使任务生命周期不再受限于单次上下文窗口,是 Agent 处理长周期软件工程任务的重要基础。
九、Claw 类产品:把 Agent 接入日常工作入口
Claw 类产品的重要价值是进入用户最常使用的接口层。Agent 接入飞书、钉钉等即时通信软件后,便可以作为持续在线的数字员工,用户通过私聊或群聊中的 @ 即可下达任务。
定时任务机制进一步让 Agent 从“被动响应”走向“自主调度”:它可以记录未来计划,并在指定时间主动执行,而不必等待用户实时触发。
NanoClaw 的架构可以概括为“一内一外”:
- 内层:运行在 Docker 容器中的核心 Agent,负责推理和任务执行;
- 外层:运行在容器之外的通信与调度机制,负责 IM 消息接入、定时触发和生命周期管理。
这种分层将 Agent 的推理执行环境与外部通信、调度能力解耦,既便于隔离权限,也便于独立扩展不同的用户入口。
十、读后总结
这本书最重要的启发,是把 Agent 开发从“模型能力崇拜”拉回到了系统工程。
一个可靠的 Agent 系统至少需要处理好以下问题:
- 用意图识别确定用户真正需要什么;
- 用计划和任务状态保证复杂目标持续推进;
- 用工具与代码连接真实世界;
- 用反思和人工审核控制错误风险;
- 用短期记忆和长期记忆维持连续性;
- 用 Skills 实现知识与能力的按需加载;
- 用 SubAgent 隔离复杂子任务产生的上下文噪声;
- 用压缩、外部文件和检索控制上下文规模;
- 用持久化任务图支撑跨会话、长周期执行;
- 用权限、容器、通信和调度系统构成模型的运行边界。
最终决定 Agent 能否进入真实生产环境的,往往不只是模型在单次回答中的聪明程度,而是 Harness 能否让它在长时间、多步骤、有风险的任务中保持可靠、可控、可恢复和可观测。
注:本文为邢云阳《Harness工程:从上下文管理到Agent系统构建》的个人读书笔记,内容依据阅读标注整理。