LangChain系列使用指南:组件介绍_模型输入输出_输出格式化


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) 的基本构建块。这意味着它们支持 invokeainvokestreamastreambatchabatchastream_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. 自定义输出解析器

在某些情况下,您可能希望实现一个自定义解析器,将模型输出结构化为自定义格式。

有两种实现自定义解析器的方式:

  1. 使用 LCEL 中的 RunnableLambdaRunnableGenerator – 我们强烈推荐这种方式用于大多数情况
  2. 通过继承输出解析的基类之一来实现 – 这是一种较为复杂的方式

这两种方法之间的区别主要是表面的,主要体现在触发哪些回调(例如 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|.|

继承解析基类

另一种实现解析器的方法是通过继承 BaseOutputParserBaseGenerationOutputParser 或其他基本解析器之一,具体取决于您需要做什么。

一般来说,我们不建议大多数情况下采用这种方法,因为这样会导致编写更多代码而没有显著的好处。

最简单的输出解析器扩展了 BaseOutputParser 类,并必须实现以下方法:

  • parse:接受模型的字符串输出并进行解析
  • (可选)_type:标识解析器的名称。

当聊天模型或 LLM 的输出格式不正确时,可以抛出 OutputParserException 来指示解析失败是因为输入有误。使用此异常可以使利用解析器的代码以一致的方式处理异常。

[!TIP]

解析器是可运行的! 🏃

因为 BaseOutputParser 实现了 Runnable 接口,您通过这种方式创建的任何自定义解析器都将成为有效的 LangChain 可运行对象,并将受益于自动异步支持、批处理接口、日志支持等。

简单解析器

这是一个简单的解析器,可以解析一个表示布尔值的 字符串(例如 YESNO)并将其转换为相应的 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 的方法。该方法接受原始模型输出(例如 GenerationChatGeneration 的列表)并返回解析后的输出。

支持 GenerationChatGeneration 可使解析器适用于常规 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 响应中提取工具调用。这意味着它们仅适用于支持函数调用的模型,特别是最新的 toolstool_choice 参数。我们建议在阅读本指南之前先熟悉 函数调用。

有几种不同的输出解析器:

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
  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值