贫瘠之地

华北无浪漫,死海扬起帆
多少个夜晚,独自望着天

0%

LangChain 文档学习 No.4 - 输出解析器

列表解析器

返回逗号分隔的项目列表时,可以使用此输出解析器

解析器和 prompt

1
2
3
4
5
6
7
8
9
10
11
# 列表解析器
output_parser = CommaSeparatedListOutputParser()

# 列表解析器自带格式说明
format_instructions = output_parser.get_format_instructions()
# prompt template
prompt_template = PromptTemplate(
template="列举出五个{subject}.\n{format_instructions}",
input_variables=["subject"],
partial_variables={"format_instructions": format_instructions}
)

使用

1
2
3
4
5
6
7
# AI 模型
model = Azure.chat_model
output = model.predict(prompt_template.format(subject="棒棒糖口味"))
# 解析
print(output_parser.parse(output))

# >> ['草莓味', '葡萄味', '橙子味', '苹果味', '蓝莓味']

日期解析器

输出解析为日期时间格式

1
2
3
4
5
6
7
8
9
10
11
12
13
# 日期解析器
output_parser = DatetimeOutputParser()
template = """Answer the users question:

{question}

{format_instructions}"""

# prompt template
prompt_template = PromptTemplate.from_template(
template,
partial_variables={"format_instructions": output_parser.get_format_instructions()},
)

使用

1
2
3
4
5
6
# AI 模型
model = Azure.chat_model
chain = LLMChain(prompt=prompt, llm=model)
output = chain.run("around when was bitcoin founded?")
# 解析
print(output_parser.parse(output))

这里可能是因为使用了 AzureOpenAI,没法按照限制的格式输出,不太稳定

1
ValueError: time data 'Bitcoin was founded on January 3, 2009. The corresponding datetime string would be "2009-01-03T00:00:00.000000Z".' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

枚举解析器

枚举解析器用来解析枚举

定义枚举

1
2
3
4
5
6
class Colors(Enum):
WHITE = "白色"
RED = "红色"
GREEN = "绿色"
BLUE = "蓝色"
YELLOW = "黄色"

解析器和 prompt

1
2
3
4
5
6
parser = EnumOutputParser(enum=Colors)
prompt_template = PromptTemplate.from_template("选择一个你最喜欢的颜色\n{format_instructions}")
prompt = prompt_template.format(format_instructions=parser.get_format_instructions())
print(prompt)

# >> 选择一个你最喜欢的颜色\nSelect one of the following options: 白色, 红色, 绿色, 蓝色, 黄色

使用

1
2
3
4
5
model = Azure.chat_model
result = model.predict(prompt)
print(parser.parse(result))

# >> Colors.BLUE

结构化解析器

当需要返回多个字段时,可以使用这种输出解析器,Pydantic  JSON 解析器相比之下更强大

定义我们想要接收的响应结构

1
2
3
4
5
6
7
8
9
# 结构定义
response_schemas = [
ResponseSchema(name="answer", description="回答用户的问题"),
ResponseSchema(name="source", description="用于回答用户问题的来源,应该是一个网站"),
ResponseSchema(name="score", description="给自己的回答打分,满分 100", type="num")
]

# 创建解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

提示模板

1
2
3
4
5
6
7
8
9
# 生成的格式说明
format_instructions = output_parser.get_format_instructions()

# 提示模板
prompt_template = PromptTemplate(
template="尽可能回答用户的问题.\n{format_instructions}\n{question}",
input_variables=["question"],
partial_variables={"format_instructions": format_instructions}
)

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
model = Azure.chat_model
prompt = prompt.format(question="中国的首都是哪个城市?")
output = model.predict(prompt)
result = output_parser.parse(output)

# 类型
print(type(result))
print(type(result["answer"]))
print(type(result["source"]))
print(type(result["score"]))
# >> <class 'dict'>
# >> <class 'str'>
# >> <class 'str'>
# >> <class 'int'>

# 答案
print(result)
# >> {'answer': '中国的首都是北京。', 'source': 'https://zh.wikipedia.org/wiki/%E5%8C%97%E4%BA%AC', 'score': 100}

Pydantic  JSON 解析器

该输出解析器允许用户指定任意的 JSON 模式,并查询符合该模式的 JSON 输出

需要注意

大型语言模型是有漏洞的,必须使用具有足够容量的 LLM 来生成格式正确的 JSON 在 OpenAI 家族中,DaVinci 的能力可靠,但 Curie 的能力不足

使用 Pydantic 来声明数据模型;Pydantic 的。BaseModel 类似于 Python 的数据类,但具有真正的类型检查和强制转换功能

定义结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Define your desired data structure.
class Joke(BaseModel):
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")

# 您可以使用 Pydantic 轻松添加自定义验证逻辑
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field

# PydanticOutputParser 解析器
parser = PydanticOutputParser(pydantic_object=Joke)

定义模板

1
2
3
4
5
6
7
8
9
# And a query intented to prompt a language model to populate the data structure.
joke_query = "Tell me a joke."

# prompt template
prompt_template = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)

使用

1
2
3
4
5
prompt = prompt_template.format_prompt(query=joke_query)
output = model(_input.to_string())
print(parser.parse(prompt))

# >> Joke(setup='Why did the chicken cross the road?', punchline='To get to the other side!')

复合类型字段的示例

1
2
3
4
# Here's another example, but with a compound typed field.
class Actor(BaseModel):
name: str = Field(description="name of an actor")
film_names: List[str] = Field(description="list of names of films they starred in")

自动修复解析器

自动修复解析器包装另一个输出解析器,如果第一个解析器失败,它会调用另一个 LLM 来修复错误

但除了抛出错误,我们还可以做其他事情,例如我们可以将格式错误的输出和格式化的指令一起传递给模型,并要求它进行修复

定义结构

1
2
3
4
5
6
class Actor(BaseModel):
name: str = Field(description="name of an actor")
film_names: List[str] = Field(description="list of names of films they starred in")

# Pydantic 解析器
parser = PydanticOutputParser(pydantic_object=Actor)

错误语句解析

1
2
mis_formatted = "{'name': '汤姆汉克斯', 'film_names': ['阿甘正传']}"
parser.parse(mis_formatted)

上面的 JSON 格式是错误的(单引号),所以抛出异常

1
langchain.schema.output_parser.OutputParserException: Failed to parse Actor from completion {'name': '汤姆汉克斯', 'film_names': ['阿甘正传']}. Got: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

使用自动修复解析器包装解析器和 LLM

1
2
3
4
5
6
new_parser = OutputFixingParser.from_llm(parser=parser, llm=Azure.chat_model, max_retries=2)
result = new_parser.parse(mis_formatted)
print(result)

# >> name='Tom Hanks' film_names=['Forrest Gump']
# 呃,这里被 AI 修复成了英文

实现原理就是进行重试,将解析器的格式说明和错误内容交给 AI 重新进行处理

重试解析器

某些情况下查看输出就可以修复解析错误

但是例如输出不仅格式不正确,而且不完整则完全无法解析

假设定义结构和错误返回

1
2
3
4
5
6
class Action(BaseModel):
action: str = Field(description="action to take")
action_input: str = Field(description="input to the action")

# 错误返回并没有 action_input 的信息
bad_response = '{"action": "search"}'

如果我们使用 OutputFixingParser 也无法完整修复,因为 AI 并不清楚数据是什么(感觉可以理解为修复的更多是格式错误,无法修复内容的不完整)

1
2
3
4
5
fix_parser = OutputFixingParser.from_llm(parser=parser, llm=Azure.chat_model)
print(fix_parser.parse(bad_response))

# 修复结果依然是不完整的
# >> Action(action='search', action_input='')

此时就需要重试进行解决

使用 RetryOutputParser

1
2
3
4
5
6
7
# 创建 RetryWithErrorOutputParser
retry_parser = RetryWithErrorOutputParser.from_llm(
parser=parser, llm=Azure.chat_model
)
retry_parser.parse_with_prompt(bad_response, prompt_value)

# >> Action(action='search', action_input='who is leo di caprios gf?')

参考

输出解析器 (Output Parsers) | 🦜️🔗 Langchain