单 Prompt 有承载上限——复杂任务需要拆解,让多个 Prompt、甚至多个模型协同工作。本文讨论三种编排模式:Prompt Chaining(链式分解)、路由策略(小模型分类 + 大模型推理)、以及 Tool-use / Agent 协作。重点不在代码实现,而在每种模式下 Prompt 的设计思路。
概述 前两篇文章讨论的都是单 Prompt 场景——一个输入,一个输出。但实际工程中的复杂任务,单 prompt 存在几项硬限制:
context window 有限,输入+输出超出窗口就无能为力
任务异构——同一任务中包含分类、生成、审查等不同性质的操作,单 prompt 容易顾此失彼
成本不可控——简单分类也用强模型,大量 token 被浪费
缺少验证——生成代码后需要另一轮独立审查才能放心
这就引出多模型工作流编排:把大任务拆成小任务,用多个 Prompt(甚至多个模型)协同完成 。
本文覆盖三种核心模式:
Prompt Chaining :链式分解,一环一个 prompt
路由策略 :小模型分类,大模型推理
Tool-use 与 Agent :工具调用与多角色协作
三种模式难度和成本递增,解决问题的复杂度也递增。
一、Prompt Chaining:任务链式分解 核心思想 一句话概括:一个 prompt 只做一件事,输出成为下一个的输入 。
1 2 3 4 5 6 7 8 9 10 [原始需求] │ ▼ Prompt 1 (规划) → 结构化输出 A │ ▼ Prompt 2 (执行) → 结构化输出 B │ ▼ Prompt 3 (审查) → 最终输出
链式 vs 单 Prompt
单 Prompt 能搞定
应使用链式
一步推理可得出答案
需要多步推理,中间结果需验证
输入+输出在 context window 内
输入+中间产物超出 context window
任务类型单一
任务异构(分析 + 生成 + 审查)
不需要中间结果
中间结果本身有价值(日志、可追溯)
一个实用判断:如果 prompt 里写了 “first… then… finally…”,大概率应该拆成链式 。
实战:代码审查流水线 假设需要对一段代码进行全面审查——分析逻辑、找出 bug、提出修复方案、验证修复后的代码。单 prompt 做这些事可能遗漏或混淆。链式做法为三环:
第一环:分析 Prompt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 分析以下 Python 代码的逻辑结构和潜在问题。 输出结构化分析报告,不要提出修复方案。 代码: ```python def process_orders(orders, threshold=100): results = [] for order in orders: total = sum(item['price'] * item['quantity'] for item in order['items']) if total > threshold: order['priority'] = 'high' else: order['priority'] = 'normal' results.append(order) return results
输出格式(JSON): { “functionality”: “函数功能描述”, “logic_flow”: [“步骤1”, “步骤2”, …], “potential_issues”: [ { “type”: “bug|performance|edge_case”, “location”: “行号或代码片段”, “description”: “问题描述” } ] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 **第二环:修复 Prompt**(接收第一环输出) ```markdown 基于以下分析报告,为每个 potential_issues 生成修复方案。 对每个 issue,给出修改后的完整函数代码。 分析报告: {第一环的 JSON 输出} 要求: - 每个修复方案标注对应 issue - 给出完整修复后的函数代码(不是 diff) - 修复不改变函数对外接口
第三环:验证 Prompt (接收第二环输出 + 原始代码)
1 2 3 4 5 6 7 8 审查以下代码修复,验证: 1. 原始问题是否被解决2. 是否引入新问题3. 修复是否改变了原有的函数行为(非预期变化)原始代码: ```python {原始代码}
修复方案与代码: {第二环输出}
输出格式: { “verification”: { “original_issues_resolved”: true/false, “new_issues_introduced”: [], “behavior_change”: “none|acceptable|unacceptable” }, “final_code”: “验证通过的最终代码” }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 三环下来,每步产出都是结构化的、可追溯的、可被单独审查的。比一个"请审查并修复这段代码"的大 prompt 可靠。 ### 设计原则 **1. 每环目标单一**:一环只做一件事。如果 prompt 里写了三步操作,说明应该拆环。 **2. 中间输出必须结构化**:链式传递的前提是下游能解析上游的输出。JSON 是第一选择——有 Schema 约束,解析代码一行搞定。 **3. 错误传播控制**:某一环的输出错误会导致后续所有环跟着错。处理方式:每环之间加验证逻辑(轻量 prompt 或代码规则检查),关键环节的中间结果缓存以便回溯。 **4. 链式是可选的**:链式增加延迟和成本。一个 prompt 能搞定的事,不要为架构感拆成多个。 ## 二、路由策略:小模型 + 大模型协同 ### 核心思想 不是所有输入都需要最强模型处理。**用便宜模型分流,昂贵模型做深度处理**。 ```text [用户输入] │ ▼ 小模型(路由 Prompt)→ 分类结果 │ ├── 简单问题 → 小模型直接回答 ├── 复杂问题 → 大模型深度处理 └── 需外部数据 → RAG 流程
路由 Prompt 设计 路由 prompt 的核心要求:极简、高确定性、低延迟 。
1 2 3 4 5 6 7 8 9 10 分类以下用户输入。只输出一个类别标签,不要任何解释。 类别: - simple: 事实查询、简单计算、常见知识问答- complex: 需要推理、分析、多步计算、代码生成- rag: 需要查阅外部文档或实时数据用户输入:PostgreSQL 的 VACUUM FULL 和 VACUUM 有什么区别? 类别:rag
设计要点:
类别定义互斥 :不让模型在两个类别间犹豫
只输出一个词 :额外解释都是浪费 token
Few-shot 覆盖边界 :放两个容易混淆的 case 进示例
用便宜模型 :DeepSeek-V3、GPT-4o-mini、Claude Haiku 足够做分类
实战:客服工单路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 你是一个客服工单分类器。根据用户消息,将工单路由到正确的处理流程。 只输出类别标签。 类别: - refund: 退款相关(退款进度、金额、条件)- technical: 技术问题(产品故障、配置、兼容性)- account: 账户相关(登录、密码重置、账户设置)- other: 无法归类示例: 用户:我密码忘了,登录不进去 类别:account 用户:你们的 API 返回 500 错误 类别:technical 用户:我要退款 类别:refund 用户:今天天气怎么样 类别:other 现在分类: {用户输入} 类别:
路由完成后,每个类别有对应的下游处理 prompt——refund 配退款政策 RAG、technical 配技术知识库 RAG + 大模型推理、account 直接走工单系统 API。路由是整个系统的中控台。
成本优化 以 OpenAI 定价为例:
模型
输入价格
输出价格
适用场景
GPT-4o-mini
$0.15/1M
$0.60/1M
路由分类、简单问答
GPT-4.1
$2.00/1M
$8.00/1M
复杂推理、代码生成
假设系统每天 10,000 次请求,60% 简单问题、30% 复杂问题、10% 需要 RAG。全部用 GPT-4.1 与路由后用 GPT-4o-mini 处理简单部分相比,后者成本约为前者的 30%。路由本身成本极低(一次分类约 200 input tokens),几乎可忽略。
回顾 Tool-use(或称 Function Calling)的基本机制已在 Tool&Function Calling 一文中详细展开。这里聚焦:工具定义的 prompt 怎么写 。
工具描述:不止 Schema,要写”说明书” 给模型定义工具时,只写 JSON Schema 不够。description 字段是给模型的”说明 prompt”——模型靠它理解何时该用这个工具。
精简描述的问题:
1 2 3 4 5 { "name" : "search_docs" , "description" : "搜索文档" , "parameters" : {...} }
模型不知道”文档”是什么、该在什么场景下调用。
有效的描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 { "name" : "search_technical_docs" , "description" : ( "搜索内部技术文档知识库。" "使用场景:用户询问 API 用法、配置参数、部署流程、故障排查等问题时调用。" "不要使用:用户询问通用编程概念、闲聊、或主观建议时不要调用。" "注意:此工具搜索的是公司内部文档,不包括开源社区内容。" ), "parameters" : { "type" : "object" , "properties" : { "query" : { "type" : "string" , "description" : ( "搜索查询,3-8 个关键词,用空格分隔。" "例如:'PostgreSQL connection pool 配置' 而非 '数据库怎么连'" ) }, "limit" : { "type" : "integer" , "description" : "返回结果数量,默认 5。用户没有指定时使用默认值。" , "default" : 5 } }, "required" : ["query" ] } }
好的工具描述包含四个要素:
要素
说明
功能
工具做什么
触发条件
什么场景该调、什么场景不该调
参数说明
每个参数含义 + 示例值
边界
工具覆盖什么、不覆盖什么
工具选择的两种策略 策略一:模型自主选择(tool_choice: "auto")
1 2 3 4 5 6 response = client.chat.completions.create( model="gpt-4.1" , messages=[...], tools=[search_docs, query_database, send_email, ...], tool_choice="auto" )
适用场景:工具间功能差异大,模型容易判断。工具描述写得好,auto 就够用。
策略二:System Prompt 显式引导
1 2 3 4 5 6 ## 工具使用规则 1. 用户询问技术问题时,优先调用 search_technical_ docs2. 用户需要实时数据时,调用 query_database 3. 不调用 send_ email 除非用户明确说"帮我发邮件"4. 一次只调用一个工具,不嵌套调用
适用场景:工具有相似功能,或调用决策有业务逻辑约束。
工具结果注入格式 工具调用完成后,结果注入回上下文的格式直接影响模型的二次理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 messages.append({ "role" : "tool" , "tool_call_id" : tool_call.id , "content" : json.dumps({ "tool" : "search_technical_docs" , "query" : "PostgreSQL connection pool" , "results" : [ { "title" : "连接池配置指南" , "snippet" : "高并发场景推荐使用 PgBouncer 作为连接池中间件..." , "url" : "/docs/postgresql/pgbouncer-setup" , "score" : 0.94 } ], "total_found" : 5 }, ensure_ascii=False ) })
工具结果同样需要结构化。非结构化文本在二次理解时可能产生歧义——结构化 JSON 让模型明确每个字段含义,理解更准确。
四、多 Agent 协作初步 从链式到 Agent Prompt Chaining 是单向线性流程:A → B → C。Agent 协作在此基础上增加两样东西:
双向交互 :Agent 之间可以互相提问、确认、否决
角色分化 :每个 Agent 有独立的 System Prompt 和行为边界
典型的 3-Agent 代码生成场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Planner (规划者) │ ├── "实现一个分页查询函数" ▼ Coder (编码者) │ ├── "这是我的实现,请审查" ▼ Reviewer (审查者) │ ├── "发现 2 个问题,请修复" ▼ Coder (第二轮) │ ├── "已修复,请重新审查" ▼ Reviewer (最终确认) → 最终输出
Agent 的角色 Prompt 设计 每个 Agent 的 System Prompt 定义其行为边界——不仅要知道该做什么,更要知道不该做什么 。
Planner :
1 2 3 4 5 6 你是一个技术方案规划者。你的职责: 1. 分析需求,拆解为可执行的子任务2. 为每个子任务设计输入输出约定3. 排序子任务的执行顺序你从不写代码。输出是结构化的任务列表。
Coder :
1 2 3 4 5 6 你是一个 Python 代码实现者。你的职责: 1. 根据 Planner 的任务规范编写代码2. 在 Reviewer 指出问题后修复代码3. 对任务规范有疑问时,向 Planner 请求澄清你从不评审自己的代码。代码质量由 Reviewer 负责。
Reviewer :
1 2 3 4 5 6 7 你是一个代码审查者。你的职责: 1. 检查代码是否符合 Planner 的规范2. 发现 bug、性能问题、安全隐患3. 输出结构化的审查报告(问题列表),不直接修改代码代码无问题时输出 "PASS"。 连续两次审查通过时输出 "FINAL_PASS" 并附上最终代码。
每个 Agent 的行为边界被精确定义——Coder 不评审自己的代码,Reviewer 不修改代码。这个”不该做什么”的约束是 Agent 协作不崩盘的基础。
Agent 间的通信协议 Agent 间消息需要固定的交互格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "from" : "planner" , "to" : "coder" , "type" : "task" , "task_id" : "task_001" , "spec" : { "function_name" : "get_paginated_users" , "input" : { "page" : "int" , "page_size" : "int" } , "output" : { "users" : "list[User]" , "total" : "int" , "page" : "int" , "total_pages" : "int" } , "constraints" : [ "使用 async/await" , "SQLAlchemy 2.0 风格" , "包含错误处理" ] , "tests" : [ "空数据库" , "单页数据" , "超出范围页码" ] } }
1 2 3 4 5 6 7 8 9 10 11 { "from" : "reviewer" , "to" : "coder" , "type" : "review" , "task_id" : "task_001" , "status" : "revision_required" , "issues" : [ { "severity" : "medium" , "line" : 24 , "description" : "缺少 page_size 上限校验" } , { "severity" : "low" , "line" : 35 , "description" : "total_pages 整数除法不够明确,建议用 math.ceil" } ] }
这个格式是约定,不是推断——它可以写在每个 Agent 的 System Prompt 中作为协议规范。
上下文交接策略 Agent 间传递信息时,核心问题是上下文膨胀 。每次交互把全部历史带上,几轮后 context 就超出窗口。
传递
不传递
结构化任务规范和结果
原始思考过程
Review 报告(问题列表)
编码者中间的推理文本
代码 diff / 最终代码
完整的旧版本代码(除非需要对比)
原则:只传递决策和结果,不传递过程 。模型内部的思考链不需要在 Agent 之间共享。
五、模式选择 三种模式并非互斥,实际使用中可以叠加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 任务复杂度 │ ├── 低 → 单 Prompt(第一篇内容) │ ├── 中 → Prompt Chaining │ ├── 任务可分步 + 每步产物有价值 → Chaining │ └── 有简单子任务 → Chaining + 路由 │ ├── 高 → Chaining + Tool-use │ ├── 需要外部数据 → 加 Tool(搜索/数据库/API) │ └── 需要交互式验证 → 加 Agent 协作 │ └── 极高 → 多 Agent 系统 └── 需要多角色反复交互、审查、修正
建议从单 Prompt 起步,需要时再往上加。每加一层复杂度,都意味着更多的调试成本、更长的延迟、更高的 token 消耗。
总结 多模型编排解决的是”一个 prompt 不够用”的问题,三种模式按复杂度递增:
模式
解决的问题
核心设计点
Prompt Chaining
任务太复杂
每环目标单一、中间输出结构化
路由策略
成本太高
路由 prompt 极简化、类别互斥
Tool-use / Agent
需要外部数据或多角色协作
工具描述含”说明书”、Agent 定义行为边界
编排层最核心的 prompt 设计原则始终不变:每段 prompt 只做一件事,输入输出都结构化,边界约束写清楚 。