前序
之前了解了Models,Prompt,但有些資料又把這塊與輸出合稱為模型輸入輸出(Model I/O)?:這是與各種大語言模型進行交互的基本組件。它允許開發者管理提示(prompt),通過通用接口調用語言模型,并從模型輸出中提取信息。簡單來說,這個組件負責與大語言模型“對話”,將請求傳遞給模型,并接收回復?。
這篇文章就補充一下這個O(output)的內容。
輸出解釋器
Output Parsers(輸出解析器),是langchain中提供給我們對模型響應內容進行格式化輸出的。LLM的輸出為文本,但在程序中除了顯示文本,如果希望獲得更多不同的結構化數據時,就可以使用langchain提供的輸出解析器(Output Parsers)來完成了。輸出解析器(Output Parsers)實際上就是結構化語言模型提供的響應處理工具類,其提供了如下兩個方法給開發者使用,也是這些響應類必須實現的兩個方法:
get_format_instructions() -> str :返回一個包含語言模型如何格式化輸出的指令字符串。
invoke()-> Any:接受一個結構化言模型的響應對象,并將其解析為指定格式
Str輸出解析器
import os
from langchain_deepseek import ChatDeepSeek
from langchain.prompts import PromptTemplate# 初始化模型
os.environ['DEEPSEEK_API_KEY'] = "sk-e2xxx"
chat_model = ChatDeepSeek(model="deepseek-chat",temperature=0.4,max_tokens=None,timeout=None,max_retries=2,)# 創建提示模板
prompt_template = PromptTemplate(input_variables=["context"],template="基于給定的文案,以幽默詼諧的風格生成一段回答文本:{context}",
)# 使用模型生成文本
context = "成都今天出太陽了,天氣真好,我們翹班出去玩吧。"
prompt = prompt_template.format(context=context)
result = chat_model.invoke(prompt)
print(result)
回答內容很長,但是我們需要的只有content那串。引入StrOutputParser
,把返回的結果,經過解析,就只有文本結果內容了
...
from langchain_core.output_parsers import StrOutputParser
res = StrOutputParser().invoke(input=result)
print(res)
List輸出解析器
import os
from langchain_deepseek import ChatDeepSeek# 初始化模型
os.environ['DEEPSEEK_API_KEY'] = "sk-e24324xxx"
chat_model = ChatDeepSeek(model="deepseek-chat",temperature=0.4,max_tokens=None,timeout=None,max_retries=2,)
from langchain_core.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import PromptTemplateparser = CommaSeparatedListOutputParser()prompt = PromptTemplate.from_template(template="請列出5個{item}的不同叫法.\n{format_instructions}\n",partial_variables={"format_instructions": parser.get_format_instructions()},
)messages = prompt.invoke({'item': '土豆'})
print(messages)result = chat_model.invoke(messages)
res = parser.invoke(result)
print(res)"""
text='請列出5個土豆的不同叫法.\nYour response should be a list of comma separated values, eg: `foo, bar, baz` or `foo,bar,baz`\n'
['馬鈴薯', '洋芋', '土豆', '薯仔', '地蛋']
"""
Json輸出解釋器
其他內容大差不差,把 CommaSeparatedListOutputParser
換成JsonOutputParser
就行
from pydantic import BaseModel, Field
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplateclass JsonParser(BaseModel):question: str = Field(description='問題')answer: str = Field(description='答案')parser = JsonOutputParser(pydantic_object=JsonParser)prompt = PromptTemplate(template="回答問題.\n{format_instructions}\n{query}\n",input_variables=["query"],partial_variables={"format_instructions": parser.get_format_instructions()},
)# print(parser.get_format_instructions())messages = prompt.invoke({'query': '講一個腦筋急轉彎的問題和答案。'})
response = chat_model.invoke(messages)
content = parser.invoke(response)
print(content) """
{'question': '什么東西越洗越臟?', 'answer': '水'}
"""
stream輸出解析器
如果輸出內容很長,一直等處理完才返回結果也不大好,比如我們正常使用在線大模型,它都是幾個字幾個字往外吐的,不是最后直接給答案。
這里就需要用到stream輸出解析器
import os
from langchain_deepseek import ChatDeepSeek# 初始化模型
os.environ['DEEPSEEK_API_KEY'] = "sk-e24xxx"
chat_model = ChatDeepSeek(model="deepseek-chat",temperature=0.4,max_tokens=None,timeout=None,max_retries=2,)from langchain_ollama.llms import OllamaLLM
from langchain_core.prompts import PromptTemplateprompt_template = PromptTemplate.from_template("你是一名名經驗豐富的{role},{ability},{prompt}")
messages = prompt_template.invoke({"role": "修仙小說作家", "ability": "熟悉各種神話傳說和修仙小說","prompt": "請你寫一部與牧神記類似的小說,要求:全書至少600章,每一章字數在8000字以上,劇情緊湊,各個角色的個性分明"})
print(messages)for chunk in chat_model.stream(messages):print(chunk, end="", flush=True)
或者這樣不好看,也可以用上面的字符串輸出解釋器來處理一下輸出內容
from langchain_core.output_parsers import StrOutputParser# 修改一下輸出這里
for chunk in chat_model.stream(messages):print(StrOutputParser().invoke(chunk), end="", flush=True)
到這里,就就開始慢慢幫我們寫小說了。
Cache
如果每次問同樣的,都調用大模型推理,那么會比較耗💰,可以把問題和答案記錄下來,以后遇到同樣的問題,則不必再使用大模型推理。
基于langchain提供的輸出緩存,讓LLM在遇到同一prompt時,直接調用緩存中的結果,也可以達到加速的效果。
# -*- coding: utf-8 -*-
# @Author : John
# @Time : 2025/02/27
# @File : langchain_cache.pyimport time
import redis
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
# from langchain_community.cache import InMemoryCache # 把輸出緩存到內存中
from langchain_community.cache import RedisCache # 把輸出緩存到Redis數據庫中
from langchain.globals import set_llm_cacheimport os
from langchain_deepseek import ChatDeepSeek# 初始化模型
os.environ['DEEPSEEK_API_KEY'] = "sk-e243xxxf"
chat_model = ChatDeepSeek(model="deepseek-chat",temperature=0.4, # temperature 溫度值,數值越趨向0,回答越謹慎,數值越趨向1,回答則越腦洞大開,主要控制大模型輸出的創造力max_tokens=None,timeout=None,max_retries=2,)prompt_template = PromptTemplate.from_template("你是一個產品顧問。請給公司剛生產出來的 {production},取一個好聽的名字和廣告語。")
messages = prompt_template.invoke({"production": "充電寶"})# 開啟緩存
redis = redis.Redis("localhost", port=6379, password="qwe123", db=2)
set_llm_cache(RedisCache(redis))timers = []
for _ in range(5):t1 = time.time()response = chat_model.invoke(messages)t2 = time.time()print(t2 - t1, response)timers.append(t2 - t1)print(timers)
可以看到輸出結果都是一致的。只有第一次真正請求了deepseek,花了65s獲得了結果,后面都是第一次結果保存到redis,從redis獲取的。