LangChain 1.0 实战进阶:Prompt 模板 + 结构化输出,搞定大模型应用的输入与输出
前言:
很多人做 LLM 应用,永远停留在「调个 API 问问题」的玩具阶段。
一问为什么落不了地,理由无非两个:要么 Prompt 写得乱七八糟,模型输出时好时坏全靠运气;要么返回的是一坨自然语言,程序没法直接用,接不了业务流程。
说白了,大模型应用开发,核心就两件事:管好用什么喂给模型,管好模型吐出来什么。
前者靠 Prompt 模板体系,后者靠结构化输出。
这两步走通了,你做的东西才能从「聊天玩具」变成「能干活的业务系统」。
这篇是 LangChain 系列第二篇,我把 Prompt 模板体系、提示词编写原则、三类 Output Parser、四个完整实战案例一次性讲透。
全是可直接落地的代码,跟着敲完,你就能写出真正可维护、可对接业务的大模型应用。
一、Prompt 模板:别再用字符串拼接写提示词了
新手最常见的写法:
prompt = f"你是客服,用户问了{question},订单状态是{status},你回复一下"
短的时候看着没问题,等 Prompt 写到几十上百行,变量十几个,维护起来就是灾难。
改个规则要翻半天代码,多个地方复用还要复制粘贴,变量拼错了排查都难。
LangChain 的 Prompt 模板,核心解决的就是这件事:把固定规则和动态变量拆开,统一管理,可复用可维护。
1. 两类模板,别用错了
PromptTemplate:纯文本模板
最简单的模板,生成一段完整字符串,没有角色概念。
适合简单的单轮文本任务,比如生成广告语、写摘要。
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template(
"请为商品 {product_name} 写一句广告语,突出卖点:{selling_points}"
)
prompt = prompt_template.invoke({
"product_name": "无线鼠标",
"selling_points": "静音、续航长、轻便"
})
print(prompt.text)
ChatPromptTemplate:对话消息模板【日常开发优先用这个】
这是现代大模型的标准用法,生成结构化的消息列表,原生支持 System、Human、AI 三种角色。
所有聊天模型、Agent、智能体场景,全用它。
from langchain_core.prompts import ChatPromptTemplate
chat_template = ChatPromptTemplate.from_messages([
("system", "你是一名电商运营专家,回答要简洁、有吸引力。"),
("human", "请为商品 {product_name} 写一段文案,卖点是:{selling_points}")
])
prompt = chat_template.invoke({
"product_name": "无线鼠标",
"selling_points": "静音、续航长、轻便"
})
一句话选型建议:
纯文本生成、简单任务用 PromptTemplate;
只要涉及角色设定、多轮对话、智能体,一律 ChatPromptTemplate。
90% 的业务场景,你都会用后者。
2. 高阶技巧:MessagesPlaceholder 给历史消息留位置
做多轮对话的时候,你不可能把历史消息硬写在模板里,这时候就用 MessagesPlaceholder。
它相当于在模板里挖一个坑,运行时把一整段历史消息列表塞进去。
完整用法:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个问答助手,回答简洁准确。"),
# 预留历史消息位置
MessagesPlaceholder(variable_name="history", optional=True, n_messages=6),
("human", "{question}")
])
三个核心参数,直接记结论:
variable_name:变量名,调用时传入的消息列表要和它对应optional=True:允许不传历史消息,首轮对话不会报错,必加n_messages=6:只保留最近 6 条消息,自动截断,控制上下文长度
调用示例:
prompt = chat_prompt.invoke({
"history": [
("human", "我叫张三"),
("ai", "你好张三,有什么可以帮你?"),
("human", "我学Python"),
("ai", "Python是很好的编程语言。")
],
"question": "我叫什么名字?"
})
实战经验:
别自己手动维护历史切片,直接用n_messages参数,模板层自动帮你截断。
代码更干净,也不容易出bug。
3. 写好 Prompt 的四条黄金原则
很多人总问「为什么我的 Prompt 不听话」,基本都是这四点没做到。
① 先定角色
第一句就告诉模型它是谁:电商专家、客服、编程老师。
角色定了,语气、风格、知识边界自然就有了。
② 明确任务
别写「帮我处理一下这段文字」,要写「从这段简历里提取姓名、工作年限、技能列表」。
任务越具体,输出越可控。
③ 写清约束
字数限制、不能说什么、输出格式、适用人群,都列清楚。
但别堆太多,核心约束 3-5 条足够,多了模型顾此失彼。
④ 输入字段结构化
别把变量揉成一团扔给模型,分条列清楚:
商品名称:{product_name}
商品卖点:{selling_points}
目标用户:{target_user}
模型越清楚每个变量是什么,出错概率越低。
4. 工程化优化:封装模型初始化,别重复写代码
每个案例都写一遍模型初始化,既啰嗦又不好维护。
抽成工具函数,全局复用:
新建 utils/model_factory.py:
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
def get_deepseek_model(temperature: float = 0.7):
load_dotenv()
model = init_chat_model(
model="deepseek-v4-flash",
model_provider="openai",
base_url=os.getenv("DEEPSEEK_BASE_URL"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=temperature
)
return model
后面所有案例,一行搞定:
from utils.model_factory import get_deepseek_model
model = get_deepseek_model(temperature=0.5)
以后换模型、改配置,只改这一个文件就行。
5. 三个经典业务场景实战
案例1:商品文案生成器
from langchain_core.prompts import ChatPromptTemplate
from utils.model_factory import get_deepseek_model
model = get_deepseek_model(0.5)
prompt_str = """
请根据下面信息生成一段商品详情页文案。
商品名称:{product_name}
商品卖点:{selling_points}
目标用户:{target_user}
要求:
1. 不超过 150 字
2. 突出用户能获得的好处
3. 不要使用虚假夸张表达
"""
chat_prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一名电商运营专家,擅长写自然、有吸引力但不过度夸张的商品文案"),
("human", prompt_str)
])
prompt = chat_prompt_template.invoke({
"product_name": "无线静音鼠标",
"selling_points": "静音按键、蓝牙连接、续航 30 天、轻便便携",
"target_user": "经常在办公室和图书馆使用电脑的人",
})
response = model.invoke(prompt)
print(response.content)
案例2:个性化学习计划生成器
from langchain_core.prompts import ChatPromptTemplate
from utils.model_factory import get_deepseek_model
model = get_deepseek_model(0.5)
prompt_str = """
请为我制定一个学习计划。
学习主题:{topic}
学习时长:{days} 天
每天可学习时间:{hours_per_day} 小时
当前基础:{level}
要求:
1. 按天列出学习内容
2. 每天给出练习任务
3. 内容要适合当前基础
"""
chat_prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一名编程课程规划老师,擅长为初学者制定可执行的学习计划。"),
("human", prompt_str)
])
prompt = chat_prompt_template.invoke({
"topic": "langchain",
"days": "3",
"hours_per_day": "1",
"level": "会python基础"
})
response = model.invoke(prompt)
print(response.content)
案例3:智能客服回复生成器
这个最贴近真实业务:用户问题来自前端,订单状态来自数据库,物流信息来自第三方接口,Prompt 负责组装所有信息生成标准回复。
from langchain_core.prompts import ChatPromptTemplate
from utils.model_factory import get_deepseek_model
model = get_deepseek_model(0.7)
prompt_str = """
请根据用户问题和订单状态,生成一段客服回复。
用户问题:{user_question}
订单状态:{order_status}
物流信息:{shipping_info}
要求:
1. 先安抚用户
2. 再说明当前情况
3. 最后给出下一步建议
4. 不超过 120 字
"""
chat_prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一名电商客服,回复要礼貌、明确,不推卸责任。"),
("human", prompt_str)
])
prompt = chat_prompt_template.invoke({
"user_question": "我的快递三天没更新了,是不是丢了?",
"order_status": "已发货",
"shipping_info": "物流显示包裹已到达上海转运中心,暂未更新下一站信息"
})
response = model.invoke(prompt)
print(response.content)
二、结构化输出:让模型的结果能直接进业务系统
Prompt 写得再好,模型返回的是一大段自然语言,程序还是没法直接用。
你总不能每次都用正则去抠字段吧?改一次 Prompt 正则就崩了。
结构化输出解决的就是这个问题:让模型严格按照你定义的字段和类型返回数据,程序可以直接读取、入库、调用接口。
信息抽取、文本分类、情感分析、工单分派,所有需要对接业务逻辑的场景,都离不开它。
1. Output Parser 是什么
简单理解就是模型输出的转换器:
模型原始输出 → Output Parser → 程序可用的数据
LangChain 里常用的三种,我按实用度排序:
| 解析器 | 作用 | 适用场景 |
|---|---|---|
StrOutputParser |
转成普通字符串 | 文案生成、问答、总结等纯文本场景 |
with_structured_output |
直接返回 Pydantic 对象 | 模型支持JSON模式时首选,写法最简洁 |
PydanticOutputParser |
手动解析JSON为Pydantic对象 | 需要理解底层逻辑、兼容不支持结构化的模型 |
2. StrOutputParser:别小看它,链式调用必备
很多人问:我直接 response.content 不就能拿到字符串吗,还要它干嘛?
答案是:为了适配 LangChain 的 Runnable 管道协议。
LangChain 推荐用 | 拼接组件形成链路,管道里的每个组件都必须是 Runnable 对象。.content 是属性,不能放进管道;StrOutputParser 是标准解析器,可以。
标准链式写法:
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
# 模板 → 模型 → 解析器,一条链路搞定
chain = chat_prompt | model | parser
result = chain.invoke({"question": "介绍一下LangChain"})
print(result) # 直接就是字符串
记住:只要你打算用链式调用,输出文本就用 StrOutputParser,别自己取 .content。
统一接口,后面加组件、换模型都方便。
3. 核心:用 Pydantic 定义输出结构
要结构化,先得说清楚要什么结构。
Pydantic 就是干这个的:定义字段名、字段类型、字段含义,既是给模型看的规范,也是给程序用的校验规则。
比如提取简历信息:
from pydantic import BaseModel, Field
class ResumeInfo(BaseModel):
name: str = Field(description="候选人姓名")
years_of_experience: int = Field(description="工作年限")
skills: list[str] = Field(description="掌握的技术技能")
target_position: str = Field(description="目标岗位")
每个字段都有类型和描述,模型照着生成,程序照着校验,类型不对直接报错。
进阶约束:Literal 字面量枚举
分类任务里,字段只能是固定几个值,就用 Literal:
from typing import Literal
class ReviewAnalysis(BaseModel):
# 只能是正面/中性/负面三个值,其他值校验不通过
sentiment: Literal["正面", "负面", "中性"] = Field(description="情感分析结果")
keywords: list[str] = Field(description="评论关键词")
summary: str = Field(description="评论总结")
needs_reply: bool = Field(description="商家是否需要回复")
相当于枚举,彻底避免模型瞎输出奇怪的分类值。
4. 两种结构化输出方式,怎么选?
方式一:PydanticOutputParser【先学它,搞懂原理】
原理很简单:把格式要求塞进 Prompt 告诉模型,模型返回 JSON 字符串,Parser 再解析成 Pydantic 对象。
优点是兼容所有模型,缺点是要手动处理格式指令。
完整示例:简历信息抽取
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from utils.model_factory import get_deepseek_model
model = get_deepseek_model()
# 1. 定义结构
class ResumeInfo(BaseModel):
name: str = Field(description="姓名")
years_of_experience: int = Field(description="工作年限")
skills: list[str] = Field(description="掌握的技术技能")
target_position: str = Field(description="目标岗位")
# 2. 创建解析器
parser = PydanticOutputParser(pydantic_object=ResumeInfo)
# 3. 生成格式说明指令
format_instructions = parser.get_format_instructions()
# 4. 把格式指令放进系统提示
template = ChatPromptTemplate.from_messages([
("system", """
你是一名招聘信息分析助手。
请严格按照指定格式返回结果。
{format_instructions}
"""),
("human", "{resume_content}")
])
prompt = template.invoke({
"format_instructions": format_instructions,
"resume_content": "我叫张三,干大模型开发10年了,擅长python,langchain,fastapi,想找智能体开发的工作。"
})
response = model.invoke(prompt)
# 5. 解析结果
result = parser.invoke(response)
print(result.name)
print(result.skills)
print(result.target_position)
方式二:with_structured_output【项目首选,写法最简洁】
LangChain 封装的高级用法,直接把结构化能力绑定到模型上,不用自己管格式指令和解析。
DeepSeek 这类兼容 OpenAI 接口的模型,指定 method="json_mode" 即可。
完整示例:商品评论分析
from typing import Literal
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from utils.model_factory import get_deepseek_model
# 1. 定义结构
class ReviewAnalysis(BaseModel):
sentiment: Literal["正面", "负面", "中性"] = Field(description="情感分析的值")
keywords: list[str] = Field(description="评论中的关键词")
summary: str = Field(description="对评论的简短总结")
needs_reply: bool = Field(description="商家是否需要回复")
model = get_deepseek_model()
template = ChatPromptTemplate.from_messages([
("system", """你是专业商品评论分析助手,仅输出纯JSON,无任何多余文字:
1. 必须包含 sentiment、keywords、summary、needs_reply 四个字段;
2. sentiment 仅允许:正面 / 中性 / 负面;
3. needs_reply:有质量问题、严重不满设为true,好评设为false。
"""),
("human", "请分析下面的商品评论:\n{review}")
])
# 2. 绑定结构化输出
structured_model = model.with_structured_output(
ReviewAnalysis,
method="json_mode"
)
# 3. 直接调用,拿到的就是结构化对象
prompt = template.invoke({
"review": "鼠标手感不错,也很安静,但是用了两周滚轮就有异响。"
})
result = structured_model.invoke(prompt)
print(result.sentiment)
print(result.keywords)
print(result.needs_reply)
选型结论:
学习阶段先写一遍 PydanticOutputParser,搞清楚底层原理;
实际项目直接用 with_structured_output,代码简洁,维护成本低。
注意:DeepSeek 必须加method="json_mode",否则会报错。
5. 实战:客服工单自动分类
这就是典型的业务落地场景:用户提交问题,模型自动分类、定优先级,然后程序自动分派。
from typing import Literal
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from utils.model_factory import get_deepseek_model
from langchain_core.exceptions import OutputParserException
class TicketResult(BaseModel):
category: Literal["订单", "物流", "退款", "产品", "其他"] = Field(description="工单分类")
priority: Literal["低", "中", "高"] = Field(description="工单优先级")
reason: str = Field(description="分类原因")
model = get_deepseek_model(temperature=0)
parser = PydanticOutputParser(pydantic_object=TicketResult)
format_instructions = parser.get_format_instructions()
template = ChatPromptTemplate.from_messages([
("system", """你是一名客服工单分类助手。
请根据用户问题完成分类。
{format_instructions}"""),
("human", "用户问题:{question}")
])
prompt = template.invoke({
"format_instructions": format_instructions,
"question": "订单显示已签收,但我没有收到商品,请尽快处理。"
})
try:
response = model.invoke(prompt)
result = parser.invoke(response)
print("分类:", result.category)
print("优先级:", result.priority)
print("原因:", result.reason)
# 直接对接业务逻辑
if result.priority == "高":
print("转人工客服优先处理")
except OutputParserException as e:
print("解析失败,记录原始输出并重试")
print("错误:", e)
6. 必做:异常捕获,别完全信模型
大模型不是程序,没法保证 100% 输出正确格式。
重要业务一定要加异常捕获:
- 保存模型原始输出,方便排查问题
- 记录错误日志
- 必要时重试一次
- 极端情况转人工处理
经验之谈:信息抽取、分类这类确定性任务,temperature 直接设为 0。
创造力不重要,稳定才重要。
三、给新手的 5 条实战心法
1. Prompt 不是写作文,是写需求文档
别写一堆华丽的修饰词,就像给实习生派活一样:说清角色、任务、要求、输入输出。
越清晰,越不容易出错。
2. 模板和结构化要配套用
输入用 ChatPromptTemplate 管理,输出用 Pydantic 约束。
两端都标准化了,你的大模型应用才真正具备工程化维护的基础。
3. 能结构化的场景,绝对不要用正则抠
不要图省事写正则提取字段,改一次 Prompt 正则就废了。
一次把 Pydantic 结构定义好,一劳永逸。
4. 业务场景优先保证稳定,再谈效果
分类、抽取这类任务,温度往低调,规则写明确。
偶尔回答不精彩没关系,输出格式错了程序直接崩,那才是生产事故。
5. 不要迷信模型会百分百听话
所有模型输出都要有兜底方案:异常捕获、默认值、人工复核。
把大模型当一个不稳定的实习生用,留好容错空间,才是合格的工程思维。
最后
到这里,LangChain 最核心的基础三件套就讲完了:模型调用、Prompt 模板、结构化输出。
这三步吃透,你已经能做绝大多数落地场景的大模型应用了。
如果文章对你有帮助,欢迎点赞收藏,有问题评论区交流。
更多推荐

所有评论(0)