LangChain入门学习笔记(六)—— Model I/O之Output Parsers

当大模型产生输出返回后,它的内容更像是一段平铺的文字没有结构。在传给下游节点处理时可能并不能符合输入要求,LangChain提供了一套机制使得模型返回的内容可以按照开发者定义的那样结构化。

在官网文档中可以看到LangChain提供了丰富的输出解析器,涵盖了常用的一些格式。比如CSV、JSON、XML和YAML,也有一些跟模型提供商相关的特定解析工具,比如OpenAI Functions和OpenAI Tools。具体内容可以快速查看这里的表中内容

快速开始

使用输出解析器的方法很简单,只需要两步:

  • 给出格式指令:给出语言模型输出应如何格式化的说明字符串。
  • 进行解析:对接受的字符串进行解析,成为符合某种结构的输出。

以JSON解析器(其他解析器使用方法类似,可以参看官方文档进行)为例,示例代码如下:

python 复制代码
from langchain.output_parsers import PydanticOutputParser
from langchain_core.output_parsers import CommaSeparatedListOutputParser, JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_community.llms import Ollama

model = Ollama(model="llama3", temperature=0.0)


# 定义输出格式的字段属性。
class Joke(BaseModel):
    setup: str = Field(description="question to set up a joke")
    punchline: str = Field(description="answer to resolve the joke")

    # 添加自定义的格式检查。
    @validator("setup")
    def validate_question_mark(cls, field):
        if field[-1] != "?":
            raise ValueError("Badly formed question!")
        return field


# 定义需要的输出解析器。
parser = JsonOutputParser(pydantic_object=Joke)

# 通过format_instructions输入格式化的指令。
prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

# LCEL组装带有输出解析器的chain,当chain被调用invoke后,parser最终按照格式解析后输出结果。
prompt_and_model = prompt | model | parser
output = prompt_and_model.invoke({"query": "Tell me a joke about bear."})
print(output)

首先,Joke类定义了输出内容的格式,其中指定了"setup"和"punchline"字段,以及提问时的格式校验。

其次,实例化JSON格式的输出解析器(JsonOutputParser)对象,指定格式为前面定义的Joke。

然后,在定义prompt时候传入format_instructions,指示后续的格式化形式。

最后,定义的chain中使用该parser。

我们可以看到输出为:

自定义输出解析器

如果觉得LangChain 提供的解析器武德不够充沛,可以自己动手打造自己专属的"武器"。LangChain提供了两种方法:

  • 使用RunnableLambda或者``RunnableGenerator,这种方法简洁明了,推荐使用。
  • 从解析器基类继承编写新的类,相对比较复杂。

RunnableLambda/RunnableGenerator自定义解析

使用这个方法,我们定义自己的处理方法,接收某个输入,然后将其做对应转换后返回。以RunnableLambda为例如下:

python 复制代码
from langchain_community.chat_models import ChatOllama
from langchain_core.messages import AIMessage
# from langchain_core.runnables import RunnableLambda


# 定义自己的解析方法
def parse(ai_message: AIMessage) -> str:
    # 传入AIMessage类型的参数,这是Chat Model的输出
    return ai_message.content.swapcase()


# 定义一个Chat Model
model = ChatOllama(model="llama3")

# LCEL定义一个chain
chain = model | parse

print(chain.invoke("hello"))

# output = model.invoke("hello")
#
# parse = RunnableLambda(parse)
# print(parse.invoke(output))

定义了自己的解析处理方法parse,注意它接受AIMessage类型的输入,这是Chat Model的输出类型,在model定义时我们使用了ChatOllama类型。它的输出给到parse解析得到最终输出,使用LCEL即是:

python 复制代码
chain = model | parse

这里的LCEL将parse自动封装为RunnableLambda,可以理解这个操作如下面内容(注释部分):

python 复制代码
output = model.invoke("hello")

parse = RunnableLambda(parse)
print(parse.invoke(output))

最终的输出:

将语言模型的输出结果进行了大小写调换。

继承解析基类

可以通过继承一个简单的定制化解析器类BaseOutputParser来自定义自己的解析器类。只需要重写parse方法,加入自己的解析逻辑即可。_type方法返回str类型信息,主要用于日志相关功能。使用自定义的解析器类对象,解析大模型的输出。如下代码所示:

python 复制代码
from langchain_community.llms.ollama import Ollama
from langchain_core.output_parsers import BaseOutputParser


# 继承BaseOutputParser,定义parse和_type
class MyCustomOutputParser(BaseOutputParser[str]):

    # 自定义解析方法
    def parse(self, text: str) -> str:
        return text.swapcase()

    # 本自定义解析器类的type属性,用于Log。 可选。
    @property
    def _type(self) -> str:
        return "my_custom_parser"


# 定义一个LLM,返回str
model = Ollama(model="llama3")

# 初始化自定义的解析器对象
parser = MyCustomOutputParser()

# 调用自定义解析器进行解析
print(parser.invoke(model.invoke("hello")))

该自定义解析子类的方法,得到跟前面的RunnableLambda方法一样的结果。

也可以通过继承BaseGenerationOutputParser类来自定义自己的解析类,这样能够更好和更多地控制解析输出结果,比如对模型输出里的metadata内容进行解析。使用方法上和继承BaseOutputParser的方法大致相当,只是重写的方法是parse_result。有兴趣可以参考这里,不再赘述。

通过Model I/O的介绍,大家基本上可以写个简单的LLM应用了。通过prompt输入用户的提示语,经过LLMs/ChatModel处理后,输出结果通过Output Parser解析后最终返回用户一个期望格式的内容。

当然,为了LLM应用更好的结果,我们可能还需要提供更多的内容给到底层的大模型来处理,这就是"Retrieval"相关的内容了。

相关推荐
小任同学Alex22 分钟前
浦语提示词工程实践(LangGPT版,服务器上部署internlm2-chat-1_8b,踩坑很多才完成的详细教程,)
人工智能·自然语言处理·大模型
nuclear201141 分钟前
使用Python 在Excel中创建和取消数据分组 - 详解
python·excel数据分组·创建excel分组·excel分类汇总·excel嵌套分组·excel大纲级别·取消excel分组
Lucky小小吴1 小时前
有关django、python版本、sqlite3版本冲突问题
python·django·sqlite
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
爱分享的码瑞哥1 小时前
Python爬虫中的IP封禁问题及其解决方案
爬虫·python·tcp/ip
傻啦嘿哟3 小时前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
B站计算机毕业设计超人3 小时前
计算机毕业设计SparkStreaming+Kafka旅游推荐系统 旅游景点客流量预测 旅游可视化 旅游大数据 Hive数据仓库 机器学习 深度学习
大数据·数据仓库·hadoop·python·kafka·课程设计·数据可视化
吃土少女古拉拉3 小时前
什么是计算机网络
计算机网络·学习笔记
IT古董3 小时前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
湫ccc3 小时前
《Python基础》之pip换国内镜像源
开发语言·python·pip