前言:
很多人做 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 模板、结构化输出。
这三步吃透,你已经能做绝大多数落地场景的大模型应用了。

如果文章对你有帮助,欢迎点赞收藏,有问题评论区交流。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐