模型回答“看起来正确”不等于程序可以直接使用。真正的工程问题是:怎样把 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 问答机器人。

Logo

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

更多推荐