← 返回首页
LangChain输出解析器
发表时间:2025-05-15 07:53:55
LangChain输出解析器

1.什么是OutputParser输出解析器

Langchain中的OutputParser是用于解析LLM(大语言模型)返回的文本结果的组件。因为大模型通常会返回非结构化的自然语言,而在实际应用中,我们需要从这些文本中提取出特定的信息并进行后续处理。

“非结构化的自然语言”时,我们指的是由人以自由形式编写的文字,这些文字没有遵循任何特定的数据格式或规则。这种类型的文本信息包含了人类日常交流中使用的所有复杂性和变化性,包括但不限于语法、语义、上下文含义等。由于缺乏预定义的格式,这类文本对于计算机处理来说往往比较困难。

因此,OutputParser的作用就是将模型的输出解析成可编程的结构化数据,如列表、字典或特定的Python对象。假设有一个大语言模型被用来回答用户的问题:“北京的天气怎么样?” 模型可能返回以下的回答:

“北京今天天气晴朗,气温适中,建议外出活动。” “目前北京的天气非常好,阳光明媚,温度大约在20度左右。”

这两个回答虽然提供了相似的信息,但是表达方式不同,而且没有遵循固定的格式。如果应用程序需要从中提取具体的天气数据(比如温度、天气状况),就需要一个OutputParser来解析这段文本,并将其转换为结构化的格式,例如以下JSON对象:

{
 "city": "北京",
 "weather": "晴朗",
 "temperature": "20度",
 "suggestion": "建议外出活动"
}

通过OutputParser,开发者可以方便地将LLM生成的内容转换为更适合编程的形式,使得与大模型的交互更加高效且可靠。

常见的OutputParser解析器有以下几种: - 列表解析器 - 时间解析器 - 枚举解析器 - 结构化输出解析器 - Pydantic(JSON)解析器 - 自动修复解析器 - 重试解析器

2.输出解析器详解

2.1 列表解析器

列表解析器用于将LLM输出的文本解析为Python列表格式。比如,当LLM返回一个包含项目的自然语言段落时,我们可以通过列表解析器将其转换为Python的list类型,以便于后续处理。

from langchain_openai import ChatOpenAI


chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import PromptTemplate

# 实例化:逗号分隔的列表解析器
parser = CommaSeparatedListOutputParser()
prompt = PromptTemplate(
    template="回答用户查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)

print("format_instructions: ", parser.get_format_instructions())
formatted_prompt = prompt.format(query="列出五种水果。")

print("formatted_prompt:", formatted_prompt)

# 生成响应
response = chat_model.invoke(formatted_prompt).content
print(response)

# 解析响应
output = parser.parse(response)
print("output:", output)

运行效果:

format_instructions:  Your response should be a list of comma separated values, eg: `foo, bar, baz` or `foo,bar,baz`
formatted_prompt: 回答用户查询。
Your response should be a list of comma separated values, eg: `foo, bar, baz` or `foo,bar,baz`
列出五种水果。请直接输出水果列表。

ChatCompletion(id='0196d32cd15038b3917daafa32681735', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='苹果,香蕉,橙子,草莓,梨', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1747299651, model='Qwen/Qwen2.5-7B-Instruct', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=10, prompt_tokens=37, total_tokens=47, completion_tokens_details=None, prompt_tokens_details=None))
苹果,香蕉,橙子,草莓,梨
output: ['苹果', '香蕉', '橙子', '草莓', '梨']

2.2 时间解析器

datetime解析器用于解析包含日期和时间的LLM输出。它可以将LLM返回的时间字符串转化为Python的datetime对象,从而便于进一步的时间计算和操作。

from langchain_openai import ChatOpenAI

chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from langchain.output_parsers import DatetimeOutputParser
from langchain_core.prompts import PromptTemplate

parser = DatetimeOutputParser()
prompt = PromptTemplate(
    template="回答用户查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)
print("format_instructions:", parser.get_format_instructions())

formatted_prompt = prompt.format(query="北京奥运会是什么时候开幕的?")

response = chat_model.invoke(formatted_prompt).content
print(response)
output = parser.parse(response)
print("output:", output)

运行效果:

format_instructions: Write a datetime string that matches the following pattern: '%Y-%m-%dT%H:%M:%S.%fZ'.

Examples: 1189-12-02T12:14:23.360603Z, 1032-04-19T13:29:16.874786Z, 1010-07-30T21:58:36.514057Z

Return ONLY this string, no other words!
ChatCompletion(id='0196d3254394ceb8a95ec6d292223a85', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='2008-08-08T20:00:00.000Z', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1747299156, model='Qwen/Qwen2.5-7B-Instruct', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=24, prompt_tokens=45, total_tokens=69, completion_tokens_details=None, prompt_tokens_details=None))
2008-08-08T20:00:00.000Z
output: 2008-08-08 20:00:00

2.3 枚举解析器

枚举解析器用于将LLM的输出解析为预定义的枚举值。它通常用于限定LLM输出的范围,并确保返回的结果符合特定的枚举选项。

from langchain_openai import ChatOpenAI

chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from enum import Enum
class Color(Enum):
    RED = "red"
    BLUE = "blue"
    YELLOW = "yellow"


from langchain.output_parsers import EnumOutputParser
from langchain_core.prompts import PromptTemplate

parser = EnumOutputParser(enum=Color)
prompt = PromptTemplate(
    template="回答用户查询,请直接输出颜色名称,如 'red', 'blue', 'yellow'。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)
print("format_instructions:", parser.get_format_instructions())
formatted_prompt = prompt.format(query="香蕉是什么颜色的?")

response = chat_model.invoke(formatted_prompt).content
print(response)
output = parser.parse(response)
print("output:", output)

运行效果:

format_instructions: Select one of the following options: red, blue, yellow
ChatCompletion(id='0196d33898088ebeab18944da447659b', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='yellow', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1747300423, model='Qwen/Qwen2.5-7B-Instruct', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=1, prompt_tokens=41, total_tokens=42, completion_tokens_details=None, prompt_tokens_details=None))
yellow
output: Color.YELLOW

2.4 结构化输出解析器

结构化输出解析器用于解析具有特定结构的LLM输出,如JSON、CSV等。它可以预定义输出的结构,并确保LLM的输出能够符合该结构化格式。

from langchain_openai import ChatOpenAI


chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from langchain.output_parsers import StructuredOutputParser, ResponseSchema
from langchain_core.prompts import PromptTemplate

# 定义响应模式
response_schemas = [
    ResponseSchema(name="event_name", description="事件名称"),
    ResponseSchema(name="date", description="时间")
]

parser = StructuredOutputParser(response_schemas=response_schemas)
prompt = PromptTemplate(
    template="回答用户查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)
print("format_instructions:", parser.get_format_instructions())
formatted_prompt = prompt.format(query="北京奥运会是什么时候开幕的?")
response = chat_model.invoke(formatted_prompt).content
print(response)
output = parser.parse(response)
print(output)

运行效果:

format_instructions: The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":

{
    "event_name": string  // 事件名称
    "date": string  // 时间
}

ChatCompletion(id='0196d343cdccb413d3ed1ebed541454a', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='```json\n{\n  "event_name": "北京奥运会",\n  "date": "2008-08-08"\n}\n```', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1747301158, model='Qwen/Qwen2.5-7B-Instruct', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=31, prompt_tokens=42, total_tokens=73, completion_tokens_details=None, prompt_tokens_details=None))

{
  "event_name": "北京奥运会",
  "date": "2008-08-08"
}

output: {'event_name': '北京奥运会', 'date': '2008-08-08'}

2.5 Pydantic(JSON)解析器

Pydantic解析器基于Pydantic库,用于将LLM的输出解析为JSON格式并转换为Pydantic模型。这种解析器特别适合需要将输出转化为特定的Python数据模型的场景,如处理API响应、数据库记录等。假设有一个语言模型,该模型生成的输出是一个 JSON 字符串,表示用户的个人信息。使用 PydanticOutputParser 来解析这个 JSON 字符串并验证其格式。当模型输出为:'{"id": 1, "name": "Alice", "email":"alice@example.com"}'时,被解析后:id=1 name='Alice'email='alice@example.com' is_active=True 。

注意:Pydantic解析可能会出错。

from langchain_openai import ChatOpenAI

chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field

# 定义输出模型
class EventDetails(BaseModel):
    event_name: str = Field(description="时间名称")
    date: str = Field(description="日期")

parser = PydanticOutputParser(pydantic_object=EventDetails)
prompt = PromptTemplate(
    template="回答用户查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)
# print("format_cinstrutions:", parser.get_format_instructions())
formatted_prompt = prompt.format(query="北京奥运会是什么时候开幕的?")
response = chat_model.invoke(formatted_prompt).content
print(response)
output = parser.parse(response)
print("output:", output)

运行效果:

ChatCompletion(id='0196d34cc9970bf4fb28e3b1b8f7a839', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='```json\n{\n  "event_name": "北京奥运会",\n  "date": "2008-08-08"\n}\n```', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1747301747, model='Qwen/Qwen2.5-7B-Instruct', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=31, prompt_tokens=42, total_tokens=73, completion_tokens_details=None, prompt_tokens_details=None))

{
  "event_name": "北京奥运会",
  "date": "2008-08-08"
}

output: event_name='北京奥运会' date='2008-08-08'

2.6 自动修复解析器

自动修复解析器用于在LLM输出不完全符合预期格式时,自动尝试修复输出,并返回可解析的结果。

工作机制: 1. 接收模型的初始输出。 2. 如果输出格式不正确或不完全符合要求,使用一个“修复工具”(通常是另一个语言模型或自定义函数)对输出进行修正。 3. 修复工具尝试尽可能从错误输出中提取有效信息并生成符合要求的格式。

适用场景: - 输出有一定的错误或不完整,但可以通过后处理来纠正。 - 希望尽量避免完全重新生成,以节省生成时间或避免丢失有效信息。

优点: - 更适合处理轻微错误(如格式问题、小的字段缺失等)。 - 修复过程通常比重新生成更快。

from langchain_openai import ChatOpenAI

chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
class Person(BaseModel):
    name: str = Field(description="姓名")
    age: int = Field(description="年龄")

# 顶一个格式不正确的输出(注意:这个代表大模型的输出结果)
misformatted = "{'name': 'John', 'age': 30}"
parser = PydanticOutputParser(pydantic_object=Person)
try:
    output = parser.parse(misformatted)
    print("output:", output)
except Exception as e:
    print(e)


from langchain.output_parsers import OutputFixingParser
new_parser = OutputFixingParser.from_llm(parser=parser, llm=chat_model)
output = new_parser.parse(misformatted)
print("output:", output)

运行效果:

Invalid json output: {'name': 'John', 'age': 30}
For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE 
output: name='John' age=30

2.7重试解析器(需要用强一点的模型)

重试解析器用于在第一次解析失败时,自动重新尝试解析LLM输出。它可以配置重新解析的次数,并在每次失败时根据不同的策略调整解析方式。

工作机制: 1. 接收模型的初始输出。 2. 如果初始输出解析失败(例如,不符合JSON格式或预期的schema),则触发重新生成。 3. 重新生成过程会传递错误信息到生成模型,让它改正之前的错误并生成符合预期的输出。

适用场景:

优点: - 能动态利用错误提示来提高生成的准确性。 - 支持多次重试。

from langchain_openai import ChatOpenAI


chat_model = ChatOpenAI(
    openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.siliconflow.cn/v1",
    model="Qwen/Qwen2.5-7B-Instruct"
)

from pydantic import BaseModel, Field
class Action(BaseModel):
    action: str = Field(description="要采取的操作")
    action_input: str = Field(description="输入的操作")

from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
parser = PydanticOutputParser(pydantic_object=Action)
prompt = PromptTemplate(
    template="回答用户查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    # 获取解析器的提示词
    partial_variables={"format_instructions": parser.get_format_instructions()}
)
prompt_value = prompt.format_prompt(query="黑悟空是谁?")

bad_response = '{"action":"search"}'

# 修复解析器
from langchain.output_parsers import OutputFixingParser
fix_parser = OutputFixingParser.from_llm(parser=parser, llm=chat_model, max_retries=1)
try:
    fix_result = fix_parser.parse(bad_response)
    print(fix_result)
except Exception as e:
    print("修复解析器发生错误:", e)

# 重试解析器
from langchain.output_parsers import RetryWithErrorOutputParser
retry_parser = RetryWithErrorOutputParser.from_llm(parser=parser, llm=chat_model, max_retries=1)
try:
    retry_result = retry_parser.parse_with_prompt(bad_response, prompt_value)
    print(retry_result)
except Exception as e:
    print(e)

以下是修复解析器与重试解析器的对比: