文章目录
www.aidoczh.com已经系统的将LangChain官网文档翻译成中文,您可以去http://www.aidoczh.com/langchain/v0.2/docs/introduction/ 查看文档,该网站www.aidoczh.com主要是将AI工具官方文档翻译成中文,还有其他工具文档可以看。
1. 语言模型输出格式化快速入门
语言模型输出文本。但很多时候,您可能希望获得比纯文本更有结构的信息。这就是输出解析器发挥作用的地方。
输出解析器是帮助构建语言模型响应结构的类。一个输出解析器必须实现两种主要方法:
- “获取格式说明”:返回一个包含语言模型输出应如何格式化的指令字符串的方法。
- “解析”:接受一个字符串(假定为语言模型的响应)并将其解析为某种结构的方法。
还有一个可选的方法:
- “带提示解析”:接受一个字符串(假定为语言模型的响应)和一个提示(假定为生成这种响应的提示)并将其解析为某种结构。提示主要是在输出解析器希望重新尝试或修复输出的情况下提供的,需要提示信息来执行此操作。
入门指南
下面我们将介绍主要类型的输出解析器,即 PydanticOutputParser
。
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import OpenAI
model = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.0)
# 定义您期望的数据结构。
class Joke(BaseModel):
setup: str = Field(description="设置笑话的问题")
punchline: str = Field(description="解答笑话的答案")
# 您可以使用 Pydantic 轻松添加自定义验证逻辑。
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("问题格式不正确!")
return field
# 设置解析器 + 将指令注入到提示模板中。
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="回答用户的查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# 以及一个旨在提示语言模型填充数据结构的查询。
prompt_and_model = prompt | model
output = prompt_and_model.invoke({"query": "告诉我一个笑话。"})
parser.invoke(output)
Joke(setup='为什么鸡会过马路?', punchline='为了到达另一边!')
LCEL
输出解析器实现了 Runnable 接口,这是 LangChain 表达语言 (LCEL) 的基本构建块。这意味着它们支持 invoke
、ainvoke
、stream
、astream
、batch
、abatch
、astream_log
调用。
输出解析器接受字符串或 BaseMessage
作为输入,并可以返回任意类型。
parser.invoke(output)
Joke(setup='为什么鸡会过马路?', punchline='为了到达另一边!')
除了手动调用解析器外,我们也可以将其添加到我们的 Runnable
序列中:
chain = prompt | model | parser
chain.invoke({"query": "告诉我一个笑话。"})
Joke(setup='为什么鸡会过马路?', punchline='为了到达另一边!')
虽然所有解析器都支持流接口,但只有某些解析器可以通过部分解析对象进行流式处理,因为这在很大程度上取决于输出类型。不能构建部分对象的解析器将简单地生成完全解析的输出。
例如,SimpleJsonOutputParser
可以通过部分输出进行流式处理:
from langchain.output_parsers.json import SimpleJsonOutputParser
json_prompt = PromptTemplate.from_template(
"返回一个带有 `answer` 键的 JSON 对象,回答以下问题:{question}"
)
json_parser = SimpleJsonOutputParser()
json_chain = json_prompt | model | json_parser
list(json_chain.stream({"question": "谁发明了显微镜?"}))
[{},
{'answer': ''},
{'answer': 'Ant'},
{'answer': 'Anton'},
{'answer': 'Antonie'},
{'answer': 'Antonie van'},
{'answer': 'Antonie van Lee'},
{'answer': 'Antonie van Leeu'},
{'answer': 'Antonie van Leeuwen'},
{'answer': 'Antonie van Leeuwenho'},
{'answer': 'Antonie van Leeuwenhoek'}]
而 PydanticOutputParser 则不能:
list(chain.stream({"query": "告诉我一个笑话。"}))
[Joke(setup='为什么鸡会过马路?', punchline='为了到达另一边!)
2. 自定义输出解析器
在某些情况下,您可能希望实现一个自定义解析器,将模型输出结构化为自定义格式。
有两种实现自定义解析器的方式:
- 使用 LCEL 中的
RunnableLambda
或RunnableGenerator
– 我们强烈推荐这种方式用于大多数情况 - 通过继承输出解析的基类之一来实现 – 这是一种较为复杂的方式
这两种方法之间的区别主要是表面的,主要体现在触发哪些回调(例如 on_chain_start
vs. on_parser_start
),以及可追踪平台(如 LangSmith)中可视化可运行 lambda 和解析器的方式。
可运行 Lambda 和生成器
推荐的解析方式是使用 可运行 lambda 和 可运行生成器!
在这里,我们将创建一个简单的解析器,将模型输出的大小写反转。
例如,如果模型输出为:“Meow”,解析器将产生 “mEOW”。
from typing import Iterable
from langchain_anthropic.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage, AIMessageChunk
model = ChatAnthropic(model_name="claude-2.1")
def parse(ai_message: AIMessage) -> str:
"""解析 AI 消息。"""
return ai_message.content.swapcase()
chain = model | parse
chain.invoke("hello")
'hELLO!'
[!NOTE]
当使用
|
语法组合时,LCEL 会自动将函数parse
升级为RunnableLambda(parse)
。如果您不喜欢这种方式,可以手动导入
RunnableLambda
,然后运行parse = RunnableLambda(parse)
。
流式处理是否有效?
for chunk in chain.stream("tell me about yourself in one sentence"):
print(chunk, end="|", flush=True)
i'M cLAUDE, AN ai ASSISTANT CREATED BY aNTHROPIC TO BE HELPFUL, HARMLESS, AND HONEST.|
不,因为解析器会在解析输出之前聚合输入。
如果我们想要实现一个流式解析器,可以让解析器接受输入的可迭代对象,并在结果可用时产生结果。
from langchain_core.runnables import RunnableGenerator
def streaming_parse(chunks: Iterable[AIMessageChunk]) -> Iterable[str]:
for chunk in chunks:
yield chunk.content.swapcase()
streaming_parse = RunnableGenerator(streaming_parse)
[!IMPORTANT]
请将流式解析器包装在
RunnableGenerator
中,因为我们可能不再自动使用|
语法升级它。
chain = model | streaming_parse
chain.invoke("hello")
'hELLO!'
让我们确认流式处理是否有效!
for chunk in chain.stream("tell me about yourself in one sentence"):
print(chunk, end="|", flush=True)
i|'M| cLAUDE|,| AN| ai| ASSISTANT| CREATED| BY| aN|THROP|IC| TO| BE| HELPFUL|,| HARMLESS|,| AND| HONEST|.|
继承解析基类
另一种实现解析器的方法是通过继承 BaseOutputParser
、BaseGenerationOutputParser
或其他基本解析器之一,具体取决于您需要做什么。
一般来说,我们不建议大多数情况下采用这种方法,因为这样会导致编写更多代码而没有显著的好处。
最简单的输出解析器扩展了 BaseOutputParser
类,并必须实现以下方法:
parse
:接受模型的字符串输出并进行解析- (可选)
_type
:标识解析器的名称。
当聊天模型或 LLM 的输出格式不正确时,可以抛出 OutputParserException
来指示解析失败是因为输入有误。使用此异常可以使利用解析器的代码以一致的方式处理异常。
[!TIP]
解析器是可运行的! 🏃
因为
BaseOutputParser
实现了Runnable
接口,您通过这种方式创建的任何自定义解析器都将成为有效的 LangChain 可运行对象,并将受益于自动异步支持、批处理接口、日志支持等。
简单解析器
这是一个简单的解析器,可以解析一个表示布尔值的 字符串(例如 YES
或 NO
)并将其转换为相应的 boolean
类型。
from langchain_core.exceptions import OutputParserException
from langchain_core.output_parsers import BaseOutputParser
# [bool] 描述了一个泛型的参数化。
# 它基本上指示 parse 的返回类型是 True 还是 False
class BooleanOutputParser(BaseOutputParser[bool]):
"""自定义布尔解析器。"""
true_val: str = "YES"
false_val: str = "NO"
def parse(self, text: str) -> bool:
cleaned_text = text.strip().upper()
if cleaned_text not in (self.true_val.upper(), self.false_val.upper()):
raise OutputParserException(
f"BooleanOutputParser 期望输出值为 {self.true_val} 或 {self.false_val}(不区分大小写)。"
f"接收到 {cleaned_text}。"
)
return cleaned_text == self.true_val.upper()
@property
def _type(self) -> str:
return "boolean_output_parser"
parser = BooleanOutputParser()
parser.invoke("YES")
True
try:
parser.invoke("MEOW")
except Exception as e:
print(f"触发了类型为:{type(e)} 的异常")
触发了类型为:<class 'langchain_core.exceptions.OutputParserException'> 的异常
让我们测试更改参数化
parser = BooleanOutputParser(true_val="OKAY")
parser.invoke("OKAY")
True
让我们确认其他 LCEL 方法是否存在
parser.batch(["OKAY", "NO"])
[True, False]
await parser.abatch(["OKAY", "NO"])
[True, False]
from langchain_anthropic.chat_models import ChatAnthropic
anthropic = ChatAnthropic(model_name="claude-2.1")
anthropic.invoke("say OKAY or NO")
AIMessage(content='OKAY')
让我们测试我们的解析器是否有效!
chain = anthropic | parser
chain.invoke("say OKAY or NO")
True
[!NOTE]
该解析器将适用于 LLM 的输出(一个字符串)或聊天模型的输出(一个
AIMessage
)!
解析原始模型输出
有时除了原始文本之外,模型输出中还有重要的元数据。一个例子是工具调用,其中意图传递给被调用函数的参数以单独的属性返回。如果您需要这种更精细的控制,可以子类化 BaseGenerationOutputParser
类。
这个类需要一个名为 parse_result
的方法。该方法接受原始模型输出(例如 Generation
或 ChatGeneration
的列表)并返回解析后的输出。
支持 Generation
和 ChatGeneration
可使解析器适用于常规 LLM 和聊天模型。
from typing import List
from langchain_core.exceptions import OutputParserException
from langchain_core.messages import AIMessage
from langchain_core.output_parsers import BaseGenerationOutputParser
from langchain_core.outputs import ChatGeneration, Generation
class StrInvertCase(BaseGenerationOutputParser[str]):
"""一个示例解析器,用于反转消息中字符的大小写。
这是一个演示示例解析器,目的是保持示例尽可能简单。
"""
def parse_result(self, result: List[Generation], *, partial: bool = False) -> str:
"""将模型生成的列表解析为特定格式。
参数:
result:要解析的 Generation 列表。假定这些 Generations 是单个模型输入的不同候选输出。
许多解析器假设只传递一个生成结果。我们将对此进行断言
partial:是否允许部分结果。这用于支持流式解析
"""
if len(result) != 1:
raise NotImplementedError(
"此输出解析器只能用于单个生成结果。"
)
generation = result[0]
if not isinstance(generation, ChatGeneration):
# 表明这只适用于聊天生成
raise OutputParserException(
"此输出解析器只能用于聊天生成。"
)
return generation.message.content.swapcase()
chain = anthropic | StrInvertCase()
让我们测试新的解析器!它应该会反转模型的输出。
chain.invoke("Tell me a short sentence about yourself")
'hELLO! mY NAME IS cLAUDE.'
3. 输出修复解析器
这个输出解析器包装了另一个输出解析器,如果第一个解析器失败,它会调用另一个 LLM 来修复任何错误。
但我们除了抛出错误之外还可以做其他事情。具体来说,我们可以将格式不正确的输出与格式化指令一起传递给模型,并要求它进行修复。
在这个示例中,我们将使用上面的 Pydantic 输出解析器。如果我们传递一个不符合模式的结果,会发生什么呢:
from typing import List
from langchain.output_parsers import PydanticOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
class Actor(BaseModel):
name: str = Field(description="演员的姓名")
film_names: List[str] = Field(description="他们主演的电影名称列表")
actor_query = "为一位随机演员生成电影作品列表。"
parser = PydanticOutputParser(pydantic_object=Actor)
misformatted = "{'name': '汤姆·汉克斯', 'film_names': ['阿甘正传']}"
parser.parse(misformatted)
---------------------------------------------------------------------------
JSONDecodeError Traceback (most recent call last)
File ~/workplace/langchain/libs/langchain/langchain/output_parsers/pydantic.py:29, in PydanticOutputParser.parse(self, text)
28 json_str = match.group()
---> 29 json_object = json.loads(json_str, strict=False)
30 return self.pydantic_object.parse_obj(json_object)
File ~/.pyenv/versions/3.10.1/lib/python3.10/json/__init__.py:359, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
358 kw['parse_constant'] = parse_constant
--> 359 return cls(**kw).decode(s)
File ~/.pyenv/versions/3.10.1/lib/python3.10/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
333 """Return the Python representation of ``s`` (a ``str`` instance
334 containing a JSON document).
335
336 """
--> 337 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
338 end = _w(s, end).end()
File ~/.pyenv/versions/3.10.1/lib/python3.10/json/decoder.py:353, in JSONDecoder.raw_decode(self, s, idx)
352 try:
--> 353 obj, end = self.scan_once(s, idx)
354 except StopIteration as err:
JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
During handling of the above exception, another exception occurred:
OutputParserException Traceback (most recent call last)
Cell In[4], line 1
----> 1 parser.parse(misformatted)
File ~/workplace/langchain/libs/langchain/langchain/output_parsers/pydantic.py:35, in PydanticOutputParser.parse(self, text)
33 name = self.pydantic_object.__name__
34 msg = f"Failed to parse {name} from completion {text}. Got: {e}"
---> 35 raise OutputParserException(msg, llm_output=text)
OutputParserException: Failed to parse Actor from completion {'name': '汤姆·汉克斯', 'film_names': ['阿甘正传']}. Got: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
现在我们可以构建并使用一个 OutputFixingParser
。这个输出解析器接受另一个输出解析器作为参数,同时也接受一个 LLM,用来尝试纠正任何格式错误。
from langchain.output_parsers import OutputFixingParser
new_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())
new_parser.parse(misformatted)
Actor(name='汤姆·汉克斯', film_names=['阿甘正传'])
4.结构化输出解析器
当您需要返回多个字段时,可以使用这个输出解析器。虽然 Pydantic/JSON 解析器更加强大,但对于功能较弱的模型来说,这个解析器非常有用。
from langchain.output_parsers import ResponseSchema, StructuredOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
response_schemas = [
ResponseSchema(name="answer", description="回答用户问题"),
ResponseSchema(
name="source",
description="用于回答用户问题的来源,应该是一个网站链接。",
),
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
现在我们得到一个包含响应格式说明的字符串,然后将其插入到我们的提示中。
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template="尽可能好地回答用户的问题。\n{format_instructions}\n{question}",
input_variables=["question"],
partial_variables={"format_instructions": format_instructions},
)
model = ChatOpenAI(temperature=0)
chain = prompt | model | output_parser
chain.invoke({"question": "法国的首都是哪里?"})
{'answer': '法国的首都是巴黎。',
'source': 'https://en.wikipedia.org/wiki/Paris'}
for s in chain.stream({"question": "法国的首都是哪里?"}):
print(s)
{'answer': '法国的首都是巴黎。', 'source': 'https://en.wikipedia.org/wiki/Paris'}
5.重试解析器
在某些情况下,通过仅查看输出就可以修复任何解析错误,但在其他情况下则不行。一个例子是当输出不仅格式不正确,而且是部分完整时。考虑下面的例子。
from langchain.output_parsers import (
OutputFixingParser,
PydanticOutputParser,
)
from langchain.prompts import (
PromptTemplate,
)
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI, OpenAI
template = """根据用户问题,提供应采取的行动和行动输入的步骤。
{format_instructions}
问题: {query}
回复:"""
class Action(BaseModel):
action: str = Field(description="要执行的动作")
action_input: str = Field(description="动作的输入")
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="who is leo di caprios gf?")
bad_response = '{"action": "search"}'
如果我们尝试直接解析这个响应,将会出现错误:
parser.parse(bad_response)
OutputParserException: 无法从完成 {"action": "search"} 解析 Action。错误:1 个验证错误 for Action
action_input
field required (type=value_error.missing)
如果我们尝试使用 OutputFixingParser
来修复这个错误,它会感到困惑 - 也就是说,它不知道实际应该为动作输入放入什么。
fix_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())
fix_parser.parse(bad_response)
Action(action='search', action_input='input')
相反,我们可以使用 RetryOutputParser,它会传递提示(以及原始输出)再次尝试获得更好的响应。
from langchain.output_parsers import RetryOutputParser
retry_parser = RetryOutputParser.from_llm(parser=parser, llm=OpenAI(temperature=0))
retry_parser.parse_with_prompt(bad_response, prompt_value)
Action(action='search', action_input='leo di caprio girlfriend')
我们还可以轻松地通过自定义链将 RetryOutputParser 添加到一个自定义链中,将原始 LLM/ChatModel 输出转换为更易处理的格式。
from langchain_core.runnables import RunnableLambda, RunnableParallel
completion_chain = prompt | OpenAI(temperature=0)
main_chain = RunnableParallel(
completion=completion_chain, prompt_value=prompt
) | RunnableLambda(lambda x: retry_parser.parse_with_prompt(**x))
main_chain.invoke({"query": "who is leo di caprios gf?"})
6. CSV解析器
这个输出解析器可用于返回一个逗号分隔的项目列表。
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
output_parser = CommaSeparatedListOutputParser()
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template="列出五种{subject}。\n{format_instructions}",
input_variables=["subject"],
partial_variables={"format_instructions": format_instructions},
)
model = ChatOpenAI(temperature=0)
chain = prompt | model | output_parser
chain.invoke({"subject": "冰淇淋口味"})
['香草',
'巧克力',
'草莓',
'薄荷巧克力片',
'曲奇与奶油']
for s in chain.stream({"subject": "冰淇淋口味"}):
print(s)
['香草']
['巧克力']
['草莓']
['薄荷巧克力片']
['曲奇与奶油']
7. JSON 解析器
这个输出解析器允许用户指定任意的 JSON 模式,并查询 LLMs 输出符合该模式的结果。
请记住,大型语言模型是有泄漏的抽象!您必须使用具有足够容量的 LLM 生成格式良好的 JSON。在 OpenAI 家族中,DaVinci 可以可靠地完成,但 Curie 的能力已经急剧下降。
您可以选择使用 Pydantic 来声明您的数据模型。
from typing import List
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
model = ChatOpenAI(temperature=0)
# 定义您期望的数据结构。
class Joke(BaseModel):
setup: str = Field(description="设置笑话的问题部分")
punchline: str = Field(description="解答笑话的答案部分")
# 以及一个用于提示语言模型填充数据结构的查询。
joke_query = "Tell me a joke."
# 设置一个解析器 + 将指令注入到提示模板中。
parser = JsonOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
chain.invoke({"query": joke_query})
{'setup': "为什么科学家不相信原子?", 'punchline': '因为它们构成了一切!'}
流式处理
这个输出解析器支持流式处理。
for s in chain.stream({"query": joke_query}):
print(s)
{'setup': ''}
{'setup': '为什么'}
{'setup': '为什么不'}
{'setup': '为什么不'}
{'setup': '为什么不相信'}
{'setup': '为什么不相信原子'}
{'setup': '为什么不相信原子?', 'punchline': ''}
{'setup': '为什么不相信原子?', 'punchline': '因为'}
{'setup': '为什么不相信原子?', 'punchline': '因为它们'}
{'setup': '为什么不相信原子?', 'punchline': '因为它们构成'}
{'setup': '为什么不相信原子?', 'punchline': '因为它们构成一切'}
{'setup': '为什么不相信原子?', 'punchline': '因为它们构成一切!'}
没有 Pydantic
您也可以在没有 Pydantic 的情况下使用这个功能。这将提示返回 JSON,但不提供关于模式应该是什么的具体信息。
joke_query = "Tell me a joke."
parser = JsonOutputParser()
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
chain.invoke({"query": joke_query})
{'joke': "为什么科学家不相信原子?因为它们构成了一切!"}
8.日期时间解析器
这个 OutputParser 可以用来将 LLM 输出解析为日期时间格式。
from langchain.output_parsers import DatetimeOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import OpenAI
output_parser = DatetimeOutputParser()
template = """回答用户的问题:
{question}
{format_instructions}"""
prompt = PromptTemplate.from_template(
template,
partial_variables={"format_instructions": output_parser.get_format_instructions()},
)
prompt
PromptTemplate(input_variables=['question'], partial_variables={'format_instructions': "Write a datetime string that matches the following pattern: '%Y-%m-%dT%H:%M:%S.%fZ'.\n\nExamples: 0668-08-09T12:56:32.732651Z, 1213-06-23T21:01:36.868629Z, 0713-07-06T18:19:02.257488Z\n\nReturn ONLY this string, no other words!"}, template='Answer the users question:\n\n{question}\n\n{format_instructions}')
chain = prompt | OpenAI() | output_parser
output = chain.invoke({"question": "when was bitcoin founded?"})
print(output)
2009-01-03 18:15:05
9.枚举解析器
这篇笔记展示了如何使用枚举输出解析器。
from langchain.output_parsers.enum import EnumOutputParser
from enum import Enum
class Colors(Enum):
RED = "red"
GREEN = "green"
BLUE = "blue"
parser = EnumOutputParser(enum=Colors)
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
prompt = PromptTemplate.from_template(
"""这个人有什么颜色的眼睛?
> 人物: {person}
说明: {instructions}"""
).partial(instructions=parser.get_format_instructions())
chain = prompt | ChatOpenAI() | parser
chain.invoke({"person": "Frank Sinatra"})
输出结果为蓝色。
10.OpenAI 函数
这些输出解析器使用 OpenAI 函数调用来构造其输出。这意味着它们只能用于支持函数调用的模型。有几种不同的变体:
- JsonOutputFunctionsParser:将函数调用的参数作为 JSON 返回
- PydanticOutputFunctionsParser:将函数调用的参数作为 Pydantic 模型返回
- JsonKeyOutputFunctionsParser:将函数调用中特定键的值作为 JSON 返回
- PydanticAttrOutputFunctionsParser:将函数调用中特定键的值作为 Pydantic 模型返回
from langchain_community.utils.openai_functions import (
convert_pydantic_to_openai_function,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import ChatOpenAI
class Joke(BaseModel):
"""要告诉用户的笑话。"""
setup: str = Field(description="设置笑话的问题部分")
punchline: str = Field(description="解答笑话的答案部分")
openai_functions = [convert_pydantic_to_openai_function(Joke)]
model = ChatOpenAI(temperature=0)
prompt = ChatPromptTemplate.from_messages(
[("system", "You are helpful assistant"), ("user", "{input}")]
)
JsonOutputFunctionsParser
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
parser = JsonOutputFunctionsParser()
chain = prompt | model.bind(functions=openai_functions) | parser
chain.invoke({"input": "tell me a joke"})
{'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'}
for s in chain.stream({"input": "tell me a joke"}):
print(s)
{}
{'setup': ''}
{'setup': 'Why'}
{'setup': 'Why don'}
{'setup': "Why don't"}
{'setup': "Why don't scientists"}
{'setup': "Why don't scientists trust"}
{'setup': "Why don't scientists trust atoms"}
{'setup': "Why don't scientists trust atoms?", 'punchline': ''}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because'}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they'}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make'}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up'}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything'}
{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}
JsonKeyOutputFunctionsParser
这只是从返回的响应中提取单个键。当您想返回一系列内容时,这很有用。
from typing import List
from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser
class Jokes(BaseModel):
"""要告诉用户的笑话。"""
joke: List[Joke]
funniness_level: int
parser = JsonKeyOutputFunctionsParser(key_name="joke")
openai_functions = [convert_pydantic_to_openai_function(Jokes)]
chain = prompt | model.bind(functions=openai_functions) | parser
chain.invoke({"input": "tell me two jokes"})
[{'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'},
{'setup': 'Why did the scarecrow win an award?',
'punchline': 'Because he was outstanding in his field!'}]
for s in chain.stream({"input": "tell me two jokes"}):
print(s)
[]
[{}]
[{'setup': ''}]
[{'setup': 'Why'}]
[{'setup': 'Why don'}]
[{'setup': "Why don't"}]
[{'setup': "Why don't scientists"}]
[{'setup': "Why don't scientists trust"}]
[{'setup': "Why don't scientists trust atoms"}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': ''}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': ''}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scare'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': ''}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was outstanding'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was outstanding in'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was outstanding in his'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was outstanding in his field'}]
[{'setup': "Why don't scientists trust atoms?", 'punchline': 'Because they make up everything!'}, {'setup': 'Why did the scarecrow win an award?', 'punchline': 'Because he was outstanding in his field!'}]
PydanticOutputFunctionsParser
这在 JsonOutputFunctionsParser
的基础上构建,并将结果传递给 Pydantic 模型。这允许您进行进一步的验证。
from langchain.output_parsers.openai_functions import PydanticOutputFunctionsParser
class Joke(BaseModel):
"""要告诉用户的笑话。"""
setup: str = Field(description="设置笑话的问题部分")
punchline: str = Field(description="解答笑话的答案部分")
# 您可以很容易地使用 Pydantic 添加自定义验证逻辑。
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("问题格式不正确!")
return field
parser = PydanticOutputFunctionsParser(pydantic_schema=Joke)
openai_functions = [convert_pydantic_to_openai_function(Joke)]
chain = prompt | model.bind(functions=openai_functions) | parser
chain.invoke({"input": "tell me a joke"})
Joke(setup="Why don't scientists trust atoms?", punchline='Because they make up everything!')
11. OpenAI 工具
这些输出解析器从 OpenAI 的函数调用 API 响应中提取工具调用。这意味着它们仅适用于支持函数调用的模型,特别是最新的 tools
和 tool_choice
参数。我们建议在阅读本指南之前先熟悉 函数调用。
有几种不同的输出解析器:
- JsonOutputToolsParser: 将函数调用的参数作为 JSON 返回
- JsonOutputKeyToolsParser: 将函数调用中特定键的值作为 JSON 返回
- PydanticToolsParser: 将函数调用的参数作为 Pydantic 模型返回
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import ChatOpenAI
class Joke(BaseModel):
"""要告诉用户的笑话。"""
setup: str = Field(description="设置笑话的问题")
punchline: str = Field(description="解答笑话的答案")
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0).bind_tools([Joke])
model.kwargs["tools"]
[{'type': 'function',
'function': {'name': 'Joke',
'description': '要告诉用户的笑话。',
'parameters': {'type': 'object',
'properties': {'setup': {'description': '设置笑话的问题',
'type': 'string'},
'punchline': {'description': '解答笑话的答案',
'type': 'string'}},
'required': ['setup', 'punchline']}}}]
prompt = ChatPromptTemplate.from_messages(
[("system", "You are helpful assistant"), ("user", "{input}")]
)
JsonOutputToolsParser
from langchain.output_parsers.openai_tools import JsonOutputToolsParser
parser = JsonOutputToolsParser()
chain = prompt | model | parser
chain.invoke({"input": "tell me a joke"})
[{'type': 'Joke',
'args': {'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'}}]
要包含工具调用的 id,我们可以指定 return_id=True
:
parser = JsonOutputToolsParser(return_id=True)
chain = prompt | model | parser
chain.invoke({"input": "tell me a joke"})
[{'type': 'Joke',
'args': {'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'},
'id': 'call_Isuoh0RTeQzzOKGg5QlQ7UqI'}]
JsonOutputKeyToolsParser
这只是从返回的响应中提取单个键。当您传入单个工具并且只想要它的参数时,这很有用。
from typing import List
from langchain.output_parsers.openai_tools import JsonOutputKeyToolsParser
parser = JsonOutputKeyToolsParser(key_name="Joke")
chain = prompt | model | parser
chain.invoke({"input": "tell me a joke"})
[{'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'}]
某些模型可以在每次调用时返回多个工具调用,因此默认情况下输出是一个列表。如果我们只想返回第一个工具调用,可以指定 return_single=True
parser = JsonOutputKeyToolsParser(key_name="Joke", return_single=True)
chain = prompt | model | parser
chain.invoke({"input": "tell me a joke"})
{'setup': "Why don't scientists trust atoms?",
'punchline': 'Because they make up everything!'}
PydanticToolsParser
这在 JsonOutputToolsParser
的基础上构建,并将结果传递给 Pydantic 模型。这允许您选择进一步进行验证。
from langchain.output_parsers.openai_tools import PydanticToolsParser
class Joke(BaseModel):
"""要告诉用户的笑话。"""
setup: str = Field(description="设置笑话的问题")
punchline: str = Field(description="解答笑话的答案")
# 您可以轻松使用 Pydantic 添加自定义验证逻辑。
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field
parser = PydanticToolsParser(tools=[Joke])
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0).bind_tools([Joke])
chain = prompt | model | parser
chain.invoke({"input": "tell me a joke"})
[Joke(setup="Why don't scientists trust atoms?", punchline='Because they make up everything!')]
12. Pandas DataFrame 解析器
Pandas DataFrame 是 Python 编程语言中一种流行的数据结构,通常用于数据处理和分析。它提供了一套全面的工具,用于处理结构化数据,使其成为数据清洗、转换和分析等任务的多功能选择。
此输出解析器允许用户指定任意 Pandas DataFrame,并查询 LLMs 以获取以格式化字典形式提取相应 DataFrame 中数据的数据。请记住,大型语言模型是有泄漏的抽象!您需要使用具有足够容量的 LLM 生成符合定义格式说明的查询。
使用 Pandas 的 DataFrame 对象声明您希望执行查询的 DataFrame。
import pprint
from typing import Any, Dict
import pandas as pd
from langchain.output_parsers import PandasDataFrameOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
model = ChatOpenAI(temperature=0)
# 仅供文档目的。
def format_parser_output(parser_output: Dict[str, Any]) -> None:
for key in parser_output.keys():
parser_output[key] = parser_output[key].to_dict()
return pprint.PrettyPrinter(width=4, compact=True).pprint(parser_output)
# 定义您想要的 Pandas DataFrame。
df = pd.DataFrame(
{
"num_legs": [2, 4, 8, 0],
"num_wings": [2, 0, 0, 0],
"num_specimen_seen": [10, 2, 1, 8],
}
)
# 设置解析器 + 将说明注入到提示模板中。
parser = PandasDataFrameOutputParser(dataframe=df)
# 这是执行列操作的示例。
df_query = "检索 num_wings 列。"
# 设置提示。
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
parser_output = chain.invoke({"query": df_query})
format_parser_output(parser_output)
{'num_wings': {0: 2,
1: 0,
2: 0,
3: 0}}
# 这是执行行操作的示例。
df_query = "检索第一行。"
# 设置提示。
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
parser_output = chain.invoke({"query": df_query})
format_parser_output(parser_output)
{'0': {'num_legs': 2,
'num_specimen_seen': 10,
'num_wings': 2}}
# 这是执行随机 Pandas DataFrame 操作并限制行数的示例
df_query = "检索第 1 到 3 行中 num_legs 列的平均值。"
# 设置提示。
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
parser_output = chain.invoke({"query": df_query})
print(parser_output)
{'mean': 4.0}
# 这是一个格式不佳查询的示例
df_query = "检索 num_fingers 列的平均值。"
# 设置提示。
prompt = PromptTemplate(
template="回答用户查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
parser_output = chain.invoke({"query": df_query})
---------------------------------------------------------------------------
OutputParserException Traceback (most recent call last)
Cell In[23], line 12
5 prompt = PromptTemplate(
6 template="Answer the user query.\n{format_instructions}\n{query}\n",
7 input_variables=["query"],
8 partial_variables={"format_instructions": parser.get_format_instructions()},
9 )
11 chain = prompt | model | parser
---> 12 parser_output = chain.invoke({"query": df_query})
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1616, in RunnableSequence.invoke(self, input, config)
1614 try:
1615 for i, step in enumerate(self.steps):
-> 1616 input = step.invoke(
1617 input,
1618 # mark each step as a child run
1619 patch_config(
1620 config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
1621 ),
1622 )
1623 # finish the root run
1624 except BaseException as e:
File ~/workplace/langchain/libs/core/langchain_core/output_parsers/base.py:170, in BaseOutputParser.invoke(self, input, config)
166 def invoke(
167 self, input: Union[str, BaseMessage], config: Optional[RunnableConfig] = None
168 ) -> T:
169 if isinstance(input, BaseMessage):
--> 170 return self._call_with_config(
171 lambda inner_input: self.parse_result(
172 [ChatGeneration(message=inner_input)]
173 ),
174 input,
175 config,
176 run_type="parser",
177 )
178 else:
179 return self._call_with_config(
180 lambda inner_input: self.parse_result([Generation(text=inner_input)]),
181 input,
182 config,
183 run_type="parser",
184 )
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:906, in Runnable._call_with_config(self, func, input, config, run_type, **kwargs)
899 run_manager = callback_manager.on_chain_start(
900 dumpd(self),
901 input,
902 run_type=run_type,
903 name=config.get("run_name"),
904 )
905 try:
--> 906 output = call_func_with_variable_args(
907 func, input, config, run_manager, **kwargs
908 )
909 except BaseException as e:
910 run_manager.on_chain_error(e)
File ~/workplace/langchain/libs/core/langchain_core/runnables/config.py:308, in call_func_with_variable_args(func, input, config, run_manager, **kwargs)
306 if run_manager is not None and accepts_run_manager(func):
307 kwargs["run_manager"] = run_manager
--> 308 return func(input, **kwargs)
File ~/workplace/langchain/libs/core/langchain_core/output_parsers/base.py:171, in BaseOutputParser.invoke.<locals>.<lambda>(inner_input)
166 def invoke(
167 self, input: Union[str, BaseMessage], config: Optional[RunnableConfig] = None
168 ) -> T:
169 if isinstance(input, BaseMessage):
170 return self._call_with_config(
--> 171 lambda inner_input: self.parse_result(
172 [ChatGeneration(message=inner_input)]
173 ),
174 input,
175 config,
176 run_type="parser",
177 )
178 else:
179 return self._call_with_config(
180 lambda inner_input: self.parse_result([Generation(text=inner_input)]),
181 input,
182 config,
183 run_type="parser",
184 )
File ~/workplace/langchain/libs/core/langchain_core/output_parsers/base.py:222, in BaseOutputParser.parse_result(self, result, partial)
209 def parse_result(self, result: List[Generation], *, partial: bool = False) -> T:
210 """Parse a list of candidate model Generations into a specific format.
211
212 The return value is parsed from only the first Generation in the result, which
(...)
220 Structured output.
221 """
--> 222 return self.parse(result[0].text)
File ~/workplace/langchain/libs/langchain/langchain/output_parsers/pandas_dataframe.py:90, in PandasDataFrameOutputParser.parse(self, request)
88 request_type, request_params = splitted_request
89 if request_type in {"Invalid column", "Invalid operation"}:
---> 90 raise OutputParserException(
91 f"{request}. 请检查格式说明。"
92 )
93 array_exists = re.search(r"(\[.*?\])", request_params)
94 if array_exists:
OutputParserException: 无效列:num_fingers。请检查格式说明。
13. Pydantic 解析器
这个输出解析器允许用户指定任意的 Pydantic 模型,并查询 LLMs 以生成符合该模式的输出。
请记住,大型语言模型是有泄漏的抽象!您必须使用具有足够容量的 LLM 来生成格式良好的 JSON。在 OpenAI 家族中,DaVinci 可以可靠地完成这项任务,但是 Curie 的能力已经急剧下降。
使用 Pydantic 声明您的数据模型。Pydantic 的 BaseModel 类似于 Python 的数据类,但具有实际的类型检查和强制转换。
from typing import List
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import ChatOpenAI
model = ChatOpenAI(temperature=0)
# 定义您期望的数据结构。
class Joke(BaseModel):
setup: str = Field(description="设置笑话的问题部分")
punchline: str = Field(description="解答笑话的答案部分")
# 您可以很容易地使用 Pydantic 添加自定义验证逻辑。
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("问题格式不正确!")
return field
# 还有一个用于提示语言模型填充数据结构的查询意图。
joke_query = "告诉我一个笑话。"
# 设置一个解析器 + 将指令注入到提示模板中。
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="回答用户的查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
chain.invoke({"query": joke_query})
# 这里是另一个示例,但带有一个复合类型的字段。
class Actor(BaseModel):
name: str = Field(description="演员的姓名")
film_names: List[str] = Field(description="他们主演的电影名称列表")
actor_query = "为一个随机演员生成电影作品列表。"
parser = PydanticOutputParser(pydantic_object=Actor)
prompt = PromptTemplate(
template="回答用户的查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
chain.invoke({"query": actor_query})
14. YAML 解析器
这个输出解析器允许用户指定任意模式,并查询 LLMs 以符合该模式的输出,使用 YAML 格式化他们的响应。
请记住,大型语言模型是有漏洞的抽象!您将需要使用具有足够容量的 LLM 来生成格式良好的 YAML。在 OpenAI 家族中,DaVinci 可以可靠地完成这项任务,但 Curie 的能力已经急剧下降。
您可以选择使用 Pydantic 来声明您的数据模型。
from typing import List
from langchain.output_parsers import YamlOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
model = ChatOpenAI(temperature=0)
# 定义您期望的数据结构。
class Joke(BaseModel):
setup: str = Field(description="设置一个笑话的问题")
punchline: str = Field(description="解答笑话的答案")
# 还有一个查询意图,旨在提示语言模型填充数据结构。
joke_query = "Tell me a joke."
# 设置一个解析器 + 将指令注入到提示模板中。
parser = YamlOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="回答用户的查询。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
chain.invoke({"query": joke_query})
Joke(setup="为什么科学家不相信原子?", punchline='因为它们构成了一切!')
15. XML解析器
这个输出解析器允许用户以流行的XML格式获取LLM的结果。
请记住,大型语言模型是有泄漏的抽象!您必须使用具有足够容量的LLM来生成格式良好的XML。
在以下示例中,我们使用了Claude模型(https://docs.anthropic.com/claude/docs),它与XML标签非常配合。
from langchain.output_parsers import XMLOutputParser
from langchain.prompts import PromptTemplate
from langchain_community.chat_models import ChatAnthropic
model = ChatAnthropic(model="claude-2", max_tokens_to_sample=512, temperature=0.1)
让我们从向模型发送简单请求开始。
actor_query = "为汤姆·汉克斯生成缩短的电影作品列表。"
output = model.invoke(
f"""{actor_query}
请将电影放在<movie></movie>标签中"""
)
print(output.content)
这是汤姆·汉克斯的缩短电影作品列表,用XML标签括起来:
<movie>Splash</movie>
<movie>Big</movie>
<movie>A League of Their Own</movie>
<movie>Sleepless in Seattle</movie>
<movie>Forrest Gump</movie>
<movie>Toy Story</movie>
<movie>Apollo 13</movie>
<movie>Saving Private Ryan</movie>
<movie>Cast Away</movie>
<movie>The Da Vinci Code</movie>
<movie>Captain Phillips</movie>
现在我们将使用XMLOutputParser来获取结构化输出。
parser = XMLOutputParser()
prompt = PromptTemplate(
template="""{query}\n{format_instructions}""",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
output = chain.invoke({"query": actor_query})
print(output)
{'filmography': [{'movie': [{'title': 'Big'}, {'year': '1988'}]}, {'movie': [{'title': 'Forrest Gump'}, {'year': '1994'}]}, {'movie': [{'title': 'Toy Story'}, {'year': '1995'}]}, {'movie': [{'title': 'Saving Private Ryan'}, {'year': '1998'}]}, {'movie': [{'title': 'Cast Away'}, {'year': '2000'}]}]}
最后,让我们添加一些标签,以定制输出以满足我们的需求。
parser = XMLOutputParser(tags=["movies", "actor", "film", "name", "genre"])
prompt = PromptTemplate(
template="""{query}\n{format_instructions}""",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
output = chain.invoke({"query": actor_query})
print(output)
{'movies': [{'actor': [{'name': 'Tom Hanks'}, {'film': [{'name': 'Forrest Gump'}, {'genre': 'Drama'}]}, {'film': [{'name': 'Cast Away'}, {'genre': 'Adventure'}]}, {'film': [{'name': 'Saving Private Ryan'}, {'genre': 'War'}]}]}]}
for s in chain.stream({"query": actor_query}):
print(s)
Traceback (most recent call last):
File ~/.pyenv/versions/3.10.1/envs/langchain/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3508 in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
Cell In[7], line 1
for s in chain.stream({"query": actor_query}):
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1984 in stream
yield from self.transform(iter([input]), config, **kwargs)
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1974 in transform
yield from self._transform_stream_with_config(
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1141 in _transform_stream_with_config
for chunk in iterator:
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1938 in _transform
for output in final_pipeline:
File ~/workplace/langchain/libs/core/langchain_core/output_parsers/transform.py:50 in transform
yield from self._transform_stream_with_config(
File ~/workplace/langchain/libs/core/langchain_core/runnables/base.py:1141 in _transform_stream_with_config
for chunk in iterator:
File ~/workplace/langchain/libs/core/langchain_core/output_parsers/xml.py:71 in _transform
for event, elem in parser.read_events():
File ~/.pyenv/versions/3.10.1/lib/python3.10/xml/etree/ElementTree.py:1329 in read_events
raise event
File ~/.pyenv/versions/3.10.1/lib/python3.10/xml/etree/ElementTree.py:1301 in feed
self._parser.feed(data)
File <string>
ParseError: syntax error: line 1, column 1