LangChain学习第一天
一、核心思想
提示词系统的本质是构建可复用、可维护、可扩展的输入输出闭环,而非简单的文本拼接。在 LangChain 中,所有能力都围绕「标准化输入→标准化调用→标准化输出」的核心链路展开:
三大核心组件
| 组件 | 核心作用 | 核心类 |
|---|---|---|
| Prompt | 定义输入结构与交互规则 | PromptTemplate/ChatPromptTemplate |
| Model | 对接大模型并控制生成行为 | ChatOpenAI/ChatAnthropic等 |
| Parser | 标准化模型输出,适配业务场景 | StrOutputParser/PydanticOutputParser |
完整执行链路(LCEL 核心)
原始变量 → PromptTemplate(填充) → 结构化输入 → Model(调用) → 原始输出 → Parser(解析) → 业务可用结果
抽象为 LCEL 表达式(LangChain 官方推荐):
chain = prompt | model | parser # 管道符串联,左到右执行
这条链路是所有 LangChain 应用的基础,支持并行执行、条件分支、错误处理等高级能力,是后续所有代码的核心框架。
二、PromptTemplate 详解(参数+场景+避坑)
PromptTemplate 是单轮文本交互的基础模板类,核心是「模板字符串 + 变量管理」,解决硬编码提示词的复用性问题。
1. 核心构造参数(全维度解析)
| 参数 | 类型 | 必选 | 作用 | 示例 |
|---|---|---|---|---|
template | str | 是 | 提示词模板,变量用 {变量名} 包裹 | "你是{domain}专家,回答:{question}" |
input_variables | list[str] | 否 | 声明必填变量(from_template 可自动识别) | ["domain", "question"] |
partial_variables | dict | 否 | 声明默认变量(固定值),format 时无需传入 | {"domain": "机器学习"} |
template_format | str | 否 | 模板解析格式,支持 f-string/jinja2(默认 f-string) | jinja2 |
validate_template | bool | 否 | 是否校验模板变量与 input_variables 一致性(默认 True) | False |
2. 基础使用方式(分场景)
方式1:手动实例化(精准控制)
from langchain.prompts import PromptTemplate
# 基础用法
prompt = PromptTemplate(
template="你是{domain}专家,请用{language}回答:{question}",
input_variables=["domain", "language", "question"],
validate_template=True # 开启变量校验,避免漏写
)
# 带默认值(partial_variables)
prompt_with_default = PromptTemplate(
template="你是{domain}专家,请用{language}回答:{question}",
input_variables=["language", "question"], # 仅声明必填项
partial_variables={"domain": "机器学习"} # 固定值
)
方式2:from_template(简化开发)
自动解析模板中的变量,无需手动声明 input_variables,推荐简单场景使用:
# 自动识别变量「topic」
prompt = PromptTemplate.from_template("请用通俗易懂的语言解释:{topic}")
print(prompt.input_variables) # 输出:['topic']
# 结合jinja2模板(支持条件、循环等高级语法)
jinja_prompt = PromptTemplate.from_template(
"""
{% if length == "short" %}
用1句话解释:{topic}
{% else %}
详细解释:{topic}
{% endif %}
""",
template_format="jinja2" # 指定解析格式
)
# 使用时传入条件变量
print(jinja_prompt.format(topic="Python", length="short"))
3. 核心方法(必掌握)
| 方法 | 作用 | 返回值 | 示例 |
|---|---|---|---|
format(**kwargs) | 填充变量,生成最终提示词字符串 | str | prompt.format(domain="AI", language="中文", question="什么是大模型") |
format_prompt(**kwargs) | 填充变量,返回 PromptValue 对象(兼容模型调用) | PromptValue | prompt_val = prompt.format_prompt(...) |
partial(**kwargs) | 动态设置默认值(替代 partial_variables) | PromptTemplate | prompt.partial(domain="深度学习") |
4. 避坑要点
-
变量名必须与模板中的
{变量名}完全一致(大小写敏感); -
使用
jinja2模板时,注意语法错误(如缺少{% endif %}); -
避免模板中出现多余的
{}(非变量),如需保留需转义:{{代表{,}}代表}。
三、ChatPromptTemplate(对话场景专用)
适用于多轮对话、带角色的交互场景(如 System/Human/AI 对话),核心区别是:
-
PromptTemplate输出字符串; -
ChatPromptTemplate输出消息对象列表(符合大模型的对话接口规范)。
1. 核心概念:Message 类型
LangChain 定义了标准化的消息类型,适配所有主流大模型的对话接口:
| 消息类型 | 作用 | 示例 |
|---|---|---|
SystemMessage | 定义模型角色、行为准则(优先级最高) | SystemMessage(content="你是专业的翻译助手,仅输出翻译结果") |
HumanMessage | 用户输入(人类消息) | HumanMessage(content="你好") |
AIMessage | 模型输出(AI 回复) | AIMessage(content="Hello") |
FunctionMessage | 工具调用结果(进阶) | FunctionMessage(name="search", content="搜索结果...") |
2. 构建方式(分场景)
方式1:快速构建(推荐简单对话)
from langchain_core.prompts import ChatPromptTemplate
# 直接定义角色+模板
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是{role},仅用{language}回答,答案不超过{length}个字"), # system角色
("human", "{question}"), # 用户输入
("ai", "示例回答:{example_answer}") # 示例(Few-shot)
])
# 填充变量,生成消息列表
messages = chat_prompt.format_messages(
role="数学老师",
language="中文",
length="50",
question="什么是勾股定理",
example_answer="勾股定理是直角三角形两直角边的平方和等于斜边的平方"
)
print(messages)
# 输出:[SystemMessage(content='你是数学老师...'), HumanMessage(content='什么是勾股定理'), AIMessage(content='示例回答:...')]
方式2:分模块构建(复杂项目/组件化)
适合将 System/Human 模板拆分管理,便于复用:
from langchain_core.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate
# 拆分构建System模板
system_template = SystemMessagePromptTemplate.from_template(
"你是{role},请遵循以下规则:{rules}"
)
# 拆分构建Human模板
human_template = HumanMessagePromptTemplate.from_template(
"问题:{question}\n要求:{requirements}"
)
# 组合为ChatPromptTemplate
chat_prompt = ChatPromptTemplate.from_messages([
system_template,
human_template
])
# 填充变量
messages = chat_prompt.format_messages(
role="客服",
rules="1. 语气友好 2. 回答简洁",
question="如何退款",
requirements="用1句话说明"
)
3. 核心方法
| 方法 | 作用 | 返回值 |
|---|---|---|
format_messages(**kwargs) | 填充变量,生成消息对象列表 | list[BaseMessage] |
format_prompt(**kwargs) | 填充变量,返回 ChatPromptValue 对象 | ChatPromptValue |
partial(**kwargs) | 动态设置默认变量 | ChatPromptTemplate |
四、Model 模型调用(ChatOpenAI 全参数解析)
ChatOpenAI 是对接 OpenAI 兼容接口的核心类(支持通义千问、讯飞星火等兼容 OpenAI 接口的模型),以下是全参数详解+最佳实践。
1. 核心导入与初始化
from langchain_openai import ChatOpenAI
from pydantic import SecretStr # 安全存储密钥
# 基础初始化(通义千问示例)
model = ChatOpenAI(
# 核心参数
model="qwen-plus", # 模型名称(不同平台不同:gpt-3.5-turbo/qwen-plus/ERNIE-4.0)
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 自定义接口地址
api_key=SecretStr("your-api-key"), # 密钥(用SecretStr避免日志泄露)
# 生成控制参数
temperature=0.7, # 随机性:0(最稳定)~2(最发散),推荐0.1~1.0
max_tokens=1024, # 最大生成token数(防止输出过长)
top_p=0.9, # 采样阈值:0~1,越小越聚焦
frequency_penalty=0.0, # 重复惩罚:0~2,越大越避免重复
presence_penalty=0.0, # 主题惩罚:0~2,越大越鼓励新主题
# 执行参数
streaming=False, # 是否流式输出(实时返回内容)
timeout=30, # 超时时间(秒)
max_retries=3, # 失败重试次数
# 高级参数
n=1, # 每次调用生成n个结果
stop=["\n\n"], # 停止符:生成到该字符时停止
)
2. 核心参数详解(必掌握)
| 参数 | 取值范围 | 核心作用 | 最佳实践 |
|---|---|---|---|
temperature | 0~2 | 控制生成随机性,值越高,回答越发散;值越低,回答越固定。 | 事实类问答:0.10.3;创作类:0.71.0 |
max_tokens | 正整数 | 限制模型输出的最大token数(输入+输出通常有总上限)。 | 根据业务场景设置(如客服:500,创作:2000) |
streaming | bool | 是否开启流式输出(逐字返回)。 | 前端交互场景开启,批量处理关闭 |
max_retries | 0~5 | 接口调用失败时的重试次数。 | 生产环境设置3~5次,避免网络波动导致失败 |
stop | list[str] | 生成停止符,模型遇到该字符立即停止输出。 | 如 ["###", "\n\n"],用于截断多余内容 |
3. 核心调用方法
| 方法 | 作用 | 使用场景 |
|---|---|---|
invoke(input) | 同步调用,返回完整结果 | 简单场景、批量处理 |
stream(input) | 流式调用,返回生成器(逐块返回) | 实时交互(如聊天界面) |
batch(inputs, batch_size=5) | 批量调用,处理多个输入 | 批量问答、数据处理 |
调用示例
# 1. 同步调用(字符串输入)
response = model.invoke("解释一下Python")
print(response.content) # 输出模型回答字符串
# 2. 同步调用(消息列表输入,推荐)
from langchain_core.messages import SystemMessage, HumanMessage
messages = [
SystemMessage(content="你是Python专家,回答简洁"),
HumanMessage(content="解释一下Python")
]
response = model.invoke(messages)
print(response.content)
# 3. 流式调用(实时输出)
model_stream = ChatOpenAI(
model="qwen-plus",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=SecretStr("your-api-key"),
streaming=True # 开启流式
)
for chunk in model_stream.stream("用100字介绍Python"):
print(chunk.content, end="", flush=True) # 逐字输出
五、OutputParser 输出解析(标准化输出)
模型返回的是 AIMessage 对象,OutputParser 用于将其转换为业务可用的格式(字符串/JSON/自定义结构)。
1. 基础解析器(必掌握)
(1)StrOutputParser:提取纯字符串
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
# 解析AIMessage对象
response = model.invoke("解释一下Python")
result = parser.invoke(response)
print(result) # 纯字符串结果
(2)JSONOutputParser:解析JSON格式输出
需在提示词中明确要求模型输出JSON,否则会解析失败:
from langchain_core.output_parsers import JsonOutputParser
# 1. 定义提示词(强制JSON输出)
prompt = PromptTemplate.from_template(
"""
分析以下产品名称:{product}
要求:
1. 输出JSON格式
2. 包含字段:category(类别)、brand(品牌)、price_range(价格区间)
"""
)
# 2. 初始化解析器
json_parser = JsonOutputParser()
# 3. 构建链并执行
chain = prompt | model | json_parser
result = chain.invoke({"product": "苹果iPhone 15 Pro Max"})
print(result) # 输出字典:{"category": "手机", "brand": "苹果", "price_range": "高端"}
(3)PydanticOutputParser:结构化解析(推荐)
通过 Pydantic 定义数据模型,强制模型输出符合结构的内容,支持自动校验:
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
# 1. 定义数据模型(Pydantic V2)
class ProductAnalysis(BaseModel):
category: str = Field(description="产品类别")
brand: str = Field(description="品牌名称")
price_range: str = Field(description="价格区间:低端/中端/高端")
tags: List[str] = Field(description="产品标签列表")
# 2. 初始化解析器
parser = PydanticOutputParser(pydantic_object=ProductAnalysis)
# 3. 定义提示词(包含解析器的格式说明)
prompt = PromptTemplate.from_template(
"""
分析以下产品:{product}
{format_instructions}
"""
)
# 4. 填充格式说明(解析器自动生成)
prompt = prompt.partial(format_instructions=parser.get_format_instructions())
# 5. 构建链并执行
chain = prompt | model | parser
result = chain.invoke({"product": "华为Mate 70 Pro"})
print(result.category) # 输出:手机
print(result.tags) # 输出:["鸿蒙系统", "高端", "拍照"]
2. 解析器异常处理
from langchain_core.output_parsers import OutputParserException
try:
result = chain.invoke({"product": "华为Mate 70 Pro"})
except OutputParserException as e:
print(f"解析失败:{e}")
# 降级处理:返回原始字符串
raw_result = StrOutputParser().invoke(e.raw_output)
print(f"原始输出:{raw_result}")
六、LCEL 链式表达(终极推荐)
LCEL(LangChain Expression Language)是 LangChain 最新的链式编程范式,核心是「管道符串联组件」,支持灵活扩展和高级操作。
1. 基础用法(核心链路)
# 1. 导入依赖
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from pydantic import SecretStr
# 2. 定义组件
prompt = ChatPromptTemplate.from_messages([
("system", "你是{role},回答简洁明了"),
("human", "{question}")
])
model = ChatOpenAI(
model="qwen-plus",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=SecretStr("your-api-key"),
temperature=0.3
)
parser = StrOutputParser()
# 3. 构建链(核心:管道符串联)
chain = prompt | model | parser
# 4. 执行链
result = chain.invoke({
"role": "Python工程师",
"question": "什么是装饰器"
})
print(result)
2. 高级用法(扩展能力)
(1)添加日志(中间件)
from langchain_core.runnables import RunnableLambda
# 定义日志函数
def log_input(inputs):
print(f"输入:{inputs}")
return inputs
def log_output(outputs):
print(f"输出:{outputs}")
return outputs
# 构建带日志的链
chain_with_log = (
RunnableLambda(log_input) # 输入日志
| prompt
| model
| parser
| RunnableLambda(log_output) # 输出日志
)
chain_with_log.invoke({"role": "测试", "question": "hello"})
(2)分支链(条件执行)
from langchain_core.runnables import RunnableBranch
# 定义不同场景的prompt
simple_prompt = ChatPromptTemplate.from_messages([("human", "简单回答:{question}")])
detailed_prompt = ChatPromptTemplate.from_messages([("human", "详细回答:{question}")])
# 定义分支规则:根据「detail_level」选择不同prompt
branch_chain = RunnableBranch(
(lambda x: x["detail_level"] == "simple", simple_prompt),
(lambda x: x["detail_level"] == "detailed", detailed_prompt),
simple_prompt # 默认分支
) | model | parser
# 执行不同分支
print(branch_chain.invoke({"question": "什么是Python", "detail_level": "simple"}))
print(branch_chain.invoke({"question": "什么是Python", "detail_level": "detailed"}))
(3)批量执行
# 批量输入
inputs = [
{"role": "数学老师", "question": "什么是质数"},
{"role": "语文老师", "question": "什么是文言文"}
]
# 批量执行(返回列表)
results = chain.batch(inputs, batch_size=2)
for res in results:
print(res)
七、传统链 LLMChain(兼容说明)
LLMChain 是 LangChain 早期的链式实现,现已被 LCEL 替代,仅作兼容说明:
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
prompt = PromptTemplate.from_template("你是{role},回答:{question}")
model = ChatOpenAI(model="qwen-plus", api_key=SecretStr("your-api-key"))
# 构建LLMChain
chain = LLMChain(prompt=prompt, llm=model)
# 执行
result = chain.invoke({"role": "医生", "question": "如何预防感冒"})
print(result["text"]) # 结果在「text」字段中
# 缺点:扩展性差,不支持流式/批量/分支等高级操作
八、最佳实践(生产环境规范)
1. 代码结构规范(可复用)
# langchain_demo.py
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from pydantic import SecretStr
import os
# 1. 配置抽离(环境变量/配置文件)
def get_model():
return ChatOpenAI(
model=os.getenv("LLM_MODEL", "qwen-plus"),
base_url=os.getenv("LLM_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
api_key=SecretStr(os.getenv("LLM_API_KEY")),
temperature=float(os.getenv("LLM_TEMPERATURE", 0.7)),
max_tokens=int(os.getenv("LLM_MAX_TOKENS", 1024)),
max_retries=3
)
# 2. 模板封装(组件化)
def get_chat_prompt(role: str = "通用助手"):
return ChatPromptTemplate.from_messages([
("system", f"你是{role},遵循以下规则:1. 回答准确 2. 语气友好 3. 拒绝敏感内容"),
("human", "{question}")
])
# 3. 链构建(统一入口)
def build_chain(role: str = "通用助手"):
prompt = get_chat_prompt(role)
model = get_model()
parser = StrOutputParser()
return prompt | model | parser
# 4. 业务调用
if __name__ == "__main__":
# 加载环境变量(推荐使用python-dotenv)
from dotenv import load_dotenv
load_dotenv() # 从.env文件加载配置
chain = build_chain(role="Python工程师")
result = chain.invoke({"question": "如何调试代码"})
print(result)
2. 环境变量配置(.env 文件)
# .env 文件(添加到.gitignore)
LLM_MODEL=qwen-plus
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=your-real-api-key
LLM_TEMPERATURE=0.3
LLM_MAX_TOKENS=1024