LangChain 学习实践(四):LCEL Chain 与 Output Parser,让大模型输出可用
模型回答“看起来正确”不等于程序可以直接使用。真正的工程问题是:怎样把 Prompt、模型调用和结果解析连接起来,并在字符串、列表、JSON 或 Pydantic 对象之间建立稳定边界?本篇用 Chain 和 Output Parser 给出答案。
阅读说明
本文基于本地 LangChain 学习代码与课程笔记整理。示例中的模型、文件路径和参数需要按实际环境调整;涉及 API Key 时只使用环境变量或占位符。
5 Chains
在LangChain中,Chains描述了将LLM与其他组件结合起来完成一个应用程序的过程。 针对上一小节的提示模版例子,zero-shot里面,我们可以用链来连接提示模版组件和模型,进而可以实现代码的更改,主要使用LCEL方法。
LCEL(Lang Chain Expression Language) 是一种声明式的方法,用于轻松组合链条。
LCEL的基本语法规则是使用|符号将不同的组件连接起来,形成一个链式结构。|符号类似于Unix的管道操作符,它将一个组件的输出作为下一个组件的输入,从而实现数据的传递和处理。
上一个组件的输出作为下一个组件的输入,输出和输入的类型必须保持一致,否则不能连接。
"""
演示
Chain链的用法
涉及到的API:
PromptTemplate
OllamaLLM
prompt | llm
"""
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
# 1.实例化模型
model = OllamaLLM(model="llama3.2:3b")
# 2.定义PromptTemplate提示词模板
template = "请给我讲个关于{name}的笑话吧"
prompt = PromptTemplate(
input_variables=["name"],
template=template
)
# 3.创建一条Chain链
chain = prompt | model
# 4.执行Chain链
result = chain.invoke("鞋子")
print(result)
print("*"*50)
当然了!一个关于鞋子的笑话是这样的:
为什么鞋子 Always 在追着你?
因为它总是在跟踪你的足prints(足印)!哈哈,hope 这让你觉得好笑!
**************************************************
如果你想将第一个模型输出的结果,直接作为第二个模型的输入,直接使用管道符, 代码如下:
"""
演示
Chain链的用法
涉及到的API:
PromptTemplate
OllamaLLM
prompt | llm
"""
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
# 1.实例化模型
model = OllamaLLM(model="llama3.2:3b")
# 2.定义PromptTemplate提示词模板
template = "请给我讲个关于{name}的笑话吧"
prompt = PromptTemplate(
input_variables=["name"],
template=template)
# 3.创建第一条Chain链
chain01 = prompt | model
# 4.执行Chain链
result = chain01.invoke(input="鞋子")
print(result)
print("*"*50)
# 5.创建第二条Chain链
prompt02 = PromptTemplate(
input_variables=["joke"],
template="帮我优化这个笑话:{joke},使它更有趣一些,面向儿童"
)
chain02 = prompt02 | model
# 6.连接两条Chain链
# all_chain = chain01 | chain02
all_chain = prompt | model | prompt02 | model
# 7.执行连接后的Chain链
result = all_chain.invoke(input="鞋子")
print(result)
当然可以!以下是一个关于鞋子的笑话:
为什么鞋子很沮丧?
答案是因为它总是觉得自己被踢走了!
哈哈,希望你觉得这道题有趣和幽默!
**************************************************
以下是优化的笑话:
**为什么鸡不应该穿鞋子?**
因为它已经有足够好的足部了!
(这个笑话简单明了,适合儿童)
**鞋子恋爱故事**
一只鞋子跟另一只鞋子开始了恋爱。他们都非常温暖地和谐地生活在一起,但是有一天,另一只鞋说:"我需要换脚!"
这把鞋很失落,因为它不想和另一只鞋分离。它担心自己会被其他鞋选中。
但幸运的是,它重新配对了,并找到了另一个爱人!现在,它的幸福更加稳定了。
(这个笑话可以让儿童用自己的想象力来理解)
建议:在分享笑话时,可以加入一些动画或声音效果,让孩子们更能参与到笑话中。
6 output_parsers
LLM 的输出是自然语言文本,但在应用开发中,经常需要将这些文本转换为结构化的数据格式,如列表、字典或对象。LangChain 输出解析器负责获取 LLM 的输出并将其转换为更合适的格式。
部分解析器如下:
| 解析器名称 | 核心功能 | 输出的 Python 类型 | 工业级应用场景 |
|---|---|---|---|
| StrOutputParser | 默认解析器。将 LLM 的输出直接解析为字符串。 | str | 只需要原始回答时(如问答任务、对话场景) |
| CommaSeparatedListParser | 将 LLM 输出的、用逗号分隔的文本解析为列表。 | list[str] | 列表/枚举型输出 |
| JsonOutputParser | 极其常用。将 LLM 输出的 JSON 字符串解析为 Python 字典。 | dict | 结构化 JSON 输出 |
| PydanticOutputParser | 极其常用。将 LLM 输出解析为预先定义的 Pydantic 对象,提供类型安全和数据验证。 | 自定义的 pydantic.BaseModel 对象 | 输出需要严格结构化(JSON-like)数据时 |
| DatetimeOutputParser | 从文本中智能地解析出日期和时间信息。 | datetime.datetime | 需要时间格式时 |
6.1 字符串解析器
StrOutputParser,最简单的解析器,用于提取模型返回的原始文本:
"""
演示
StrOutputParser 字符串解析器的用法
涉及到的API:
StrOutputParser
ChatPromptTemplate
ChatOpenAI # 这里使用ChatOpenAI调用qwen3.6-plus模型,返回的结果不是字符串,而是一个包含content属性的对象,需要使用StrOutputParser进行解析
OllamaLLM # 这里使用OllamaLLM调用qwen3:4b模型,返回的结果就是字符串,不需要解析
"""
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_ollama import OllamaLLM
import os
from dotenv import load_dotenv # 加载环境变量的库, 需要安装dotenv: pip install dotenv
# 0.加载环境变量,获取API密钥
load_dotenv()
# 1.实例化模型
# model = ChatOpenAI(
# base_url=os.environ.get('base_url',"https://dashscope.aliyuncs.com/compatible-mode/v1"),
# model="qwen3.6-plus",
# openai_api_key=os.environ.get('DASHSCOPE_API_KEY'),
# max_tokens=1000,
# temperature=0
# )
model = OllamaLLM(model="deepseek-r1:1.5b")
# 2.定义提示词模版
prompt_template = ChatPromptTemplate.from_template(
"解释{topic}是什么?回答控制20字以内")
# 3.创建简单链
chain1 = prompt_template | model
result = chain1.invoke(input={"topic": "AI"})
print(f"result1-->{result}")
# 4.添加字符串解析器
parser = StrOutputParser()
chain2 = prompt_template | model | parser
result = chain2.invoke(input={"topic": "AI"})
print(f"result2-->{result}")
result1-->AI stands for Artificial Intelligence. It refers to systems that simulate human intelligence through data, algorithms, and optimization. Its applications aim to enhance productivity, personalization, and overall well-being.
result2-->人工智能(AI)是指计算机能够模仿和超越人类智能的技术或系统。
6.2 列表解析器
CommaSeparatedListOutputParser,将逗号分隔的文本转换为Python列表:
"""
演示
CommaSeparatedListOutputParser 列表解析器的使用
涉及到的API:
CommaSeparatedListOutputParser
ChatPromptTemplate
OllamaLLM
"""
from langchain_core.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 1.实例化模型
model = OllamaLLM(model="deepseek-r1:1.5b")
# 2.创建列表解析器
parser = CommaSeparatedListOutputParser()
# 3.定义提示词模版
format_instructions = parser.get_format_instructions()
prompt_template = ChatPromptTemplate.from_template(
"用中文列出{topic}的五个最重要特点?\n{format_instructions}")
# 4.创建链
print("未使用解析器:")
chain1 = prompt_template | model
# 5.调用链
result = chain1.invoke(
input={
"topic": "AI",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result->{result}")
print("*"*50)
# 6.添加列表解析器
print("使用列表解析器:")
chain2 = prompt_template | model | parser
# 7.调用链
result = chain2.invoke(
input={
"topic": "AI",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result->{result}")
未使用解析器:
<class 'str'>
result->人工智能的五个最重要的特点:
1. 智能化
2. 自主性
3. 个性化
4. 自我学习
5. 创新
**************************************************
使用列表解析器:
<class 'list'>
result->['数量、影响、范围、核心']
6.3 JSON解析器
JsonOutputParser,将JSON格式文本转换为Python字典或列表:
"""
演示
JsonOutputParser JSON解析器的使用
涉及到的API:
JsonOutputParser
ChatPromptTemplate
OllamaLLM
"""
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 1.实例化模型
model = OllamaLLM(model="deepseek-r1:1.5b")
# 2.创建JSON解析器
parser = JsonOutputParser()
# 3.创建提示词模版
format_instructions = parser.get_format_instructions()
prompt_template = ChatPromptTemplate.from_template(
"生成一个包含{person}基本信息的JSON。包括姓名、职业、年龄、兴趣和技能列表,不要包含任何额外内容。\n{format_instructions}")
# 4.创建简单链
print("未使用解析器:")
chain1 = prompt_template | model
# 5.调用链
result = chain1.invoke(
input={
"person": "猪八戒",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result1->{result}")
print("*"*50)
# 6.添加JSON解析器
print("使用JSON解析器:")
chain2 = prompt_template | model | parser
result = chain2.invoke(
input={
"person": "猪八戒",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result2->{result}")
未使用解析器:
<class 'str'>
result1->{
"姓名": "猪八戒",
"职业": "大慈大悲天师",
"年龄": "20到30岁之间",
"兴趣": {
"天道之法": "了解和掌握天命规律的智慧",
"武艺技巧": "精研各种武学技能和动作,
"战斗技巧": "精通各类战斗场景中的策略,
"心理应对": "心理素质强,能够有效应对挑战"
},
"技能列表": [
{
"天道之法": "掌握多种神秘教义和神话传说的知识",
"武艺技巧": "熟练使用各种武器、招式和动作",
"战斗技巧": "擅长在敌人困境中运用战术,
"心理应对": "具备强大的心理抗压能力"
},
{
"天道之法": "了解星象规律和社会习俗的信息",
"武艺技巧": "掌握多种自然法则的掌握,
"战斗技巧": "擅长利用环境变化来应变,
"心理应对": "能迅速判断局势并做出反应"
}
]
}
**************************************************
使用JSON解析器:
<class 'dict'>
result2->{'姓名': '猪八戒', '职业': '贪治module', '年龄': 108, '兴趣': ['聪明机警', '谋略高明'], '技能': ['机智', '处理矛盾', '沟通协调', '危机处理']}
6.4 Pydantic解析器
PydanticOutputParser,使用Pydantic模型定义输出结构:
"""
演示
PydanticOutputParser Pydantic表格解析器的使用
涉及到的API:
PydanticOutputParser
ChatPromptTemplate
OllamaLLM
"""
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
from pydantic import BaseModel, Field
from typing import List
# 1.实例化模型
model = OllamaLLM(model="deepseek-r1:1.5b")
# 2.创建Pydantic解析器
# 定义Pydantic模型
class Movie(BaseModel):
title: str = Field(description="电影标题")
director: str = Field(description="导演")
year: int = Field(description="上映年份")
genre: List[str] = Field(description="电影类型")
score: float = Field(description="评分(1-10)")
parser = PydanticOutputParser(pydantic_object= Movie)
# 3.创建提示词模版
format_instructions = parser.get_format_instructions()
prompt_template = ChatPromptTemplate.from_template(
"生成一部{genre}电影的信息。\n{format_instructions}")
# 4.创建简单链
print("未使用解析器:")
chain1 = prompt_template | model
# 5.调用链
result = chain1.invoke(
input={
"genre": "科幻",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result1->{result}")
print("*"*50)
# 5.添加Pydantic解析器
chain2 = prompt_template | model | parser
result = chain2.invoke(
input={
"genre": "科幻",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result2->{result}")
未使用解析器:
<class 'str'>
result1->```json
{
"properties": {
"title": {
"description": "电影标题",
"title": "Title",
"type": "string"
},
"director": {
"description": "导演",
"title": "Director",
"type": "string"
},
"year": {
"description": "上映年份",
"title": "Year",
"type": "integer"
},
"genre": {
"description": "电影类型",
"items": {
"type": "string"
},
"title": "Genre",
"type": "array"
},
"score": {
"description": "评分(1-10)",
"title": "Score",
"type": "number"
}
},
"required": ["title", "director", "year", "genre", "score"]
}
```
**************************************************
<class '__main__.Movie'>
result2->title='星际探索者:文明之光' director='星辰集团' year=2039 genre=['科幻', '太空探险'] score=8.5
6.5 自定义解析器
创建自定义输出解析器
"""
演示
CustomizeOutputParser 自定义解析器的使用
涉及到的API:
BaseOutputParser
ChatPromptTemplate
OllamaLLM
"""
from langchain_core.output_parsers import BaseOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
from typing import Dict, Any
# 1.实例化模型
model = OllamaLLM(model="deepseek-r1:1.5b")
# 2.创建自定义解析器
class CustomKeyValueParser(BaseOutputParser[Dict[str, Any]]):
"""自定义解析器,将输出解析为键值对格式"""
def parse(self, text: str) -> Dict[str, Any]:
"""从文本中解析键值对"""
result = {}
lines = text.strip().split('\n')
# 遍历行,解析键值对
for line in lines:
if ':' in line:
key, value = line.split(':', 1)
result[key.strip()] = value.strip()
return result
def get_format_instructions(self) -> str:
"""说明模型输出格式"""
return """请以'键: 值'的格式返回信息,每行一个键值对。
例如:
姓名: 张三
职业: AI大模型工程师
年龄: 30
兴趣: 编程、泡脚
技能: Python、Java、C++"""
parser = CustomKeyValueParser()
# 3.创建提示词模版
format_instructions = parser.get_format_instructions()
prompt_template = ChatPromptTemplate.from_template(
"提供{person}的基本信息。\n{format_instructions}")
# 4.创建简单链
print("未使用解析器:")
chain1 = prompt_template | model
# 5.调用链
result = chain1.invoke(
input={
"person": "孙悟空",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result1->{result}")
print("*"*50)
# 6.添加自定义解析器
print("使用解析器:")
chain2 = prompt_template | model | parser
# 7.调用链
result = chain2.invoke(
input={
"person": "孙悟空",
"format_instructions": format_instructions
}
)
print(type(result))
print(f"result2->{result}")
未使用解析器:
<class 'str'>
result1->{
"姓名": "孙悟空",
"职业": "西门子大圣 或 唐僧大闹天宫",
"年龄": 35,
"兴趣": [
"骑剑",
"听经",
"变强",
"打转筋骨",
"金甲虫",
"龙争斗天",
"吃猪八戒经"
],
"成就": [
"金甲虫",
"龙争斗天",
"西无处找东西",
"金箍棒",
"华龙 Telephony",
"真经",
"龙宫之歌《唐僧降魔界》",
"佛祖保佑",
"龙场舞",
"龙珠"
],
"特别之处": [
"骑剑技巧极强,能飞身冲。",
"听觉敏锐,听经时能听到经文声。",
"变强能力也非常突出。"
]
}
**************************************************
使用解析器:
<class 'dict'>
result2->{'"姓名"': '"孙悟空",', '"职业"': '"传说故事",', '"年龄"': '"67岁",', '"兴趣"': '"观察思考"', '能力': ''}
本篇小结
Chain 的价值是让步骤可组合、可定位;Parser 的价值是让自然语言输出变成业务可消费的数据。下一篇用这些组件构建完整 RAG 问答机器人。
更多推荐




所有评论(0)