1. 从 Pi 到 DSH 的演进逻辑为什么 Agent Harness 需要「自生长」1.1 先搞清楚 Pi 和 DSH 到底指什么在聊 Agent Harness 的演进之前得先把 Pi 和 DSH 这两个概念说清楚。Pi 在这里指的是一种极简的 Agent 运行骨架——它的设计哲学是「最小可运行单元」只提供最基础的工具调用、上下文管理和循环控制能力。你可以把它理解成一个毛坯房水电通了墙刷了但家具家电一概没有住进去得自己慢慢添置。DSH 则是另一个阶段的产物它代表的是 Dynamic Self-growing Harness也就是动态自生长框架。和 Pi 最大的区别在于DSH 不满足于「你给我什么工具我就用什么工具」它会根据任务执行过程中暴露出来的能力缺口主动生成新的工具、新的子 Agent、新的编排逻辑甚至修改自己的调度策略。这两个东西放在一起看其实是一条很清晰的演进路线从「可扩展」到「自生长」。可扩展的意思是框架预留了接口你作为开发者可以往里塞东西自生长的意思是框架自己发现缺什么然后自己补上不需要你手动介入。1.2 为什么「可扩展」不够用了我最早接触 Pi 这类框架的时候觉得它已经很好用了。你注册几个工具函数写一个 system prompt配一个循环控制逻辑一个能跑起来的 Agent 就成型了。遇到新需求怎么办加工具就行了。工具不够用写个插件注册进去。这套逻辑在早期确实够用因为那时候大家做的 Agent 任务相对固定——无非就是搜索、计算、读写文件这几类。但问题很快就暴露了。当 Agent 面对的任务复杂度上升比如需要多步推理、跨领域知识整合、动态调整策略的时候「可扩展」的瓶颈就非常明显了。我踩过的一个典型坑是我为一个数据分析 Agent 注册了十几个工具结果它在执行过程中频繁选错工具或者把多个工具串行调用搞得一团糟。原因很简单——工具是我预设的但任务路径是动态的预设的工具集和动态的任务需求之间存在结构性错配。更麻烦的是当任务需要的能力不在预设工具集里时Agent 只能报错或者降级处理。你得停下来分析它缺什么手动写一个新工具注册进去再重新跑。这个循环太慢了而且高度依赖人的判断。说白了「可扩展」把扩展的责任放在了人身上而 Agent 本身只是一个被动的执行者。1.3 自生长到底「长」的是什么DSH 的核心思路是把这个扩展责任从人转移到 Agent 自己身上。但「自生长」不是说什么东西都凭空冒出来它长的是三个层面的东西第一层是工具层面的自生长。当 Agent 发现现有工具无法完成某个子任务时它会尝试用已有的基础能力比如代码执行、文本生成临时构造一个新的工具函数注册到当前会话的工具池里然后调用它。这个过程不需要人工干预也不需要重启框架。第二层是编排层面的自生长。单个 Agent 搞不定的任务DSH 会动态生成子 Agent给每个子 Agent 分配特定的角色和工具集然后设计一套编排逻辑把它们串起来。这套编排逻辑本身也是动态生成的不是预先写死的 DAG。第三层是策略层面的自生长。Agent 会记录自己在不同任务上的成功率和失败模式根据这些历史数据调整自己的调度策略。比如某个工具在特定场景下经常出错它会自动降低这个工具的优先级或者在使用前增加一步验证。这三层加起来才构成完整的「自生长」。只做第一层那叫「动态工具生成」只做第二层那叫「多 Agent 编排」三层都做并且形成一个闭环反馈才配得上 DSH 这个说法。1.4 演进路线背后的技术推力从 Pi 到 DSH 的演进不是拍脑袋决定的背后有几个技术推力在起作用。其一是大模型本身能力的提升。早期模型在代码生成和逻辑推理上的能力有限你让它自己写工具它写出来的东西大概率跑不通。现在模型在这方面的能力上来了自生长才有了技术基础。其二是任务复杂度的上升。单轮问答、单工具调用的场景已经不够看了现在大家做的是多步推理、跨系统操作、长周期任务这些场景对 Agent 的适应能力提出了更高要求。其三是成本结构的改变。以前人工写工具、调编排的成本相对可控因为任务数量少、变化慢。现在任务数量和变化速度都上来了人工维护的成本急剧上升自生长从「锦上添花」变成了「不得不做」。这里有一个常见的误解很多人以为自生长就是让模型随便发挥。实际上DSH 的自生长是在一套严格的约束框架内进行的——工具生成有模板约束编排生成有接口约束策略调整有边界约束。没有约束的自生长就是失控。2. Agent Harness 的核心架构拆解2.1 Harness 到底承担了什么角色Harness 这个词在英文里是「马具」的意思套在马身上用来控制马的方向和速度。在 Agent 语境下Harness 的角色类似——它不直接完成任务但它负责把模型的能力「套住」让模型在可控的范围内发挥。具体来说Harness 承担了以下几件事上下文管理决定哪些信息进入模型的上下文窗口哪些信息被压缩或丢弃。这直接影响了模型能否做出正确决策。工具调度管理工具注册、工具选择、工具调用和结果回传的整个流程。循环控制决定 Agent 什么时候继续执行、什么时候停止、什么时候触发异常处理。状态维护记录任务执行过程中的中间状态支持断点续跑和回滚。安全边界限制 Agent 的行为范围防止它执行危险操作。Pi 的 Harness 实现比较薄基本上就是上面这几件事的最小实现。DSH 的 Harness 则要厚得多因为自生长意味着 Harness 本身也需要具备动态调整的能力。2.2 从静态注册到动态生成工具系统的改造Pi 的工具系统是静态注册的。你在初始化的时候把工具函数注册进去运行时就固定了。DSH 的工具系统需要支持动态生成这就带来了一系列架构上的改动。首先是工具描述的问题。静态注册的工具描述是人写的清晰准确。动态生成的工具描述是模型写的可能含糊不清。如果描述不准确后续的工具选择就会出问题。DSH 的解法是引入一个「工具验证器」在工具注册前检查描述是否足够清晰、参数定义是否完整、返回值格式是否规范。其次是工具隔离的问题。动态生成的工具可能和已有工具产生命名冲突或功能重叠。DSH 的做法是给每个动态生成的工具分配一个命名空间并且在工具选择时做去重和优先级排序。最后是工具生命周期的问题。静态工具一直存在动态工具需要有一个过期机制。如果一个动态工具在多次调用中表现不佳或者任务已经结束它应该被回收避免工具池无限膨胀。# 动态工具注册的简化示意 class DynamicToolRegistry: def __init__(self): self.static_tools {} self.dynamic_tools {} self.tool_stats {} def register_dynamic(self, tool_func, description, namespace): tool_id f{namespace}.{tool_func.__name__} if not self._validate_description(description): raise ValueError(工具描述不达标) self.dynamic_tools[tool_id] { func: tool_func, desc: description, created_at: time.time(), call_count: 0, success_count: 0 } def select_tool(self, task_desc): # 合并静态和动态工具按历史成功率排序 candidates {**self.static_tools, **self.dynamic_tools} scored [] for tid, info in candidates.items(): score self._relevance_score(task_desc, info[desc]) if tid in self.tool_stats: score * self.tool_stats[tid][success_rate] scored.append((tid, score)) return max(scored, keylambda x: x[1])[0]上面这段代码只是一个示意实际实现要复杂得多。但核心思路是清楚的动态工具需要验证、需要隔离、需要统计、需要回收。2.3 编排层的自生长从固定 DAG 到动态拓扑Pi 的编排层通常是固定的。你定义一个 DAG节点是工具调用或子任务边是依赖关系。运行时按照 DAG 执行遇到分支就按预设条件走。DSH 的编排层是动态的。它不预设 DAG而是根据任务描述和当前可用工具实时生成一个执行拓扑。这个拓扑可能是线性的也可能是树状的甚至可能是带环的用于迭代优化。动态编排的难点在于如何保证生成的拓扑是可执行的我的经验是需要引入一个「编排验证器」在拓扑生成后检查以下几点是否存在循环依赖除非是刻意设计的迭代结构每个节点的输入是否都能从上游节点或初始状态获取是否存在孤立的节点没有任何连接整体拓扑的深度是否超过限制防止无限递归如果验证不通过编排器需要重新生成或者回退到一个更保守的拓扑。2.4 策略层的自生长反馈闭环怎么建策略层的自生长是最容易被忽略的一层但也是最能体现「自生长」价值的一层。它的核心是建立一个反馈闭环Agent 执行任务 → 记录执行结果 → 分析成功/失败模式 → 调整策略 → 下次执行时应用新策略。这个闭环要跑通需要解决几个问题数据收集的粒度。你不能只记录「成功」或「失败」需要记录每一步的决策、每个工具的调用结果、每次编排的执行时间。粒度太粗分析不出问题粒度太细数据量爆炸。模式识别的准确性。从大量执行记录中识别出有意义的模式需要一定的统计分析能力。简单的做法是统计每个工具在不同任务类型下的成功率复杂的做法是用聚类算法发现任务类型和工具组合之间的关联。策略调整的保守性。策略调整不能太激进否则会导致 Agent 行为不稳定。我的做法是设置一个「调整阈值」——只有当某个模式的统计显著性超过阈值时才触发策略调整。而且调整幅度要有限制不能一次调太多。3. 实操搭建一个最小可用的自生长 Harness3.1 环境准备与基础依赖要搭建一个最小可用的 DSH你需要以下几样东西一个支持函数调用的大模型接口这是基础没有这个什么都做不了一个代码执行沙箱用于动态生成的工具代码的安全执行一个向量数据库或简单的文本索引用于工具描述的检索和匹配一个状态存储可以用 SQLite也可以用内存字典看你的需求我建议从最简单的开始内存字典做状态存储简单的字符串匹配做工具检索代码执行用受限的 eval 或者 subprocess。等跑通了再逐步替换成更健壮的组件。3.2 工具动态生成的关键步骤工具动态生成是整个 DSH 里最核心也最容易出问题的环节。我把它拆成几个步骤第一步能力缺口检测。Agent 在执行任务时如果连续多次尝试都失败或者明确表示「我没有合适的工具来完成这个子任务」就触发缺口检测。缺口检测的输出是一个自然语言描述的能力需求比如「需要一个能把 JSON 数据转换成 CSV 格式的工具」。第二步工具代码生成。把能力需求描述和当前可用的基础能力比如 Python 标准库、已注册的底层函数一起喂给模型让它生成工具代码。这里的关键是给模型足够的约束——输入参数格式、返回值格式、异常处理要求都要在 prompt 里写清楚。第三步工具验证。生成的代码不能直接注册需要先验证。验证包括语法检查、单元测试用模型生成的测试用例、沙箱执行确保不会执行危险操作。第四步工具注册与试用。验证通过后注册到动态工具池并在当前任务中试用。试用结果会被记录用于后续的策略调整。# 工具生成的 prompt 模板示意 TOOL_GEN_PROMPT 你需要生成一个 Python 函数来完成以下任务 {capability_desc} 约束条件 1. 函数名必须为 {func_name} 2. 输入参数{input_params} 3. 返回值{output_format} 4. 必须包含异常处理异常时返回 {{error: 错误信息}} 5. 只能使用 Python 标准库和以下已注册函数{available_funcs} 请直接输出函数代码不要包含任何解释。 这个模板看起来简单但实际使用时有几个细节要注意。available_funcs要尽量精简只列出真正相关的底层函数否则模型会乱用。input_params和output_format要尽可能具体最好给出示例。异常处理的格式要统一方便后续的调用方处理。3.3 编排逻辑的动态生成与验证编排逻辑的动态生成本质上是一个规划问题。给定任务描述和可用工具集生成一个执行计划。这个计划可以用 JSON 表示每个节点包含工具名、输入参数、依赖节点列表。我常用的编排生成 prompt 是这样的ORCHESTRATION_PROMPT 任务{task_desc} 可用工具{tool_list} 请生成一个执行计划格式为 JSON 数组每个元素包含 - id: 节点唯一标识 - tool: 工具名称 - input: 输入参数可以是常量也可以是 {{node_id.output}} 形式的引用 - depends_on: 依赖的节点 id 列表 要求 1. 计划必须可执行不存在循环依赖 2. 每个节点的输入必须能从上游节点或初始状态获取 3. 尽量使用最少的节点完成任务 生成计划后需要验证。验证器检查依赖关系、输入可用性、工具存在性。如果验证不通过把错误信息反馈给模型让它重新生成。这个重试循环通常跑两三次就能得到可执行的计划。3.4 反馈闭环的落地实现反馈闭环的落地我建议从最简单的统计开始。每次任务执行完记录以下数据字段说明task_id任务唯一标识task_type任务类型可以自动分类或手动标注tools_used使用的工具列表tool_sequence工具调用顺序success整体是否成功step_results每个步骤的结果duration总耗时error_info如果失败记录错误信息有了这些数据你就可以做基础分析了。比如某个工具在特定任务类型下的成功率是多少某个工具组合的调用顺序是否影响成功率平均需要多少次重试才能成功基于这些分析你可以调整策略。比如降低低成功率工具的优先级在特定任务类型下推荐特定的工具组合对容易出错的步骤增加验证环节。4. 常见问题与排查技巧实录4.1 动态生成的工具跑不通怎么办这是最常见的问题。模型生成的代码看起来没问题一跑就报错。我的排查思路是这样的先看错误类型。如果是语法错误说明模型生成的代码本身有问题需要重新生成。如果是运行时错误比如参数类型不对、文件不存在说明代码逻辑有问题需要检查输入参数和运行环境。再看错误位置。如果是工具内部报错检查工具代码的逻辑。如果是调用方报错检查参数传递和返回值处理。最后看错误频率。如果同一个工具在多次调用中频繁出错说明这个工具的质量不行应该标记为「低质量工具」降低其优先级或者直接回收。我踩过的一个坑是模型生成的工具代码里用了某个第三方库但沙箱环境里没有安装这个库。后来我在 prompt 里明确限制了「只能使用 Python 标准库」这个问题就少了很多。4.2 编排计划执行到一半卡住了编排计划卡住的原因通常有几个某个节点的输入依赖没有满足。比如节点 B 依赖节点 A 的输出但节点 A 执行失败了节点 B 就一直等。某个工具调用超时。工具执行时间过长没有设置超时机制整个流程就挂在那里。循环依赖。虽然验证器会检查但有时候隐式的循环依赖检测不出来。排查方法给每个节点设置超时时间超时后触发异常处理。在编排执行器中加入「依赖检查」逻辑如果某个节点的依赖永远无法满足就跳过该节点并记录错误。4.3 工具池膨胀导致选择困难动态生成的工具如果不回收工具池会越来越大工具选择的准确率会下降。我的做法是给每个动态工具设置一个「生命周期」比如 24 小时或 100 次调用。定期清理低成功率的工具成功率低于 30% 且调用次数超过 10 次。对功能重叠的工具做合并或去重。这里有一个经验值动态工具池的大小控制在 50 个以内比较合适。超过这个数量工具选择的准确率会明显下降。4.4 策略调整导致行为不稳定策略调整太频繁或幅度太大会导致 Agent 行为不稳定。我的做法是设置调整冷却期比如每 100 次任务执行才允许调整一次策略。限制单次调整的幅度比如优先级调整不超过 20%。保留调整日志方便回滚。4.5 常见问题速查表问题现象可能原因排查方法解决方案工具生成后无法注册描述不清晰或参数定义不完整检查工具验证器的输出重新生成或手动修正描述编排计划验证不通过存在循环依赖或输入不可用检查验证器的错误信息反馈给模型重新生成工具调用频繁失败工具质量差或参数不匹配查看工具的成功率统计回收低质量工具或修正参数任务执行时间过长工具超时或编排过于复杂检查各节点耗时设置超时简化编排策略调整后效果变差调整幅度过大或数据不足对比调整前后的成功率回滚调整增加数据量5. 自生长 Harness 的边界与取舍5.1 什么情况下不该用自生长自生长不是万能的。以下几种情况下用静态的 Pi 反而更合适任务类型固定且变化慢。如果你的 Agent 只做一类任务而且这类任务的流程很稳定那静态工具集就够了没必要引入自生长的复杂度。对稳定性要求极高。自生长会引入不确定性如果你的场景不允许任何意外那还是用静态方案。资源受限。自生长需要额外的计算资源代码生成、验证、沙箱执行如果资源紧张静态方案更经济。5.2 自生长的安全边界怎么设自生长最大的风险是失控。Agent 自己生成工具、自己编排、自己调整策略如果没有边界可能会做出意料之外的事情。我的做法是设置三层边界第一层是代码执行边界。动态生成的代码只能在沙箱里执行不能访问网络、不能读写敏感文件、不能执行系统命令。第二层是工具注册边界。动态工具不能覆盖静态工具不能使用保留命名空间不能注册危险操作如删除文件、发送请求。第三层是策略调整边界。策略调整不能改变核心调度逻辑只能在预设的参数范围内微调。5.3 自生长和可维护性的平衡自生长带来的一个副作用是系统的行为变得难以预测和调试。今天跑得好好的明天可能因为策略调整就出问题了。为了平衡自生长和可维护性我建议保留完整的执行日志包括每次工具生成、编排生成、策略调整的详细记录。提供「冻结」功能在需要稳定运行时可以冻结自生长使用当前的策略和工具集。定期做「回归测试」用一组标准任务测试 Agent 的表现确保自生长没有导致性能下降。6. 从 Pi 到 DSH 的迁移路径6.1 渐进式迁移的步骤如果你已经有一个基于 Pi 的 Agent想迁移到 DSH我建议分步走第一步保留 Pi 的核心增加工具动态生成。先不动编排和策略层只把工具系统改成支持动态生成。这一步的改动相对小风险可控。第二步引入动态编排。在工具动态生成跑通后把编排层从固定 DAG 改成动态生成。这一步需要增加编排验证器。第三步建立反馈闭环。在编排层稳定后开始收集执行数据建立反馈闭环实现策略层的自生长。每一步之间留出足够的观察期确保上一层的改动没有引入问题再进入下一层。6.2 迁移过程中的注意事项不要一次性全部改完。自生长的三层是相互依赖的但同时改三层出了问题很难定位。保留回退路径。每一层都要有开关出问题时可以快速回退到静态模式。做好数据迁移。如果你在 Pi 阶段已经积累了一些执行数据迁移到 DSH 后要确保这些数据能被新的反馈闭环利用。6.3 迁移后的效果评估迁移完成后怎么判断 DSH 比 Pi 更好我通常看几个指标指标Pi 基线DSH 目标任务成功率基线值提升 10% 以上人工干预次数基线值降低 50% 以上新任务适应时间需要人工写工具自动适应时间缩短工具复用率低高如果 DSH 在这些指标上没有明显优势那可能说明你的场景不适合自生长或者自生长的实现有问题。7. 一些实操中的个人体会我在实际搭建和运行 DSH 的过程中有几个体会比较深。第一个是自生长的价值不在于「全自动」而在于「减少人工介入的频率」。你不需要追求完全无人干预只要能把人工介入从「每个新任务都要介入」降低到「每周介入一次」就已经很有价值了。第二个是工具生成的质量比数量重要得多。我一开始追求生成尽可能多的工具结果工具池里一堆低质量工具反而拖累了整体表现。后来我把重点放在提高单个工具的质量上效果明显好转。第三个是反馈闭环的数据量比算法复杂度重要。你不需要用很复杂的机器学习算法来分析执行数据简单的统计就够用了。关键是数据量要够数据质量要高。第四个是自生长 Harness 的调试比传统系统难得多。因为系统的行为是动态变化的同样的输入在不同时间可能得到不同的输出。我的应对方法是保留完整的执行日志并且在调试时冻结自生长用固定的工具集和策略复现问题。最后分享一个小技巧在工具生成的 prompt 里加上一句「请参考以下成功案例的代码风格」然后附上一两个高质量的工具代码示例。这样生成出来的工具代码质量会明显提升而且风格更统一后续维护也更容易。