Agent Harness工程:提示词设计的期望值管理与分层架构

📅 发布时间:2026/10/17 11:52:47
Agent Harness工程:提示词设计的期望值管理与分层架构
1. 先从期望值管理说起提示词设计的真正瓶颈在哪里我在接手相关项目时最容易踩的坑不是模型能力不够而是把提示词写成了需求说明书——罗列一堆功能、一堆约束看起来面面俱到实际跑起来一塌糊涂。做过几个智能体项目之后我逐渐意识到一个反直觉的事实Agent Harness Engineering 的核心工作不是把话术写漂亮而是把期望值管理好——既包括对模型的期望值也包括对任务本身的期望值。什么叫期望值管理举一个生活化的例子。你让一个新同事去整理会议室如果只说把会议室弄干净他大概率只会擦桌子不会想到去检查投影仪线缆、补充白板笔。这不是他笨而是你对干净的定义没跟上他的执行维度。Agent 也是一样模型接收到提示词时它面对的是一个个 token 序列它不知道你心里那个正确结果长什么样。它只知道你让我做什么、做到什么程度、做到什么算好、做不到会怎样。所以在设计 AI Agent 的 Harness 提示词时我的第一个原则就是先把任务维度和评估维度说清楚再谈具体执行。换句话说提示词就是 Agent 的岗位说明书而 Harness 则是那套绩效指标。没有绩效指标的岗位说明书员工只能凭感觉干活——模型尤其如此它比你想象的更擅长猜也比你想象的更擅长猜错。这里还要澄清一个常见的误区很多人觉得 Agent 提示词越长越详细越好于是堆了几千字背景资料。实测下来长提示词在简单任务上反而容易引入噪声模型会被无关细节带偏。真正高效的 Harness 提示词长度取决于任务本身的复杂度边界你能用一句话说清楚的任务就不要用三段话去稀释它而需要多轮工具调用的任务则必须把状态流转和结果判定写进去否则 Agent 会陷入无限循环。一句话总结Harness 提示词不是写作文是搭框架。框架里最重要的是目标可度量、过程可观察、失败可兜底。1.1 为什么越长的提示词越不一定高效我做过一组对照实验同一个数据抽取任务分别用 200 字、800 字、2000 字三种提示词去跑。结果很有意思——2000 字版本在抽取准确率上并没有显著优于 800 字版本反而在模型响应延迟和token 消耗上明显变差。原因是模型在长上下文中需要处理的信息量变大注意力分布被稀释它需要额外判断哪些信息是约束、哪些信息是背景、哪些信息是无关描述这本身就是一种认知负担。如果你在做 Agent Harness 工程一定要注意一件反直觉的事提示词中每增加一句看似重要的话都可能增加一个让模型误解的维度。真正的工程化做法是把提示词拆成变体模板和固定骨架固定骨架负责结构化约束变体模板只注入任务相关的动态参数。这样既保持了提示词的稳定质量又避免了每次任务都从零编写长文本的不可控性。1.2 期望值管理包含三件具体的事第一明确完成的定义。不要只写提取文章要点要写提取 3 个要点每个要点不超过 20 字必须直接引用原文关键句。当你给模型一个可度量的标准它就能自我校验而不是交给你一份看起来像要点但其实是总结的四不像。第二明确失败的形态。告诉 Agent 什么情况下应该承认失败什么情况下可以重试什么情况下必须调用人工介入。我见过太多 Agent 在找不到答案时反复自我编造原因就是提示词里只有你要完成任务没有如果完成不了你会付出什么代价。这里的代价机制不一定要写得像惩罚可以是如果你无法确定答案请明确输出 UNKNOWN 并附带理由。一个负反馈清晰的 Agent比一个只会硬刚的 Agent 可靠得多。第三明确过程的可见性。对多步骤任务要让 Agent 在每一步输出一个结构化的中间结果标记例如 Step_Status: IN_PROGRESS / DONE / FAILED。这样不仅方便调试也能让模型在逻辑上保持连贯。很多 Agent 跑飞不是因为模型笨而是它自己都忘了自己已经做完了哪一步。2. 把 Harness 工程拆成三个层次约束、编排、反馈我把 Agent Harness 提示词抽象成三个层次和基础模型调用层解耦。这三个层次分别是约束层Constraint Layer、编排层Orchestration Layer、反馈层Feedback Layer。每个层次解决一类问题约束层解决不能做什么编排层解决按什么顺序做反馈层解决做成什么样下一步怎么办。这个分层思路不是我拍脑袋想出来的而是从一次失败的项目中总结的。当时我设计了一个自动调研 Agent提示词里既包含不要编造来源按顺序调用搜索工具如果结果不充分就补充搜索等要求结果模型在长上下文里把这三类信息全搅在一起。它以为先搜索再整理是一个顺序建议而非硬性流程最后产出了一份结构混乱、来源缺失的调研报告。后来我做了分层把不同类型的指令放到不同的标记区块里准确率立刻上升了。2.1 约束层如何用反例而不是口号告诉模型不要做什么约束层最容易犯的错是写禁止幻觉不要编造请保持事实准确。这种口号式约束对模型几乎没有约束力因为它们只是抽象层面的形容词没有具体到行为层面。真正有效的约束写法是给反例且给出反例的判断标准。举个例子不要写不得编造数据来源而要写当你引用某个数据来源时必须满足以下条件该来源是调用搜索工具返回的链接或输入文档中出现过的显式来源如果你无法确认来源请输出 UNKNOWN_SOURCE。这种写法把编造从伦理问题变成了流程问题模型更容易理解它应该怎么做。我常用的约束层模板结构是约束规则 - 当你无法从上下文或工具结果中确认某条信息时禁止生成该信息。 - 若工具调用失败两次输出 ERROR 标记并终止当前步骤。 - 输出格式必须符合 JSON Schema不允许包含 Markdown 代码块包裹。关键在于每条约束前加一个触发条件。模型本质上是一个条件驱动的系统你要告诉它在什么状态、什么情况下执行什么规则而不是不分青红皂白地铺一片禁令。2.2 编排层从做事情到知道自己在做事情的哪个阶段编排层是 Agent Harness 提示词最核心的部分它负责把任务拆成若干步骤并为每个步骤定义输入、输出和出口条件。这里我的核心心得是编排层不要写成你首先做 A然后做 B最后做 C而要写成在阶段 A 中你的目标是 X如果 X 达成进入阶段 B如果 X 未达成输出 DEBUG 信息并停止。前一种写法是线性的流程建议模型不会因为外部工具返回异常而导致流程中断——它会强行把流程走完哪怕中间结果已经无效。我较常用的一种编排模式叫工作流状态机。提示词中显式维护一个状态变量例如当前状态: SEARCH 允许状态: SEARCH - ANALYZE - RETRY - DONE 目标产出: 结构化 JSON 列表 转移条件: - SEARCH 状态中若获得至少1条有效结果转移到 ANALYZE。 - SEARCH 状态中若连续2次工具调用超时转移到 RETRY。 - RETRY 状态下若重试次数达到3次输出 FAILED 并终止。有些开发者会觉得这种写法太重、太机械但我的经验是在 Agent 自动执行任务时一个明确的状态机比一段散文式的流程描述可靠十倍。模型毕竟是概率系统它在长距离推理中容易走失状态机相当于给它的每一步都钉了一个锚点让它不至于迷路。2.3 反馈层如何让 Agent 学会自我纠偏而不是自我感动反馈层常常被忽略但它是让 Agent 从能用走向好用的关键。很多 Agent 跑出来的结果不是不能看而是看着能用、细看全是错这就是自我纠偏能力不足的表现。反馈层的设计思路很简单在每个关键步骤后要求 Agent 对自身输出做一个快速评估。评估不是让它写我认为结果不错这种废话而是给它一个可执行的检查清单。比如步骤完成后自查 - [ ] 所有引用的来源均出现于工具返回结果中。 - [ ] 输出 JSON 已通过语法校验。 - [ ] 未出现与任务目标无关的额外说明。 - 若以上检查失败请标记 SELF_CHECK_FAILED 并输出修正后的版本。实测中这种自检清单能显著减少低质量输出因为模型在生成时会主动增加一层内部注意力对照清单去检查自己写的内容。虽然它不能保证完全正确但它的意义在于让模型在自我感觉良好和实际输出合格之间建立一条索引从而减少幻觉的静默传播。3. 从一次失败的企业内部知识库问答 Agent复盘 Harness 设计的坑我做过一个企业内部知识库问答 Agent当时的任务比较简单根据员工提出的问题在知识库中检索相关文档并回答。听起来很常规但在实际部署中遇到了大量问题一度让我怀疑是不是模型能力不行。后来复盘时发现问题几乎全部出在 Harness 提示词设计上而不是模型本身。这个项目让我积累了一套排查经验。可以说提示词工程的最难之处不是写出来而是排掉那些写出来的字带来的副作用。有些字词看起来能帮助模型理解任务却可能在关键路径上引入歧义。下面记录四个真实踩过的坑每一个都有对应的修复方案。3.1 坑一用你是一个智能助手开场反而削弱了工具调用表现当时我的提示词开头写的是你是一个友好的智能助手请帮助用户解答知识库相关的问题。乍看之下没问题但实际跑起来发现模型在调用检索工具时的主动性很低经常不调用工具就直接凭训练记忆回答甚至生成一些编造的段落。原因在于友好助手这种角色设定激活了模型偏社交化的回答模式它更倾向于直接给出流畅回答而不是先通过工具获取证据。修复方式很简单把所有冗余人设去掉改成你是知识库检索系统的查询执行器。你唯一的目标是通过检索工具找出与用户问题相关的内容并基于检索结果回答。如果你未调用检索工具必须输出 TOOL_REQUIRED。这样改完之后工具调用率提升了约 30%。这个坑让我明白一点角色设定不是装饰品是行为开关。你赋予模型什么身份它就会更倾向于使用与该身份相关的行为框架。给检索型 Agent 套贴心助手人设等于让它偏向聊天而不是偏向执行。3.2 坑二把背景知识写在前面结果污染了答案抽取为了帮助模型理解知识库背景我在提示词里放了一段很详细的业务描述包括部门职责、产品线、术语定义等。结果发现模型经常从这段背景知识里找答案而不是去检索工具返回的实际文档。比如用户问某个产品的发布时间模型会从背景知识中的某个年份推断出一个答案而不是去知识库核实。修复方式是把背景知识单独放入参考知识区并明确标注此区域仅用于理解术语所有回答依据必须来自工具返回内容。更极端的做法是在任务执行阶段直接把背景知识从上下文中移出等模型进入某些需要背景理解的步骤时再动态注入。这样做的原理是模型在注意力机制下会倾向于使用上下文中位置靠前、信息密度高的内容如果背景知识太靠前且太详细它就默认这些内容是关键上下文从而替代真正的检索结果。所以我现在在 Harness 设计中有一个习惯优先放置执行规则其次放置动态输入检索结果最后才放置可供参考的静态背景。顺序影响权重权重影响行为。3.3 坑三工具返回的错误信息被当作答案原样输出了知识库检索工具偶尔会超时或返回空结果。当时的提示词只写了请调用工具获取答案但没说明工具异常时该怎么做。结果模型在工具返回比如TimeoutError: request exceeded时会把这条错误消息原封不动地写进回答里用户看到的就是一堆乱码似的报错文本非常影响体验。这个问题本质上是工具信号和用户信号没有做区分。修复方式是在提示词中增加一层明确的翻译规则当工具返回 ERROR 或异常状态时模型不得直接将原始消息输出给用户而应转化为资源访问失败请稍后重试并回调重试流程。更重要的是把工具返回内容和最终用户可见内容分别定义强调二者不能混淆。我当时用了一个很简单的技巧就是把提示词中的输出要求改成工具结果区 - 工具输出可能包含 JSON、错误信息、超时提示等原始内容。 - 你必须在最终回答中将工具原始内容转换为用户可读的自然语言。 - 转换时不得新增任何工具未提供的事实。这个改动虽然小但对用户侧的感知提升是巨大的。好的 Harness 不只是让 Agent 完成任务还要让它成为一个透明度良好的执行者——它要能区分内部信号和外部输出避免把中间过程泄漏给终端用户。3.4 坑四多轮对话状态下上下文里堆满了历史动作导致目标遗忘知识库问答经常是多轮对话用户会连续追问。问题在于模型不仅要理解本次问题还要结合前面的上下文。当时我没有对历史对话和当前任务做隔离结果模型在调用工具时偶尔会顺着之前的话题飞行比如用户上一轮问 A 产品这一轮问 B 公司模型却依然去检索 A 产品的资料。修复方案是做一个对话摘记区块每轮对话结束后用一句话提炼当前用户意图作为下一轮上下文的锚点。这个锚点不仅要包含最新问题还要包含用户的核心限制条件和工作状态。例如对话状态锚点 - 用户当前意图查询 B 公司未来一周的公开活动 - 已确认限制仅限官方渠道发布信息 - 当前工具状态NOT_CALLED有了锚点之后模型在进行工具调用前的推理会更聚焦即使对话内容很长它也知道自己该关注哪一层的状态而不是被整段历史带偏。这个技巧后来被我广泛应用到所有需要多轮交互的 Agent 场景里效果稳定。4. Harness Engineering 的提示词模板一套可以直接套用的框架经过几次项目的迭代我沉淀了一套相对通用的 Harness 提示词模板。这套模板的出发点不是万能而是可以让一个普通模型在工具调用、状态管理、自我评估等方面都有基本的表现。我把它分成五个区块每个区块承担一项职责。4.1 模板总览用五个区块锁定必需信息区块作用核心内容角色与目标定义执行身份与最终产出一句话说清 Agent 是什么、为谁服务约束与边界定义不可为与触发条件条件式规则 反例 失败标记执行流程定义步骤、状态、转移条件状态机式的步骤编排上下文组织区分参考知识、动态输入、工具结果让模型知道哪些信息能用于回答输出规范定义最终格式、自查清单JSON Schema、错误标记、用户可见语言转换每一个区块在语境中的地位不同。角色与目标提供最顶层的我是谁约束与边界提供我不能做什么执行流程提供我按什么路径做事上下文组织解决我从哪里找依据输出规范解决我交什么货、交到什么标准。五个区块的组合就构成了一个 Agent 的最小 Harness 框架。4.2 直接可用的模板示例模拟项目X以下是我实际用过的一个知识库问答 Agent 模板包含核心结构。你可根据自己的任务场景做替换但区块顺序和逻辑关系建议保留。你是知识库查询执行器。你的目标是基于工具返回结果回答用户问题。 你不提供任何未经工具返回内容支撑的信息。 约束 - 当工具未返回有效结果时禁止生成答案输出 NO_ANSWER。 - 当工具调用失败时将错误消息转为获取资源失败并重试一次。 - 不得在原答案中直接输出工具原始报错信息。 执行流程 1. 读取用户问题标记为 QUERY_RECEIVED。 2. 调用知识库检索工具若返回结构体进入下一步否则标记 RETRY。 3. 在检索结果中提取与问题相关的片段标记相关性等级。 4. 若相关性等级低于0.6扩大检索范围若仍无结果输出 NO_ANSWER。 5. 整理最终答案进入输出规范检查。 上下文组织 - 检索结果区使用 tool_result 包裹此区域是回答依据的唯一来源。 - 背景资料区使用 context 包裹仅用于理解术语不作为回答依据。 输出规范 - 输出 JSON{ answer: 自然语言答案, sources: [来源链接1, 来源链接2], confidence: 0.x } - 若无法确认来源sources 输出 []confidence 不高于 0.3。 - 检查 JSON 语法是否通过检查 answer 是否包含非检索结果内容。这套模板的好处在于把关键决策都显式化了模型至少知道什么情况下继续、什么情况下放弃、什么情况下求助。当然它并不完美但对于大多数企业内部工具型 Agent 来说已经可以显著减少不可控输出。4.3 模板背后的设计逻辑为什么有些字段会互相冲突很多初学者看到模板会问为什么既要给约束又要给输出规范不是说约束越少越好吗其实这两者不冲突关键在于约束管的是行为路径输出规范管的是最终产物。行为路径上约束太宽松模型容易自由发挥最终产物上规范太复杂模型容易为了格式而牺牲内容。因此需要分开设计各管一段。我见过一个失败案例开发者在输出规范里同时写了必须包含 200 字以内的摘要和必须包含所有检索到的相关要点。这两个要求本质上是冲突的尤其当检索结果很多时模型要么压缩得不像摘要要么超长。正确的做法是给输出规范定义优先级比如回答简洁性优先于完整性或者引入一个可调参数 max_points3让模型在约束内自行取舍。我的经验是模板中的每一个字段都必须能被问题回答成 YES/NO。如果某个字段让模型需要做主观判断那就是设计失败。你给模型的规则越接近布尔逻辑它执行得越稳定。5. 工具调用的 Harness 设计让 Agent 真正学会开车而非推车工具调用是 Agent 区别于普通问答模型的核心能力。但我在实际操作中发现很多 Agent 尽管会调用工具行为方式却像推车——每走一步都要重新思考没有形成一个连贯的驾驶过程。原因在于Harness 提示词没有把工具调用设计成一件事的一部分而是把它设计成了一个孤立的动作。想真正提升工具调用的效率和质量需要在 Harness 中解决三个问题什么时候调工具、拿到结果后怎么用、用完工具后怎么继续。下面分别展开。5.1 工具选择与其让模型自由选择不如先限制候选集合给 Agent 一大堆工具定义然后让它自己选这个思路听上去灵活实际却容易让模型选错。尤其在工具数量超过 5 个且名称相似时模型经常会选到一个看起来相关但实际不该用的工具。我的做法是在提示词中显式列出可用工具并对每个工具标注适用条件和禁止使用场景。例如可用工具 - search_knowledge_base: 适用于关键词检索、模糊查询。禁止用于计算任务。 - fetch_price: 适用于查询商品实时价格。禁止用于历史价格对比。 - get_user_profile: 仅当用户明确请求个人数据时使用。这样模型在选择工具时就像在做一个条件匹配题而不是开放式选择题。实测下来工具误调用率下降非常明显尤其对复杂任务的多步工具编排来说这种显式候选集设计可以大幅降低模型在选择上的心智负担。5.2 工具结果解读如何防止模型把工具输出直接搬进答案工具结果往往是结构化 JSON 或半结构文本模型如果直接搬运很容易制造出用户看不懂的回答。我在提示词中增加了一个解读规则区要求模型必须把工具输出翻译成用户能理解的自然语言同时保留出处信息。这个规则的写法不只是用自然语言输出而是给出明确的结构要求工具结果解读规则 - 从工具输出中提取用户问题直接涉及的字段。 - 将字段值与用户问题结合生成一句话回答。 - 回答中不得出现 JSON 键名、字段名、错误码。 - 若工具输出存在冲突字段标记 CONFLICT 并按可信度更高的字段回答。这部分的细节往往决定用户对 Agent 的评价。很多 Agent 明明检索对了却因为输出格式太技术化而被嫌弃。一个合格的 Harness 工程师应该像产品经理一样去设计输出体验而不只是围绕上下文堆指令。5.3 多工具编排从依次调用到条件化切换当任务需要多个工具协作时简单的先调 A 再调 B是不够的。因为工具可能返回异常、可能没有结果、可能需要反复调用。我的经验是为每个工具调用步骤定义出口条件并显式说明下一步去哪。与其让模型自己脑补流程不如把流程画成一个可被自然语言描述的二叉树。举个例子步骤1调用 search_knowledge_base - 若返回结果数量 0进入步骤2。 - 若返回结果数量 0再调用 search_web_fallback。 - 若 search_web_fallback 仍无结果输出 NO_ANSWER。 步骤2调用 fetch_price 获取对应价格 - 若 fetch_price 成功进入输出步骤。 - 若 fetch_price 失败使用 search_knowledge_base 中最近更新时间的价格标注非实时价格。这种条件化切换让 Agent 的行为路径变得可预测、可测试。我经常说Harness 工程本质上是把不可见的概率行为变成可追踪的状态流。它不能消除模型的随机性但能把随机性限制在可控范围内。6. 评测与迭代提示词写得好不好不能靠感觉很多团队在模型配置完成后就认为工作结束了剩下的交给运气。但实际上Agent Harness 提示词是需要持续迭代的活。因为模型版本一更新、任务数据一变一套提示词可能就失效了。所以我在每个项目中都坚持做三件事建立评估集、做回归测试、记录失败样本。6.1 建立评估集不要用 10 条测试用例验证一个 Agent有人用 10 条用例测完就宣布Agent 好用这种评估粒度远远不够。真实场景中同一个任务可能有几十种表达方式、多种边界情况、多个工具异常分支。我的做法是维护一个至少包含 100 条样本的评估集覆盖以下类别正常路径20%标准提问、常规工具返回。边界路径30%空结果、模糊问题、超大输入。异常路径30%工具超时、工具报错、结果缺失。对抗路径20%多轮干扰、角色诱导、上下文中的虚假信息。建立评估集的关键是让样本能区分模型能力不足和Harness 设计不足。如果 100 条里 80 条失败大概率是基础模型本身不合适如果只有 20 条失败且集中在某类边界场景那大概率是提示词中缺少对应的约束或流程分支。6.2 回归测试一次修改引发的新问题往往藏在旧用例里提示词改动后最怕的是修好了 A 类问题弄坏了 B 类问题。所以我在每次修改后都会跑一遍全量评估集对比前后的输出差异。差异不一定是坏信号但要警惕意料之外的输出格式变化和低置信度回答数量的上升。这里分享一个实用技巧把每次修改前的提示词和修改后的提示词分别保存为版本号并在评估集中记录每个样本在新旧版本下的输出。如果某条样本从正确变成错误就要仔细分析是提示词顺序问题、新增约束的副作用还是模型对某个词汇的触发行为变了。回归测试是 Agent 工程中最容易被省略、却最能保证长期稳定性的环节。6.3 失败样本驱动的迭代让每一次错误都变成提示词改进的输入我从不直接改模型参数来修复提示词层面的问题而是把失败样本当作提示词改进的养分。每条失败样本都值得问三个问题模型是否理解了这个场景的目标模型是否知道该在何时调用哪个工具模型是否在输出阶段缺少某种校验如果答案是理解目标但不知道步骤我会在编排层增加状态转移分支如果答案是知道步骤但输出混乱我会在输出规范区增加格式示例如果答案是完全不知道发生了什么那我就会怀疑基础模型对这类任务的拟合度考虑换一个更合适的模型版本。这类诊断逻辑让提示词迭代不再是瞎调而是真正有方向的工程化过程。7. 我的最后几条心得关于稳定、安全和工程化心态操作了多个 Agent 项目后我想把几条最零散但也最实用的心得放在最后都是踩过不少坑才总结出来的。第一给 Agent 一个我不确定的出口比逼它一定答对更重要。很多业务方希望 Agent 永远有答案但这在工程上是不现实的。与其让模型编造答案不如让它在置信度不足时主动输出 UNKNOWN。只要未知被正确定义产品质量反而会提升因为系统层面可以对低置信度结果走人工通道。第二安全边界不要写在提示词最后要放在约束层最前面。模型对上下文前部的注意力更强安全类规则如果放在最后很容易被长上下文稀释。我遇到过实际案例一段禁止输出隐私信息的规则放在文末结果模型在长对话中完全忽略了它。调整顺序后敏感信息泄漏率明显下降。第三不要对提示词做过度的版本迷信。有人总想找一个完美提示词但现实中不存在完美提示词只有适合当前模型、当前任务、当前评估集的提示词。所以每更换一次模型版本都要重新跑一遍回归测试。好的 Harness 工程是一个控制过程你在约束模型的行为边界也在适应模型的行为漂移。最后说一句可能有点抽象的话Agent Harness Engineering 提示词设计表面上是写文本实质上是在和概率系统做一个约定。你把约定写清楚概率系统就能在大多数情况下按约定执行你写得模糊它就会在那些模糊处随机发挥。希望这篇文章里的思路、模板和踩坑记录能让你以后写提示词时少一点碰运气多一点工程感。这也正是我当初整理这套方法论时最想达到的效果。