资讯详情

资讯详情

AI Agent Harness 拆解:7 大子系统让大模型从聊天到干活

1. 拆开 AI Agent 的“干活引擎”Harness 到底在管什么很多人第一次听到 Harness 这个词脑子里浮现的是测试框架或者 CI 流水线里的那个 runner。但在 AI Agent 的语境下Harness 的含义要具体得多——它是让大模型从“会聊天”变成“能干活”的那层工程骨架。你给一个模型接上工具、记忆、循环控制、错误恢复、上下文管理这一整套东西加起来就是 Harness。我刚开始搭 Agent 的时候也走过弯路。那时候觉得 Agent 不就是“LLM 几个 tool call”吗写个 while 循环把工具返回塞回 messages 里再调一次模型完事。结果真跑起来才发现问题根本不在模型聪不聪明而在 Harness 这一层有没有把脏活累活接住。模型会重复调同一个工具、会在工具报错后卡死、会把上下文撑爆、会在多轮任务里忘记最初的目标。这些都不是 prompt 能解决的得靠 Harness 的子系统去兜底。所以这篇文章我想把 Harness 拆开讲。不是讲某个具体框架的 API而是讲它内部到底由哪几块组成、每块解决什么问题、为什么缺一不可。你把这 7 个子系统理解透了不管后面用 LangGraph、Spring AI 还是自己手写心里都有一张清晰的图。1.1 先给 Harness 一个不绕弯的定义Harness 直译是“马具”套在马身上让马能拉车的那套东西。放到 AI Agent 里这个比喻其实很准模型是那匹马有力气但方向感差、容易受惊、不知道什么时候该停Harness 就是套在它身上的缰绳、轭具和刹车把它的能力约束到一条能交付结果的轨道上。从工程角度看Harness 是介于“模型调用”和“业务目标”之间的中间层。它向上承接用户的任务描述向下管理模型的每一次推理和每一次工具执行中间还要处理状态、上下文、错误、并发、可观测性。你可以把它理解成 Agent 的运行时runtime模型只是它调用的一个计算单元。这里有个常见的认知偏差很多人把 Agent 框架和 Harness 混为一谈。框架提供的是抽象和接口Harness 提供的是实际运转的控制逻辑。同一个框架你可以写出很烂的 Harness也可以写出很稳的 Harness。差别就在于你有没有认真对待那 7 个子系统。1.2 为什么“7 个子系统”这个拆法值得参考市面上讲 Agent 架构的文章有的按功能分层有的按数据流分层有的干脆只讲 prompt 工程。7 个子系统这个拆法的好处是它按“职责边界”来切每一块都有明确的输入输出和失败模式。你排查问题的时候能快速定位到是哪一层出了毛病而不是对着一坨代码干瞪眼。我自己的经验是Agent 出问题90% 不是模型能力不够而是某个子系统没做好。比如工具调用失败没有重试策略、上下文没有裁剪机制、循环没有终止条件、状态没有持久化。这些问题在 demo 阶段看不出来一上真实任务就全暴露了。下面这张表先给个全局印象后面每个子系统我会展开讲。子系统核心职责典型失败表现Agent Loop控制推理-行动循环死循环、提前终止LLM Integration模型调用与输出解析格式错乱、调用超时Tool Runtime工具注册、执行、隔离工具报错卡死、权限越界Memory State短期/长期状态管理上下文丢失、状态污染Context Manager上下文组装与裁剪超长截断、关键信息丢失Error Recovery错误分类与恢复策略一错就崩、无法自愈Observability日志、追踪、评估黑盒运行、无法复盘这 7 块不是孤立的它们之间有明确的调用关系和数据流向。Agent Loop 是主控它每一轮会向 Context Manager 要上下文向 LLM Integration 发请求拿到结果后交给 Tool Runtime 执行执行结果再写回 Memory State出错时走 Error Recovery全程由 Observability 记录。理解这个数据流比记住名词重要得多。2. Agent Loop 与 LLM Integration主循环和模型接口怎么配合这两个子系统是 Harness 的心脏和血管。Agent Loop 决定“什么时候想、什么时候做、什么时候停”LLM Integration 决定“怎么把想的结果稳定地拿回来”。它们配合不好Agent 要么原地打转要么直接跑飞。2.1 Agent Loop 不是简单的 while True很多人写 Agent Loop 就是while True: response llm.chat(messages) if response.tool_calls: for call in response.tool_calls: result execute(call) messages.append(result) else: break这个结构在 demo 里能跑但真实任务里会出三类问题。第一没有最大轮次限制模型如果一直调工具循环就停不下来。第二没有进度判断模型可能反复调同一个工具做无效功。第三没有中间检查点跑到一半崩了前面所有状态全丢。我后来改成的结构是这样的主循环带一个max_steps硬上限每一轮结束后做一次“进展评估”——对比当前状态和目标状态的差距有没有缩小。如果连续 N 轮没有实质进展就触发降级策略要么换一种推理方式要么把问题抛回给用户澄清。class AgentLoop: def __init__(self, max_steps20, no_progress_limit3): self.max_steps max_steps self.no_progress_limit no_progress_limit def run(self, task): state self.init_state(task) no_progress_count 0 for step in range(self.max_steps): context self.context_manager.build(state) response self.llm_integration.invoke(context) if response.is_final: return self.finalize(response, state) result self.tool_runtime.execute(response.tool_calls) new_state self.memory.update(state, response, result) if not self.has_progress(state, new_state): no_progress_count 1 if no_progress_count self.no_progress_limit: return self.escalate(state) else: no_progress_count 0 state new_state return self.timeout(state)这里的关键设计是“进展评估”函数。它不需要很复杂可以是一个简单的状态 diff也可以是让模型自己判断“距离目标是否更近了”。我实测下来加了这个判断之后无效循环减少了大概七成。注意max_steps不要设太大。我见过有人设 100结果一个任务跑了十几分钟烧了一堆 token最后还没结果。一般任务 15 到 25 步足够复杂任务可以到 40再高就要考虑拆任务了。2.2 LLM Integration 要处理的脏活比你想的多LLM Integration 表面上是“调模型 API”实际上它要处理的问题包括多模型路由、输出格式约束、流式与非流式切换、超时与重试、token 计数、成本控制。这些每一项做不好都会让上层 Loop 变得脆弱。先说输出格式。Agent 需要模型返回结构化的 tool call但模型有时候会返回一段自然语言里夹着 JSON有时候 JSON 缺字段有时候干脆返回一个不存在的工具名。我的做法是在 Integration 层做“解析 校验 修复”三步。解析用宽松的 JSON 提取校验用 schema修复则是把错误信息塞回给模型让它重试一次。def parse_tool_call(raw_output, tool_schema): try: data extract_json(raw_output) validate(data, tool_schema) return data except ParseError as e: repair_prompt f你的输出格式有误{e}。请只返回合法 JSON。 retry_output self.llm.chat(repair_prompt) return extract_json(retry_output)再说多模型路由。不是所有步骤都需要最强的模型。意图识别、格式修复这类任务用小模型就够了复杂推理和规划才需要上大模型。我在 Integration 层做了一个简单的路由表按任务类型和 token 预算选择模型。这样整体成本能降下来不少延迟也更好控制。任务类型推荐模型档位理由意图识别小模型分类任务不需要深度推理工具参数生成中模型需要一定理解力但结构固定多步规划大模型需要全局视野和推理能力格式修复小模型机械性任务最终总结中模型需要语言组织但不需要复杂推理超时和重试策略也要在 Integration 层统一处理。我的经验是单次调用超时设 30 秒重试 2 次退避用指数加随机抖动。如果三次都失败不要继续重试而是把失败信息返回给 Loop让它决定是换模型还是降级。2.3 两者之间的契约要写清楚Agent Loop 和 LLM Integration 之间要有一个明确的契约Loop 传给 Integration 的是“上下文 可用工具列表 输出格式要求”Integration 返回的是“标准化的响应对象”包含is_final、tool_calls、content、usage这些字段。这个契约一旦定下来两边就可以独立演进。我踩过的一个坑是早期没有定义这个契约Loop 直接拿模型的原始返回做判断结果换了一个模型之后返回格式变了整个 Loop 就崩了。后来把 Integration 层做成适配器模式不管底层换什么模型上层拿到的都是统一结构问题就少了。3. Tool Runtime 与 Memory State让 Agent 的手和记忆都可靠Agent 要干活靠的是工具要连续干活靠的是记忆。这两个子系统一个管“手”一个管“脑”配合不好就会出现“手忙脚乱”或者“失忆”的情况。3.1 Tool Runtime 的核心是隔离和可观测工具执行听起来简单不就是调个函数吗。但真实场景里工具可能是 HTTP 请求、数据库查询、文件操作、甚至另一个 Agent。每一种都有不同的失败模式网络超时、权限不足、返回格式异常、副作用不可逆。我在 Tool Runtime 里坚持三个原则。第一每个工具必须有明确的 schema包括参数类型、必填项、返回值结构、超时时间。第二工具执行必须在沙箱或至少是受控环境里不能让模型随便执行任意代码。第三每次工具调用都要记录输入、输出、耗时、状态方便后面排查。class ToolRuntime: def execute(self, tool_calls): results [] for call in tool_calls: tool self.registry.get(call.name) if not tool: results.append(ToolResult.error(f未知工具{call.name})) continue try: validated_args tool.schema.validate(call.args) output self.run_with_timeout(tool, validated_args, tool.timeout) results.append(ToolResult.success(output)) except ValidationError as e: results.append(ToolResult.error(f参数错误{e})) except TimeoutError: results.append(ToolResult.error(f工具 {call.name} 超时)) except Exception as e: results.append(ToolResult.error(f工具异常{e})) return results这里有个细节工具报错不要直接抛异常中断整个 Loop而是把错误作为结果返回给模型让模型决定下一步。模型看到“参数错误”可能会修正参数重试看到“超时”可能会换一个工具。这比直接崩掉要健壮得多。实操心得给每个工具设一个“副作用等级”。只读工具可以放心重试写操作工具要谨慎不可逆操作比如发邮件、删数据必须加确认机制。我一般会在 schema 里加一个side_effect字段Runtime 根据这个字段决定重试策略。3.2 Memory State 要分短期和长期两层Memory 这块最容易做乱。很多人把所有东西都塞进 messages 列表结果上下文越来越长模型越来越糊涂。我的做法是分两层短期状态working memory和长期记忆long-term memory。短期状态是当前任务的工作台包含任务目标、已完成步骤、当前中间结果、待办事项。它每一轮都会被 Context Manager 读取组装成模型输入。长期记忆则是跨任务的知识比如用户偏好、历史成功案例、领域知识它不直接进上下文而是按需检索。class Memory: def __init__(self): self.working WorkingMemory() self.long_term LongTermStore() def update(self, state, response, tool_results): self.working.add_step(response, tool_results) if self.should_consolidate(state): summary self.summarize(self.working.recent()) self.long_term.save(summary) self.working.compact() return self.working.snapshot()compact这个操作很关键。当工作记忆超过一定长度就把早期步骤压缩成摘要只保留关键结论释放上下文空间。我一般设的阈值是工作记忆不超过 20 条记录超过就触发压缩。长期记忆的检索也有讲究。不要用简单的关键词匹配最好用向量检索加元数据过滤。比如用户问“上次那个报表怎么做的”你要能检索到历史任务里跟“报表”相关的记录同时过滤掉其他用户的。这块做得好Agent 会有“越用越懂你”的感觉。3.3 状态一致性不能靠运气多工具、多轮次的任务里状态一致性是个大问题。比如模型先查了库存然后下单然后更新库存这三步之间如果状态没同步就会出现超卖。我的做法是在 Memory 层加一个“事务边界”的概念一组相关的工具调用要么全成功要么全回滚。实现上可以用一个简单的补偿机制每个写操作工具配一个对应的回滚工具执行前记录快照失败时按逆序回滚。这比分布式事务轻量对 Agent 场景够用。操作类型回滚策略示例创建删除创建订单 → 取消订单更新恢复旧值改库存 → 改回原值删除从快照恢复删记录 → 重新插入外部调用补偿操作发通知 → 发更正通知这套机制不是万能的但能覆盖大部分 Agent 场景。关键是你要在工具设计阶段就想好回滚方案而不是出了事再补。4. Context Manager 与 Error Recovery上下文和错误都要有策略这两个子系统是 Harness 的“后勤”和“急救”。Context Manager 决定模型每轮看到什么Error Recovery 决定出问题时怎么救。它们做得好Agent 的稳定性和成功率会有质的提升。4.1 Context Manager 的核心是“取舍”模型的上下文窗口是有限的而 Agent 任务产生的信息是无限的。Context Manager 的职责就是在有限窗口里放最有用的信息。这不是简单的截断而是有策略的组装。我的组装顺序是这样的系统提示词固定→ 任务目标固定→ 长期记忆检索结果按相关性→ 工作记忆摘要压缩后→ 最近 N 轮完整记录按时间倒序→ 当前可用工具列表。这个顺序保证最重要的信息在最前面最近的细节在最后面。def build_context(self, state): parts [] parts.append(self.system_prompt) parts.append(f任务目标{state.goal}) relevant_memories self.long_term.search(state.goal, top_k3) parts.extend(relevant_memories) parts.append(self.working.summarize()) recent self.working.recent(n5) parts.extend(recent) parts.append(self.tool_registry.describe()) return self.fit_to_window(parts, max_tokens8000)fit_to_window是最后一道防线。如果组装完还是超了就按优先级从低到高丢弃先丢长期记忆再丢工作记忆摘要最后才动最近记录。系统提示词和任务目标永远不丢。注意不要用简单的字符截断。我见过有人直接context[:8000]结果把工具列表截了一半模型调了一个不存在的工具。要用 token 计数加结构化裁剪。4.2 Error Recovery 要分类处理错误处理最忌讳一刀切。网络超时和参数错误是完全不同的问题需要不同的恢复策略。我在 Error Recovery 里把错误分成四类瞬时错误、输入错误、逻辑错误、系统错误。瞬时错误超时、限流用重试加退避。输入错误参数格式、缺少字段把错误信息返回给模型让它修正。逻辑错误工具返回结果不符合预期触发重新规划。系统错误服务不可用、权限问题则直接上报不要浪费轮次重试。class ErrorRecovery: def handle(self, error, state): category self.classify(error) if category transient: return self.retry_with_backoff(state) elif category input: return self.feedback_to_model(error, state) elif category logic: return self.replan(state) else: return self.escalate(error, state)分类的准确性很关键。我的做法是给每个工具定义它可能抛出的错误类型Runtime 捕获后打上标签Recovery 直接按标签处理。这样比让 Recovery 去猜要可靠。4.3 两者配合的典型场景举个实际例子。模型要查一个数据库Context Manager 组装上下文时发现工作记忆里已经有一次相同的查询记录于是把那次结果直接放进上下文并标注“已查询过”。模型看到后就不会重复查询直接基于已有结果继续推理。这就是 Context Manager 和 Memory 配合减少无效步骤。再比如模型调用一个 API 超时了。Error Recovery 判断是瞬时错误触发重试。重试期间Context Manager 在下一轮上下文里加入“上次调用超时正在重试”的提示让模型知道当前状态不要急着换方案。这种跨子系统的信息同步是 Harness 设计里容易被忽略但很重要的细节。5. Observability没有它Harness 就是个黑盒Observability 放在最后讲但它其实是贯穿所有子系统的。没有可观测性你根本不知道 Agent 为什么成功、为什么失败、哪一步慢、哪一步贵。我见过太多团队Agent 跑不通就改 prompt改来改去全靠猜。有了 Observability你才能做数据驱动的优化。5.1 要记录哪些数据最小可用的 Observability 要记录四类数据每轮 LLM 调用的输入输出和 token 消耗、每次工具调用的参数结果和耗时、状态变化的历史、错误和恢复动作。这些数据要能按任务 ID 串起来形成完整的执行轨迹。class Tracer: def log_llm_call(self, task_id, step, input_tokens, output_tokens, latency, model): self.store.write({ task_id: task_id, step: step, type: llm_call, input_tokens: input_tokens, output_tokens: output_tokens, latency_ms: latency, model: model, timestamp: now() }) def log_tool_call(self, task_id, step, tool_name, args, result, latency, status): self.store.write({ task_id: task_id, step: step, type: tool_call, tool: tool_name, args: sanitize(args), result: truncate(result), latency_ms: latency, status: status, timestamp: now() })记录的时候要注意脱敏和截断。工具参数里可能有敏感信息结果可能很长直接存会爆存储。我一般对参数做字段级脱敏对结果只存前 500 字符加哈希。5.2 怎么用这些数据做优化数据记下来不用就是浪费。我常用的分析维度有三个。第一按步骤看耗时分布找出瓶颈在哪。如果大部分时间花在某个工具上就考虑优化那个工具或者加缓存。第二按任务看成功率找出失败模式。如果某类任务总是失败就针对性改进对应的子系统。第三按 token 看成本找出浪费。如果某个步骤 token 消耗异常高就检查上下文组装是不是有问题。分析维度关注指标优化动作耗时各步骤 P95 延迟优化慢工具、加并行成功率任务完成率、错误分布改进 Recovery 策略成本每任务 token 消耗优化上下文、换小模型质量人工评分、重试率改进 prompt、加校验我自己的习惯是每周拉一次数据看看有没有异常任务。有一次发现某个工具的平均耗时从 200ms 涨到了 3 秒查下来是下游服务的问题及时反馈给相关团队避免了大面积超时。5.3 评估不能只看最终结果Agent 的评估比传统软件复杂因为同一个任务可能有多条合理路径。只看最终结果对不对会漏掉很多问题。我的做法是加一个“过程评估”检查每一步的工具选择是否合理、参数是否正确、有没有绕弯路。这个过程评估可以用规则做也可以用一个专门的评估模型做。比如一个查询任务模型先查了 A 表发现不对又查了 B 表最后才查 C 表。最终结果是对的但过程明显低效。过程评估会标记这个任务为“可优化”提示你检查是不是工具描述不清楚或者上下文里缺少关键信息。实操心得评估集要覆盖三类任务——简单单步任务、中等多步任务、复杂长链路任务。每类至少 20 个用例定期跑回归。我一般用 60% 简单、30% 中等、10% 复杂的比例这样既能快速发现基础问题又能覆盖边界情况。6. 把这 7 块拼起来一个可落地的最小 Harness 骨架讲完 7 个子系统你可能觉得东西太多不知道从哪下手。我的建议是先搭一个最小骨架把主循环、模型调用、工具执行、状态管理这四块跑通然后再逐步加 Context Manager、Error Recovery 和 Observability。6.1 最小骨架的目录结构harness/ loop.py # Agent Loop llm.py # LLM Integration tools/ registry.py # Tool Runtime 注册 executor.py # Tool Runtime 执行 memory.py # Memory State context.py # Context Manager recovery.py # Error Recovery tracer.py # Observability config.py # 配置这个结构的好处是每个子系统一个文件职责清晰替换方便。比如你想换一个模型供应商只改llm.py想换存储只改memory.py。6.2 启动流程和依赖关系启动时先初始化配置然后按依赖顺序初始化各子系统Tracer → Memory → ToolRegistry → LLMIntegration → ContextManager → ErrorRecovery → AgentLoop。AgentLoop 持有其他所有子系统的引用作为主控调度。def bootstrap(config): tracer Tracer(config.tracer) memory Memory(config.memory) registry ToolRegistry() register_builtin_tools(registry) llm LLMIntegration(config.llm, tracer) context ContextManager(config.context, memory, registry) recovery ErrorRecovery(config.recovery, llm) loop AgentLoop(llm, registry, memory, context, recovery, tracer, config.loop) return loop这个启动顺序保证了依赖不会缺失。Tracer 最先初始化因为其他子系统都要用它记录。Memory 在 Context 之前因为 Context 要读 Memory。LLM 在 Recovery 之前因为 Recovery 可能要用 LLM 做修复。6.3 从最小骨架到生产级的演进路径最小骨架跑通后按这个顺序加东西先加 Observability 的完整记录因为没有数据就没法优化。然后加 Error Recovery 的分类处理提升稳定性。再加 Context Manager 的智能裁剪控制成本和延迟。最后加 Memory 的长期存储和检索提升跨任务能力。每一步都要有对应的测试和评估。不要一次性全加上那样出了问题很难定位。我一般每加一个子系统就跑一遍评估集对比前后指标。如果成功率没提升或者延迟涨太多就回退重新设计。阶段新增子系统预期收益风险1最小骨架能跑通简单任务不稳定2Observability可排查、可优化存储成本3Error Recovery成功率提升逻辑复杂4Context Manager成本降低、延迟下降裁剪失误5Memory 长期化跨任务能力检索不准这套演进路径是我自己走过一遍的每一步的收益和坑都比较清楚。你可以根据自己的场景调整顺序但核心原则不变先能跑再能看再能稳最后才是能记住。6.4 几个容易忽略的工程细节第一个是配置管理。Harness 的参数很多模型、超时、重试次数、上下文窗口、工具开关这些都要能通过配置调整不要硬编码。我用的是分层配置默认配置 环境配置 任务级覆盖。第二个是并发控制。如果你的 Harness 要同时处理多个任务Memory 和 Tracer 要支持并发写入。我一般用任务 ID 做隔离每个任务有独立的 working memory长期存储用连接池。第三个是版本管理。Prompt、工具 schema、模型版本都要有版本号。出了问题能回滚到上一个稳定版本。我见过有人改了 prompt 没记录结果效果变差找不到原因只能一个个试。第四个是安全边界。工具执行要有权限控制不能让模型调用它不该调的工具。我的做法是在 ToolRegistry 里给每个工具打标签AgentLoop 根据任务类型决定哪些工具可见。这比在 prompt 里写“不要调用某某工具”要可靠得多。7. 常见问题排查Harness 跑不通时先看哪里Agent 出问题的时候新手容易慌到处改。其实按子系统排查大部分问题都能快速定位。下面这张表是我自己总结的速查表按症状找原因。症状可能原因排查方向循环停不下来缺少 max_steps 或进展判断检查 Agent Loop 终止条件模型返回格式错乱输出解析太严格或太宽松检查 LLM Integration 解析逻辑工具调用失败后卡死错误没返回给模型检查 Tool Runtime 异常处理上下文越来越长没有压缩机制检查 Memory compact 策略重复调同一个工具工作记忆没记录已执行步骤检查 Memory 更新逻辑任务跑一半状态丢失没有持久化检查 Memory 存储成本异常高上下文组装冗余检查 Context Manager 裁剪错误反复出现Recovery 分类不准检查错误标签和策略排查的时候有个技巧先看 Tracer 记录找到第一个出错的步骤然后看那个步骤涉及哪个子系统针对性检查。不要一上来就改 prompt大部分问题不在 prompt。7.1 三个我踩过的真实坑第一个坑是工具 schema 写得太宽松。有个工具的参数字段我写成了object结果模型传了一个嵌套很深的 JSON工具解析不了报错信息又不清楚排查了半天。后来把 schema 写严格每个字段都指定类型和约束问题就没了。第二个坑是上下文裁剪把工具列表截掉了。早期我用简单的 token 截断有一次任务比较复杂上下文超了截断后工具列表只剩一半模型调了一个不存在的工具直接报错。后来改成结构化裁剪工具列表永远保留问题解决。第三个坑是错误重试没有上限。有个外部 API 不稳定模型每次调用都超时Recovery 一直重试结果一个任务跑了五分钟还没结束。后来加了重试上限和熔断机制连续失败三次就放弃这个工具换方案或者上报。7.2 性能优化的几个入手点如果 Harness 跑得慢先看 Tracer 里的耗时分布。常见的优化点有三个。第一把能并行的工具调用并行化。比如同时查三个数据源不要串行。第二给频繁调用的只读工具加缓存。同样的查询在短时间内重复直接返回缓存结果。第三用小模型做简单步骤。意图识别、格式修复这些不需要大模型换小的能省不少时间和成本。延迟优化之外还要关注成功率。有时候慢一点但成功率高比快但经常失败要好。我的原则是先保证成功率再优化延迟。成功率上不去再快也没意义。7.3 什么情况下该考虑换框架如果你发现当前框架的抽象跟你的需求不匹配比如它不支持你要的循环控制、或者工具系统太封闭、或者可观测性太弱那就该考虑换。但换之前先确认问题真的在框架还是你的 Harness 没写好。我见过不少人频繁换框架但每个框架都用得很浅问题始终存在。判断标准很简单如果你能用当前框架写出清晰的 7 个子系统只是某些地方不够优雅那不用换。如果你发现某个子系统在当前框架下根本没法实现或者实现起来极其别扭那才考虑换。框架是工具Harness 设计才是核心能力。8. 最后分享几个实操中攒下来的小技巧工具描述要写得像给新人看的文档。模型选工具靠的是描述描述里要写清楚“这个工具做什么、什么时候用、参数什么意思、返回什么”。我一般会加一个“使用示例”字段模型看到示例后选对的概率明显提高。给模型留“退路”。不是所有任务都能靠工具解决有些需要模型直接回答有些需要向用户澄清。在工具列表里加一个ask_user工具让模型在信息不足时能主动提问比硬猜要好。状态快照要定期做。长任务跑到一半崩了如果有快照就能恢复不用从头再来。我一般每 5 步存一次快照存到外部存储任务恢复时从最近快照加载。评估集要持续更新。每次发现新的失败案例就把它加进评估集。这样回归测试越来越全面新改动不容易引入旧问题。我现在的评估集有 80 多个用例覆盖了大部分常见场景和边界情况。日志要能按任务 ID 一键拉取。排查问题时能快速拿到一个任务的完整执行轨迹比翻一堆分散日志高效得多。我在 Tracer 里做了一个get_trace(task_id)接口返回结构化的执行历史排查效率提升很明显。这套 Harness 的 7 个子系统我前后迭代了大半年从最开始的一个 while 循环到现在能稳定跑复杂多步任务。核心体会就一句话Agent 的能力上限由模型决定但稳定性下限由 Harness 决定。把 Harness 做扎实模型才能真的下地干活。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →