如何使用輸出解析器將 LLM 回應解析為結構化格式
語言模型輸出文字。但有時您希望獲得比文字更結構化的資訊。雖然有些模型提供者支援內建方式來返回結構化輸出,但並非所有都支援。
輸出解析器是協助結構化語言模型回應的類別。輸出解析器必須實作兩個主要方法
- 「取得格式指示」:一種方法,返回一個字串,其中包含關於應如何格式化語言模型輸出的指示。
- 「解析」:一種方法,接收一個字串(假設為來自語言模型的回應)並將其解析為某種結構。
然後是一個可選的方法
- 「使用提示解析」:一種方法,接收一個字串(假設為來自語言模型的回應)和一個提示(假設為產生此回應的提示),並將其解析為某種結構。提供提示主要是為了讓 OutputParser 想要重試或以某種方式修復輸出,並且需要來自提示的資訊才能這樣做。
開始使用
下面我們將介紹主要類型的輸出解析器 PydanticOutputParser
。
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
from pydantic import BaseModel, Field, model_validator
model = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.0)
# 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")
# You can add custom validation logic easily with Pydantic.
@model_validator(mode="before")
@classmethod
def question_ends_with_question_mark(cls, values: dict) -> dict:
setup = values.get("setup")
if setup and setup[-1] != "?":
raise ValueError("Badly formed question!")
return values
# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# And a query intended to prompt a language model to populate the data structure.
prompt_and_model = prompt | model
output = prompt_and_model.invoke({"query": "Tell me a joke."})
parser.invoke(output)
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')
LCEL
輸出解析器實作了 Runnable 介面,這是 LangChain 運算式語言 (LCEL) 的基本建構模組。這表示它們支援 invoke
、ainvoke
、stream
、astream
、batch
、abatch
、astream_log
呼叫。
輸出解析器接受字串或 BaseMessage
作為輸入,並且可以返回任意類型。
parser.invoke(output)
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')
除了手動調用解析器之外,我們也可以將其添加到我們的 Runnable
序列中
chain = prompt | model | parser
chain.invoke({"query": "Tell me a joke."})
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')
雖然所有解析器都支援串流介面,但只有某些解析器可以透過部分解析的物件進行串流,因為這高度依賴於輸出類型。無法建構部分物件的解析器將只產生完全解析的輸出。
例如,SimpleJsonOutputParser
可以透過部分輸出進行串流
from langchain.output_parsers.json import SimpleJsonOutputParser
json_prompt = PromptTemplate.from_template(
"Return a JSON object with an `answer` key that answers the following question: {question}"
)
json_parser = SimpleJsonOutputParser()
json_chain = json_prompt | model | json_parser
API 參考:SimpleJsonOutputParser
list(json_chain.stream({"question": "Who invented the microscope?"}))
[{},
{'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": "Tell me a joke."}))
[Joke(setup='Why did the tomato turn red?', punchline=''),
Joke(setup='Why did the tomato turn red?', punchline='Because'),
Joke(setup='Why did the tomato turn red?', punchline='Because it'),
Joke(setup='Why did the tomato turn red?', punchline='Because it saw'),
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the'),
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad'),
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing'),
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')]