
1. 从一个类比开始为什么 Agent 需要一套“马具”第一次听到 Agent Harness 这个词的时候我脑子里冒出来的画面是骑马。马本身有力量、有速度、有自己的脾气但你不可能直接抓着马鬃就上路。你需要一套马具——缰绳、马鞍、嚼子、肚带——把马的力量引导到你想去的方向同时保证骑手不被甩下来。Agent 和 LLM 的关系几乎一模一样。LLM 是那匹马它有推理能力、有知识储备、有语言生成能力但它本身只是一个“文本进、文本出”的函数。你给它一段话它给你一段话仅此而已。它不会自己去查资料、不会自己执行代码、不会自己记住上次对话的内容、更不会在任务失败的时候自己重试。而 Agent 要做的事情是把一个模糊的用户目标拆解成一系列可执行的步骤调用工具去完成每一步处理中间出现的错误最后把结果拼装成用户能看懂的答案。这中间的落差就是 Harness 要填的坑。所以如果有人问我“harness 和 agent 到底啥区别”我一般会这么解释Agent 是一个概念指的是“能自主完成任务的智能体”Harness 是让这个概念落地的工程骨架。你可以把 Agent 理解成“司机”把 Harness 理解成“车”——司机决定去哪、怎么走但车提供了方向盘、油门、刹车、仪表盘、安全带。没有车司机只能跑步没有 HarnessAgent 只能停留在论文里的流程图。这篇文章我想做的事情是用一个足够直观的类比把 Agent Harness 拆成 12 个组件逐个讲清楚每个组件解决什么问题、为什么需要它、实际做的时候容易踩什么坑。这不是一篇学术综述而是一个做过几个 Agent 项目的人把自己踩过的坑和想明白的道理整理出来。提示本文讨论的 Harness 是通用意义上的 Agent 运行时系统不绑定任何特定厂商或框架。不同框架对组件的命名和拆分方式可能不同但核心职责是相通的。2. 先搞清楚 Harness 到底在管什么2.1 没有 Harness 的 LLM 调用是什么样的最原始的 LLM 调用长这样你拼一个 prompt发给模型 API拿到一段文本结束。这个过程是无状态的、单轮的、没有工具能力的。你让它“帮我查一下明天北京的天气”它只能根据训练数据编一个答案因为它没有实时数据也没有调用天气 API 的能力。稍微进阶一点的用法是 ReAct 模式让模型输出“Thought / Action / Observation”的循环模型自己决定要不要调用工具工具返回结果后再喂给模型模型继续推理。这个模式已经有点 Agent 的影子了但它仍然是一个“裸调用”——没有状态管理、没有错误恢复、没有并发控制、没有成本追踪。我早期做的一个项目就是直接用 ReAct 循环硬写结果跑了不到二十轮就崩了模型开始重复调用同一个工具、上下文窗口被工具返回的原始数据撑爆、某次 API 超时导致整个循环卡死。这些问题不是模型能力不够而是缺少一层工程基础设施。2.2 Harness 的核心职责可以用一句话概括Harness 要解决的问题是让一个不确定的、可能出错的、有上下文限制的模型在一个可控的、可观测的、可恢复的运行时环境里稳定地完成多步骤任务。这句话里有几个关键词值得拆开看。“不确定”意味着你需要重试和兜底机制“有上下文限制”意味着你需要记忆管理和信息压缩“可控”意味着你需要权限和边界控制“可观测”意味着你需要日志和追踪“可恢复”意味着你需要状态持久化和断点续跑。把这几个需求翻译成工程组件就得到了下面要讲的 12 个模块。我用一个表格先做个总览后面再逐个展开。编号组件名称一句话职责类比1任务解析器把用户输入转成结构化目标导航输入目的地2规划器把目标拆成可执行步骤规划行车路线3工具注册表管理可调用的工具集合车上的各种按钮4工具执行器实际调用工具并处理返回按下按钮并读取结果5上下文管理器管理对话历史和中间状态行车记录仪加记忆6记忆系统长期存储和检索关键信息你记住常走的路7模型路由层选择合适的模型处理不同任务市区用电车高速用油车8输出解析器从模型输出中提取结构化数据读懂仪表盘读数9错误处理与重试处理失败并决定下一步爆胎了换备胎10循环控制器控制 Agent 的执行节奏和终止条件油门和刹车11可观测性层记录、追踪、回放整个执行过程黑匣子12安全与权限层限制 Agent 的行为边界安全带和限速3. 输入侧组件任务解析、规划与工具准备3.1 任务解析器别急着让模型干活很多人拿到用户输入之后第一反应是直接丢给模型让它开始干活。这个做法在简单任务上没问题但在复杂任务上几乎必然翻车。原因很简单用户的自然语言输入里充满了歧义、隐含假设和缺失信息。举个例子用户说“帮我整理一下最近的销售数据”。这句话里至少有五个未定义的东西哪个时间段算“最近”哪个区域或产品线的销售数据整理成什么格式输出到哪里需不需要做汇总计算任务解析器要做的事情就是把这些歧义显式化。常见的做法是先用一个轻量模型做意图识别和槽位填充把用户输入转成一个结构化的任务描述。这个结构化的描述通常包含任务类型、目标对象、约束条件、期望输出格式、优先级。我在实际项目里的做法是维护一个任务模板库每个模板定义了必需的槽位和可选的槽位。解析器的工作就是判断用户输入匹配哪个模板然后抽取槽位值。缺失的必需槽位要么追问用户要么用默认值填充并在后续步骤中标注假设。注意任务解析这一步不要用最强的模型。它需要的是稳定性和速度不是创造力。用一个小模型加一套清晰的抽取规则效果往往比用大模型硬 prompt 更好而且成本低一个数量级。3.2 规划器从目标到步骤的桥梁规划器是 Harness 里最容易被低估的组件。很多人觉得规划就是让模型输出一个步骤列表然后照着执行就行了。但实际做起来规划的质量直接决定了整个任务的成败。规划器要解决的核心问题是给定一个目标和一组可用工具生成一个步骤序列使得每一步的输入可以从前面步骤的输出或初始上下文中获得且整个序列在有限的步骤内能完成目标。这里面的难点在于依赖管理。步骤之间不是独立的后一步往往依赖前一步的输出。如果规划器生成的步骤序列存在循环依赖或者缺失依赖执行阶段就会卡住。我见过的最常见的错误是规划器生成了一个“先汇总再查询”的序列但汇总需要的数据还没查出来。我的经验是让规划器输出一个带依赖标注的 DAG有向无环图而不是一个线性的步骤列表。每个步骤标注它依赖哪些前置步骤的输出。这样在执行阶段循环控制器可以按照拓扑排序来决定哪些步骤可以并行执行哪些必须串行等待。另一个经验是规划粒度要适中。太粗的规划会导致单步任务过于复杂模型在执行时容易迷失太细的规划会导致步骤数量爆炸上下文被大量中间结果占满。一般来说单个步骤的工作量控制在“一次模型调用加一到两次工具调用”能完成的范围内比较合适。3.3 工具注册表Agent 的手和脚工具注册表管理的是 Agent 能调用的所有工具。每个工具需要定义名称、描述、参数 schema、返回值格式、调用方式、超时时间、是否需要权限。这里有一个很容易被忽视的点工具描述的质量直接影响模型选择工具的正确率。我见过太多项目把工具描述写得含糊不清比如“查询数据”这种描述模型根本不知道这个工具查的是什么数据、需要什么参数、返回什么格式。好的工具描述应该像一份微型 API 文档让模型一看就知道什么时候该用它、怎么用。工具注册表还需要处理工具版本管理的问题。当工具的参数或返回值发生变化时需要保证正在运行的 Agent 任务不受影响。常见的做法是给工具加版本号注册表同时维护多个版本规划器在生成步骤时指定使用哪个版本。工具属性说明常见坑名称唯一标识符名称太相似导致模型混淆描述功能说明和使用场景描述太短或太模糊参数 schema参数类型和约束缺少必填/选填标注返回值格式返回数据的结构返回格式不稳定超时时间最大等待时间超时设置过长拖慢整体权限要求是否需要额外授权权限检查遗漏导致越权3.4 工具执行器调用只是开始工具执行器负责实际调用工具并处理返回结果。听起来很简单但实际做起来有一堆细节要处理。首先是参数校验。模型生成的工具调用参数经常有格式问题该传数字的传了字符串、该传数组的传了单个值、必填参数缺失。执行器需要在调用之前做一轮校验校验失败时要么自动修正要么把错误信息返回给模型让它重新生成。其次是超时和并发控制。有些工具调用可能很慢如果不设超时整个 Agent 循环就会被一个卡住的工具拖死。并发控制则是防止 Agent 在短时间内发起大量工具调用导致外部服务被限流或产生意外费用。最后是返回值处理。工具返回的原始数据往往很大直接塞进上下文会迅速耗尽窗口。执行器需要做一轮预处理截断过长的文本、提取关键字段、把结构化数据转成模型更容易理解的格式。4. 状态与记忆让 Agent 不“失忆”4.1 上下文管理器窗口是稀缺资源上下文窗口是 Agent 最稀缺的资源。每一次模型调用都要把系统提示、对话历史、工具返回结果、当前任务状态全部塞进去很容易就撑满了。上下文管理器要做的就是在这个限制下尽可能保留对当前任务最有用的信息。常见的策略有三种。第一种是滑动窗口只保留最近 N 轮对话更早的内容丢弃。这个策略简单但粗暴容易丢掉关键的前置信息。第二种是摘要压缩把较早的对话用模型总结成一段简短摘要保留核心信息。第三种是结构化提取把对话中的关键事实抽取成结构化字段只保留字段不保留原始对话。我在实际项目里通常组合使用最近的几轮保留原文中间部分做摘要更早的部分只保留结构化提取的关键事实。这样既保证了近期上下文的完整性又不会让窗口被历史信息占满。提示上下文管理的一个实用技巧是给不同类型的信息打标签比如“用户目标”“已完成步骤”“待办步骤”“关键事实”“工具返回”。在拼接上下文时按照标签优先级决定保留顺序优先保留“用户目标”和“待办步骤”。4.2 记忆系统跨会话的知识沉淀上下文管理器管的是单次任务内的短期记忆记忆系统管的是跨任务的长期记忆。这两者的区别类似于你工作时的桌面和你的文件柜桌面放当前正在处理的东西文件柜放以后可能用到的东西。记忆系统通常需要支持两种操作写入和检索。写入的时机包括任务完成后的结果总结、用户明确表达的偏好、任务执行中发现的重要事实。检索的时机是在新任务开始时根据任务描述去记忆库里找相关的历史信息。检索的实现方式有几种基于关键词的检索、基于向量相似度的检索、基于知识图谱的检索。关键词检索简单但召回率低向量检索召回率高但可能引入不相关的结果知识图谱检索精度高但构建成本大。实际项目中常见的是向量检索加关键词检索的混合方案。我踩过的一个坑是记忆系统写入的内容没有做去重和冲突检测导致同一个事实被反复写入检索时返回一堆重复结果。后来加了一个简单的去重逻辑写入前先检索是否有相似内容如果有就更新而不是新增。4.3 模型路由层不是所有任务都需要最强模型模型路由层根据任务的特点选择合适的模型。这个组件的存在理由很直接最强模型最贵也最慢但很多子任务并不需要那么强的能力。任务解析、格式转换、简单摘要这类任务用一个小模型就够了。复杂的推理、规划、代码生成才需要上大模型。路由层需要维护一个任务类型到模型的映射表并根据实际效果动态调整。路由的另一个维度是成本控制。当任务预算有限时路由层需要优先使用低成本模型只在关键步骤上使用高成本模型。我通常会在规划阶段就给每个步骤标注一个“重要性”分数路由层根据分数决定用哪个档位的模型。任务类型推荐模型档位理由意图识别小模型任务简单追求速度和成本槽位抽取小模型规则性强小模型足够任务规划大模型需要推理和全局视野工具选择中模型需要理解工具描述和任务上下文结果总结中模型需要语言组织能力错误诊断大模型需要分析复杂失败原因5. 执行控制循环、错误与终止5.1 循环控制器Agent 的心跳循环控制器是 Harness 的发动机它决定了 Agent 以什么节奏执行、什么时候继续、什么时候停止。一个典型的 Agent 循环是这样的检查当前状态决定下一步动作执行动作更新状态判断是否完成如果没完成就继续循环。这个循环里最容易出问题的地方是终止条件。如果终止条件设置不当Agent 可能陷入死循环反复执行同一个动作、反复调用同一个工具、或者在两个状态之间来回跳转。我见过最离谱的一次是 Agent 在“查询数据”和“数据格式不对重新查询”之间循环了四十多次烧掉了一大笔 API 费用。防止死循环的常见手段包括设置最大循环次数、检测重复动作、检测状态是否在收敛。最大循环次数是最简单的兜底但设置多少合适需要根据任务复杂度来定。重复动作检测是检查最近几步的动作是否高度相似如果是就强制中断或切换策略。状态收敛检测是看当前状态和目标状态的差距是否在缩小如果连续几步都没有缩小就说明策略有问题。5.2 错误处理与重试失败是常态在 Agent 系统里失败不是异常而是常态。工具会超时、API 会限流、模型会输出格式错误的内容、外部服务会临时不可用。错误处理组件的职责就是让这些失败不至于让整个任务崩溃。错误处理的第一步是分类。不同类型的错误需要不同的处理策略。工具超时可以重试参数错误需要修正后重试权限错误需要升级或跳过外部服务不可用可能需要等待或切换备用方案。重试策略需要设置退避机制。立即重试往往没用因为失败原因可能还没恢复。常见的做法是指数退避第一次等 1 秒第二次等 2 秒第三次等 4 秒以此类推。同时要设置最大重试次数避免无限重试。注意重试不是万能的。对于“模型输出了格式错误的内容”这类错误单纯重试可能得到同样的错误结果。这时候需要把错误信息反馈给模型让它基于错误信息重新生成。这个模式叫做“反思重试”比盲目重试有效得多。5.3 输出解析器从自由文本到结构化数据模型输出的是自然语言文本但 Harness 需要的是结构化数据。输出解析器负责在这两者之间做转换。最简单的做法是让模型输出 JSON然后用 JSON 解析器解析。但实际做起来模型输出的 JSON 经常有问题多了 markdown 代码块标记、少了闭合括号、字符串里有未转义的特殊字符。解析器需要做容错处理先尝试直接解析失败后尝试提取 JSON 片段再失败就尝试修复常见格式问题。更稳健的做法是使用结构化输出功能如果模型 API 支持的话或者使用函数调用模式让模型直接输出结构化的工具调用请求。这样可以从源头上避免格式问题。解析器还需要处理“模型输出不符合预期 schema”的情况。比如期望一个数组但得到了一个字符串期望一个枚举值但得到了一个不在枚举范围内的值。这时候需要决定是报错重试还是做宽松处理。6. 可观测性与安全让 Agent 可控可查6.1 可观测性层黑匣子不能是黑的Agent 执行过程如果不记录出了问题根本没法排查。可观测性层要记录的东西包括每次模型调用的输入输出、每次工具调用的参数和返回、每一步的状态变化、每个决策点的选择理由、整个任务的耗时和成本。记录的粒度需要平衡。记录太粗排查问题时信息不够记录太细存储成本高且噪音大。我的经验是模型调用的完整输入输出必须记录工具调用的参数和返回摘要必须记录中间状态的完整快照可以按需记录。除了记录可观测性层还需要支持追踪和回放。追踪是把一个任务的完整执行链路串起来方便看每一步的因果关系。回放是把历史执行过程重新播放出来用于调试和优化。这两个功能在排查复杂问题时特别有用。记录内容详细程度用途模型调用输入完整排查 prompt 问题模型调用输出完整排查生成质量问题工具调用参数完整排查参数错误工具返回结果摘要排查数据问题状态变化关键字段排查逻辑问题决策理由简要说明排查策略问题耗时和成本每步统计优化性能成本6.2 安全与权限层给 Agent 划红线Agent 能调用工具、能执行代码、能访问外部服务这意味着如果不管控它可能造成真实的损害。安全与权限层要做的就是给 Agent 的行为划出明确的边界。权限控制的第一层是工具级别的白名单。不是所有工具都对所有任务开放根据任务类型和用户权限决定哪些工具可用。第二层是参数级别的校验比如文件路径必须在指定目录下、API 调用不能超过频率限制、金额操作不能超过阈值。第三层是操作级别的审批对于高风险操作如删除数据、发送邮件、执行支付需要人工确认或二次授权。除了权限控制安全层还需要做输入输出的内容过滤。输入侧防止 prompt 注入攻击输出侧防止敏感信息泄露。这些过滤规则需要根据具体应用场景来定制。提示安全层的一个实用设计是“干跑模式”。在真正执行之前让 Agent 先输出它打算做什么人工确认后再实际执行。这个模式在开发调试阶段特别有用可以避免 Agent 在调试过程中造成意外损害。7. 12 个组件之外那些容易被忽略的工程细节7.1 组件之间的通信协议12 个组件不是孤立的它们之间需要通信。通信协议的设计直接影响系统的可维护性和可扩展性。我见过一些项目把组件之间的调用写成了硬编码的函数调用结果想换一个规划器实现时发现要改十几个地方。比较好的做法是定义清晰的数据契约每个组件的输入和输出都用统一的数据结构表示组件之间通过这个数据结构通信而不是直接互相调用。这样每个组件都可以独立替换和测试。7.2 配置管理Agent 系统有大量配置项模型选择、超时时间、重试次数、上下文窗口大小、工具权限、安全阈值。这些配置如果散落在代码各处维护起来会非常痛苦。建议统一到一个配置文件或配置中心支持按环境开发/测试/生产切换。7.3 测试策略Agent 系统的测试比传统软件难得多因为输出是不确定的。常见的测试策略包括单元测试覆盖各个组件的确定性逻辑、集成测试用固定输入验证端到端流程、评估测试用一组标准任务衡量整体效果。评估测试需要定义清晰的评分标准比如任务完成率、平均步骤数、平均成本、错误率。我在实际项目里会维护一个“回归测试集”收集历史上出现过的典型任务和对应的正确结果每次修改 Harness 之后跑一遍确保没有引入回归。这个习惯帮我省了很多次线上事故。7.4 成本控制Agent 系统的成本可能很高因为一次任务可能涉及几十次模型调用和工具调用。成本控制需要从多个层面入手模型路由层选择合适档位的模型、上下文管理器控制输入长度、循环控制器限制最大步骤数、可观测性层实时监控成本。我通常会设置一个任务级别的成本上限当累计成本接近上限时循环控制器会强制 Agent 进入“收尾模式”不再探索新方案而是基于已有信息给出当前最优答案。8. 常见问题与排查技巧实录8.1 Agent 陷入死循环怎么办这是最常见的问题。排查思路是先看可观测性层的记录找到循环的模式。是重复调用同一个工具还是在两个状态之间跳转还是每次都在同一步失败重试如果是重复调用同一个工具检查工具返回的结果是否被正确解析和传递。很多时候是返回值解析失败导致模型以为工具没被调用于是反复调用。如果是状态跳转检查状态更新逻辑是否有 bug。如果是失败重试检查错误处理策略是否合理是否需要升级到人工介入。8.2 上下文窗口不够用怎么办先看是什么占满了窗口。如果是工具返回的原始数据太大在工具执行器里加截断和摘要。如果是对话历史太长在上下文管理器里加摘要压缩。如果是系统提示太长精简提示内容把不必要的信息移到记忆系统里按需检索。一个实用的技巧是给上下文做“分层”核心信息用户目标、当前步骤、关键约束始终保留辅助信息历史对话、工具返回详情按需加载背景信息系统提示、工具描述做精简。8.3 模型不按预期格式输出怎么办首先检查 prompt 是否足够清晰。很多时候格式问题是因为 prompt 里没有明确说明期望的格式。其次检查是否使用了结构化输出或函数调用功能这些功能可以从源头保证格式。如果都不行在输出解析器里加容错逻辑并在解析失败时把错误信息反馈给模型让它重新生成。8.4 工具调用失败率太高怎么办先分类失败原因。如果是参数错误检查工具描述是否清晰、参数 schema 是否准确。如果是超时检查工具本身的性能必要时增加超时时间或优化工具实现。如果是权限问题检查权限配置是否正确。如果是外部服务不稳定考虑增加重试和备用方案。问题现象可能原因排查方向死循环状态更新 bug 或返回值解析失败检查可观测性记录上下文溢出工具返回太大或历史太长加截断和摘要格式错误prompt 不清晰或缺少结构化输出优化 prompt 或启用函数调用工具失败率高参数错误、超时、权限、外部服务分类排查成本过高模型档位过高或步骤太多优化路由和循环控制结果质量差规划不合理或上下文丢失检查规划器和上下文管理8.5 几个我踩过的坑第一个坑是过度依赖大模型做所有事情。早期我让大模型同时负责规划、工具选择、结果总结结果成本高得离谱而且因为上下文太长导致质量下降。后来拆分成多个小模型各司其职成本和效果都好了很多。第二个坑是忽略了工具返回值的预处理。有一次工具返回了一个巨大的 JSON直接塞进上下文导致窗口溢出Agent 直接崩溃。后来在工具执行器里加了返回值摘要逻辑只提取关键字段。第三个坑是没有做状态持久化。有一次任务跑到一半服务重启所有中间状态丢失只能从头开始。后来加了状态快照支持断点续跑。第四个坑是权限控制太宽松。调试阶段给 Agent 开了所有工具的权限结果它在一个测试任务里误删了数据。后来严格按任务类型分配工具权限高风险操作必须二次确认。9. 从 12 个组件回看那个类比回到开头那个骑马的类比。现在我们可以把 12 个组件对应到马具的各个部分任务解析器是你看地图确定目的地规划器是你规划路线工具注册表是你马鞍上挂的各种装备工具执行器是你实际使用这些装备上下文管理器是你的短期记忆记忆系统是你的长期经验模型路由层是你根据路况选择快马还是慢马输出解析器是你读懂马的反馈错误处理与重试是你遇到障碍时的应对循环控制器是你的骑乘节奏可观测性层是你的行车记录安全与权限层是你的缰绳和护栏。这套马具的目的不是限制马的能力而是让马的能力可以被安全、可控、可重复地使用。Agent Harness 也一样它不是要替代 LLM 的能力而是要让 LLM 的能力在真实任务中稳定发挥。我在实际项目里的体会是Harness 的工程质量往往比模型选择更能决定 Agent 系统的成败。同一个模型套上不同的 Harness效果可能差出好几倍。所以如果你在做 Agent 开发花时间打磨 Harness 的每一个组件比反复换模型试效果要划算得多。最后分享一个小技巧如果你刚开始做 Agent Harness不要一上来就把 12 个组件全部实现。先从最小的可用闭环开始——任务解析、规划、工具执行、循环控制这四个组件就能跑通一个基本流程。然后根据实际遇到的问题逐步补充上下文管理、错误处理、可观测性等组件。这样迭代出来的 Harness 更贴合你的实际需求也不会在一开始就被过度设计拖垮。