LangChain内部提供了很多标准的、可以扩展的接口,以及集成了不少第三方工具,了解这些组件也是非常重要的,后面我们开发LangChain都需要使用到这些Component,下面我们介绍它们,后面会针对场景来介绍如何使用LangChain进行开发:
Chat models
第一次看到Chat models和LLMs有点搞不懂这两个有什么区别,我理解Chat models是用来逐步替代LLMs的。
它的输入是 messages,返回也是 messages(不是string)。支持为对话的消息赋予不同的roles,用来区分AI、users、system messages等。
不过为了能很好的替代LLMs,也支持接受strings作为输入,内部会将其转化为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。
其实目前通过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的输入类型,还是有区别的。
Messages
messages这个类型,无疑是非常的重要了,所有的messages都有几个属性需要注意:
- role:描述WHO,saying了这个消息
- content:消息的内容
- string:大部分的消息类型都是如此
- diction 列表:用于多模态输入,比如包括了input type,input location等
- response_metadata:帮助我们更好的理解返回数据类型
HumanMessage
表示用户的输入message。
AIMessage
表示model的消息,除了content属性,这个消息也包括了response_metadata。
tool_calls:这个字段也是可能由AIMessage生成的,表示的是需要调用工具了。
- name:tool的名称
- args:tool的参数
- id:tool call的id
SystemMessage
其实是告诉model的角色,并非所有model支持。
FunctionMessage
function call的结果。除了role,content字段,还提供了name字段,告诉function的名字。
ToolMessage
表示的是tool call的结果。调用工具的时候,除了role和content,还会产生tool_call_id参数。
Prompt templates
用于将用户的input、parameters,翻译为instructions。
接受一个dictionary类型的input,每个key表示的是变量;输出的是PromptValue,这个PromptValue可以传给LLM、ChatModel,也可以转为string、message。
我们来看几种prompt templates:
- String PromptTemplates
用于format一个string,通常用于简单的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一个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
这个也挺有用,主要用于将messages列表,添加到某个特别的地方。
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来实现。
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
("placeholder", "{msgs}") # <-- This is the changed part
])
Example selectors
有时候希望提供一些样例,可以这么做:
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,解析为更加结构化的内容。
越来越多的models,支持function/tool调用,建议优先使用function/tool调用,而非output parser。
LangChain有很多的output parser,不过这里要解释一些概念,再来说明一下有哪些output parser。
- name:output parser 的名称
- streaming的支持:output parser是否支持streaming
- 是否有format指令:output parser是否有format指令
- 是否调用LLM:output parser是否调用了LLM,一般用于纠正一些错误的格式
- 输入类型:期望的输入类型。有一些functions需要message有具体的kwargs(关键字参数)
- 输出类型:parser的输出类型
- 描述:对outputparser的补充性描述
| 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有时候是需要了解对话历史的,这样方便上下文对话。
对话系统需要能够访问到past messages。
ChatHistory可以用来包裹chain,会跟进inputs、outputs,将消息加入到一个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",
)