
1. 提示词模板管理到底在管什么1.1 从“复制粘贴”到“可维护资产”的转变做 Agent 开发的人十有八九都经历过这个阶段提示词写在代码里用三引号一包改一次就要重新部署一次。项目小的时候还行一旦 Agent 数量超过三五个提示词版本就开始满天飞。今天调好的“客服话术模板”明天被另一个同事改成了“销售转化模板”后天线上出了事故翻半天 Git 记录才发现是某次提交把系统提示词里的约束条件删掉了。提示词模板管理要解决的核心问题就是把提示词从“代码里的字符串”变成“可版本化、可复用、可测试的资产”。这件事听起来简单但真正落地的时候涉及的东西比想象中多得多。我自己的项目里最早也是把提示词硬编码在 Python 文件里。后来 Agent 数量涨到十二个每个 Agent 又有三到五套不同场景的提示词维护成本直接爆炸。有一次做 A/B 测试需要同时跑两套提示词结果因为变量名不一致导致其中一套的{user_name}没有被正确替换线上直接输出了带花括号的原始文本。那次事故之后我才下定决心把提示词模板管理单独抽出来做。1.2 模板管理的四个核心能力一个合格的提示词模板管理系统至少要具备四个能力。第一是变量抽象。提示词里不应该出现硬编码的用户名、时间、产品名。这些都应该抽成TemplateVariable在运行时注入。这样做的好处是同一套模板可以服务多个场景也方便做单元测试——你只需要构造不同的变量组合就能验证模板的输出是否符合预期。第二是版本控制。每次修改都要留下记录能回滚能对比。这一点很多人会忽略觉得用 Git 管代码就够了。但提示词的修改往往是非技术人员参与的运营、产品、甚至客服主管都可能提修改意见。让他们去学 Git 不现实所以模板管理系统需要提供更友好的版本界面。第三是环境隔离。开发环境、测试环境、生产环境的提示词应该分开管理。我见过太多团队直接在线上改提示词改完发现效果不对想回滚却找不到之前的版本。环境隔离不是可选项是必选项。第四是渲染与校验。模板渲染看起来只是字符串替换但实际上有很多坑。变量缺失怎么办变量类型不对怎么办模板里有嵌套引用怎么办这些都需要在渲染层做处理。校验则是在渲染之前检查模板的语法是否正确避免运行时才报错。1.3 为什么 Agent 场景下模板管理更复杂普通的大模型调用提示词模板相对简单通常就是“系统提示 用户输入”两段式。但 Agent 场景下提示词的结构要复杂得多。一个典型的 Agent 提示词至少包含这几个部分角色定义、能力边界、工具描述、输出格式约束、少样本示例、当前任务上下文。这些部分有些是静态的有些是动态的有些需要在不同轮次之间保持稳定有些则需要根据对话历史实时调整。更麻烦的是Agent 往往涉及多轮交互。第一轮的系统提示词和第五轮的可能完全不一样因为中间插入了工具调用结果、用户反馈、以及 Agent 自己的思考过程。如果模板管理没有设计好很容易出现“提示词拼接混乱”的问题——该保留的上下文被截断了该丢弃的历史信息却一直带着导致 token 消耗飙升。我踩过的一个典型坑是在 ReAct 风格的 Agent 里每一轮都要把之前的“思考-行动-观察”循环追加到提示词里。最开始我没有做长度控制结果跑到第八轮的时候直接超出了模型上下文限制Agent 执行报错终止。后来在模板层加了“历史压缩”的逻辑只保留最近三轮的完整循环更早的则摘要成一句话问题才解决。2. 提示词模板的工程化设计2.1 模板的存储结构选型提示词模板存哪里这个问题没有标准答案但有几个常见的方案各有优劣。方案一纯文件存储。用 YAML 或 JSON 文件管理模板每个模板一个文件目录结构按 Agent 名称划分。这种方案的好处是简单、直观、容易做版本控制。缺点是查询和检索不方便模板多了之后找起来费劲。适合 Agent 数量在十个以内的小项目。方案二数据库存储。把模板存在关系型数据库或文档数据库里配合管理后台做增删改查。好处是查询方便可以做权限控制适合多人协作。缺点是需要额外维护数据库而且模板的版本对比不如文件系统直观。方案三混合方案。模板文件放在 Git 仓库里做版本管理同时同步一份到数据库供运行时读取。这种方案兼顾了版本控制和查询效率但需要处理同步逻辑。我目前的项目用的就是这种方案CI 流程里加一个步骤把合并到主分支的模板文件自动同步到数据库。选型的时候要考虑团队规模和使用习惯。如果团队里技术人员占主导文件存储就够了。如果有非技术人员参与提示词编写那数据库加管理后台的方案会更合适。2.2 TemplateVariable 的设计细节变量设计看起来简单但细节很多。首先变量要有类型。字符串、数字、列表、字典不同类型的变量在渲染时的处理方式不一样。比如列表类型的变量可能需要渲染成逗号分隔的字符串也可能需要渲染成带序号的列表这取决于模板的上下文。其次变量要有默认值。不是所有变量在每次调用时都能提供有些是可选参数。如果没有默认值渲染时就会报错。我的做法是给每个变量定义required和default两个属性渲染前先做校验缺失必填变量直接抛异常可选变量则用默认值填充。第三变量要有作用域。有些变量是全局的比如当前日期、系统版本号有些是 Agent 级别的比如 Agent 名称、可用工具列表有些是会话级别的比如用户 ID、对话历史。作用域设计好了可以避免变量名冲突也方便做批量替换。第四变量要支持嵌套引用。比如{user.profile.name}这种写法渲染时需要从嵌套的字典里取值。这个功能在 Python 里用string.Template实现不了需要自己写解析逻辑或者用 Jinja2 这样的模板引擎。我现在的做法是直接用 Jinja2 作为渲染引擎因为它支持条件判断、循环、过滤器等高级功能而且语法成熟不容易出坑。但 Jinja2 也有缺点就是它的语法比较灵活容易写出过于复杂的模板。所以我在团队里定了一条规矩模板里只允许用变量替换和简单的条件判断不允许写循环和复杂表达式。复杂逻辑应该在代码里处理完再把结果作为变量传进来。2.3 模板的继承与组合Agent 场景下很多提示词是有共性的。比如所有 Agent 都需要“角色定义”和“输出格式约束”只是具体内容不同。如果每个模板都从头写会有大量重复。解决方法是模板继承。定义一个基础模板包含通用的部分然后子模板继承基础模板只覆盖需要修改的部分。Jinja2 原生支持{% extends %}和{% block %}用起来很方便。但继承也有坑。最常见的问题是“继承链太深”。A 继承 BB 继承 CC 继承 D改一个底层模板上面所有模板都受影响。所以我在项目里限制继承层级最多两层一个基础模板一个具体模板。超过两层就要考虑是不是抽象过度了。除了继承组合也很重要。有些提示词片段是独立的比如“安全约束”、“输出格式说明”、“工具使用指南”这些片段可以在多个模板里复用。我的做法是把这些片段单独存成partial文件然后在主模板里用{% include %}引入。这样修改安全约束的时候所有引用了这个片段的模板都会自动更新。2.4 版本管理与回滚策略版本管理不只是存历史记录还要考虑回滚的粒度。是整体回滚到某个版本还是只回滚某个片段我的经验是整体回滚更安全因为提示词各部分之间往往有依赖关系单独回滚一个片段可能导致不一致。版本号的设计也有讲究。我见过用日期做版本号的也见过用 Git commit hash 的还有用自增数字的。我推荐用语义化版本号比如v1.2.3主版本号表示不兼容的修改次版本号表示新增功能修订号表示修复问题。这样从版本号就能看出修改的影响范围。回滚的时候要注意缓存。如果模板在运行时被缓存了回滚之后需要清缓存才能生效。我吃过这个亏回滚了数据库里的模板但内存缓存没清线上跑了半天还是旧版本。后来在回滚流程里强制加了一步“清除所有节点缓存”问题才解决。3. Agent 提示词编排的核心逻辑3.1 什么是提示词编排提示词编排和模板管理是两个不同的概念。模板管理解决的是“单个提示词怎么组织和复用”的问题编排解决的是“多个提示词怎么按顺序组合和执行”的问题。一个 Agent 在一次任务执行中可能会用到多个提示词。比如先用“意图识别提示词”判断用户想干什么再用“工具选择提示词”决定调用哪个工具然后用“结果总结提示词”把工具返回的数据整理成自然语言。这三个提示词不是孤立的它们之间有数据传递和逻辑依赖编排就是管理这些依赖关系的。编排的核心是“流程定义”。你需要定义清楚第一步执行什么输出什么变量第二步依赖哪些变量输出什么如果某一步失败是重试还是跳过还是终止。这些逻辑如果散落在代码里会非常难维护。好的编排系统应该让流程定义和具体实现分离流程用配置文件描述实现则封装成独立的函数或服务。3.2 顺序编排与条件编排最简单的编排是顺序执行提示词 A 执行完把结果传给提示词 BB 执行完传给 C。这种模式适合线性流程比如“信息提取 - 数据校验 - 格式化输出”。但实际场景往往更复杂。比如客服 Agent需要先判断用户情绪。如果情绪是负面的走“安抚流程”如果是正面的走“推荐流程”。这就是条件编排。条件编排的实现方式有两种。一种是在编排层做判断根据上一步的输出决定下一步走哪个分支。另一种是在提示词内部做判断让模型自己决定走哪个分支。两种方式各有适用场景。编排层判断更可控但需要额外的判断逻辑提示词内部判断更灵活但可靠性依赖模型能力。我的经验是关键路径上的分支用编排层判断非关键路径的分支可以让模型自己决定。比如“是否需要转人工”这种关键决策一定要在编排层用规则判断不能完全交给模型。而“用哪种语气回复”这种非关键决策可以让模型根据上下文自行选择。3.3 循环编排与终止条件Agent 场景下循环编排非常常见。ReAct 模式本质上就是一个循环思考 - 行动 - 观察 - 再思考直到任务完成或达到最大轮次。循环编排最难处理的是终止条件。如果终止条件设置不当Agent 可能会陷入死循环不断调用同一个工具消耗大量 token 却解决不了问题。我见过最夸张的案例是一个 Agent 连续调用了二十三次同一个搜索工具每次返回的结果都差不多但模型就是认为“还需要更多信息”。解决这个问题需要多层防护。第一层是硬性轮次限制比如最多执行十轮超过就强制终止。第二层是重复检测如果连续三轮调用了同一个工具且参数相同就中断循环并返回错误。第三层是进度评估每一轮结束后让模型判断“当前信息是否足够回答问题”如果足够就提前终止。这三层防护里硬性轮次限制是必须的另外两层可以根据场景选配。但要注意硬性限制的轮次数不能设得太低否则复杂任务还没完成就被中断了。我的经验值是简单问答类 Agent 设 5 轮工具调用类 Agent 设 10 轮复杂规划类 Agent 设 20 轮。3.4 多 Agent 协作中的编排单个 Agent 的编排已经够复杂了多 Agent 协作的编排更是难上加难。多个 Agent 之间需要通信、需要协调、需要处理冲突。常见的多 Agent 协作模式有三种。第一种是“主管- worker”模式一个主管 Agent 负责拆解任务把子任务分配给不同的 worker Agent最后汇总结果。第二种是“流水线”模式每个 Agent 负责一个环节前一个的输出是后一个的输入。第三种是“辩论”模式多个 Agent 对同一个问题给出不同答案然后通过投票或讨论达成一致。编排层需要为每种模式提供不同的支持。主管模式需要任务分配和结果汇总的逻辑流水线模式需要数据格式转换和错误传递的逻辑辩论模式需要投票机制和冲突解决的逻辑。我目前只在实际项目里用过前两种模式。主管模式适合任务边界清晰的场景比如“写一份报告”可以拆成“收集资料”、“撰写初稿”、“审核修改”三个子任务。流水线模式适合数据处理场景比如“用户反馈分析”可以拆成“情感分类”、“关键词提取”、“摘要生成”三个环节。辩论模式我试过一个小 demo效果不太稳定。主要问题是多个 Agent 之间容易陷入“互相说服”的循环讨论半天达不成一致。后来加了一个“裁判 Agent”来强制裁决才勉强能用。但整体来说辩论模式的成本收益比不高除非任务本身需要多视角分析否则不建议用。4. 实操从零搭建一套提示词编排系统4.1 目录结构与技术选型先说一下我目前项目的目录结构供参考。prompt-system/ ├── templates/ │ ├── base/ │ │ ├── role.j2 │ │ ├── safety.j2 │ │ └── output_format.j2 │ ├── agents/ │ │ ├── customer_service/ │ │ │ ├── intent.j2 │ │ │ ├── response.j2 │ │ │ └── escalate.j2 │ │ └── data_analysis/ │ │ ├── plan.j2 │ │ ├── execute.j2 │ │ └── summarize.j2 │ └── partials/ │ ├── tool_usage.j2 │ └── few_shot.j2 ├── variables/ │ ├── global.yaml │ ├── customer_service.yaml │ └── data_analysis.yaml ├── flows/ │ ├── customer_service_flow.yaml │ └── data_analysis_flow.yaml ├── src/ │ ├── renderer.py │ ├── validator.py │ └── orchestrator.py └── tests/ ├── test_renderer.py └── test_orchestrator.py技术选型方面模板引擎用 Jinja2流程定义用 YAML变量管理用 YAML 加环境变量覆盖。渲染器自己写核心逻辑就是加载模板、注入变量、输出最终提示词。校验器负责检查模板语法和变量完整性。编排器读取流程定义按顺序执行各个步骤。这套结构不算复杂但足够支撑中等规模的 Agent 项目。如果 Agent 数量超过五十个可能需要考虑引入更专业的工作流引擎比如用 DAG 来描述流程依赖。但对于大多数团队来说YAML 定义的线性流程加条件分支已经够用了。4.2 模板渲染器的实现要点渲染器的核心代码不长但有几个细节要注意。from jinja2 import Environment, FileSystemLoader, StrictUndefined import yaml class PromptRenderer: def __init__(self, template_dir, variable_dir): self.env Environment( loaderFileSystemLoader(template_dir), undefinedStrictUndefined, trim_blocksTrue, lstrip_blocksTrue ) self.variables self._load_variables(variable_dir) def _load_variables(self, variable_dir): variables {} for file in Path(variable_dir).glob(*.yaml): with open(file) as f: variables.update(yaml.safe_load(f)) return variables def render(self, template_name, context): merged {**self.variables, **context} template self.env.get_template(template_name) return template.render(**merged)第一个要点是StrictUndefined。Jinja2 默认对未定义的变量是静默处理渲染成空字符串。这在提示词场景下很危险因为变量缺失可能导致模型收到不完整的指令。用StrictUndefined可以让未定义变量直接抛异常尽早发现问题。第二个要点是trim_blocks和lstrip_blocks。这两个选项控制模板标签周围的空白字符处理。开启之后{% if %}和{% endif %}所在的行不会产生多余的空行最终渲染出来的提示词更干净。第三个要点是变量合并顺序。全局变量先加载然后被 Agent 级别的变量覆盖最后被运行时传入的上下文覆盖。这个顺序保证了优先级运行时上下文 Agent 配置 全局默认值。4.3 流程编排的 YAML 定义流程定义用 YAML 描述每个步骤包含名称、使用的模板、输入变量、输出变量、失败处理策略。name: customer_service_flow steps: - name: intent_recognition template: agents/customer_service/intent.j2 inputs: user_input: {{ user_input }} history: {{ history }} outputs: intent: {{ result.intent }} confidence: {{ result.confidence }} on_failure: abort - name: route_by_intent type: condition condition: {{ intent complaint }} then: complaint_handling else: general_response - name: complaint_handling template: agents/customer_service/escalate.j2 inputs: user_input: {{ user_input }} intent: {{ intent }} outputs: response: {{ result.response }} on_failure: retry max_retries: 2 - name: general_response template: agents/customer_service/response.j2 inputs: user_input: {{ user_input }} intent: {{ intent }} outputs: response: {{ result.response }} on_failure: fallback fallback_template: agents/customer_service/default_response.j2这个定义里intent_recognition是第一步调用意图识别模板输出intent和confidence两个变量。route_by_intent是条件步骤根据intent的值决定走complaint_handling还是general_response。每个步骤都可以配置失败处理策略abort表示终止流程retry表示重试fallback表示走备用模板。编排器读取这个 YAML按顺序执行步骤维护一个上下文变量池每一步的输出都注入到池子里供后续步骤使用。条件步骤则根据表达式求值结果决定跳转目标。4.4 变量注入与上下文传递上下文传递是编排里最容易出问题的地方。变量名冲突、变量作用域混乱、变量生命周期不清晰都会导致难以排查的 bug。我的做法是给变量加命名空间。全局变量以global.开头Agent 级别变量以agent.开头步骤输出以step.开头运行时输入以input.开头。这样在模板里引用变量时一眼就能看出这个变量来自哪里。你是一个{{ agent.role_name }}当前时间是{{ global.current_date }}。 用户的问题是{{ input.user_input }} 上一步的分析结果是{{ step.intent_recognition.intent }}命名空间的好处是避免了变量覆盖。比如两个步骤都输出了result变量如果没有命名空间后一个会覆盖前一个。有了命名空间它们分别是step.step_a.result和step.step_b.result互不干扰。上下文传递还有一个细节是“变量清理”。流程执行完毕后上下文里的变量应该被清理掉避免影响下一次执行。如果编排器是长驻进程这一点尤其重要。我的做法是每次执行流程时创建一个新的上下文对象执行完毕后丢弃不做全局缓存。4.5 测试与验证策略提示词编排系统的测试比普通代码测试更难因为输出是非确定性的。同样的输入模型可能给出不同的输出。所以测试策略要调整。第一层测试是模板渲染测试。这部分是确定性的可以写单元测试。给定模板和变量断言渲染结果符合预期。重点测试边界情况变量缺失、变量类型错误、模板语法错误。第二层测试是流程编排测试。用 mock 的模型响应来测试流程逻辑。比如 mock 意图识别返回complaint断言流程走了complaint_handling分支。这部分也是确定性的可以自动化。第三层测试是端到端测试。用真实模型跑完整流程评估输出质量。这部分无法自动化断言但可以设置一些启发式规则比如“响应时间不超过 10 秒”、“输出不包含敏感词”、“输出长度在合理范围内”。超过阈值就告警人工介入检查。我目前项目里第一层和第二层测试是 CI 必过的第三层测试是每天定时跑一次结果发到群里供团队 review。这样既保证了基础逻辑的正确性又能及时发现模型层面的问题。5. 常见问题与排查技巧实录5.1 模板渲染失败的典型原因模板渲染失败是最常见的问题表现是 Agent 执行直接报错终止。根据我的排查经验原因主要有这么几类。变量缺失是最常见的。模板里写了{{ user_name }}但运行时上下文里没有这个变量。如果用了StrictUndefined会直接抛异常如果没用会渲染成空字符串导致提示词语义不完整。排查方法是检查变量定义文件和运行时上下文的合并结果确认所有必填变量都有值。变量类型不匹配也很常见。比如模板里期望history是一个列表用{% for item in history %}遍历但实际传入的是一个字符串。这种情况下 Jinja2 不会报错而是把字符串当成字符列表遍历输出完全错误的结果。排查方法是给关键变量加类型校验在渲染前检查类型是否符合预期。模板语法错误通常发生在修改模板之后。比如漏了{% endif %}或者{{和}}不匹配。Jinja2 在加载模板时就会报语法错误所以这类问题比较容易发现。但有一种情况比较隐蔽模板里用了自定义过滤器但过滤器没有注册渲染时才会报错。编码问题偶尔也会遇到。如果模板文件不是 UTF-8 编码中文内容会乱码。这个问题在 Windows 环境下更常见因为默认编码可能是 GBK。解决办法是在加载模板时显式指定编码为 UTF-8。5.2 Agent 执行中断的排查思路Agent 执行中断的原因比模板渲染失败更复杂因为涉及模型调用、工具执行、流程控制等多个环节。第一步是看日志。编排器应该在每个步骤开始和结束时打日志记录步骤名称、输入变量、输出结果、耗时。中断的时候日志会停在某个步骤从那里开始排查。如果中断发生在模型调用环节常见原因是 token 超限。提示词太长超过了模型的上下文窗口。解决办法是压缩提示词去掉不必要的少样本示例或者对历史对话做摘要。我一般会在编排器里加一个 token 计数器超过阈值就告警而不是等到模型报错才发现。如果中断发生在工具调用环节常见原因是工具执行超时或返回格式错误。比如调用了一个外部 APIAPI 挂了或者返回了非 JSON 格式的数据。解决办法是在工具层加超时控制和格式校验返回错误时给模型一个友好的错误信息让模型决定是重试还是换一个工具。如果中断发生在流程控制环节常见原因是条件表达式求值失败。比如{{ intent complaint }}里的intent变量是None比较操作就会出问题。解决办法是在条件判断前加空值检查或者给变量设默认值。5.3 提示词效果不稳定的调优经验提示词效果不稳定表现为同样的输入有时候输出很好有时候输出很差。这个问题没有银弹但有一些通用的调优思路。降低温度参数是最直接的方法。温度越低输出越确定。但温度太低会导致输出过于死板缺乏灵活性。我的经验是需要精确控制的场景如格式输出、分类任务用 0.1 到 0.3需要创造性的场景如文案生成、头脑风暴用 0.7 到 0.9。增加输出格式约束也很有效。在提示词里明确写出期望的输出格式比如“请以 JSON 格式输出包含intent和confidence两个字段”。模型看到明确的格式要求后输出会稳定很多。如果模型仍然不遵守可以在提示词末尾再加一句“如果输出格式不正确系统将无法解析”。提供少样本示例是提升稳定性的利器。给模型看两到三个输入输出示例它就能更好地理解你的期望。示例的选择很重要要覆盖典型场景和边界场景。我一般会放一个正常示例、一个边界示例、一个错误处理示例。拆分复杂任务也能提升稳定性。如果一个提示词要求模型同时做多件事比如“分析情感、提取关键词、生成摘要”模型很容易顾此失彼。拆成三个独立的提示词每个只做一件事稳定性会大幅提升。代价是调用次数增加成本上升需要权衡。5.4 常见问题速查表问题现象可能原因排查方法解决方案渲染报错提示变量未定义变量缺失或拼写错误检查变量定义和上下文补全变量或修正拼写渲染结果包含花括号变量未被替换检查变量名是否匹配确认变量名一致Agent 执行中途终止token 超限或工具报错查看日志定位中断步骤压缩提示词或修复工具输出格式不符合预期提示词约束不够明确检查输出格式说明增加格式约束和示例同样输入输出差异大温度参数过高检查模型调用参数降低温度或增加约束流程走错分支条件表达式求值错误检查条件变量值修正表达式或加空值检查模板修改后不生效缓存未清除检查缓存配置清除缓存或重启服务中文输出乱码编码不一致检查文件编码统一使用 UTF-85.5 几个容易忽略的细节模板里的空格和换行。Jinja2 渲染时{% if %}标签所在的行会留下空行除非开启trim_blocks。这些空行在提示词里可能无关紧要但如果提示词对格式要求严格比如要求输出 JSON多余的空行可能导致解析失败。所以一定要开启trim_blocks和lstrip_blocks。变量值的转义。如果变量值里包含特殊字符比如{、}、%可能会被 Jinja2 误认为是模板语法。解决办法是用| e过滤器做 HTML 转义或者用{% raw %}包裹。但更稳妥的做法是在传入变量之前就做好清洗去掉可能引起歧义的字符。模板的加载顺序。如果模板目录下有同名文件Jinja2 的加载顺序取决于文件系统的遍历顺序这是不确定的。所以一定要避免同名文件用清晰的目录结构区分不同 Agent 的模板。版本回滚后的缓存问题。前面提过回滚模板后要清缓存。但缓存可能有多层内存缓存、Redis 缓存、CDN 缓存。回滚流程要确保所有层的缓存都被清除否则会出现“部分节点用新版本部分节点用旧版本”的情况。日志里的敏感信息。提示词里可能包含用户数据比如姓名、电话、地址。打日志的时候要注意脱敏否则可能违反隐私合规要求。我的做法是在日志输出前过一遍脱敏过滤器把手机号、身份证号等敏感字段替换成掩码。6. 从单 Agent 到多 Agent 的编排演进6.1 什么时候需要多 Agent单 Agent 能解决的问题尽量不要用多 Agent。多 Agent 带来的复杂度是指数级增长的通信开销、协调逻辑、错误传播、调试难度每一项都比单 Agent 麻烦得多。我判断是否需要多 Agent 的标准是任务是否可以清晰地拆分成多个独立的子任务且子任务之间的依赖关系是线性的或树状的。如果是可以考虑多 Agent。如果子任务之间有复杂的循环依赖或者需要频繁的双向通信那多 Agent 可能不是好选择不如用一个 Agent 加更复杂的提示词。举个例子“写一份行业分析报告”可以拆成“收集数据”、“分析数据”、“撰写报告”三个子任务依赖关系是线性的适合多 Agent。“和用户进行多轮谈判”涉及频繁的双向交互依赖关系是网状的不适合多 Agent单 Agent 加对话管理更合适。6.2 主管模式的编排实现主管模式是我用得最多的多 Agent 模式。一个主管 Agent 负责理解任务、拆解子任务、分配给 worker、汇总结果。worker Agent 各自负责一个具体的子任务。编排层需要做几件事。第一是任务拆解主管 Agent 输出一个子任务列表每个子任务包含描述和期望的输出格式。第二是任务分配编排层根据子任务描述选择合适的 worker Agent。第三是并行执行没有依赖关系的子任务可以并行跑提升效率。第四是结果汇总所有子任务完成后把结果交给主管 Agent 做最终整合。任务分配的策略有两种。一种是静态分配在配置里写死“子任务类型 A 分配给 worker A”。另一种是动态分配根据子任务描述和 worker 的能力描述做匹配。静态分配简单可控但灵活性差。动态分配灵活但需要额外的匹配逻辑而且匹配错误的风险更高。我目前用的是静态分配加人工兜底大部分子任务走静态分配遇到无法匹配的子任务就转人工处理。6.3 流水线模式的编排实现流水线模式适合数据处理场景。每个 Agent 负责一个处理环节前一个的输出是后一个的输入。编排层只需要按顺序调用各个 Agent把上一个的输出传给下一个。流水线模式的关键是数据格式的约定。每个环节的输入输出格式必须严格定义否则上下游对不齐。我的做法是用 JSON Schema 定义每个环节的输入输出格式编排层在传递数据时做校验格式不对就中断流水线并报错。另一个关键是错误处理。流水线中间某个环节失败了怎么办是整体回滚还是跳过继续这取决于业务需求。如果是数据清洗流水线某个环节失败可能导致后续所有数据都不可用应该整体回滚。如果是内容生成流水线某个环节失败可能只影响部分内容可以跳过继续最后标记哪些内容不完整。6.4 多 Agent 编排的调试技巧多 Agent 编排的调试比单 Agent 难得多因为问题可能出在任何一个 Agent 上也可能出在 Agent 之间的通信上。我的调试策略是“分段隔离”。先把每个 Agent 单独跑一遍确认单个 Agent 的输出符合预期。然后把 Agent 两两组合跑确认通信格式正确。最后跑完整流程确认整体逻辑正确。这样可以把问题定位到具体的环节而不是面对一个黑盒。日志方面每个 Agent 的输入输出都要单独记录并且带上 trace ID。这样在排查问题时可以通过 trace ID 把一次完整执行的所有日志串起来看到数据在各个 Agent 之间的流转过程。还有一个技巧是“快照回放”。在编排层记录每个步骤的输入输出快照出问题的时候可以用快照回放整个流程而不需要重新调用模型。这样既节省成本又方便复现问题。我目前项目里快照默认保留七天超过七天自动清理。7. 一些个人体会提示词模板管理和 Agent 编排这件事技术难度其实不算高但工程细节特别多。我见过很多团队在模型选型、算法调优上花了很多精力却在提示词管理这种“脏活累活”上栽跟头。线上事故十次有八次是提示词问题不是模型问题。我的建议是项目早期就要把模板管理做起来不要等到提示词满天飞了再重构。早期投入可能看起来“过度设计”但后期节省的维护成本是值得的。模板目录结构、变量命名规范、版本管理流程这些基础工作做扎实了后面加 Agent、改提示词都会顺畅很多。编排方面不要追求大而全的框架。很多开源编排框架功能很强大但学习成本也高而且往往有很多你用不上的功能。从简单的 YAML 流程定义开始遇到不够用的情况再逐步扩展这样更务实。我自己的编排器迭代了三个版本第一版只支持顺序执行第二版加了条件分支第三版加了循环和并行。每一步都是被实际需求驱动的没有过度设计。最后分享一个小心得提示词模板的命名一定要规范。我见过用prompt1.j2、prompt2.j2命名的过两个月自己都忘了哪个是哪个。我的命名规则是{agent_name}_{function}_{version}.j2比如customer_service_intent_v2.j2。虽然名字长一点但一眼就能看出用途和版本省去了很多翻文档的时间。