
去年底我接手了一个代号为 Agency Agents 的项目。这串英文名当时吓退过不少人听得最多的问题是Agency 是代理的意思Agents 还是代理叠在一起到底想干什么。等我把第一版跑通再回看这个名字反而觉得它概括得很准这不是一个单独的工具而是一组可以按需组合、协同工作的编程智能体集合背后需要一整套运行时和平台抽象层才能在不同操作系统、不同开发环境里稳定运转。Agency Agents 要解决的真实痛点很直接单个大模型写代码的 Demo 已经够多了但一旦要放进真实项目面对不同用户的终端环境、不同操作系统的路径规则、不同权限体系绝大多数单体 Agent 方案会迅速失效。“跑得通”和“能落地”之间差的正是跨平台适配、任务编排、工具调用安全这一整套工程化内容。所以这篇文章不只是介绍这个概念而是把我从架构设计到工程落地全程的关键决策、核心代码组织、踩过的坑都拆出来。如果你正在做 AI 编程助手、本地自动化 Agent或者想设计一套可插拔的智能体插件系统这里的思路可以直接参考。1. 项目背景与核心设计思路1.1 什么是 Agency Agents从“单个智能体”到“智能体生态”先给这个概念划一条明确的边界。Agency Agents 本质上是一个项目代号描述的是一组有明确分工、能互相协作的 AI Agent 集群。常见角色包括代码生成者、测试执行者、静态扫描者、文档维护者、命令执行者等。它们共享同一个运行时通过统一协议交换消息和工具调用结果最终对外表现为一个“跨平台 AI 编程智能体生态”。这个名字里的 Agency 强调的不是网络代理而是“代理处”这种组织形式。你可以把整个系统想象成一支开发小队有人负责写代码有人负责跑测试有人负责查日志有人负责收尾清理。每个 Agent 都是小队的成员单看能力都不算全能但组合起来能覆盖一个完整任务的闭环。单体 Agent 更像“全栈超人”看起来什么都能做一旦遇到环境差异、上下文超限、任务过长就会变成谁都要管的瓶颈而多 Agent 生态把复杂度拆到不同角色上每个角色可以被单独替换、单独测试、单独升级。这个设计也决定了我们后续所有的技术选型Agent 之间不能硬编码调用而是通过“意图”和“事件”通信每个 Agent 只暴露自己擅长的工具接口任务调度层负责拆解、分发、汇总。这样一个生态才能做到可插拔否则就只是把一堆函数堆在一起仍然不是 Agent 体系。1.2 为什么跨平台是刚需从单机脚本到多环境协作很多人最开始不理解为什么要强调“跨平台”。我当时的判断是如果一套 Agent 生态只能在开发者自己的笔记本上跑那它就没有完成工程化的使命。实际落地时同一个 Agent 编排很可能要出现在三类场景里用户在自己的桌面环境里通过命令行或编辑器插件调用团队在共享服务器上跑批量代码分析CI 流水线里自动执行失败用例分析。这三类环境的操作系统、终端、文件权限和可用命令完全不同。桌面环境可能是图形化系统路径用反斜杠或正斜杠、换行符也不一样服务器环境普遍是精简安装很多常用命令不一定存在CI 环境大概率是容器默认没有图形界面环境变量和用户权限也都不同。如果 Agent 直接拼装 shell 命令同样的pytest tests/在不同平台上可能写法一样但一旦涉及路径传参、环境变量注入、文件查找差异立刻暴露。跨平台抽象不是给每个平台写一份业务逻辑而是把系统差异统一收敛到一个适配层业务 Agent 只跟“读文件”“写文件”“执行命令”“解析路径”这类抽象能力打交道。这样当新平台接入时只需要新しい适配器实现不用改任何 Agent 业务代码。这个思路是整个 Agency Agents 架构的基础也是我踩过大量坑之后最想强调的一点。1.3 从单体脚本到生态架构演进的三次重构Agency Agents 不是一开始就长这样前后经历了三次比较大的重构。第一版是“单体脚本”。所有工具函数、模型调用、命令执行都写在同一个 Python 文件里大概三千行。Demo 阶段很好用往终端里输入一个任务描述脚本就能生成一段代码并尝试运行。但问题很快出现新增一个 Agent 能力就要改主流程模型返回一个意外格式整个任务就中断想分析失败原因只能靠print满天飞。第二版是“多 Agent 消息队列”。我把不同能力拆成独立 Python 服务通过进程间消息通信联动。职责清晰了一些但跨平台问题散落在每个 Agent 里路径处理逻辑复制了三份有一处不一致就会出现诡异问题。这让我意识到缺少一个统一抽象层所谓“多 Agent 协作”就是一句空话。第三版就是我接下来要展开的架构引入平台适配层、工具协议、会话上下文和插件注册机制。Agent 变成纯逻辑模块不直接触碰操作系统细节外部操作全部走工具层平台相关代码只在适配器里出现。这次重构之后新平台接入成本从改几十处降到只写一个适配器新 Agent 扩展从改主流程降到新增一个文件。架构的收益是长期才能看出来的但这次重构从第三天开始就明显减少了拉扯。2. 总体架构设计分层与模块拆解2.1 分层模型入口、编排、能力、基础设施第三版架构按四层设计每层职责单一不互相越权。层级核心职责主要模块入口层对接用户和外部系统命令行工具、编辑器插件适配、HTTP 服务编排层管理任务、会话、Agent 路由任务调度器、会话管理器、上下文缓存能力层实现具体智能体能力代码生成 Agent、测试 Agent、文档 Agent基础设施层屏蔽平台差异、控制权限、记录日志平台适配器、工具沙箱、结构化日志入口层解决的是“用户从哪进来”。同一个任务从终端发起和从编辑器插件发起参数和反馈形式都不一样但编排层接收到的应该是一份标准化任务描述。所以入口层只做格式转换不拼装业务逻辑。编排层是整个系统的心脏。它负责拆解用户意图把大任务变成小步骤决定哪个 Agent 先跑、哪个 Agent 后跑以及整个任务是否已经收敛。它同时管理会话上下文保证多轮对话和长任务有记忆。能力层的 Agent 是纯逻辑模块。每个 Agent 接收一张任务卡返回一份结果卡。它们不直接创建进程、不直接读写文件系统所有动作都通过调用工具接口完成。这一条看似简单却是后期安全和调试的基础。基础设施层是跨平台的落点。它把路径解析、命令执行、环境变量、文件读写这些系统相关操作全部包起来向上提供一个稳定接口。日志、指标和权限校验也放在这一层确保整个生态可观测、可控。2.2 跨平台抽象层一套接口三端适配跨平台抽象层最核心的是一个很小的接口集合我们称之为 PlatformAdapter。它只定义十来个方法但所有业务代码都必须走这些方法。class PlatformAdapter: def resolve(self, path): ... def read_text(self, path): ... def write_text(self, path, content): ... def execute(self, cmd, cwd, env, timeout): ... def exists(self, path): ... def list_dir(self, path): ...resolve 是所有路径操作的统一入口。它把上层传来的路径字符串改成当前平台的绝对路径并做规范化处理防止..路径穿越。read_text 和 write_text 负责文件读写内部统一处理编码和换行符。execute 负责创建子进程它接收的是一个参数列表而不是 shell 字符串从根本上规避了 shell 语法在不同平台的差异。我最想强调 execute 的入参设计。如果你给 Agent 一个“执行命令”的字符串参数Agent 为了适配不同平台就必须自己判断 shell 类型、拼接引号、处理转义这是灾难。让 Agent 和用户都传参数列表平台适配器负责把参数列表转换成当前系统能执行的进程调用业务逻辑就能彻底和 shell 解耦。比如要运行python -m pytest tests/上层传的就是[python, -m, pytest, tests/]适配器在原平台用默认方式执行在某些需要包装 shell 的环境里再内部处理上层完全不感知。2.3 会话上下文与记忆管理AI 编程智能体和普通脚本最大的区别在于有状态。多轮对话、连续修改、逐步执行都要求系统记住“之前发生了什么”。但大模型的上下文窗口是有限的直接无限积累历史消息会让程序越来越慢、越来越贵最后报错。Agency Agents 的会话管理分三层。第一层是全量会话存储记录每次请求和响应的原始内容方便审计和复盘第二层是短期上下文给模型用的最近若干轮消息第三层是任务摘要系统每隔一段时间把已执行的动作归纳成一段简短的进度总结存进上下文替代被裁剪掉的老消息。我实际采用的参数策略是如果模型上下文窗口按 token 计算把总预算分成三份——输入历史占一半工具返回结果占两成模型输出预留三成。假设一个模型单次请求支持 32768 token那么输入历史大约留 49152不对这里要重新计算。32768 的百分之五十是 16384工具结果占 6554输出预留 9830。这组数字只作为保守基线。真正做的时候还要根据模型实际最大输出限制再调整。只要发现某次工具返回超过几千 token就不再原样塞进上下文而是把结果写入工作目录的临时文件让 Agent 拿到文件路径和头部摘要。这个做法让长任务稳定性提升非常明显。2.4 多 Agent 协作与任务编排多 Agent 协作不是简单地把多个 Agent 跑一遍而是通过一套消息结构把任务拆开、分发、汇总。消息格式大致是{ trace_id: abc123, sender: dispatcher, receiver: code_writer, intent: generate_code, payload: { task_id: t-42, description: 实现一个跨平台文件扫描函数 } }每个 Agent 关注 receiver 和 intent。不匹配的消息直接忽略避免互相打扰。任务调度器根据意图和当前状态决定下一跳。比如用户说“帮我修复测试失败”调度器把任务分配给 code_reviewer 先读失败日志再由 command_executor 重现失败然后 code_writer 修改代码最后反过来通知 dispatcher 重新跑测试。这样形成一个闭环。协作过程中必须限制单次任务的迭代次数。我见过很多死循环案例Agent 修完代码发现测试还是挂继续修又修坏了另一个测试无限循环。给每个编排任务加一个max_iterations5的上限达到上限后强制让用户决定是继续还是停手虽然简单但能保住整个系统的可用性。3. 核心模块实现与工程实操指南3.1 插件化 Agent 注册机制为了让生态可以持续扩展Agency Agents 内部做了一个非常朴素的插件注册表。每个 Agent 模块暴露一个装饰器调度器启动时会扫描指定目录下所有 Python 文件自动加载注册过的 Agent。# agents/registry.py AGENT_REGISTRY {} def register_agent(name): def decorator(cls): if name in AGENT_REGISTRY: raise ValueError(fagent name duplicated: {name}) AGENT_REGISTRY[name] cls return cls return decorator使用起来很直接新 Agent 文件里写一个类再加上装饰器# agents/code_writer.py register_agent(code_writer) class CodeWriterAgent: def run(self, task, context): ... return result注册机制的核心价值是“约定大于配置”。新 Agent 无需改动任务调度器只要遵循统一的run(task, context)接口签名就能被加载。这要求所有 Agent 必须严格遵守输入输出结构不能偷偷返回自定义对象。我们在工程中加重了协议校验Agent 返回值如果缺少必填字段则直接报错防止某一次上线把全链路带崩。动态发现目录也要小心控制。不要让调度器去扫描整个全系统目录而是限定在白名单目录内避免从某个临时目录加载恶意模块。生产环境我们还对插件目录做了 Hash 校验防止文件被动过。3.2 工具调用协议与权限沙箱Agent 生态最敏感的部分是工具调用。一个能执行命令、读写文件、安装依赖的 Agent如果不设权限边界本质上就是让大模型拿到了“万能执行器”。Agency Agents 把所有外部操作都收敛成 Tool 对象Agent 不能直接调用操作系统 API只能调用工具层提供的统一操作。工具接口定义大致如下class Tool: name: str async def run(self, params, context) - ToolResult: raise NotImplementedError调度器在每次 Agent 调用工具之前会经历一次权限检查。权限规则按“默认拒绝白名单放行”的原则设计。命令执行工具只允许执行白名单里的命令例如git、python、pytest、rg所有被禁止的命令直接返回拒绝结果。文件工具同样受限每个任务被分配一个独立工作目录工具内部验证目标路径必须位于该目录之下越权访问一律拒绝。这里有一个容易被忽略的坑路径校验不能只做字符串前缀判断。/workspace/task1/../task2/secret.txt的前缀看起来在 task1 下面实际打开的是 task2 的文件。必须先把路径标准化再判断最终路径是否在允许目录内。这个逻辑我们在安全攻防演练中反复验证过属于必改项。3.3 跨平台进程与终端适配细节命令执行是所有跨平台问题里最集中、最容易翻车的部分。我曾经遇到一种情况同一个命令在调试阶段正常打包之后莫名失败最后定位到原因是执行器内部用了字符串拼接不同系统的引号转义规则不一样。现在我们的所有命令执行都用参数列表模式禁止使用 shell 字符串执行。import subprocess proc subprocess.run( [python, -m, pytest, str(test_path)], cwdstr(workspace), envmerged_env, capture_outputTrue, textTrue, timeout30, )这个写法有四个好处不需要本地 shell 参与减少了命令注入面命令分割逻辑由系统正确处理空格、引号、特殊字符不再是问题超时控制明确捕获输出时编码可控。不要写VARvalue command这种 POSIX 风格的环境变量赋值在部分平台上完全无法运行。正确做法是把环境变量合并进env参数。输出编码也要统一处理。大模型返回的内容几乎都是 UTF-8但如果被调用的脚本输出 GBK 或系统默认编码textTrue时解码就会报乱码。我们统一给子进程设置encodingutf-8同时在启动环境里加上和 UTF-8 相关的环境变量不让系统区域设置影响解析结果。如果遇到程序强制输出其他编码就在适配器里做一次字节转码而不是依赖默认行为。3.4 日志、观测与失败恢复分布式 Agent 体系如果没有可观测性出了问题等于盲人摸象。Agency Agents 在日志层面做了一件简单但有效的事所有日志都必须携带 trace_id、session_id、agent_name 三个字段。{time: 2025-01-12T10:22:31Z, level: INFO, trace_id: t-42-abc, session_id: s-88, agent: command_executor, msg: run cmd: [pytest, tests/test_scan.py], duration_ms: 254}每条日志统一输出为 JSON 一行方便后续用日志工具检索。排查问题时只要拿到一个 trace_id就能把所有 Agent 的动作串成一条完整链路一眼看出卡在哪个环节。这个习惯看起来多写了几行代码但真的能救命。失败恢复采用检查点机制。每个 Agent 在执行前先写一条检查点记录当前任务状态、依赖条件、已完成步骤执行完成后更新检查点。调度器如果发现 Agent 进程崩溃或者网络超时会读取最近的检查点从断点继续而不是从头开始。重试时采用指数退避策略第一次等 1 秒、第二次等 2 秒、第三次等 4 秒不把压力继续压到模型服务和本地进程上。4. 工程落地与部署实践4.1 仓库结构与配置设计一个可以落地的项目仓库结构必须一眼可以看出“哪里有新的 Agent、哪里改适配器、哪里看日志”。Agency Agents 的目录是这样的agency_agents/ agents/ # Agent 模块插件化发现 tools/ # 工具实现命令执行、文件读写 adapters/ # 平台适配器实现 runtime/ # 调度器、会话管理、权限中心 entrypoints/ # CLI、编辑器插件适配、HTTP 服务 configs/ # 配置文件模板 workspaces/ # 任务工作目录 logs/ # 运行时日志配置文件采用 YAML 格式核心示例model: endpoint: ${MODEL_ENDPOINT} max_tokens: 4096 runtime: workspace_root: ~/.agency_agents/workspaces default_timeout: 30 max_iterations: 5 tools: allowlist: [git, python, pytest, rg] agents: enabled: [code_writer, code_reviewer, command_executor]配置里绝不写明文密钥。模型 API 密钥、数据库密码这类信息全部通过环境变量注入配置文件里只保留占位符。项目里所有日志输出前会经过一层脱敏过滤器把看似密钥的字符串替换成***防止有人在调试时不小心把密钥打出来。4.2 多平台构建与发布跨平台程序的发布策略我总结成一句话能依赖纯 Python 版本库尽量用纯 Python 版本少碰需要本地编译的扩展。大部分流行库会提供预编译包但部分性能敏感的库在不同平台可能需要额外配置。为保证 Agent 能在更多环境中快速安装我们把相对路径处理、文本解析等操作全部用标准库实现只有确实有性能需求的地方才引入外部依赖。发布物可以做成几种形态命令行程序通过入口脚本暴露编辑器插件端发布轻量级客户端CI 场景发布容器镜像。容器化的最大好处是环境一致性命令、权限、路径都是可控的很多本地平台差异被容器层屏蔽。但容器也有局限比如用户要访问本机文件、本机 Git 配置时挂载和权限处理反而更复杂。因此容器主要用于服务端批处理本地交互场景还是由平台适配器负责。4.3 性能指标与质量基线跨平台 Agent 生态需要一套明确的质量基线否则很容易在“能跑”和“好用”之间摇摆。我们常用的指标如下指标目标值监控方式启动时间小于 2 秒CLI 入口记录单次工具命令超时默认 30 秒子进程超时上下文使用率不超过 80%会话管理器统计任务完成率大于 90%调度器上报未授权工具调用率0权限中心统计性能优化时最值得投入的是“减少无效模型调用”。很多任务其实不需要大模型参与比如执行一次pytest、读取一个文件内容完全可以用确定性代码完成。Agency Agents 里默认动作是“先看有没有必要调模型”能用工具逻辑完成的绝不浪费模型调用。这一条既降延迟又省钱还能提高稳定性。4.4 影响范围与落地形态Agency Agents 做成之后我们实际接了几个不同的业务场景。第一个是编辑器内嵌代码助手用户选中一段代码Agent 生态自动拆解成“读取选中内容—生成修改建议—在临时工作区验证—输出 diff”全程跨平台行为一致。第二个是命令行故障排查工具运维人员描述一句现象比如“磁盘占用突然升高”调度器会把任务分给文件扫描 Agent 执行查找大文件、定位最近生成的临时文件、检查进程列表最终汇总成结构化报告。第三个是 CI 失败分析。原来每次流水线挂了都要人工打开日志找原因。现在把失败日志直接交给日志分析 Agent它先复现编译命令再定位错误段落随后把修复建议推到消息里。这个场景对并发要求不高但跨平台稳定要求极高因为 CI 环境经常清理缓存、权限受限平台适配层在这里发挥了真正价值。Agency Agents 的价值不只是“能自动改代码”而是把 AI 能力变成了工程基础设施的一部分。不同的团队可以在同一套生态上接入自己的 Agent、工具和白名单策略形成各自场景下的智能体工作流同时不需要重复造基础设施。5. 常见问题排查与避坑实录5.1 “能跑但不稳定”路径问题最常见的不稳定来自相对路径。开发者在调试时工作目录恰好是项目根目录所有路径都正常打包或换目录执行后Agent 读不到配置文件生成的文件也找不到。Agency Agents 规定所有进入业务层的路径都必须是绝对路径由适配器通过resolve统一处理。排查时看路径是绝对还是相对通常一眼就能定位问题。我在某次调试中遇到过Agent 生成一个output.txt到当前目录看起来成功了但后续 Agent 根本找不到它。原因是执行时进程的工作目录被切换到了临时沙箱和调度器当时记录的工作目录不一致。解决办法是让 Agent 显式传递目标路径给工具而不是依赖“当前目录”这个隐式状态。这也是跨平台系统里比较隐蔽的坑。5.2 跨平台边界问题换行符、编码、命令差异跨平台最容易出现的三类边界问题是换行符、默认编码和 shell 语法。换行符问题主要影响文本对比和 diff 生成处理方法是所有文本写入统一使用\n只在对外输出给不同平台时再转换。默认编码问题就是 3.3 节提到的子进程编码设置在适配器层强制 UTF-8。shell 语法差异是最危险的一旦出现“开发环境能跑、测试环境挂掉”的情况优先怀疑有人绕过了参数列表模式直接拼了 shell 字符串。5.3 上下文爆炸长任务和代码库扫描代码库扫描是最容易触发上下文爆炸的场景。一个仓库可能有几千个文件Agent 如果试图把每个文件内容都装进上下文再大的窗口也不够用。我们的处理方式是扫描类工具只返回文件路径、大小、扩展名和首行摘要真正需要分析内容时先过滤再读全文。同时给每个上下文窗口设置了硬上限超过之后强制触发摘要压缩。发生过一次线上任务因为读了一个 10 万行的日志文件把模型请求直接打爆修复方式是工具层对读取文件大小设置了阈值超过 200KB 不再允许直接读全文而是分片读取或拒绝。5.4 一次误删文件与命令注入教训印象最深的一次事故是这样的一个代码扫描 Agent 读到了仓库 README 里的一段恶意示例命令里面包含删除操作。由于当时命令执行工具的白名单制度还不严格Agent 把示例当成了真实指令尝试执行幸好目标路径被工作目录限制挡住没有造成实际损失。但这已经足够敲响警钟。后来我做了几件事所有工具默认拒绝除白名单外的命令Agent 想新增命令必须由用户在配置里明确打开涉及删除、覆盖、安装这类高影响操作一律先返回“需要人工确认”状态由用户点了确认才真正执行。大模型本身没有恶意但它会误解内容、会被输入里的恶意片段诱导权限沙箱就是我们和事故之间的最后一道墙。5.5 排查问题速查表问题现象直接原因处理方案同一条命令在不同系统结果不一致内部使用了 shell 字符串改为参数列表模式中文输出乱码子进程编码未固定统一 UTF-8 并设置环境变量Agent 重复修同一处死循环缺少迭代上限配置 max_iterations模型上下文持续增长崩溃工具输出全部塞进上下文长输出写临时文件只传摘要找不到配置文件依赖相对路径用适配器 resolve 成绝对路径Agent 删除了不该删的文件权限校验失效默认拒绝、白名单、路径标准化任务卡死 10 分钟不返回子进程没有超时控制设置 timeout 并 kill 超时进程日志难以追踪跨 Agent 调用缺少 trace_id所有日志带 trace_id 全链路检索6. 实践经验与扩展建议6.1 架构落地时最重要的三条铁律跨平台 AI 编程智能体生态做了三轮重构之后我总结出三条铁律。第一条是接口隔离Agent 永远不直接触碰操作系统所有文件、进程、环境变量都通过适配器访问。第二条是最小权限默认拒绝一切外部操作只有明确需要的工具才放开高影响操作必须人工确认。第三条是可观测性没有 trace_id、没有检查点、没有结构化日志的 Agent 体系基本不可维护上线前先把观测做扎实。这三条顺序不能乱。先有接口隔离才能保证权限控制不绕路先有权限控制才能保证可观测数据可信。如果一个 Agent 绕过工具层直接调子进程那日志再全也看不到真实动作安全策略形同虚设。6.2 可以继续扩展的方向Agency Agents 目前的形态可以让很多场景快速落地。后续我觉得比较有潜力的扩展方向有几个一是把任务编排升级成真正的 DAG 引擎支持条件分支、自动重跑和并行分支二是接入本地知识库检索让 Agent 在修改代码前先搜索项目里的历史约定三是增加多模态输入比如把设计稿截图、报错弹窗直接交给视觉 Agent 分析四是把审批流程和 Agent 执行集成得更紧密形成“Agent 建议、人确认、Agent 执行”的闭环。最后说一句我个人想强调的体会不要把智能体当作一个黑盒“神仙函数”来写把它当成一个带权限、带状态、带日志的微服务来设计。当你用这个心态去做跨平台 AI 编程智能体生态时很多所谓的技术难点其实在架构层面就被消解了。Agency Agents 只是这个概念下的一个具体工程样本但它验证了一条路AI Agent 要真正成为生产力工具工程化设计和跨平台能力跟模型能力同等重要。