ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从harness工程到认知工程:Agent架构升级实战与复杂任务优化

从harness工程到认知工程:Agent架构升级实战与复杂任务优化 1. 从 harness 工程到认知工程一次 Agent 架构的认知跃迁过去大半年我一直在折腾 Agent 相关的东西。从最早的 prompt 拼接到后来的工具调用编排再到最近把整套 harness 工程重构了一遍踩的坑比写的代码还多。今天想聊的这个话题是我最近思考比较多的一个方向把 harness 工程升级到认知工程。这不是一个简单的技术升级而是整个 Agent 系统设计思路的转变。先说清楚我在说什么。harness 工程简单理解就是围绕 Agent 的“执行外壳”做工程化——工具注册、调用编排、上下文管理、错误重试、输出解析这些都属于 harness 的范畴。你去看现在市面上大部分 Agent 框架本质上都在解决 harness 层面的问题怎么让模型稳定地调用工具、怎么管理多轮对话的上下文、怎么处理工具返回的异常。这些工作很重要但它们解决的是“Agent 能不能跑起来”的问题而不是“Agent 能不能想明白”的问题。认知工程则是往上一层走。它关心的不是工具怎么调而是 Agent 在调用工具之前脑子里发生了什么它怎么理解当前任务、怎么判断自己知不知道、怎么决定要不要拆解、怎么评估自己的方案靠不靠谱。换句话说harness 工程管的是“手脚”认知工程管的是“脑子”。我最近把手上一个 Agent 项目从纯 harness 架构改造成带认知层的架构效果提升非常明显尤其是在复杂任务上的表现跟之前完全不是一个量级。这篇文章适合谁看如果你正在做 Agent 开发已经过了“跑通 demo”的阶段开始遇到“任务一复杂就翻车”的问题那这篇内容应该对你有用。如果你还在学习 Agent 开发的基础知识也可以看看至少能知道后面要往哪个方向走。我会从架构设计、核心模块、实操步骤、踩坑经验几个方面展开尽量把“为什么这么设计”讲清楚而不只是丢一堆代码。2. 为什么 harness 工程不够用了2.1 harness 工程的本质与边界先给 harness 工程画个像。一个典型的 harness 架构大概包含这些东西工具注册表tool registry、调用解析器call parser、执行器executor、结果处理器result handler、上下文管理器context manager。模型输出一段文本harness 负责解析出工具调用意图执行工具把结果塞回上下文再让模型继续。这个循环就是 ReAct 那套东西的工程化实现。我最早做 Agent 的时候觉得这套东西已经很完整了。工具能调、上下文能管、错误能重试还要啥自行车但跑了一段时间真实任务之后问题就暴露出来了。最典型的一个场景我让 Agent 帮我分析一份数据报告它上来就开始调工具读文件、算统计、画图表一套操作猛如虎最后输出一个完全跑偏的结论。问题出在哪出在它根本没想清楚“这个任务到底要什么”就开始动手了。harness 工程的边界就在这里它能保证工具调用的正确性但保证不了任务理解的正确性。模型说调 A 工具harness 就调 A 工具至于该不该调 A、调完 A 之后该干嘛harness 不管。这就好比一个执行力很强的实习生你说啥他干啥但他不会问你“你确定要这么干吗”。2.2 复杂任务暴露的三个核心缺陷我复盘了一下过去半年 Agent 翻车的案例发现大部分问题可以归到三类。第一类是任务理解偏差。用户说“帮我优化一下这段代码的性能”Agent 直接开始改代码但它没搞清楚用户说的“性能”是指运行速度、内存占用还是可读性。这种偏差在 harness 层面是看不出来的因为工具调用本身没问题问题在于调用之前的理解环节缺失。第二类是自我评估缺失。Agent 调完一个工具拿到结果它不会判断这个结果是否合理。比如它调了一个搜索工具返回的结果明显不相关但它照样把结果塞进上下文继续往下走。harness 工程里通常只有“调用成功/失败”的判断没有“结果质量”的判断。第三类是策略僵化。harness 工程里的执行流程通常是固定的解析、调用、处理、循环。但真实任务需要动态策略——简单任务直接答复杂任务先拆解不确定的任务先澄清。固定流程应对不了这种变化。这三个缺陷的共同点是它们都发生在“工具调用之前”或“工具调用之间”属于认知层面的问题不是执行层面的问题。这就是为什么我说 harness 工程不够用了——不是它做得不好而是它管不到这些地方。2.3 认知工程要解决的核心问题认知工程的目标是在 harness 之上加一层“思考层”。这层要解决的核心问题包括Agent 怎么知道自己知不知道元认知、怎么把复杂任务拆成可执行的子任务任务分解、怎么判断自己的方案是否靠谱自我评估、怎么根据反馈调整策略策略调整。我打个比方。harness 工程像是给 Agent 装了一双手让它能操作工具。认知工程是给这双手配一个大脑让它在动手之前先想一想我要干什么、我能不能干、我该怎么干、干完之后对不对。没有大脑的手只能做机械重复的动作有了大脑的手才能做需要判断和调整的复杂工作。这个升级不是把 harness 推翻重来而是在它上面叠加一层。harness 依然是基础设施认知层是决策层。两者配合才能让 Agent 从“能跑”变成“能想”。3. 认知工程的核心模块拆解3.1 元认知模块让 Agent 知道自己不知道元认知这个词听起来很学术说白了就是“对自己认知状态的认知”。放到 Agent 场景里就是让 Agent 能判断这个问题我能不能答、这个任务我能不能做、我现在的信息够不够。我实现元认知模块的方式是在每次任务开始前加一个“能力评估”步骤。具体做法是让模型先输出一个自评格式大概是这样的{ task_understanding: 用户想要..., confidence: 0.7, known_unknowns: [缺少XX信息, 不确定YY的含义], suggested_action: clarify | proceed | decompose }这个自评不是让模型随便说说而是有明确的判断标准。confidence 低于某个阈值我一般设 0.6就触发澄清流程known_unknowns 非空且影响核心决策也触发澄清如果任务明显复杂suggested_action 就是 decompose。实测下来这个模块最大的价值是减少了“自信地犯错”。以前 Agent 遇到不懂的问题它会硬答答得还特别自信。现在它会说“我不确定这个需要你补充一下”虽然多了一轮交互但整体成功率提升了很多。注意元认知模块的 prompt 设计很关键。如果你只是问“你确定吗”模型大概率会说“确定”。要用结构化的方式逼它列出“已知”和“未知”它才会认真思考。3.2 任务分解模块从“一把梭”到“分步走”任务分解是认知工程里最实用的模块。harness 工程里的 Agent 通常是“一把梭”——拿到任务直接开干。但复杂任务需要先拆解再逐步执行。我的做法是加一个 planner 层。用户输入任务后planner 先输出一个执行计划格式是子任务列表每个子任务包含目标、所需工具、依赖关系、完成标准。然后 executor 按计划逐步执行每完成一步就更新计划状态。这里有个关键设计计划不是一次性的而是动态调整的。每执行完一个子任务planner 会重新评估剩余计划是否还合理。如果发现前面的结果改变了任务理解就重新规划。这个机制我称之为“滚动规划”比一次性规划灵活很多。举个例子。我让 Agent 分析一份销售数据planner 初始计划是读取数据、清洗数据、计算统计、生成报告。执行到“清洗数据”时发现数据格式跟预期不一样planner 就动态插入一个“格式转换”子任务再继续往下走。如果是 harness 工程遇到这种情况大概率是报错或者硬着头皮往下走。3.3 自我评估模块给 Agent 装一个“检查器”自我评估模块解决的是“结果质量”问题。harness 工程只判断工具调用是否成功不判断结果是否合理。认知工程要加一层质量检查。我的实现方式是在每个子任务完成后加一个 evaluator 步骤。evaluator 拿到子任务目标和实际结果输出一个评估{ goal_achieved: true, quality_score: 0.8, issues: [结果缺少XX维度], suggested_fix: 补充XX分析 }如果 quality_score 低于阈值或者 goal_achieved 为 false就触发修复流程。修复流程可以是重新执行、调整方案、或者向用户求助。这个模块最明显的效果是减少了“垃圾进垃圾出”。以前 Agent 拿到一个不相关的搜索结果照样往下走最后输出一堆废话。现在它会判断“这个结果不相关”然后重新搜索或者换策略。3.4 策略调整模块让 Agent 学会“随机应变”策略调整模块是认知工程的“自适应层”。它根据执行过程中的反馈动态调整 Agent 的行为策略。我实现的策略调整逻辑大概是这样的维护一个策略状态机包含几种策略模式——直接回答、澄清提问、分解执行、求助用户。每次任务开始或子任务完成后根据元认知评估、执行结果、历史成功率决定切换到哪种策略。比如如果连续两个子任务都执行失败策略就从“分解执行”切换到“求助用户”。如果元认知评估显示 confidence 很高且任务简单策略就是“直接回答”。这个状态机让 Agent 的行为不再是固定的而是根据实际情况动态变化。4. 实操从 harness 到认知工程的改造步骤4.1 改造前的准备工作在动手改造之前有几件事要先想清楚。第一确认你的 harness 工程是稳定的。认知工程是叠加层如果底层 harness 本身就不稳定叠上去只会更乱。先确保工具调用、上下文管理、错误重试这些基础功能没问题。第二定义清楚认知层的输入输出接口。认知层和 harness 层之间要有清晰的边界。我的做法是定义两个接口cognitive_pre_process(task)和cognitive_post_process(result)。前者在 harness 执行前调用返回执行计划后者在执行后调用返回评估结果。第三准备好评估数据。认知工程的效果需要量化评估不然你不知道改造有没有用。我准备了一组测试任务涵盖简单问答、中等复杂度分析、复杂多步任务三类每类 10 个用来对比改造前后的成功率。4.2 第一步搭建元认知评估层元认知评估层是认知工程的入口。每次任务进来先过这一层。具体实现上我写了一个MetaCognition类核心方法是assess(task)。它调用模型输出结构化自评然后根据自评结果决定下一步动作。代码大概长这样class MetaCognition: def assess(self, task): prompt self._build_assessment_prompt(task) response self.llm.generate(prompt) assessment self._parse_response(response) if assessment[confidence] 0.6: return {action: clarify, questions: assessment[known_unknowns]} if assessment[complexity] high: return {action: decompose, task: task} return {action: proceed, task: task}这里有个细节_build_assessment_prompt的设计很关键。我试过几种 prompt 结构最后发现最有效的是“先让模型复述任务再让它列已知未知最后让它给建议”。复述任务这一步能逼模型认真理解减少理解偏差。4.3 第二步接入任务分解与规划任务分解层的核心是 planner。我实现了一个Planner类核心方法是plan(task)和replan(task, executed_steps, current_state)。plan方法输出初始计划格式是子任务列表。每个子任务包含id、goal、tools、dependencies、success_criteria。replan方法在每步执行后调用判断是否需要调整计划。这里有个实操心得子任务的粒度很重要。太粗了跟没拆一样太细了执行开销大。我的经验是每个子任务对应 1-3 次工具调用比较合适。如果某个子任务需要 5 次以上工具调用说明拆得不够细。另外success_criteria要写得具体可判断。比如“获取到数据”就不够具体“获取到包含日期、金额、类别的结构化数据”就比较好判断。这个字段直接影响后续的自我评估模块。4.4 第三步实现自我评估与修复循环自我评估层的核心是 evaluator。我实现了一个Evaluator类核心方法是evaluate(subtask, result)。评估的逻辑分两步先判断目标是否达成再判断质量是否达标。目标达成判断用规则模型结合的方式——能用规则判断的用规则比如“是否返回了数据”不能用规则的用模型判断比如“分析是否合理”。质量判断主要靠模型给一个 0-1 的分数。如果评估不通过触发修复循环。修复策略有三种retry重试、adjust调整方案、escalate上报用户。选择哪种策略根据失败原因决定如果是工具调用失败retry如果是方案不对adjust如果是信息不足escalate。注意修复循环要设最大次数限制不然可能陷入死循环。我一般设 3 次超过就 escalate。4.5 第四步串联策略调整状态机策略调整层是认知工程的“调度中心”。我实现了一个StrategyManager类维护一个状态机。状态机的状态包括DIRECT_ANSWER、CLARIFY、DECOMPOSE、EXECUTE、EVALUATE、REPAIR、ESCALATE。转移条件根据元认知评估、执行结果、评估分数、重试次数等决定。这个状态机的实现不复杂但设计转移条件需要仔细。我的经验是转移条件要尽量简单明确不要搞太复杂的组合条件。比如“confidence 0.6 就 CLARIFY”比“confidence 0.6 且 complexity 0.5 且 history_success_rate 0.7 就 CLARIFY”好维护得多。4.6 改造后的效果对比改造完成后我用之前准备的测试任务跑了一轮对比。结果如下任务类型harness 工程成功率认知工程成功率提升幅度简单问答92%94%2%中等复杂度分析68%85%17%复杂多步任务41%73%32%简单任务提升不大因为简单任务本来就不需要太多认知。但复杂任务提升非常明显成功率从 41% 提到 73%翻了将近一倍。这个结果验证了我的判断认知工程的价值在复杂任务上体现得最明显。5. 踩坑记录与常见问题排查5.1 元认知评估的“过度自信”问题刚开始做元认知评估的时候我发现模型的自评分数普遍偏高。明明任务很复杂它给自己打 0.9 的 confidence。后来我调整了 prompt加了“请列出你不确定的地方”这个要求分数才降下来。这个问题的本质是模型倾向于“表现得自信”。你要用结构化的方式逼它暴露不确定性不能直接问“你确定吗”。我现在的做法是要求它必须列出至少一个“已知未知”如果确实没有就写“无”。这个强制列出的机制让模型不得不认真审视自己的知识边界。5.2 任务分解的“粒度失控”任务分解最容易出的问题是粒度失控。要么拆得太粗子任务还是太大要么拆得太细执行开销爆炸。我试过几种控制粒度的方法。最有效的是在 planner 的 prompt 里加一个约束“每个子任务应该对应 1-3 次工具调用”。这个约束很具体模型能理解。另外我会在 replan 的时候检查子任务数量如果超过 10 个就提示 planner 合并一些子任务。还有一个坑是子任务之间的依赖关系处理。有些子任务可以并行有些必须串行。我一开始没处理这个所有子任务都串行执行效率很低。后来加了依赖关系字段能并行的就并行效率提升了不少。5.3 自我评估的“标准漂移”自我评估模块跑一段时间后我发现评估标准会“漂移”。同样的结果有时候评 0.8有时候评 0.6。这是因为模型评估本身有随机性加上上下文变化标准就不稳定了。解决方法是把评估标准显式化。我在 evaluator 的 prompt 里加了一个评分标准表明确什么情况打什么分。比如“结果完整且准确0.9-1.0结果基本准确但缺少细节0.7-0.8结果有错误0.4-0.6结果不相关0-0.3”。有了这个标准表评分稳定性好了很多。5.4 策略状态机的“死循环”风险策略状态机最怕的是死循环。比如 EXECUTE 失败进 REPAIRREPAIR 失败又回 EXECUTE来回转。我的解决方案是加两个机制一是重试计数器每个子任务的重试次数单独计数超过 3 次就强制 ESCALATE二是状态转移历史如果发现最近 5 次状态转移在重复同样的模式就强制跳出。这两个机制加上之后死循环问题基本解决了。5.5 常见问题速查表问题现象可能原因排查方法解决方案元认知评分普遍偏高prompt 缺少强制暴露不确定性的要求检查 assessment prompt加“必须列出已知未知”约束子任务粒度过粗planner prompt 缺少粒度约束检查子任务对应的工具调用次数加“1-3次工具调用”约束评估分数不稳定评估标准未显式化对比同一结果的多次评分加评分标准表状态机死循环缺少重试计数和转移历史检查检查状态转移日志加重试计数器和转移历史检查修复循环不收敛修复策略选择不当检查失败原因和修复策略的匹配度按失败原因选择修复策略并行子任务未并行依赖关系未正确解析检查子任务依赖字段完善依赖关系解析逻辑5.6 几个实操心得第一个心得认知层不要做太重。我一开始想把认知层做得特别完善加了很多评估维度和策略分支结果执行开销很大简单任务也要跑好几秒。后来精简了只保留核心的元认知、分解、评估、策略四个模块效果好很多。认知层是“够用就好”不是“越多越好”。第二个心得评估数据要持续积累。我维护了一个评估数据集每次改造或调参后都跑一遍对比效果。这个数据集不用很大但要有代表性。我的数据集是 30 个任务覆盖三类复杂度跑一轮大概 10 分钟很实用。第三个心得认知层的 prompt 要版本管理。认知层的效果很大程度上取决于 prompt 设计而 prompt 调优是个反复迭代的过程。我用 git 管理 prompt 文件每次改动都记录效果变化这样能追溯哪个改动带来了提升。6. 认知工程的扩展方向6.1 记忆机制与认知工程的结合认知工程目前主要处理“当前任务”的认知但 Agent 的认知能力还可以扩展到“跨任务”的记忆。我最近在尝试把长期记忆机制接入认知层让 Agent 能记住之前处理类似任务的经验。具体做法是每次任务完成后把任务特征、执行计划、评估结果存到记忆库。下次遇到类似任务时元认知模块先检索记忆库看看有没有可参考的经验。这个机制在重复性任务上效果很好能显著减少规划开销。6.2 多 Agent 协作中的认知分工单 Agent 的认知工程做完了下一步自然是多 Agent 协作。我的想法是让不同 Agent 承担不同的认知角色一个负责元认知评估一个负责规划一个负责执行一个负责评估。这种分工能让每个 Agent 的认知模块更专注整体效果可能更好。不过多 Agent 协作的复杂度也高很多通信开销、一致性、冲突处理都是问题。我目前还在实验阶段等有稳定结果了再分享。6.3 认知工程的可观测性建设认知工程比 harness 工程更难调试因为“思考过程”是隐性的。我最近在建设认知层的可观测性把每个认知步骤的输入输出都记录下来形成一个“认知轨迹”。这个轨迹能帮我定位问题是元认知评估错了还是规划错了还是评估错了。可观测性建设是个长期工作但对认知工程的迭代很重要。没有可观测性调优就是盲调。6.4 从认知工程到元认知工程最后聊一个更远的方向。认知工程是让 Agent“会想”元认知工程是让 Agent“知道自己怎么想”。前者是认知能力后者是对认知能力的认知和调控。我目前只在元认知评估这个点上做了一些尝试离完整的元认知工程还很远。但我觉得这个方向值得投入因为 Agent 要真正可靠不仅要知道自己在干什么还要知道自己为什么这么干、这么干对不对。这是从“工具”到“伙伴”的关键一步。我在实际项目中的体会是认知工程的投入产出比在复杂任务场景下非常高。如果你的 Agent 主要处理简单任务可能不需要认知层但如果你的 Agent 要处理需要判断、拆解、调整的复杂任务认知层几乎是必需的。改造的过程不轻松但效果值得。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进