[Prompt Engineering] 多模型工作流编排

单 Prompt 有承载上限——复杂任务需要拆解,让多个 Prompt、甚至多个模型协同工作。本文讨论三种编排模式:Prompt Chaining(链式分解)、路由策略(小模型分类 + 大模型推理)、以及 Tool-use / Agent 协作。重点不在代码实现,而在每种模式下 Prompt 的设计思路。

概述

前两篇文章讨论的都是单 Prompt 场景——一个输入,一个输出。但实际工程中的复杂任务,单 prompt 存在几项硬限制:

  • context window 有限,输入+输出超出窗口就无能为力
  • 任务异构——同一任务中包含分类、生成、审查等不同性质的操作,单 prompt 容易顾此失彼
  • 成本不可控——简单分类也用强模型,大量 token 被浪费
  • 缺少验证——生成代码后需要另一轮独立审查才能放心

这就引出多模型工作流编排:把大任务拆成小任务,用多个 Prompt(甚至多个模型)协同完成

本文覆盖三种核心模式:

  1. Prompt Chaining:链式分解,一环一个 prompt
  2. 路由策略:小模型分类,大模型推理
  3. 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

设计要点:

  1. 类别定义互斥:不让模型在两个类别间犹豫
  2. 只输出一个词:额外解释都是浪费 token
  3. Few-shot 覆盖边界:放两个容易混淆的 case 进示例
  4. 用便宜模型: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 中的 Prompt 设计

回顾

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_docs
2. 用户需要实时数据时,调用 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 协作在此基础上增加两样东西:

  1. 双向交互:Agent 之间可以互相提问、确认、否决
  2. 角色分化:每个 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 只做一件事,输入输出都结构化,边界约束写清楚