摘要 :Prompt Chaining(提示链)将复杂任务拆成多步小任务,每步用单一提示完成,上一步输出作为下一步输入,从而提升大模型在多阶段任务上的可靠性与可维护性。本文介绍其动机、结构化输出、典型应用场景,并以 LangGraph 的 StateGraph 实现两阶段「提取 → 转换」流水线示例,便于读者动手实践与扩展。
关键词:Prompt Chaining;提示链;Pipeline 模式;LangGraph;StateGraph;多步推理;结构化输出;Agentic Design Patterns
源代码地址:链接
1 为什么需要 Prompt Chaining?
想象一下:你让大模型「读一份市场调研报告,总结要点、找出三个趋势并附上数据、再给市场部写一封邮件」。如果只发一条超长提示,模型很容易出现:漏掉其中某一项、中途「跑题」、前面错一点后面全歪,或者因为上下文太长而表现不稳。这类问题在复杂任务里非常常见。
Prompt Chaining (提示链,有时也叫 Pipeline 模式 )的做法是:把一个大任务拆成多步小任务,每一步用一个专注的提示完成一件事,上一步的输出当作下一步的输入。就像流水线:先拧螺丝,再装外壳,最后质检------每一步职责清晰,出错也容易定位。
💡 理解要点 :单次 Prompt 在「多目标、多步骤」任务上容易指令遗漏、上下文漂移、错误放大;拆成链式多步可以显著提高可靠性和可控性。
2 Prompt Chaining 在解决什么?
- 单一大提示的局限:任务一多,模型容易忽略部分约束、偏离主题、或在前序错误基础上继续推理,甚至因认知负载增加而更容易「幻觉」。
- 链式分解带来的好处 :
- 每一步目标单一:例如「只做摘要」「只做趋势抽取」「只写邮件」,模型负担小、表现更稳。
- 上一步输出约束下一步:例如先得到「摘要」,再在摘要基础上抽趋势,再基于趋势写邮件,形成清晰的依赖链。
- 便于调试与迭代:哪一步效果不好,就改哪一步的提示或逻辑,不必重写整条流程。
- 中间可插入工具与校验:例如某步之后做格式校验、调用计算器或数据库,再交给下一步。
🔍 实际例子 :同一份市场报告,可以拆成三步------
①「请总结以下报告要点」→ ②「根据该摘要,列出前三个趋势并附支持数据」→ ③「根据上述趋势与数据,写一封给市场部的简短邮件」。每一步输入明确、输出可验证,整体更可靠。
3 结构化输出:链的「接口」
链的可靠性很大程度取决于步骤之间传递的数据是否清晰、可解析 。如果上一步输出是含糊的自然语言,下一步就容易理解偏差。因此,为中间结果和最终结果约定结构化格式(如 JSON、XML)是常见做法。
例如,趋势抽取步骤可以要求输出如下 JSON:
json
{
"trends": [
{
"trend_name": "AI 驱动的个性化",
"supporting_data": "73% 的消费者更愿意与使用个性化体验的品牌打交道。"
},
{
"trend_name": "可持续与伦理品牌",
"supporting_data": "带 ESG 声明的产品过去五年销售增长 28%,无声明产品为 20%。"
}
]
}
这样下一步(例如写邮件)可以直接解析并填入模板,减少歧义和解析错误。
💡 理解要点:链中每一步的「输出格式」相当于接口契约;用 JSON/XML 等结构化输出能减少歧义,便于程序化处理与后续节点使用。
4 典型应用场景(简述)
| 场景 | 链的典型步骤 |
|---|---|
| 信息处理流水线 | 抓取/解析 → 摘要 → 实体抽取 → 知识库查询 → 生成报告 |
| 复杂问答 | 拆子问题 → 分别检索 → 综合多源信息 → 生成答案 |
| 数据抽取与转换 | 从文档抽取字段 → 校验/补全 → 输出结构化数据,必要时插入计算或工具 |
| 内容生成 | 选题 → 列提纲 → 分节撰写 → 统稿与润色 |
| 对话与状态 | 每轮识别意图与实体 → 更新状态 → 根据状态生成回复(多轮可视为链的重复) |
| 代码生成与优化 | 理解需求 → 写伪代码/提纲 → 写初版代码 → 静态分析/审查 → 修正与补充文档或测试 |
这些场景的共同点是:多阶段、有先后依赖;Prompt Chaining 负责把「阶段」和「依赖」显式地建模成链(或图)。
5 用 LangGraph 实现 Prompt Chaining
LangChain 的 LCEL 适合线性链;LangGraph 则用图 来编排节点与边,天然适合「多步、有状态、可扩展」的流程。下面用 LangGraph 实现一个两阶段流水线 :先从文本中提取技术规格描述,再把这些描述整理成带 cpu、memory、storage 的 JSON。整体是线性链:START → 提取 → 转换 → END。

5.1 状态设计
图上的所有节点共享同一份状态 (State)。我们用一个 TypedDict 表示「链上传递的数据」:
python
class ChainState(TypedDict):
text_input: str # 用户输入的原始文本
specifications: str # 第一阶段的输出:提取出的规格描述
result: str # 第二阶段的输出:最终 JSON 字符串
- 输入只填
text_input。 - 第一个节点只写
specifications。 - 第二个节点只写
result。
这样每个节点职责清晰,状态在节点间自动合并(默认是覆盖式更新)。
5.2 两个节点
节点一:提取(extract)
只做一件事:从 state["text_input"] 里提取技术规格,用自然语言概括,写回 specifications。
节点二:转换(transform)
只做一件事:把 state["specifications"] 整理成只含 cpu、memory、storage 的 JSON 字符串,写回 result。
节点实现里使用 ChatPromptTemplate + ChatOpenAI + StrOutputParser(),和 LangChain 的用法一致;区别在于这里每个节点是「图上的一个节点函数」,接收 state,返回要更新的字段字典。
5.3 建图与编译
python
workflow = StateGraph(ChainState)
workflow.add_node("extract", node_extract)
workflow.add_node("transform", node_transform)
workflow.add_edge(START, "extract")
workflow.add_edge("extract", "transform")
workflow.add_edge("transform", END)
graph = workflow.compile()
执行时传入初始状态即可:
python
initial_state = {
"text_input": "新款笔记本配备 3.5 GHz 八核处理器、16GB 内存和 1TB NVMe 固态硬盘。",
"specifications": "",
"result": "",
}
final_state = graph.invoke(initial_state)
# 使用 final_state["specifications"] 和 final_state["result"]
🔍 实际例子 :完整可运行代码(含节点实现、环境与运行说明)在本章的 demo_codes 目录中:
- 代码入口:
demo_codes/chain.py - 安装与运行:见
demo_codes/README.md
运行后会在控制台看到「阶段一:提取的规格」和「阶段二:最终 JSON」,便于对照理解链式数据流。
5.4 完整代码
py
"""
Prompt Chaining 示例:使用 LangGraph 实现两阶段数据处理流水线。
流程:原始文本 → [节点1: 提取规格] → [节点2: 转为 JSON] → 最终输出
运行前请配置环境变量 OPENAI_API_KEY,或在项目根目录放置 .env 文件。
"""
import os
from typing import TypedDict
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langgraph.graph import StateGraph, START, END
from dotenv import load_dotenv
load_dotenv()
# 支持 OpenAI 或 DashScope 等兼容接口:未设置时 model 必须有默认值,否则 ChatOpenAI 会报错
dashscope_api_key = os.getenv("DASHSCOPE_API_KEY") or os.getenv("OPENAI_API_KEY")
dashscope_base_url = os.getenv("BASE_URL") # 使用 DashScope 时配置,OpenAI 可不设
model = os.getenv("MODEL") # 必须为有效字符串,不可为 None
# ---------------------------------------------------------------------------
# 状态定义:节点之间通过 State 传递数据
# ---------------------------------------------------------------------------
class ChainState(TypedDict):
"""图的状态:贯穿「提取」与「转换」两阶段。"""
text_input: str # 用户输入的原始文本
specifications: str # 第一阶段的输出:提取出的规格描述
result: str # 第二阶段的输出:最终 JSON 字符串
# ---------------------------------------------------------------------------
# 图节点:每个节点接收 state,返回要写回 state 的更新
# ---------------------------------------------------------------------------
def _build_llm():
"""构建 LLM(可从环境变量读取 API Key)。"""
llm = ChatOpenAI(
model=model,
api_key=dashscope_api_key,
base_url=dashscope_base_url,
temperature=0.7, # 可根据需要调整,0 更确定,1 更随机
# max_tokens=1000, # 可选:限制最大输出长度
# timeout=30, # 可选:设置超时时间
)
return llm
def node_extract(state: ChainState) -> dict:
"""
节点一:从原始文本中提取技术规格描述。
仅负责「提取」,不负责格式化,降低单步认知负担。
"""
prompt = ChatPromptTemplate.from_messages([
("user", "从以下文本中提取技术规格信息,用自然语言概括即可:\n\n{text_input}"),
])
llm = _build_llm()
chain = prompt | llm | StrOutputParser()
specifications = chain.invoke({"text_input": state["text_input"]})
return {"specifications": specifications}
def node_transform(state: ChainState) -> dict:
"""
节点二:将规格描述转换为带 cpu / memory / storage 的 JSON。
依赖上一节点的 specifications,实现「链式」传递。
"""
prompt = ChatPromptTemplate.from_messages([
("user", "将以下规格说明整理为 JSON 对象,仅包含键:cpu、memory、storage。"
"只输出 JSON,不要其他说明。\n\n{specifications}"),
])
llm = _build_llm()
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"specifications": state["specifications"]})
return {"result": result}
# ---------------------------------------------------------------------------
# 构建图:线性链 extract → transform
# ---------------------------------------------------------------------------
def build_prompt_chain_graph():
"""构建并编译 Prompt Chaining 图。"""
workflow = StateGraph(ChainState)
workflow.add_node("extract", node_extract)
workflow.add_node("transform", node_transform)
workflow.add_edge(START, "extract")
workflow.add_edge("extract", "transform")
workflow.add_edge("transform", END)
return workflow.compile()
# ---------------------------------------------------------------------------
# 入口:本地运行示例
# ---------------------------------------------------------------------------
if __name__ == "__main__":
# 可从 .env 加载 OPENAI_API_KEY
graph = build_prompt_chain_graph()
input_text = (
"新款笔记本配备 3.5 GHz 八核处理器、16GB 内存和 1TB NVMe 固态硬盘。"
)
initial_state: ChainState = {
"text_input": input_text,
"specifications": "",
"result": "",
}
final_state = graph.invoke(initial_state)
print("\n--- 阶段一:提取的规格 ---")
print(final_state.get("specifications", ""))
print("\n--- 阶段二:最终 JSON ---")
print(final_state.get("result", ""))
打印输出:
from dotenv import load_dotenv
from chain import build_prompt_chain_graph
from chain import ChainState
load_dotenv()
graph = build_prompt_chain_graph()
from IPython.display import Image, display
display(Image(graph.get_graph(xray=True).draw_mermaid_png()))
当然,这是一个含简单的例子,但是我们从这里开始了解 LangGraph。
6 Context Engineering 与 Prompt Engineering 的关系
Context Engineering (上下文工程)强调:在模型真正生成内容之前,为它构建完整、结构化的信息环境------包括系统提示、检索到的文档、工具调用结果、用户身份与历史等。输出质量往往更取决于「给了什么上下文」,而不仅是「问了哪一句话」。
Prompt Chaining 可以看作是 Context Engineering 在「多步流程」上的体现:
- 每一步的 prompt 负责把「当前该知道的信息」组织好(上一步结果、当前角色、格式要求等);
- 链的编排则决定了「何时、以何种顺序」把哪些信息注入到哪一步。
因此,在设计链时,除了写好单步提示,也要考虑:每一步的输入上下文是否足够、是否结构化(例如上一步的 JSON),以及是否需要在链中插入检索、工具或人工校验。
💡 理解要点:链中每一步都是在为模型构造「当前步骤的上下文」;把链设计好,就是在做多步的 Context Engineering。
7 小结与使用建议
- What:Prompt Chaining 把复杂任务拆成多步小任务,每步一个专注的提示,上一步输出作为下一步输入,形成链(或管道)。
- Why:单一大提示在多目标、多步骤任务上容易指令遗漏、上下文漂移和错误放大;链式分解能提高可靠性、可调试性和可扩展性。
- When:任务过于复杂、包含多个明显阶段、需要在步骤间调用工具或做校验、或你要做多步推理/状态维护时,都可以考虑 Prompt Chaining。
- How(本文) :用 LangGraph 的
StateGraph定义状态和线性链(两个节点:提取 → 转换),状态在节点间自动传递;完整示例见demo_codes/。
关键要点:
- 链的可靠性依赖步骤间数据的结构化(如 JSON);
- LangGraph 用图与状态清晰表达「谁先谁后、传什么」;
- 掌握 Prompt Chaining 是构建多步 Agent、复杂工作流的基础。