
如果你关注 AI 应用开发一定已经感觉到一个明显风向大家慢慢不再满足于“一个对话框 一个超长 Prompt”的单体智能体而是开始研究怎么让多个智能体像一家小型公司那样分工协作。去年我花了不少时间做多智能体编排前后试过好几套思路最后沉淀出了一个叫“agency-agents”的轻量级框架。它的核心想法很简单把复杂任务拆给不同角色的 Agent让调度器决定谁在什么节点干活、上下文怎么传、结果怎么汇总整体效果就像运营一家数字代理机构所以才取了这么个名字。这篇文章我会从设计动机讲起然后是核心架构拆解、最小可运行实现、一个完整的内容生产流水线案例最后附上我在实际使用中踩过的坑。内容会偏工程但不会只丢代码我会把每一步“为什么这么做”也讲清楚。无论你是在研究多智能体框架想手写一套自己的 Agent 编排系统还是只是想把大模型接进正式工作流这篇文章应该都能给你一些超出文档之外的实际经验。1. agency-agents 到底做了什么从一个痛点说起1.1 单 Agent 的天花板为什么必须拆先说说我最初遇到的真实现象。早期我用单个 Agent 去完成类似“帮我写一份新品推广方案”的任务时通常做法是准备一个非常长的系统提示词里面包含你要扮演什么角色、项目背景、目标用户、必须包含哪些章节、输出格式要求、相关竞品资料、语气风格限制……这种“大而全”的 Prompt 初看效率很高一个请求全搞定但实际用一段时间就会发现问题。最大的问题是上下文竞争。角色设定和背景资料挤在一起模型注意力会被分散经常出现“前面说了面向年轻用户后面调研部分又写中年人市场”这种内部打架的情况。想改其中一个细节比如调整语气风格你会担心动到其他部分的稳定性最后只能小心翼翼地重写整段提示词。而且当任务包含明显不同性质的工作时比如先做资料检索、再做竞品分析、最后写文案单个 Agent 很难保证每个环节都达到专业水准。我的实测是让它先“认真检索”再“动笔写”它经常检索完还沉浸在资料里输出一段像报告不像文案的东西。某次我尝试用一个超长 Pipeline 式 Prompt 跑一轮完整的市场调研加内容产出结果模型把战略分析写成了产品功能列表逻辑完全乱了。这时候我开始意识到真正靠谱的路径不是把任务越堆越满而是把任务拆开让不同角色各管一段。这就是“agency-agents”最早的产品动机用多个分工明确的 Agent 模拟一个真实代理公司的协作方式而不是祈祷一个全知全能的大模型把一件事一次性做完美。1.2 拆成“数字代理机构”agency-agents 的核心思路你可以把 agency-agents 想象成一家小型广告公司有客户经理负责接需求有策略总监负责拆解目标有调研专员去搜资料有文案或设计负责产出还有质检人员做最终检查。这些角色各自独立但通过公司内部的交接流程协同起来。放到代码层面这家“公司”由四个核心组件构成Agent角色实体只负责完成一项特定类型的工作内部封装提示词模板、模型调用、工具调用逻辑。Registry注册中心所有 Agent 的身份登记处系统能通过 Agent 名称查到它的实现、描述和配置。Dispatcher调度器相当于项目经理接收任务后按路由规则决定发给哪个 Agent并根据反馈决定下一步给谁。Context / Memory上下文与记忆一套跨 Agent 传递信息的机制保证每个环节都能拿到前面环节的结果且不会被无效信息淹没。一个典型流程是这样的任务进入 Dispatcher它先看任务类型如果命中“市场方案类”就先把任务派给 planning agent 拆解成步骤拆完确认需要做外部调研便再派给 research agent拿到调研结果后发给 writing agent 写初稿最后给 reviewing agent 做质检如果质检不通过就打回修改通过则把最终结果写回任务结果区。之所以选择这种架构而不是直接写一段“让多个 Agent 通过自然语言互相聊天”的代码原因很实际可调试性和可替换性。聊天式的多 Agent 协作看着很炫但一旦出现问题你很难说是哪个 Agent 理解错了而通过 Dispatcher 控制编排顺序每个环节的输入输出都有明确的记录和格式出了错直接把日志翻出来很容易定位到是哪一步、哪个 Agent、哪个提示词出了问题。后面我的所有项目几乎都在吃这个架构红利。2. 核心架构拆解注册中心、调度器与上下文传递2.1 让每个 Agent 只做一件事角色定义与注册机制在 agency-agents 里每个 Agent 被设计成只做一件事。我认为这是整个框架最重要的约束。举例来说“研究员”就只负责收集和整理信息并输出结构化摘要“写手”就只负责根据摘要写初稿“校对”就只负责提出修改意见。谁都不许越界谁都不许顺手把别人的活也干了。代码上我定义了一个非常薄的基础类核心思想是“输入一个任务对象输出一个结果对象”# base_agent.py from abc import ABC, abstractmethod from dataclasses import dataclass, field dataclass class Task: id: str type: str payload: dict metadata: dict field(default_factorydict) dataclass class AgentResult: status: str # success / failed / needs_revision output: dict trace: list field(default_factorylist) class BaseAgent(ABC): name: str base description: str def __init__(self, llm_client, toolsNone, configNone): self.llm llm_client self.tools tools or {} self.config config or {} abstractmethod async def run(self, task: Task, context: Context) - AgentResult: ...然后通过一个注册表把 Agent 的字符串名字和类关联起来# registry.py AGENT_REGISTRY {} def register_agent(name): def decorator(cls): AGENT_REGISTRY[name] cls return cls return decorator这样在别处定义 Agent 时一行装饰器就能完成注册register_agent(planner) class PlanningAgent(BaseAgent): async def run(self, task: Task, context): # 调用模型把任务拆解成步骤 steps await self.llm.chat(...) return AgentResult(statussuccess, output{steps: steps})为什么非要搞一套注册机制而不是直接用 import 引用类因为在实际项目中Agent 的数量会从两三个涨到十几个如果每个 Agent 都在代码里被手动引用就会出现大量重复的初始化代码而且新增一个角色时要改多处调用点。注册表本质上是一个“配置即代码”的模式Agent 的实现和调度逻辑完全解耦后续甚至可以做成配置化注册。实测下来这种方式在 Agent 数量超过 5 个后节省的维护成本非常明显。2.2 调度器怎么知道“下一步该谁上”路由与状态机Dispatcher 是整个系统的大脑。它的核心任务是维护一个执行 DAG有向无环图也就是把一条条 Agent 之间的流转关系定义清楚。例如固定链式planner - researcher - writer - reviewer这种万能流程。分支路由当任务类型是product时走产品组是content时走内容组。条件回溯当 reviewer 返回needs_revision时回给 writer但最多回两次。实现时不需要太重一个简单的状态表就够用# dispatcher.py class Dispatcher: def __init__(self, registry): self.registry registry async def dispatch(self, task: Task, context: Context, flow_map: dict[str, list[str]]): current_step flow_map.get(task.type, flow_map.get(default)) for agent_name in current_step: agent_cls self.registry.get(agent_name) agent agent_cls(...) result await agent.run(task, context) # 写入上下文供后续 Agent 使用 context.add_result(agent_name, result) # 特殊逻辑reviewer 打回修改 if result.status needs_revision and context.revision_count 2: context.revision_count 1 # 跳回上一个 agent return await self.dispatch(task, context, {default: current_step[:-1]}) return context关键点在于Agent 之间绝不能直接互相调用。哪怕写手需要调研员的数据也不能在代码里import ResearcherAgent然后直接调用它的方法而是要把调研结果写到 Context 里再由 Dispatcher 在下一步把写手唤醒并传给它。这样做的好处是每个 Agent 都可以独立测试替换实现时不会牵连其他角色而且执行路径一目了然。项目中实际发生过多条 Agent 互相调到 Python 递归栈溢出的情况后来规定“Agent 只能见 Context不能见彼此”这个问题就根除了。2.3 上下文与记忆多 Agent 之间怎么传话多 Agent 之间的“传话”是另一个极其容易翻车的点。如果每个 Agent 都拿到完整的全部聊天记录很快 Context 就会爆掉而且模型会分不清哪些是当前任务要处理的。agency-agents 的 Context 设计分两层。短期上下文保存当前任务从开始到现在所有 Agent 的结果摘要每个 Agent 完成工作后把关键信息按固定 schema 写进context.results长期记忆则是把过往任务沉淀到外部存储Redis、向量库或 JSON 文件里便于后续任务检索。两层的接口可以统一成简单的get和set。# context.py class Context: def __init__(self, task_id: str): self.task_id task_id self.results {} self.revision_count 0 def add_result(self, agent_name: str, result: AgentResult): # 只保留结构化摘要不存完整原文 self.results[agent_name] { status: result.status, output: result.output, } def get_all_results(self): return self.results在实现时有一个很隐蔽的细节不要把完整原文丢进 Context。最开始我把 researcher 找到的几万字原始资料全部塞进context.results结果 writer 收到任务时光传参就要传几万 token既慢又贵。后来改为让 researcher 必须输出“结构化摘要”并且只输出对当前写稿真正有用的那部分信息。这相当于逼迫每个 Agent 做完信息压缩再交接效果立竿见影——速度提升、费用下降、效果还更稳定。建议在你这边的实现中把“Agent 输出必须比输入短”作为一条硬性约定。3. 从零实现环境准备、代码骨架与关键配置3.1 环境与依赖Python 3.10 asyncio 一个大模型接口这套框架我用 Python 3.10 实现异步用的是标准库 asyncio外部依赖控制在很克制的范围内。具体依赖如下# requirements.txt aiohttp3.9 PyYAML6.0 pydantic2.0 jinja23.0 redis5.0 # 可选用于长期记忆与队列第三方包我只保留了“发 HTTP 请求、读配置、做模板渲染、做数据校验”这几件事其余都靠标准库。之所以刻意保持轻量是因为 Agent 编排系统本身的复杂度不低如果底层再铺一层重量级框架出了问题排查难度会成倍增加。异步框架选了 aiohttp没有特别深层的原因主要是它稳定而且对大模型接口的流式响应支持良好。如果你更熟悉 httpx完全可以用 httpx 替代。所有 Agent 共享同一个大模型客户端封装客户端负责处理 API 调用、超时重试、流式输出。实际项目中我把具体的模型调用隔离在llm_client.py这样一个文件里切换模型供应商时只需要改这个文件。3.2 最小可运行骨架让两个 Agent 先跑起来演进一个新系统切忌一上来就写一堆复杂流程。我先做了一个只有两个 Agent 的最小 Demo一个 planner 负责把用户需求拆成步骤一个 executor 负责按步骤返回模拟结果。跑通之后再逐步加入真正的模型调用和流程跳转。# main.py import asyncio from context import Context from dispatcher import Dispatcher from registry import AGENT_REGISTRY, register_agent register_agent(planner) class PlannerAgent(BaseAgent): async def run(self, task: Task, context: Context): plan [市场调研, 方案撰写, 审核修订] return AgentResult(statussuccess, output{plan: plan}) register_agent(executor) class ExecutorAgent(BaseAgent): async def run(self, task: Task, context: Context): # 实际项目中这里会调用 LLM 或外部工具 result f已完成计划: {context.results[planner][output][plan]} return AgentResult(statussuccess, output{detail: result}) async def main(): registry AGENT_REGISTRY task Task(id1, typedemo, payload{requirement: 做一份方案}) ctx Context(task_id1) dispatcher Dispatcher(registry) await dispatcher.dispatch(task, ctx, flow_map{demo: [planner, executor]}) print(ctx.results) asyncio.run(main())这个 Demo 的价值不在于功能而在于验证流程骨架是否跑通。我就是从这一步开始逐步把BaseAgent内部的模型调用逻辑填进去把flow_map扩充成真正的搜索、写作、审核链路。如果你是自己搭框架也一定要留一个这么小的“呼吸测试”每次改动大结构后先跑这个最小版本确认没有破坏基础链路。3.3 YAML 配置别把 Prompt 写死在代码里Agent 系统的提示词是整个系统里变化最频繁的部分。我的经验是Prompt 绝对不能硬编码在 Python 文件里否则改一句话就要重新部署一次。agency-agents 里所有 Agent 的提示词和参数都放在 YAML 配置文件中# agents.yaml agents: planner: model: gpt-4o-mini temperature: 0.3 system_prompt: | 你是一名经验丰富的项目策划专家。请把用户需求拆解为可执行的步骤。 要求 1. 步骤不超过 5 个 2. 每个步骤必须包含目标描述 3. 直接输出 JSON 数组不要额外解释 writer: model: gpt-4o temperature: 0.7 system_prompt: | 你是一名资深内容创作者。请结合调研资料完成文案撰写。 要求文风简洁有力突出核心卖点。加载时用 YAML Jinja2 模板引擎。为什么不用 f-string 直接拼 Prompt因为 Agent 的 Prompt 里经常要嵌入多份调研摘要、用户画像、历史记录用 f-string 很容易写出长到难以维护的表达式而且转义问题很烦人。Jinja2 模板天然支持变量循环和条件判断写起来清楚还能提前在模板里加上默认值。# prompt_renderer.py from jinja2 import Environment, FileSystemLoader env Environment(loaderFileSystemLoader(prompts)) template env.get_template(writer_prompt.md.j2) prompt template.render(requirementtask.payload[requirement])这里埋一个我在实际项目里踩过的坑让模型输出的格式尽量保持“结构化但简单”。刚开始我要求 Researcher 输出包含多个嵌套层级、带评分、带优先级的复杂 JSON模型倒是照着输出了但我解析时经常遇到模型生成多余逗号、把null写成None等问题。后来统一规定Agent 输出的 JSON 最多两层嵌套数组项控制在 20 个以内。换来的稳定性不值那点信息量损失。3.4 接入外部能力的挂载点工具调用怎么设计一个纯靠模型“脑补”的 Agent能力天花板非常明显。迟早得让 Agent 能搜索、能读网页、能查数据库。我在框架设计里给 Agent 加了一个 tools 接口用意是让 Agent 从“只会对话”进化为“能动手干活”。工具调用统一走 schema工具名称 输入参数字典返回必须是一个 JSON。# tool.py class Tool: async def execute(self, params: dict) - dict: raise NotImplementedError # 模拟一个搜索工具 class SearchTool(Tool): async def execute(self, params: dict): query params.get(query, ) # 真实项目里这里接搜索引擎 API return {results: [{title: f关于{query}的前几条摘要, url: example.com}]}在 BaseAgent 内部模型输出中如果包含工具调用指令就执行对应工具并返回结果。这一段是标准套路我就不展开完整代码了。但有一个方向建议不要一开始就把工具系统做得特别复杂先只加三个以内的工具比如搜索引擎和网页正文提取、内部知识库检索跑通之后再慢慢增多。工具太多会让 Agent 的意图选择准确率明显下降这个小细节等到后面踩坑部分我会再提。4. 实战用 agency-agents 搭建“选题—调研—写作—质检”内容流水线4.1 场景设定给一个小型营销团队做内容生产这次实战我模拟的是一个真实发生在我工作里的场景某小型营销工作室每周要产出一篇面向行业客户的产品推广长文。之前流程是人工搜集资料、人工写初稿、人工审核一周一篇都很勉强。我们把这套流程搬上 agency-agents定义四个角色选题策划根据目标读者和产品卖点输出选题方向和写作大纲。调研员根据大纲去检索资料输出事实摘要和引用来源。写手结合大纲和摘要写初稿。审核员按清单检查内容准确性、逻辑连贯性和表达力度给出修改意见。这个组合已经能覆盖绝大多数内容类任务你也可以换成“产品经理 技术调研 文档工程师 QA”来写技术方案。4.2 Agent 分工与路由配置四类 Agent 的注册代码不复杂直接用第一节定义的基础类扩展即可。真正的关键是 flow_map 怎么写。我的设计是第一版用固定链式planner - researcher - writer - reviewerreviewer 返回needs_revision时跳回writer允许最多三次重试超过次数直接放行并标记“需人工复核”这个“三次上限”非常重要没有它会出现无限循环烧钱的问题后面的踩坑部分会细讲。Flow 配置放在 YAML 里起了作用# flow.yaml flows: content: steps: - planner - researcher - writer - reviewer max_revision: 3 fallback: human4.3 关键代码串联整个流水线主程序的核心就是在dispatch中传入 flow 配置。注意我在ReviewerAgent中设置了一个检查逻辑它既要判断输出内容是否满足“包含核心关键词”“有数据支撑”“无事实性错误”还要控制是否needs_revision。register_agent(reviewer) class ReviewerAgent(BaseAgent): async def run(self, task: Task, context): writer_output context.results[writer][output] # 这里调用 LLM 进行质量评估并返回 json: {passed: bool, comments: [...]} evaluation await self.llm.chat(...) if not evaluation[passed]: return AgentResult(statusneeds_revision, outputevaluation) return AgentResult(statussuccess, outputevaluation)运行时只需要在主入口里加载配置并调度async def run_flow(user_requirement: str): task Task(iduuid4().hex, typecontent, payload{requirement: user_requirement}) ctx Context(task_idtask.id) flow_config load_flow_config(flow.yaml) await dispatcher.dispatch(task, ctx, flow_config) return ctx.results整套系统跑起来后最开始的效果并不理想。调研员返回的资料要么太泛、要么太旧写手写出的稿子经常和产品卖点脱节。后来我逐步给每个 Agent 的 Prompt 里加入了“目标读者锁定”“竞品关键词映射表”这类约束情况才明显改善。这里的重点不是代码本身而是多 Agent 系统的成功与否很大程度取决于每个 Agent 的输入输出边界定义得清不清楚这个在系统里是由 Prompt 和输出格式共同保证的。4.4 调优记录如何把单次任务从 3 分钟压到 40 秒流水线跑通之后我发现单次任务耗时太长。最初版本是四个 Agent 串行执行每个 Agent 调用一次大模型正常情况要 3 分钟。我做了三件事终于把平均耗时压到 40 秒上下第一步把调研员改成“内部并行检索”一次触发 3 个搜索请求等全部返回后一次性汇总。使用asyncio.gather几乎是零成本提速。第二步给写手配置一个更快的小模型承担 70% 的常规内容生成审核员不满意再切大模型。用“高中低配模型分层”的方式整体费用和速度最均衡。第三步减少串联等待。让审核员在检查时就生成一份“修改建议清单”writer 接收到清单后直接定位到对应段落重写而不是整篇重写。效果比整篇重新生成要好得多。这一轮优化给我的最大体会是多 Agent 系统不能一直加分堆逻辑它需要阶段性做减法。把慢的环节并行掉把贵的环节便宜掉把重复的环节合并掉系统的价值才能体现出来。纯堆 Agent 数量不一定带来效率提升反而会让调度成本、延迟和出错概率一起变大。5. 老坑实录死循环、并发限流、上下文爆掉5.1 死循环审核不通过时怎么办第一次上线审核链路时我犯过一个挺蠢的错误审核不通过就直接让写手重写写手重写之后审核又觉得不行无限循环下去。在真实测试中角色分配越明确这个循环越容易发生因为每个 Agent 都会坚定地坚持自己的判断谁都不妥协。大模型不会累但你会看着账单心疼。解决办法就是前面提到过的“最多重试三次三次不过就转人工”。在 Dispatcher 里增加一个简单的全局计数每进入 writer 一次revision_count 1当达到上限时强制让 reviewer 输出“已通过但备注需人工确认”。同时在日志系统里记录失败原因便于分析瓶颈。总结成一句话所有带反馈回路的流程必须设计终止条件否则系统迟早失控。5.2 并发限流频繁 429 错误真正跑起来后第二个高频问题是模型服务返回 429请求过多或者超时。多 Agent 并行化之后不仅没解决这个问题反而让情况更严重。我一开始天真地以为“并发越高越快”结果同时发出 10 个请求瞬间触发了服务商的限流整个任务全部失败重来比串行还慢。后来在llm_client里加了一个简单的令牌桶限速器全局所有 Agent 共享保证每秒请求数不超过规定阈值。import asyncio import time class RateLimiter: def __init__(self, max_requests: int, per_seconds: float 1.0): self.max_requests max_requests self.period per_seconds self.tokens max_requests self.updated_at time.monotonic() async def acquire(self): while True: now time.monotonic() self.tokens min(self.max_requests, self.tokens (now - self.updated_at) / self.period * self.max_requests) self.updated_at now if self.tokens 1: self.tokens - 1 return await asyncio.sleep(0.05)这个限速器的粒度是整个进程级别同一时刻所有 Agent 共用一把锁。如果是多机部署可以换成 Redis 分布式限流。这里要提醒的是具体阈值不能随便拍脑袋我用的是“先设置低值、压测后逐步上调”的方法找到一个既能跑得快又不触发 429 的安全区间。5.3 上下文爆掉长文档与多 Agent 记忆第三个高频问题是 Context 无限膨胀。最初设计 Context 时以为把数据都放内存字典里就没事结果跑了几轮长文写作任务内存上涨很快而且单次任务的 token 消耗也高得惊人。解决策略分两层。短期上下文这边我给每次写入 Context 的内容设置了“摘要化”约束研究员输出的内容必须压缩到 500 字以内写手写作时读到的是精炼摘要而不是海量原文。长期记忆这边用向量库存储“任务级别的历史结论”当下次遇到相近任务时只把最相关的 3 到 5 条历史摘要注入到 Context 中而不是把所有历史记录都带进去。顺手还解决了一个容易被忽视的问题任务在传递长文档时不应该传文档全文而是传文档 ID其他 Agent 需要时再按 ID 去文档库加载。这个设计初看只是省 token实际上还附带解决了缓存一致性问题避免每个 Agent 各拿一份并各自脑补内容。5.4 工具返回格式不一致接口协议的坑最后还有一个细节工具调用多了以后“什么格式的返回结果”会成为灾难。搜索工具返回的是数组数据库查询工具返回的是列表文件读取工具返回的是字符串而 Agent 的 Prompt 里要求它统一处理这些数据模型很容易被格式带偏经常出现“列表里套 JSON 字符串再套 Markdown”这种奇奇怪怪的嵌套。我后来规定所有工具输出必须是统一的ToolResult结构内容字段是一个字符串但允许包含 Markdown结构化数据字段是一个 JSON 对象原始数据字段是数组或字典。Agent 的 Prompt 一律只读取这两种固定字段其他字段不做解析。这个约束极大降低了模型的解析错误率也让调试日志变得清晰。如果到后期工具数量继续增长建议顺便引入 JSON Schema 做校验而不是靠“约定”来约束。6. 后续扩展把模型层做实、把部署做稳6.1 部署形态Docker Compose 一键启动服务单体脚本跑通只是第一步实际要落地到一个团队使用部署形态必须稳定。我的做法是把主服务、Redis、日志系统用 Docker Compose 打包在一起同时把配置和 Prompt 模板挂载到外部目录这样改 Prompt 不用重新构建镜像。# docker-compose.yml services: engine: build: . ports: - 8080:8080 volumes: - ./config:/app/config:ro - ./prompts:/app/prompts:ro environment: - LOG_LEVELINFO depends_on: - redis redis: image: redis:7-alpine volumes: - redis-data:/data这里有一个实战建议Docker 容器里的 Python 进程要多开几个 worker因为大模型接口的等待时间很长单 worker 很容易并发撑不住。我用的是 multiprocessing asyncio 的组合主进程收任务子进程跑 Agent 循环Redis 做任务队列。这个组合方案虽然有些老土但胜在稳定几乎不需要额外维护。6.2 Agent 的可观测性日志与链路追踪多 Agent 系统的可观测性比单 Agent 重要得多。原因很简单单 Agent 出问题复盘时看一个输入输出就够了多 Agent 出问题你可能需要看十几个环节的输入输出如果没有一个统一的 trace_id问题几乎没法排查。agency-agents 的做法是在 Task 创建时生成一个trace_id贯穿所有后续调用和日志每个 Agent 的日志输出都带上自己的名称和 trace_id。日志格式直接用 JSON方便导入日志分析平台{trace_id: 3f2a..., agent: writer, event: start, ts: 1720000000} {trace_id: 3f2a..., agent: writer, event: llm_call, model: gpt-4o-mini, latency_ms: 2100}千万别小看这一招。有一次调研员总是返回过时数据我排查了半天没有头绪后来把搜索工具调用的 trace_id 对应日志一查发现是工具传参时少了时间范围限制导致模型默认用当前时间之前的所有结果。没有链路日志这种跨组件问题可能要折腾好几天。6.3 模块化方向插件、协议与市场最后聊聊这套系统的未来扩展方向。我做这套框架时给 Agent 定义的接口足够简洁也意味着它可以很容易演变成一个半开放的插件系统。每种 Agent 可以对应一个 Python 包包内自带配置模板和 Prompt 模板注册中心负责加载。更进一步可以把 Agent 的输入输出协议标准化成 JSON Schema这样不同团队开发的 Agent 可以互相兼容整个“数字代理机构”就有机会变成一个生态。我目前的想法是往“协议优先”的方向演进完全不管具体实现是什么只约好消息格式和接口契约A 团队写的调研员可以被 B 团队的项目直接使用。当然这也带来新的问题比如能力的可靠性评价、恶意 Agent 的隔离、消息的权限控制等等。不过这些属于平台化阶段的事情当前阶段最重要的是把单机的 Agent 编排逻辑磨得足够稳。回到我自己这套“agency-agents”框架至少有一点我确定用“代理机构”的思维去设计多 Agent 系统远比给单个 Agent 堆砌更多 Prompt 要健康得多。如今每当我接到一个新任务第一反应不再是“我该怎么写一个完美的提示词”而是“这需要一个什么样的团队团队里每个人干什么谁先谁后哪里需要检查复核”。这种思维转变大概就是这个项目给我留下的最实在的收获。以后如果再投入时间我会优先把 Agent 的“身份记忆”和“长期工作习惯”做深让每个 Agent 在多次任务中越用越顺手而不仅仅是完成一次孤立的工作。