LangChain內(nèi)部提供了很多標(biāo)準(zhǔn)的、可以擴(kuò)展的接口,以及集成了不少第三方工具,了解這些組件也是非常重要的,后面我們開發(fā)LangChain都需要使用到這些Component,下面我們介紹它們,后面會(huì)針對(duì)場(chǎng)景來介紹如何使用LangChain進(jìn)行開發(fā):
Chat models
第一次看到Chat models和LLMs有點(diǎn)搞不懂這兩個(gè)有什么區(qū)別,我理解Chat models是用來逐步替代LLMs的。
它的輸入是 messages,返回也是 messages(不是string)。支持為對(duì)話的消息賦予不同的roles,用來區(qū)分AI、users、system messages等。
不過為了能很好的替代LLMs,也支持接受strings作為輸入,內(nèi)部會(huì)將其轉(zhuǎn)化為HumanMessage。
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4")
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="Translate the following from English into Italian"),
HumanMessage(content="hi!"),
]
model.invoke(messages)
LLMs
LLMs 接受string作為輸入,返回也是string。
其實(shí)目前通過LangChain的wrappers,也支持了接受message作為輸入。
from langchain_openai import OpenAI
llm = OpenAI()
question = "What NFL team won the Super Bowl in the year Justin Beiber was born?"
llm.invoke(question)
看的出來,和ChatModels的輸入類型,還是有區(qū)別的。
Messages
messages這個(gè)類型,無疑是非常的重要了,所有的messages都有幾個(gè)屬性需要注意:
- role:描述WHO,saying了這個(gè)消息
- content:消息的內(nèi)容
- string:大部分的消息類型都是如此
- diction 列表:用于多模態(tài)輸入,比如包括了input type,input location等
- response_metadata:幫助我們更好的理解返回?cái)?shù)據(jù)類型
HumanMessage
表示用戶的輸入message。
AIMessage
表示model的消息,除了content屬性,這個(gè)消息也包括了response_metadata。
tool_calls:這個(gè)字段也是可能由AIMessage生成的,表示的是需要調(diào)用工具了。
- name:tool的名稱
- args:tool的參數(shù)
- id:tool call的id
SystemMessage
其實(shí)是告訴model的角色,并非所有model支持。
FunctionMessage
function call的結(jié)果。除了role,content字段,還提供了name字段,告訴function的名字。
ToolMessage
表示的是tool call的結(jié)果。調(diào)用工具的時(shí)候,除了role和content,還會(huì)產(chǎn)生tool_call_id參數(shù)。
Prompt templates
用于將用戶的input、parameters,翻譯為instructions。
接受一個(gè)dictionary類型的input,每個(gè)key表示的是變量;輸出的是PromptValue,這個(gè)PromptValue可以傳給LLM、ChatModel,也可以轉(zhuǎn)為string、message。
我們來看幾種prompt templates:
- String PromptTemplates
用于format一個(gè)string,通常用于簡(jiǎn)單的inputs。
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")
prompt_template.invoke({"topic": "cats"})
- Chat PromptTemplates
用于format一個(gè)messages列表,示例如下:
from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
("user", "Tell me a joke about {topic}")
])
prompt_template.invoke({"topic": "cats"})
- MessagesPlaceholder
這個(gè)也挺有用,主要用于將messages列表,添加到某個(gè)特別的地方。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
MessagesPlaceholder("msgs")
])
prompt_template.invoke({"msgs": [HumanMessage(content="hi!"), AIMessage(content="ciao!")]})
有意思的是,也可以用ChatPromptTemplate來實(shí)現(xiàn)。
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
("placeholder", "{msgs}") # <-- This is the changed part
])
Example selectors
有時(shí)候希望提供一些樣例,可以這么做:
from langchain.prompts.example_selector.base import BaseExampleSelector
from typing import Dict, List
import numpy as np
class CustomExampleSelector(BaseExampleSelector):
def __init__(self, examples: List[Dict[str, str]]):
self.examples = examples
def add_example(self, example: Dict[str, str]) -> None:
"""Add new example to store for a key."""
self.examples.append(example)
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""Select which examples to use based on the inputs."""
return np.random.choice(self.examples, size=2, replace=False)
examples = [
{"foo": "1"},
{"foo": "2"},
{"foo": "3"}
]
# Initialize example selector.
example_selector = CustomExampleSelector(examples)
# Select examples
example_selector.select_examples({"foo": "foo"})
# -> array([{'foo': '2'}, {'foo': '3'}], dtype=object)
# Add new example to the set of examples
example_selector.add_example({"foo": "4"})
example_selector.examples
# -> [{'foo': '1'}, {'foo': '2'}, {'foo': '3'}, {'foo': '4'}]
# Select examples
example_selector.select_examples({"foo": "foo"})
Output parsers
指的是parsers,接收model的output,解析為更加結(jié)構(gòu)化的內(nèi)容。
越來越多的models,支持function/tool調(diào)用,建議優(yōu)先使用function/tool調(diào)用,而非output parser。
LangChain有很多的output parser,不過這里要解釋一些概念,再來說明一下有哪些output parser。
- name:output parser 的名稱
- streaming的支持:output parser是否支持streaming
- 是否有format指令:output parser是否有format指令
- 是否調(diào)用LLM:output parser是否調(diào)用了LLM,一般用于糾正一些錯(cuò)誤的格式
- 輸入類型:期望的輸入類型。有一些functions需要message有具體的kwargs(關(guān)鍵字參數(shù))
- 輸出類型:parser的輸出類型
- 描述:對(duì)outputparser的補(bǔ)充性描述
| Name | Supports Streaming | Has Format Instructions | Call's LLM | Input Type | Output Type | Desc |
|---|---|---|---|---|---|---|
| Json | Y | Y | Str/Message | JSON Object | ||
| XML | Y | Y | Str/Message | dict | ||
| CSV | Y | Y | Str/Message | List[str] | ||
| OutputFixing | Y | Str/Message | ||||
| RetryWithError | Y | Str/Message | ||||
| Pydantic | Y | Str/Message | pydantic.BaseModel | |||
| YAML | Y | Str/Message | pydantic.BaseModel | |||
| PandasDataFrame | Y | Str/Message | dict | |||
| Enum | Y | Str/Message | Enum | |||
| Datatime | Y | Str/Message | datetime.datetime | |||
| Structured | Y | Str/Message | Dict[str, str] |
Chat history
我們知道LLM有時(shí)候是需要了解對(duì)話歷史的,這樣方便上下文對(duì)話。
對(duì)話系統(tǒng)需要能夠訪問到past messages。
ChatHistory可以用來包裹c(diǎn)hain,會(huì)跟進(jìn)inputs、outputs,將消息加入到一個(gè)message database,后面作為模型的輸入。
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
conversational_rag_chain = RunnableWithMessageHistory(
rag_chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
output_messages_key="answer",
)