
我刚完成一个 Agent 项目项目代号叫Agent-Reach。起这个名字的初衷很直接——做完一个 Agent 之后我最大的感受是现阶段 Agent 缺的已经不是聪明而是触达。模型再会推理如果碰不到文件系统、打不了浏览器、调不起命令行、读不到外部知识源那它本质上还是被关在聊天框里的鹦鹉。Agent-Reach 想解决的就是这个问题让 Agent 的能力不被对话边界锁死能真正抵达它该抵达的地方。这篇文章会把我在 Agent-Reach 里踩过的坑、做过的技术选型、以及最终沉淀下来的实操方案一次性讲透。适合正在做 Agent 开发、Agent 架构设计或者单纯想把 Agent 从聊天机器人推向能干活的工作助理的开发者参考。1. 为什么我做了 Agent-Reach先解决触达再谈智能1.1 一次让我崩溃的 Agent 翻车经历事情得从三个月前说起。当时我给一个内部知识库项目接了个 Agent用来帮同事做文档问答和摘要。初期效果还不错模型理解能力在线问答准确率也能看。直到有人提了一句能不能让它顺手把网页内容保存成 PDF 发到群里我当时天真地想这不就是给 Agent 加个工具的事儿吗结果发现模型确实懂怎么保存网页但它根本没有能力执行保存网页这个动作——它不能自己开浏览器、不能写文件、不能调用群里机器人接口。所有的动手能力都靠我在代码里提前写死换一个场景就要重新写一套胶水代码。更痛苦的是每加一种新能力就要改一遍 Prompt、改一遍工具注册逻辑、改一遍参数解析整个链路又脆又散。那次之后我意识到一个核心问题一套 Agent 架构如果只解决模型多聪明不解决模型能碰触哪些资源那它永远只是个高级玩具。Agent-Reach 就是从这个问题出发的项目重点不是再造一个模型封装而是把 Agent 的工具触达、知识触达、执行触达做成一套可插拔、可编排、可隔离的通用体系。1.2 触达这个词在 Agent 体系里到底指什么我做 Agent-Reach 时把触达拆成了三个层次这三个层次也是整个项目的三个核心设计支柱。第一层是工具触达。Agent 能不能调用外部 API能不能操作浏览器能不能读写文件能不能跑一段代码这一层解决的是手脚问题。很多 Agent 项目做到最后发现交互还行但一落到替用户干点实事就卡住了根子几乎都在工具触达层设计得太死板。第二层是知识触达。Agent 能不能访问长期记忆能不能检索知识库能不能把网页保存成结构化文档再喂给自己这一层解决的是情报问题。没有知识触达的 Agent 只能依赖单次对话里塞进去的上下文稍微涉及历史信息就断片而一旦能把外部知识变成统一格式输送给模型Agent 的可用性会立刻上一个台阶。第三层是执行触达。Agent 的能力不只体现在给出答案更体现在在受控环境下实际执行动作。比如让 Agent 去解析一个爬虫任务、跑一轮数据处理脚本、做一次自动化测试。执行触达最大的难点不是执行本身而是执行的安全性——这也是 Agent-Reach 里我花时间最多的地方后面会单独开一节细说。Agent-Reach 的产品定位很清晰**做一个以技能和编排层为双核心的 Agent 能力扩展底座。**模型可以用任何主流大模型但能做什么、能碰什么、怎么安全地做全部由 Agent-Reach 统一接管。这样无论底层模型换成 GPT、Claude 还是开源模型上层能力都不会塌。2. Skills 模块可插拔四肢的设计与一个完整示例2.1 从 function calling 到 Skills差的不只是叫法提到给 Agent 加能力很多人第一反应是 function calling。OpenAI 和 Claude 都支持概念也简单把函数定义丢给模型模型决定调不调、传什么参数。但实际项目跑起来之后你会发现 function calling 有几个天生短板。第一它只能解决调函数这一步解决不了函数的输入怎么来。比如我想让 Agent 把网页存成 Markdown函数签名可以是save_webpage(url, output_path)可模型并不知道应该用哪个抓取引擎、要不要启用 JS 渲染、遇到登录页怎么处理。函数对模型来说是个黑盒模型只能靠函数名和描述猜测怎么用。第二function calling 的粒度太细导致你每换一个使用场景就要重新注册一堆函数。Agent 项目在起步阶段还行一旦技能数量超过二十个函数列表本身就会占用大量上下文模型的选择准确率还会明显下降。第三也是最重要的function calling 没有环境概念。函数一旦被调用它就跑在 Agent 的进程里权限、超时、资源消耗全部混在一起很难隔离。我前几版 Agent 里有一个自动截图的技能因为底层 Playwright 连接没释放直接把整个服务的文件描述符耗光了——这就是没有边界管理的典型翻车。Agent-Reach 做 Skills 模块时从一开始就把技能定义为一个自治的能力单元核心思路是让技能自己有说明书、有运行环境、有资源边界。模型不再面向一大堆函数做选择而是面向一个技能目录做决策选择后由编排层接管技能和 Agent 主进程隔离运行。2.2 实战一个网页转 Markdown技能从零落地我以 Agent-Reach 里最常见的一个技能来演示整个链路——把网页保存成 Markdown。这个技能在内部叫web2md结构是这样的skills/ web2md/ SKILL.md # 技能说明文件给模型看 run.sh # 技能入口 requirements.txt # 依赖声明 handler.py # 核心逻辑 assets/ # 静态资源模板、配置SKILL.md是这个技能的使用手册里面写清楚技能能做什么、需要什么输入、有什么限制。我一开始写技能说明踩过坑——写太简单模型不知道怎么用写太复杂模型选择时容易被误导。最后摸索出来的模板是固定三段式用途、输入参数、注意事项。# Skill: web2md ## 用途 抓取一个网页并将正文内容转换为干净的 Markdown 格式 适合用于知识库采集、文档归档、内容摘要等场景。 ## 输入参数 - url: 目标网页地址必须包含协议头http/https - output_path: 保存路径可选默认保存到当前工作目录 - js_render: 是否启用 JS 渲染布尔值默认 false ## 注意事项 - 仅支持公开可访问的页面需要登录的页面请调用 auth 技能 - 页面内容过大时超过 500KB只保留正文主区域 - 图片链接保留原 url不做下载handler.py里是实际抓取逻辑。选型时我用的是requestsBeautifulSoup做基础抓取遇到 JS 渲染页面再退化到 Playwright。这里有个非常现实的性能问题默认技能不能一上来就用重量级渲染引擎因为 90% 的页面用静态抓取就够了。只有用户显式声明js_rendertrue才会走到 Playwright 分支。# 伪代码示意实际项目要加超时和重试 def fetch(url: str, js_render: bool False) - str: if js_render: return render_with_playwright(url) return fetch_static_html(url)技能写好之后还要在 Agent-Reach 的技能注册表里登记。注册时我会为每个技能定义一个能力签名类似给技能打索引标签。比如web2md的签名是[web, markdown, save, content_extraction]。当 Agent 收到把这篇文档存成 Markdown的请求时编排层会通过签名检索把候选技能范围缩小到两三个再连描述一起交给模型做最终选择。这套先检索再选择的流程比直接把几十个技能全部塞给模型要稳定得多。2.3 技能和模型之间到底怎么通信Skills 模块能跑通核心是解决了模型怎么操作技能的交互协议。我在 Agent-Reach 里没有用自定义格式而是直接跑在标准函数调用协议之上只是把函数这个粒度替换成了技能粒度的注册和调用。这样做的好处很多任何支持 function calling 的模型都可以直接适配不需要改模型侧的接口。整个调用流程是这样的模型根据用户请求和技能描述输出一个结构化动作比如call_skill(skillweb2md, params{url: ..., output_path: ...})编排层拦截这个动作做一次参数合法性校验必须的——模型幻觉参数的时候真的能把路径写飞校验通过后编排层在沙箱环境里拉起技能进程传入参数并监控执行状态技能执行结束后输出结果可能是结构化数据也可能是文件路径回传给编排层编排层把返回值压缩成一段摘要连同执行状态一起填充给模型模型再接着生成对用户的回复这里有一个我反复强调给团队的原则模型不应该直接看到技能的原始输出。比如web2md抓下来 300KB 的网页如果直接把全文塞回模型上下文一次对话的 token 预算瞬间就没了一半。Agent-Reach 会在技能输出层做一个正交压缩——把抓取结果先转成摘要、结构化大纲再喂给模型。关于这块踩过的 token 爆炸的坑后面我会单独作为一节详细说。3. Harness 编排层Agent 到底应该怎么动手干活3.1 harness 和 agent 的区别大脑不能自己长手脚刚开始接触 Agent 生态的人经常混淆两个概念Agent 和 Harness。我自己的理解是这样——Agent 是大脑负责理解、规划、决策Harness 是身体和感官系统负责把所有动手的过程串起来包括工具调用、技能路由、记忆处理、上下文管理、沙箱执行和错误恢复。光有 Agent 没有 Harness模型就只能嘴上说说什么也执行不了光有 Harness 没有 Agent那也就是个流程自动化脚本没有任何自适应能力。Agent-Reach 的定位里Harness 层才是真正决定一个 Agent 项目能不能落地的关键因为大模型能力大家拉不开差距差距全在谁能把能力稳定地接出来。我见过很多团队一上来就买最好的模型然后花两个月什么也没做成最后发现卡在模型不给力上。其实不是模型不给力是他们把模型当成了整个系统而没有给它配一个可靠的 Harness。Agent-Reach 的 Harness 我做了大半年中间删了三次重写其中最核心的设计是任务生命周期管理加上下文预算控制。这两个东西少一个Agent 跑长任务就一定会挂。3.2 Agent-Reach 的 Harness 工作流详解Agent-Reach 的 Harness 把一次 Agent 请求拆成了六个阶段每个阶段都有明确的状态记录和异常处理逻辑意图接收接收用户原始请求做基础清洗比如把 帮我存下这个网页 https://xxx/article/123 识别为保存网页意图技能检索通过技能签名和关键词匹配从技能注册表里筛出候选技能列表动作决策把候选技能的描述、参数模板、限制条件打包成模型可读格式交给模型做出最终选择参数解析与校验解析模型返回的动作参数逐项校验类型、格式、安全边界最重要的阶段沙箱执行拉起沙箱环境执行技能全程监控 CPU、内存、网络、超时结果回填把技能输出压缩、提炼后回填给模型辅助模型生成最终用户回复这个流程里我踩过的一个大坑是第 2 步和第 3 步的衔接。早期版本我把所有技能描述一次性全塞给模型数量一多模型就开始犯迷糊明明用户只是要存网页模型偏要调用邮件发送技能还一本正经地填了收件人地址。后来改成先粗筛、后决策的两阶段路由准确率才从 60% 出头拉升到 95% 以上。粗筛规则很简单基于关键词和技能标签做向量匹配就行不需要太复杂的模型。3.3 第三方工作台接入实践我为什么坚持标准协议Agent-Reach 做到中后期面临一个很现实的需求外部工作台怎么接进来市面上的 Agent 项目无论是开源社区还是商业产品很多都有自己的一套工具协议。比如有的用 LangChain tools有的用 OpenAI 的 function schema还有的干脆自研一套 JSON-RPC。我需要在 Agent-Reach 的 Harness 层做一个接入层能把这些不同来源的能力统一纳管。我最终选择了兼容 MCPModel Context Protocol风格的接口规范同时保留一层薄薄的适配器。这样做的理由有三点。一是 MCP 已经基本成为 Agent 工具生态里的事实标准社区里大量现成的 server 可以直接拉起来挂载二是我希望 Agent-Reach 的价值不是绑定在某一个大模型厂商上而是能和任何会说话的 Agent协作三是适配器层让我能兼容老项目里那些不适合改造的私有协议不至于为了接一个第三方工作台把整个技能体系推倒重来。这里以接入 an Agent 工作台 为例说下具体步骤。Agent-Reach 的 Harness 对外暴露一个标准技能服务端口工作台只需要配置一个 MCP 客户端指向这个端口就能看到 Agent-Reach 里注册的全部技能。工作台发起调用时Agent-Reach 会先校验来源身份的认证 token再进入正常流程执行技能。我实测下来接一个第三方工作台从 0 到跑通一次技能调用大约只需要半小时大部分时间花在调试认证配置上。必须提醒的是第三方接入最头疼的不是协议本身而是超时策略的差异。很多工作台默认请求超时只有 30 秒但 Agent-Reach 里有的技能比如复杂页面爬取动辄要跑 3-5 分钟。如果不在接入层做双向适配——即一方面提高服务端超时阈值另一方面给工作台客户端配置长任务轮询模式——就会出现工作台这边以为调用失败了、但沙箱里技能其实还在跑的尴尬局面。这个问题我在接入早期频繁碰到后来我在技能服务的响应头里增加了x-agent-task-id字段配合轮询接口才彻底解决。4. 框架对比与 Agent-Reach 的架构取舍LangChain、Dify、CrewAI 之外的选择4.1 主流 Agent 框架的真实差异别被社区话术带偏做 Agent 开发绕不开框架选型。社区里关于 LangChain、Dify、CrewAI 谁更好的争论几乎天天都有我自己的看法是没有谁绝对更好只有谁更契合你的问题域。我把它们整理成一个横向对比表方便大家按图索骥维度LangChainDifyCrewAI定位开发库/工具链低代码应用平台多 Agent 协作编排框架上手成本中高需要熟悉链式 API低主要在可视化界面配置中需要理解角色/任务抽象灵活度高代码在手什么都能改中受平台能力边界限制中高但复杂协作逻辑仍需写码多 Agent 支持需自行设计工作流形式有限支持核心卖点开箱即用技能扩展强但组织散乱依赖插件体系弱主要靠内部工具定义生产级部署需要自己搭平台能力封装良好需要自行搭建监控与持久化调试友好度低链路深时很难定位高可视化看板直观中角色间消息易追踪但细节有限我的实测体验更直白LangChain 适合做过程研究和快速实验它的组件拆得很细方便你理解一个 Agent 的各个环节是怎么组装起来的但真到了生产环境你会发现它的抽象层次太多排查一个问题要在多层 Tape 之间跳来跳去很费神。Dify 适合做业务产品化特别是非核心研发团队想快速搭一个带知识库的问答应用用 Dify 几乎不需要写代码但它把底层逻辑包得太严想深入定制某个技能执行流程会碰到天花板。CrewAI 的卖点在多 Agent 仿真适合研究多个角色怎么协作这类课题但实际生产中多数场景其实不需要那么复杂的多角色博弈——两个能干的 Agent 已经能覆盖 90% 的需求再多就是表演了。4.2 Agent-Reach 为什么不直接用现成框架你可能要问既然有这么多框架为什么 Agent-Reach 还要自研一套我的回答可能会得罪人但确实是我做完整个项目后的真实体会现成框架解决的是从 0 到 1 把 Agent 跑起来的问题而 Agent-Reach 解决的是从 1 到 100 让 Agent 可靠地在业务里落地的问题。两者的目标不同导致架构取舍完全不同。LangChain 之类的框架首先保证的是灵活和覆盖度所以在追求极致安全边界和资源可控时它的设计反而成了阻碍。举个例子Agent-Reach 的沙箱要求技能进程和主进程之间用严格的 IPC 协议隔离主进程崩溃不能拖垮技能执行技能执行翻车也不能污染主进程状态。这个需求在 LangChain 里实现起来非常别扭——它的工具执行链路默认就在同一进程里你要做隔离得先在框架层面开洞。与其在框架的限制下妥协不如按照自己的核心诉求重新设计执行内核。另一个原因是上下文预算管理。我前文提到 Agent-Reach 对模型上下文采取精打细算的策略。现成框架基本没有提供细粒度的 token 使用控制它们更倾向于把上下文喂饱。而 Agent-Reach 几乎每个环节都在做压缩和取舍——技能输出要提炼、历史对话要裁剪、长文档要分片。这些逻辑如果寄生在别人的框架里侵入性太强维护成本太高。4.3 底层为什么选 Rust不是跟风是这几个痛点逼的Agent-Reach 的性能敏感模块和沙箱模块用 Rust 实现上层业务逻辑用 Python 快速迭代。这个组合不是一开始就定下来的是被性能和安全问题逼出来的方案。最直接的动机是并发执行。Agent 项目跑起来之后同一时刻往往有多个技能在并发执行有的在抓网页有的在跑数据处理有的在调外部 API。负责人体线程、子进程和资源监控的部分如果全部用 PythonGIL 会卡住整条执行链而且子进程管理在 Python 里稍不注意就会出僵尸进程或 fd 泄漏。Rust 的并发能力刚好补齐这个短板——技术栈上我只需要专注内存安全和高吞吐的事件循环就可以把多任务的资源隔离做成可靠底座。同时沙箱的安全边界也需要底层语言级别的保障。Rust 的所有权系统提供了无 GC 但内存安全的强约束对构建可信边界有天然优势——不过这属于我个人技术偏好如果你的团队不熟 Rust用 Go 或 Python 容器隔离也有可行路径什么语言不是唯一解边界模型才是。还有一个现实考虑是体积和可移植性。Agent-Reach 希望技能环境能跑在服务器、容器甚至边缘设备上。Rust 编译出来是单文件二进制不依赖运行时部署成本极低。配合容器镜像反而比纯 Python 虚拟环境的方案更容易在资源受限场景里分发。4.4 多 Agent 协作时我在 Agent-Reach 里怎么设计热搜词里多 agent出现频率很高说明很多人对多 Agent 协作感兴趣但我要泼一盆冷水多 Agent 系统 90% 的性能问题出在通信协议和任务同步上而不是出在角色设计上。Agent-Reach 的多 Agent 协作机制走的是群聊式消息总线路线。每个 Agent 是一个独立的执行单元有自己的技能集和上下文通过 App-Reach 的消息总线发布/订阅消息。比如任务发起者 Agent A 发现网页转储需要知识库辅助就发一条消息到knowledge-agent的订阅队列等待响应知识 Agent 处理完后再把结果发回main-agent的响应主题。这种设计的优点是模块解耦任何一个 Agent 挂了不会影响整个任务链。但坏处是消息乱序和任务超时管理非常考验编排层。我踩过的最典型的一个坑是两个 Agent 同时往总线里发消息主 Agent 这边的回调函数没有做好幂等处理结果一个文档被重复处理了三次知识库里一下子多了三份内容近似的条目。后来我统一给所有消息加了request_id和unique_key在处理端做去重问题才彻底解决。在这一点上Agent-Reach 的编排层有一个任务协调器专门负责监控跨 Agent 任务的完成状态包括超时兜底、失败重试、以及结果校验。没有这个协调器多 Agent 系统很容易陷入鸡生蛋蛋生鸡的互相等待僵局。这个设计也是我整个项目里认为最有复用价值的组件之一。5. 沙箱与安全能力越大越需要边界5.1 隔离等级的比较别把安全当成一个按钮给 Agent 加技能最让人担心的就是安全。一个能执行命令、能访问网络、能写文件的 Agent如果没有任何边界约束本质上就是给黑客递了一把装了自动瞄准的枪。Agent-Reach 里我把隔离分成了四个等级不同的技能匹配不同等级宁严勿松。隔离等级技术手段适用场景安全强度性能损耗L0 无隔离主进程直接调用纯计算型技能无副作用最低零L1 进程隔离子进程 系统调用过滤执行脚本、调用 CLI中低低L2 沙箱隔离容器/namespace 资源限制网络访问、文件读写、代码执行中高中L3 虚拟机隔离硬件虚拟化不受信代码、外部输入最高高Agent-Reach 默认把所有技能放到 L2 沙箱里运行除非技能开发者明确声明我这个技能不需要网络、不需要写文件才能降级到 L1。这里的逻辑很简单安全策略默认从严放开权限必须经过显式审批。很多 Agent 生产事故都是因为开发者觉得跑在本机临时跑一下没关系最后把漏洞带到了生产环境。5.2 Agent-Reach 沙箱的具体配置策略沙箱的核心不是创建一个隔离环境就完事而是要把最小权限原则落实到系统层面。Agent-Reach 的沙箱配置我总结成五条硬规则每条都是踩出来的经验——理论上通用容器技术就能满足但下面这几点是排坑后我最终强化的关键项只读文件系统技能运行的根目录是只读的只有设计好的临时目录和输出目录可写网络白名单默认禁止所有网络访问只有技能声明了明确的外部域名才放行放行列表必须写到沙箱配置里细粒度的系统调用过滤拦截掉危险的系统调用比如reboot、mount、ptrace资源限额CPU 时间、内存上限、文件大小、子进程数量逐项设置超额则直接杀掉任务临时文件自动清理沙箱进程退出后所有写入的临时文件统一回收不留残留我举一个具体的配置例子。web2md技能需要的权限是能访问任意公开网页和能写输出文件那它在 Agent-Reach 里的沙箱配置大致长这样sandbox: level: L2 read_only_root: true network: egress_enabled: true allow_domains: [*] block_domains: [10.0.0.0/8, 169.254.169.254] resources: cpu_time_sec: 120 memory_mb: 512 max_file_size_mb: 20 max_processes: 4 cleanup: temp_dir: true cache_dir: true注意这里的网络配置我特意加了block_domains用来拦最臭名昭著的元数据 IP云环境里的169.254.169.254。如果沙箱能任意访问内网那 Agent 一旦被提示注入攻击整个内网都将暴露给攻击者。这个细节也是 Agent-Reach 评估时内部测试人员最早发现的漏洞之一。5.3 沙箱环境变量泄漏一次完整的排查链路在沙箱上线第一周我就碰到一个意外技能在沙箱里执行时能读到宿主环境的数据库密码。用户只是让 Agent 把网页存成 Markdown不该碰到这些机密。排查过程是这样的。首先我怀疑是不是沙箱配置没生效检查 namespace 隔离参数后确认网络和挂载确实独立。接着我做了一个小实验在宿主机设置一个特殊环境变量然后在沙箱里执行printenv发现这个变量依然存在。问题锁定在环境变量传递链路上。继续追查发现 Agent-Reach 在启动沙箱进程时为方便技能定位一些基础配置如语言、时区把宿主机的部分环境变量透传了进去。我本意是只传几个白名单变量结果当时为了图省事直接用 Python 的os.environ全量复制了一份。修复方案分两步第一步重构沙箱启动逻辑切断所有环境变量继承改成显式传入一个白名单环境变量字典第二步把宿主机上的敏感信息全部迁移到独立的密钥管理系统只在 Agent-Reach 主进程内部解密沙箱内永远不出现真实的数据库密码。这个坑的根因其实不是沙箱的技术不行而是我在边界设计时留了一条方便之门。教训很深刻安全边界最怕的不是敌人太强而是开发者图方便。现在 Agent-Reach 的所有沙箱配置都必须在代码评审时过一遍最小权限清单我也把这个清单做成了一套自动检查工具每次上线前自动跑一遍。5.4 给 Agent 提权之前先过这张自检清单我在 Agent-Reach 里沉淀了一份技能权限评审清单每次新技能上线前都按它逐条打勾该技能是否明确需要访问网络如果不需要是否已经设为egress_enabled: false该技能是否只需读取固定路径如果是文件系统是否已设为只读技能运行时最长可能跑多久是否已设定充裕却不至于导致僵尸进程的超时时间技能代码里有没有硬编码走特殊渠道逃逸沙箱的写法技能依赖的第三方库是否存在已知的高危漏洞上线前跑一次依赖安全扫描技能是否输出敏感信息如果输出结果会回传给大模型是否需要做失敏过滤这套清单看起来简单但每次执行都能拦住几个潜在问题。最常拦下的其实是技能描述和实际行为不一致——开发者把技能包装成了一个只读网页的角色实际代码里却偷偷写了文件。这不一定是有意的更多是代码迭代时顺手加的调试逻辑忘删了。清单机制配合代码审查双保险。6. 开发过程中最典型的三个坑与完整排查链路6.1 坑一技能输出把上下文窗口直接撑爆execution terminated due to error我最早在 Agent-Reach 里跑一个整站抓取技能准备把某个网站的文档全站保存下来。结果技能没跑完就报错过一次报错信息是典型的agent execution terminated due to error.。一开始我以为是脚本崩了去日志里排查却发现脚本正常结束问题出在模型侧——技能把整站抓到的内容全部回填给了模型导致多轮调用后上下文超出了模型的 token 上限。这个坑的本质是上下文预算失衡。技能执行结果动辄几万 token而模型能接受的上下文总量是有硬边界的。你让模型看到的东西越多后面的新信息能进来的空间就越小甚至会直接把请求顶爆。修复思路也很直接Agent-Reach 里所有技能的输出都不直接进上下文而是经过三层处理。第一层是最大长度截断超过 6000 字符的输出强制截断截断时优先保留开头部分通常包含文档标题和核心摘要。第二层是结构化提取如果输出本身是网页或文档先让规则引擎抽取出标题、段落、链接列表比把原文全量丢进去轻量得多如果规则不够用再用一个小型模型做一次离线摘要。第三层是引用保留我始终把原始输出保存到一个可寻址位置比如临时文件或对象存储回填给主模型的只是一段摘要 详细内容存储路径如果用户后续追问细节再按需读取原文。这套链路下来单次技能调用的 token 开销从可能吃掉几十万压缩到平均两千以内整体稳定性和响应速度都肉眼可见地提升了。6.2 坑二多技能并发时的资源竞争与僵尸进程Agent-Reach 上线没多久运维同事反馈服务偶发卡死重启后恢复但过一阵又复现。我第一反应是看日志里的慢查询和锁竞争排查半天没发现异常。后来在一次卡死期间强行 dump 了进程堆栈才发现大量僵尸子进程堆积把进程表给占满了。根因出在技能执行的并发管理逻辑上。我最初封装技能调用时用subprocess.Popen拉起子进程但缺少两层保障第一子进程的退出状态没有在主循环里定期回收waitpid没接干净第二技能执行超时后只杀了子进程本身没有杀完整的进程组——技能里如果再拉孙进程就容易留下孤儿进程继续跑。修复分三步。第一步把所有子进程的启动改成进程组模式超时杀任务时执行killpg保证整棵进程树不残留。第二步在沙箱的资源配置里显式限制max_processes: 4阻断任何进程无限繁殖的路径。第三步写了一个定时调度任务每三十秒巡检一次子进程回收队列发现僵尸立即清理。这三步下去之后Agent-Reach 连续跑了一周没再出同样的卡死问题。这个坑给了一个重要经验Agent 的技能执行本质上是不可信代码在跑必须假设它可能拉出任意数量的子进程所以一切资源都要在沙箱配置里显式封顶。6.3 坑三技能结果被大模型二次创作后失真有一次用户要求 Agent 把一份 JSON 格式的统计数据转成自然语言报告。技能端的输出是一段精确的数字比如{sales_total: 1234567.89, growth_rate: 12.5}。结果模型在生成报告时把数字脑补成了 1230000 和 12%。用户一眼看出对不上整个任务的信任度瞬间崩了。问题出在回填机制上。我把技能输出和用户提示词一起喂给模型模型在生成自然语言时对数字做了合理化近似——它觉得 1234567.89 看起来不够整齐于是自作主张做了四舍五入。这类无意识失真在 Agent 场景里特别危险因为它不像明显的错误那样容易被发现而是悄悄地把数据精度抹掉了。修复方案是给技能输出加上一层数据保真协议。在 Agent-Reach 里凡是技能输出中标记为critical_fidelity的字段如金额、数量、日期回填给模型时都用强格式包裹比如用DATA_LOCK[1234567.89]END这种不可被改写的方式标记同时在系统提示词里明确写死规则所有 DATA_LOCK 内的内容必须原样呈现禁止改写、近似或重述。 这一招虽然笨但在实测中非常有效模型对这类强约束的遵循率几乎达到百分之百。另外我在技能描述里也加了一条约束输出结构化数据时尽量返回表格 原始数值双通道而不是只有一段自然语言。这样既方便模型理解语境又保留了最终核对的锚点。6.4 这三个坑的共同根源回头总结这三个典型的坑它们其实指向同一个根源Agent 系统中的边界没有设计清楚。技能输出与模型上下文的边界、技能进程与宿主资源的边界、模型生成与事实数据的边界只要有一条模糊系统就会从偶尔出错滑向频繁翻车。这也是我从 Agent-Reach 这个项目里得到的最大的经验做 Agent最关键的不是模型多聪明而是把每一层边界都做成显式的、可配置的、可测试的。模型负责聪明的部分工程负责可靠的部分两边都要在线。最后分享一个自己常用的质量保障技巧技能探针整个 Agent-Reach 做完之后我沉淀了一个特别想分享的实操技巧——技能探针Skill Probe。所谓技能探针就是为每一个上线的技能准备一组最小探测用例每次环境变更或技能升级后自动跑一遍探针来验证技能是否仍然可用。比如web2md的探针就是三组固定 URL第一组是一个纯静态页面测基础抓取是否正常第二组是带 JS 渲染的单页应用测 Playwright 分支是否正常第三组是一个故意设置超时的请求测超时兜底逻辑是否符合预期。探针的价值在于它把技能合格从感觉变成了一套标准。以前新技能上线大家全凭应该没什么问题就放行之后出事才追悔莫及。现在每个技能必须过探针才允许注册到技能目录里模型才能看到它。这既保护了业务稳定性也省掉了后面大量救火时间。Agent-Reach 这个项目做到现在给我最大的感受就是Agent 的名字里带了个智能但真正让它有价值的从来都是背后的工程体系。技能、编排、沙箱、上下文预算、数据保真——这些词单独拿出来都不算性感但拼在一起才有可能让一个模型真正变成能干活的 Agent。如果你也在做 Agent 项目尤其是正准备给 Agent 接技能和工具我建议你先停下来想想你的 Agent 有多大的触达范围这个范围的边界是清晰的还是模糊的边界一旦模糊后面所有的问题都会从这个裂缝里钻出来。先把触达做清楚再谈智能才是 Agent 工程的正路。