《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)通过“推理—行动—观察”循环驱动任务执行:

  1. 理解问题并进行推理;
  2. 判断是否需要调用外部工具;
  3. 选择工具并生成参数;
  4. 获取工具执行结果;
  5. 将观察结果加入上下文;
  6. 基于更新后的上下文继续推理,或者输出最终答案。

从上下文工程的视角看,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:检查正确性、完整性、安全性和合理性。

系统通过“生成—评估—改进”的闭环不断提高结果质量。典型流程包括:

  1. 生成节点产生结果;
  2. 反思节点进行多维度审查;
  3. 条件分支判断是否需要再次迭代;
  4. 达到质量要求或迭代上限后结束。

反思不应是无限循环,工程实现中需要明确终止条件、重试次数和评价标准。

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):摘要、翻译、代码编写、图片编辑、预约等任务类型。

推理阶段根据输入的语义、关键词和上下文线索判断领域及操作,再按照预设规则选择下游能力。

可靠的路由输出需要满足三个条件:

  1. 支持兜底:无法准确匹配时返回 other,不要强行分类;
  2. 精准匹配:依据选项描述和用户最新输入进行判断;
  3. 严格格式:输出稳定、可直接解析的 JSON。

例如:

1
{"route": "other"}

4.2 小模型分类,大模型兜底

通用路由模型面对金融、医疗、工业运维等垂直领域时,可能无法准确识别业务边界。书中给出了一种小模型与大模型并行的思路:

  • 使用企业自有标注数据微调小模型,处理高频、明确的业务意图;
  • 对低置信度、长尾或未覆盖请求,使用大模型通过零样本或少样本提示进行二次判断。

小模型负责低成本、高吞吐的常规分类,大模型负责复杂语义和长尾场景。

训练数据应覆盖:

  • 正样本;
  • 负样本;
  • 长尾意图样本。

训练方式可根据数据和算力条件选择全量微调或 LoRA。相比全量训练,LoRA 冻结原模型参数,仅训练额外的低秩模块,更适合资源有限和需要快速迭代的场景。

五、Deep Research:主动构建研究上下文

检索增强能力经历了从静态到动态、从被动到主动的演进:

  1. RAG:从静态知识库检索资料并生成答案;
  2. WAG:通过搜索引擎获取动态网络信息;
  3. 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
2
3
4
5
skill-name/
├── SKILL.md
├── scripts/
├── references/
└── assets/

各部分职责为:

  • 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 应当回答五个问题:

  1. 定时机:什么情况下应该触发这个 Skill?
  2. 立目标:它要解决的具体问题是什么?
  3. 理规则:输入如何处理,任务按什么步骤执行,输出遵循什么格式?
  4. 给示例:正确用法和常见错误分别是什么?
  5. 划边界:哪些任务不处理,信息不足或发生异常时如何兜底?

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:三级上下文压缩

书中给出了一种三级压缩思路:

  1. 每轮对话开始前,压缩较早的工具调用及返回结果;
  2. Token 超过阈值时,将完整历史保存到磁盘,再由 LLM 生成摘要替代原始历史;
  3. 为 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 系统至少需要处理好以下问题:

  1. 用意图识别确定用户真正需要什么;
  2. 用计划和任务状态保证复杂目标持续推进;
  3. 用工具与代码连接真实世界;
  4. 用反思和人工审核控制错误风险;
  5. 用短期记忆和长期记忆维持连续性;
  6. 用 Skills 实现知识与能力的按需加载;
  7. 用 SubAgent 隔离复杂子任务产生的上下文噪声;
  8. 用压缩、外部文件和检索控制上下文规模;
  9. 用持久化任务图支撑跨会话、长周期执行;
  10. 用权限、容器、通信和调度系统构成模型的运行边界。

最终决定 Agent 能否进入真实生产环境的,往往不只是模型在单次回答中的聪明程度,而是 Harness 能否让它在长时间、多步骤、有风险的任务中保持可靠、可控、可恢复和可观测

注:本文为邢云阳《Harness工程:从上下文管理到Agent系统构建》的个人读书笔记,内容依据阅读标注整理。