复杂任务怎么拆成多步 Prompt?

进阶实践场景题约 7 分钟读完

一句话回答

把复杂任务拆成几个简单的步骤,每一步用一个专门的 Prompt,上一步的输出作为下一步的输入,由代码串起来,这就是 Prompt 链。常见模式有顺序链、路由、并行后汇总、生成后校验。好处是每一步任务单一、更容易写好,还能单独测试、观测和更换模型;代价是调用次数多,延迟和成本增加。执行路径由开发者在代码里确定,这是它和 Agent 的根本区别。

详细解析

为什么要拆

一个 Prompt 里塞进太多要求,模型容易顾此失彼;出了问题,也很难判断是哪个环节没做好。拆开之后:

  • 每一步只做一件事,指令更短、更清晰
  • 每一步可以选不同的模型和参数:分类用便宜的小模型,生成用更强的模型
  • 步骤之间可以插入代码:校验、查数据库、执行业务规则
  • 中间结果可见,出问题时能定位到具体的步骤

不需要拆的情况:任务本身简单,一次调用就能做好;步骤之间要共享大量上下文,拆开反而丢信息;对延迟非常敏感。

常见模式

模式 做法 例子
顺序链 上一步的输出作为下一步的输入 提取要点 → 写初稿 → 润色
路由 先分类,再交给对应的专用 Prompt 客服问题先分成退款、技术、其他,再分别处理
并行后汇总 互不依赖的子任务并发执行,最后合并 代码审查分别检查安全、性能、可读性,再汇总成报告
生成后校验 生成者产出结果,评审者按标准检查,不通过就带着意见重写 生成客服回复后,检查是否承诺了政策以外的内容

设计要点

  1. 步骤之间传结构化数据:每一步输出 JSON 并校验,相当于定义了步骤之间的接口,见 怎么让大模型稳定地输出 JSON
  2. 在关键步骤之间加检查:不合格就提前结束或走兜底,不要让第一步的错误一路传到最后
  3. 只传下一步需要的信息,不要把所有中间结果都塞给每一步
  4. 每一步单独评测:给每个步骤准备小的测试集,效果变差时能定位到具体的步骤
  5. 记录每一步的输入、输出、耗时和 token,便于排查,见 链路追踪
  6. 评审要有明确的标准:能用规则检查的(长度、必含字段、禁用词)用代码检查;需要模型评审时给出检查清单,重写循环要设最大次数

和 Agent 的区别

Prompt 链的每一步、每个分支都是开发者预先写好的,路由也只是在几条预定的路径里选一条;Agent 则由模型在运行时决定下一步调用什么工具、做什么。步骤能预先确定时,优先用 Prompt 链,更可控,也更容易测试,见 工作流和 Agent 怎么选。

代码示例

客服工单处理:路由 → 专用 Prompt 生成回复 → 评审,不通过就带着意见重写。chatJson 是自己封装的函数:调用模型并用 zod 校验返回的 JSON;generateReply 用指定的 System Prompt 生成回复,有评审意见时附在消息里。

TypeScript
import { z } from 'zod'

const Category = z.object({ category: z.enum(['refund', 'bug', 'other']) })
const Review = z.object({ pass: z.boolean(), issues: z.array(z.string()) })

const REPLY_PROMPTS = {
  refund: '你是退款专员。根据退款政策回答,不要承诺政策以外的金额和时间……',
  bug: '你是技术支持。先确认问题现象和复现步骤,再给出排查建议……',
  other: '你是客服助手。礼貌、简短地回答用户的问题……',
}

async function handleTicket(ticket: string) {
  // 第 1 步:分类。任务简单,用小模型,temperature 设为 0
  const { category } = await chatJson(Category, {
    model: 'small-model',
    temperature: 0,
    prompt: `把工单分为 refund、bug、other 之一,输出 {"category": "..."}。\n<ticket>\n${ticket}\n</ticket>`,
  })

  // 第 2 步:用对应类别的专用 Prompt 生成回复
  let reply = await generateReply(REPLY_PROMPTS[category], ticket)

  // 第 3 步:评审,不通过就带着意见重写,最多重写 2 次
  for (let round = 0; ; round++) {
    const review = await chatJson(Review, {
      temperature: 0,
      prompt: `检查客服回复:是否回答了用户的问题、是否承诺了政策以外的内容、语气是否礼貌。输出 {"pass": true 或 false, "issues": ["问题描述"]}。\n<ticket>\n${ticket}\n</ticket>\n<reply>\n${reply}\n</reply>`,
    })
    if (review.pass) return { category, reply }
    if (round === 2) return { category, reply: null } // 仍然不合格,转人工处理
    reply = await generateReply(REPLY_PROMPTS[category], ticket, review.issues)
  }
}

面试官可能追问

链条是不是越长越好?

不是。每一步都有出错的可能,错误会沿着链条累积:假设每一步的成功率是 95%,串联 5 步后,端到端的成功率只有约 77%。步骤越多,延迟和成本也越高。能合并的步骤就合并,以端到端的评测结果为准。

生成后校验时,评审者能用同一个模型吗?

可以,但同一个模型有相同的盲区,还可能偏向自己生成的内容。能用规则检查的尽量用代码;必须让模型评审时,给出明确的检查清单,重要场景可以换一个模型来评审,见 LLM-as-a-Judge。

多步调用延迟太高,怎么优化?

互不依赖的步骤并发执行;简单的步骤用小模型;固定的前缀利用提示缓存;最后一步用流式输出,让用户尽早看到内容;按条件跳过步骤,比如只对高风险的回复做评审。

中间某一步失败了怎么办?

超时、限流这类可重试的错误,做有限次重试;校验不通过时,带上错误信息让模型修正;仍然失败就走兜底路径,比如返回默认结果或转人工。长流程要保存中间结果,失败后从断点继续,而不是从头再来。

易错点

  • 步骤之间传自由文本,下一步解析不稳定
  • 没有中间检查,第一步的错误一路传到最后
  • 为了拆而拆,简单任务拆成多步,反而更慢、更贵
  • 评审和重写的循环不设上限,模型反复修改停不下来

AI 模拟面试官

用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮

登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。