文章目录
www.aidoczh.com已经系统的将LangChain官网文档翻译成中文,您可以去http://www.aidoczh.com/langchain/v0.2/docs/introduction/ 查看文档,该网站www.aidoczh.com主要是将AI工具官方文档翻译成中文,还有其他工具文档可以看。
1. 聊天模型快速入门
聊天模型是语言模型的一种变体。虽然聊天模型在内部使用语言模型,但它们使用的界面略有不同。与使用“输入文本,输出文本”的 API 不同,它们使用的是以“聊天消息”为输入和输出的界面。
设置
在本示例中,我们需要安装 OpenAI 合作伙伴包:
pip install langchain-openai
访问 API 需要 API 密钥,您可以通过创建帐户并转到此处获取。一旦获得密钥,我们需要将其设置为环境变量,方法是运行:
export OPENAI_API_KEY="..."
如果您不想设置环境变量,可以在初始化 OpenAI LLM 类时直接通过 openai_api_key
命名参数传递密钥:
from langchain_openai import ChatOpenAI
chat = ChatOpenAI(openai_api_key="...")
否则,您可以不带任何参数进行初始化:
from langchain_openai import ChatOpenAI
chat = ChatOpenAI()
消息
聊天模型界面是基于消息而不是原始文本的。LangChain 目前支持的消息类型有 AIMessage
、HumanMessage
、SystemMessage
、FunctionMessage
和 ChatMessage
– ChatMessage
接受一个任意的角色参数。大多数情况下,您将只处理 HumanMessage
、AIMessage
和 SystemMessage
。
LCEL
聊天模型实现了Runnable 接口,这是LangChain 表达语言 (LCEL)的基本构建块。这意味着它们支持 invoke
、ainvoke
、stream
、astream
、batch
、abatch
、astream_log
调用。
聊天模型接受 List[BaseMessage]
作为输入,或者可以被强制转换为消息的对象,包括 str
(转换为 HumanMessage
)和 PromptValue
。
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="You're a helpful assistant"),
HumanMessage(content="What is the purpose of model regularization?"),
]
chat.invoke(messages)
AIMessage(content="The purpose of model regularization is to prevent overfitting in machine learning models. Overfitting occurs when a model becomes too complex and starts to fit the noise in the training data, leading to poor generalization on unseen data. Regularization techniques introduce additional constraints or penalties to the model's objective function, discouraging it from becoming overly complex and promoting simpler and more generalizable models. Regularization helps to strike a balance between fitting the training data well and avoiding overfitting, leading to better performance on new, unseen data.")
for chunk in chat.stream(messages):
print(chunk.content, end="", flush=True)
The purpose of model regularization is to prevent overfitting and improve the generalization of a machine learning model. Overfitting occurs when a model is too complex and learns the noise or random variations in the training data, which leads to poor performance on new, unseen data. Regularization techniques introduce additional constraints or penalties to the model's learning process, discouraging it from fitting the noise and reducing the complexity of the model. This helps to improve the model's ability to generalize well and make accurate predictions on unseen data.
chat.batch([messages])
[AIMessage(content="The purpose of model regularization is to prevent overfitting in machine learning models. Overfitting occurs when a model becomes too complex and starts to learn the noise or random fluctuations in the training data, rather than the underlying patterns or relationships. Regularization techniques add a penalty term to the model's objective function, which discourages the model from becoming too complex and helps it generalize better to new, unseen data. This improves the model's ability to make accurate predictions on new data by reducing the variance and increasing the model's overall performance.")]
await chat.ainvoke(messages)
AIMessage(content='The purpose of model regularization is to prevent overfitting in machine learning models. Overfitting occurs when a model becomes too complex and starts to memorize the training data instead of learning general patterns and relationships. This leads to poor performance on new, unseen data.\n\nRegularization techniques introduce additional constraints or penalties to the model during training, discouraging it from becoming overly complex. This helps to strike a balance between fitting the training data well and generalizing to new data. Regularization techniques can include adding a penalty term to the loss function, such as L1 or L2 regularization, or using techniques like dropout or early stopping. By regularizing the model, it encourages it to learn the most relevant features and reduce the impact of noise or outliers in the data.')
async for chunk in chat.astream(messages):
print(chunk.content, end="", flush=True)
The purpose of model regularization is to prevent overfitting in machine learning models. Overfitting occurs when a model becomes too complex and starts to memorize the training data instead of learning the underlying patterns. Regularization techniques help in reducing the complexity of the model by adding a penalty to the loss function. This penalty encourages the model to have smaller weights or fewer features, making it more generalized and less prone to overfitting. The goal is to find the right balance between fitting the training data well and being able to generalize well to unseen data.
async for chunk in chat.astream_log(messages):
print(chunk)
RunLogPatch({'op': 'replace',
'path': '',
'value': {'final_output': None,
'id': '754c4143-2348-46c4-ad2b-3095913084c6',
'logs': {},
'streamed_output': []}})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='The')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' purpose')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' of')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' model')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' regularization')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' is')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' to')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' prevent')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' a')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' machine')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' learning')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' model')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' from')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' over')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='fit')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='ting')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' the')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' training')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' data')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' and')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' improve')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' its')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' general')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='ization')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' ability')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='.')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' Over')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='fit')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='ting')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' occurs')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' when')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' a')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 变得')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 太')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 复杂')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 并且')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 学会')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 适应')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=' 噪音')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='或者')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='随机')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='波动')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='而不是')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='捕捉')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='数据')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='中的')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='基本')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='模式')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='和')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='关系')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='。')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='正则化')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='技术')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='引入')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='一个')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='惩罚项')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='到')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='模型')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='的')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='目标')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='函数')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=',')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='这')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='有助于')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='控制')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='模型')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='的')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='复杂度')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='并且')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='减少')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='过拟合')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='风险')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content=',')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='提高')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='在')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='未见过的')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='数据上')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='的')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='性能')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='。')})
RunLogPatch({'op': 'add',
'path': '/streamed_output/-',
'value': AIMessageChunk(content='')})
RunLogPatch({'op': 'replace',
'path': '/final_output',
'value': {'generations': [[{'generation_info': {'finish_reason': 'stop'},
'message': AIMessageChunk(content="模型正则化的目的是防止机器学习模型过度拟合训练数据,提高其泛化能力。过度拟合发生在模型变得过于复杂并学会拟合训练数据中的噪音或随机波动,而不是捕捉数据中的基本模式和关系。正则化技术引入一个惩罚项到模型的目标函数,这会阻止模型变得过于复杂。这有助于控制模型的复杂度并减少过拟合风险,从而在未见过的数据上获得更好的性能。"),
'text': '模型正则化的目的是防止机器学习模型过度拟合训练数据,提高其泛化能力。'}}]}})
LangSmith
所有的 ChatModel
都内置了 LangSmith 追踪功能。只需设置以下环境变量:
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY=<your-api-key>
任何 ChatModel
的调用(无论是嵌套在链中还是独立调用)都将自动被追踪。追踪将包括输入、输出、延迟、令牌使用情况、调用参数、环境参数等。点击这里查看示例:https://smith.langchain.com/public/a54192ae-dd5c-4f7a-88d1-daa1eaba1af7/r。
在 LangSmith 中,您可以为任何追踪提供反馈,编译带注释的数据集进行评估,在 playground 中调试性能等。
[Legacy] __call__
输入消息 -> 输出消息
为了方便起见,您还可以将聊天模型视为可调用对象。通过向聊天模型传递一个或多个消息,您可以获取聊天完成的响应。
from langchain_core.messages import HumanMessage, SystemMessage
chat(
[
HumanMessage(
content="Translate this sentence from English to French: I love programming."
)
]
)
AIMessage(content="J'adore la programmation.")
OpenAI 的聊天模型支持多个消息作为输入。有关更多信息,请参阅这里。以下是向聊天模型发送系统消息和用户消息的示例:
messages = [
SystemMessage(
content="You are a helpful assistant that translates English to French."
),
HumanMessage(content="I love programming."),
]
chat(messages)
AIMessage(content="J'adore la programmation.")
[Legacy] generate
批量调用,更丰富的输出
您可以进一步使用 generate
为多组消息生成完成。这将返回一个带有额外 message
参数的 LLMResult
。这将包括有关每个生成的更多信息,超出返回消息的信息(例如完成原因),以及有关完整 API 调用的其他信息(例如使用的总令牌数)。
batch_messages = [
[
SystemMessage(
content="You are a helpful assistant that translates English to French."
),
HumanMessage(content="I love programming."),
],
[
SystemMessage(
content="You are a helpful assistant that translates English to French."
),
HumanMessage(content="I love artificial intelligence."),
],
]
result = chat.generate(batch_messages)
result
LLMResult(generations=[[ChatGeneration(text="J'adore programmer.", generation_info={'finish_reason': 'stop'}, message=AIMessage(content="J'adore programmer."))], [ChatGeneration(text="J'adore l'intelligence artificielle.", generation_info={'finish_reason': 'stop'}, message=AIMessage(content="J'adore l'intelligence artificielle."))]], llm_output={'token_usage': {'prompt_tokens': 53, 'completion_tokens': 18, 'total_tokens': 71}, 'model_name': 'gpt-3.5-turbo'}, run=[RunInfo(run_id=UUID('077917a9-026c-47c4-b308-77b37c3a3bfa')), RunInfo(run_id=UUID('0a70a0bf-c599-4f51-932a-c7d42202c984'))])
您可以从这个 LLMResult
中获取诸如令牌使用情况之类的信息:
result.llm_output
{'token_usage': {'prompt_tokens': 53,
'completion_tokens': 18,
'total_tokens': 71},
'model_name': 'gpt-3.5-turbo'}
2. 聊天记录缓存
LangChain为聊天模型提供了一个可选的缓存层。这有两个好处:
- 如果您经常多次请求相同的完成结果,它可以通过减少您向LLM提供程序发出的API调用次数来为您节省金钱。
- 通过减少您向LLM提供程序发出的API调用次数,它可以加快您的应用程序速度。
from langchain.globals import set_llm_cache
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
内存缓存
%%time
from langchain.cache import InMemoryCache
set_llm_cache(InMemoryCache())
# 第一次,因为尚未缓存,所以需要更长时间
llm.predict("Tell me a joke")
CPU times: user 17.7 ms, sys: 9.35 ms, total: 27.1 ms
Wall time: 801 ms
%%time
# 第二次,速度更快
llm.predict("Tell me a joke")
CPU times: user 1.42 ms, sys: 419 µs, total: 1.83 ms
Wall time: 1.83 ms
SQLite缓存
!rm .langchain.db
# 我们可以使用SQLite缓存做同样的事情
from langchain.cache import SQLiteCache
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
%%time
# 第一次,因为尚未缓存,所以需要更长时间
llm.predict("Tell me a joke")
CPU times: user 23.2 ms, sys: 17.8 ms, total: 40.9 ms
Wall time: 592 ms
%%time
# 第二次,速度更快
llm.predict("Tell me a joke")
CPU times: user 5.61 ms, sys: 22.5 ms, total: 28.1 ms
Wall time: 47.5 ms
3. 自定义聊天模型
在本指南中,我们将学习如何使用 LangChain 抽象创建自定义聊天模型。
将您的 LLM 与标准的 ChatModel
接口包装起来,可以让您在现有的 LangChain 程序中使用您的 LLM,并且只需进行最少的代码修改!
作为额外福利,您的 LLM 将自动成为 LangChain 的 Runnable
,并且将从一些优化中受益(例如,通过线程池进行批处理),异步支持,astream_events
API 等等。
输入和输出
首先,我们需要讨论消息,这些是聊天模型的输入和输出。
消息
聊天模型将消息作为输入,并返回消息作为输出。
LangChain 有一些内置的消息类型:
SystemMessage
:用于启动 AI 行为,通常作为一系列输入消息中的第一个传入。HumanMessage
:表示与聊天模型交互的人的消息。AIMessage
:表示来自聊天模型的消息。这可以是文本,也可以是请求调用工具。FunctionMessage
/ToolMessage
:用于将工具调用的结果传递回模型的消息。
[!NOTE]
ToolMessage
和FunctionMessage
紧密遵循 OpenAI 的function
和tool
参数。这是一个快速发展的领域,随着更多模型添加函数调用功能,预计会对此模式进行补充。
from langchain_core.messages import (
AIMessage,
BaseMessage,
FunctionMessage,
HumanMessage,
SystemMessage,
ToolMessage,
)
流式变体
所有聊天消息都有一个包含 Chunk
的流式变体。
from langchain_core.messages import (
AIMessageChunk,
FunctionMessageChunk,
HumanMessageChunk,
SystemMessageChunk,
ToolMessageChunk,
)
这些块在从聊天模型输出流时使用,并且它们都定义了一个可添加的属性!
AIMessageChunk(content="你好") + AIMessageChunk(content=" 世界!")
AIMessageChunk(content='你好 世界!')
简单聊天模型
从 SimpleChatModel
继承非常适合用于原型设计!
它不允许您实现可能希望从聊天模型中获得的所有功能,但它很快实现,如果需要更多功能,可以过渡到下面展示的 BaseChatModel
。
让我们实现一个聊天模型,它会回显提示的最后 n
个字符!
您需要实现以下内容:
_call
方法 - 用于从提示生成聊天结果。
此外,您可以指定以下内容:
_identifying_params
属性 - 用于记录模型参数化。
可选的:
_stream
- 用于实现流式处理。
基础聊天模型
让我们实现一个聊天模型,它会回显提示中最后一条消息的前 n
个字符!
为此,我们将从 BaseChatModel
继承,并且我们需要实现以下方法/属性:
此外,您可以指定以下内容:
从 BaseChatModel
继承,这是一个较低级别的类,并实现以下方法:
_generate
- 用于从提示生成聊天结果。_llm_type
属性 - 用于唯一标识模型的类型。用于记录。
可选的:
_stream
- 用于实现流式处理。_agenerate
- 用于实现本机异步方法。_astream
- 用于实现_stream
的异步版本。_identifying_params
属性 - 用于记录模型参数化。
[!WARNING]
目前,要使异步流式处理正常工作(通过
astream
),您必须提供_astream
的实现。默认情况下,如果未提供
_astream
,则异步流式处理将退回到不支持逐标记流式处理的_agenerate
。
实现
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional
from langchain_core.callbacks import (
AsyncCallbackManagerForLLMRun,
CallbackManagerForLLMRun,
)
from langchain_core.language_models import BaseChatModel, SimpleChatModel
from langchain_core.messages import AIMessageChunk, BaseMessage, HumanMessage
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
from langchain_core.runnables import run_in_executor
class CustomChatModelAdvanced(BaseChatModel):
"""一个自定义聊天模型,回显输入的前 `n` 个字符。
在为 LangChain 贡献实现时,仔细记录模型,包括初始化参数,
包括如何初始化模型的示例,并包括任何相关的
链接到底层模型文档或 API。
示例:
.. code-block:: python
model = CustomChatModel(n=2)
result = model.invoke([HumanMessage(content="hello")])
result = model.batch([[HumanMessage(content="hello")],
[HumanMessage(content="world")]])
"""
n: int
"""要回显的提示的最后一条消息的字符数。"""
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
"""覆盖 _generate 方法以实现聊天模型逻辑。
这可以是对 API 的调用,对本地模型的调用,或者生成对输入提示的响应的任何其他实现。
Args:
messages: 由消息列表组成的提示。
stop: 模型应停止生成的字符串列表。
如果生成由于停止令牌而停止,则停止令牌本身
应包含在输出中。目前尚未强制执行此操作
跨模型,但最好遵循此操作,因为
这样可以更轻松地解析模型的输出
下游并了解为什么生成停止。
run_manager: 具有 LLM 回调的运行管理器。
"""
last_message = messages[-1]
tokens = last_message.content[: self.n]
message = AIMessage(content=tokens)
generation = ChatGeneration(message=message)
return ChatResult(generations=[generation])
def _stream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> Iterator[ChatGenerationChunk]:
"""流式处理模型的输出。
如果模型支持流式处理,则应实现此方法。如果模型不支持流式处理,
则不要实现它。在这种情况下,流式处理请求将自动处理
由 _generate 方法处理。
Args:
messages: 由消息列表组成的提示。
stop: 模型应停止生成的字符串列表。
如果生成由于停止令牌而停止,则停止令牌本身
应包含在输出中。目前尚未强制执行此操作
跨模型,但最好遵循此操作,因为
这样可以更轻松地解析模型的输出
下游并了解为什么生成停止。
run_manager: 具有 LLM 回调的运行管理器。
"""
last_message = messages[-1]
tokens = last_message.content[: self.n]
for token in tokens:
chunk = ChatGenerationChunk(message=AIMessageChunk(content=token))
if run_manager:
run_manager.on_llm_new_token(token, chunk=chunk)
yield chunk
async def _astream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> AsyncIterator[ChatGenerationChunk]:
"""astream 的异步变体。
如果未提供,则默认行为是委托给 _generate 方法。
下面的实现将委托给 `_stream`,并将
在单独的线程中启动它。
如果您能够本机支持异步,请务必这样做!
"""
result = await run_in_executor(
None,
self._stream,
messages,
stop=stop,
run_manager=run_manager.get_sync() if run_manager else None,
**kwargs,
)
for chunk in result:
yield chunk
@property
def _llm_type(self) -> str:
"""获取此聊天模型使用的语言模型类型。"""
return "echoing-chat-model-advanced"
@property
def _identifying_params(self) -> Dict[str, Any]:
"""返回标识参数的字典。"""
return {"n": self.n}
[!TIP]
_astream
实现使用run_in_executor
来在单独的线程中启动同步_stream
。如果要重用
_stream
实现,可以使用此技巧,但如果能够实现本机异步代码,那将是更好的解决方案,因为该代码将具有更少的开销。
让我们进行测试 🧪
聊天模型将实现 LangChain 的标准 Runnable
接口,许多 LangChain 抽象支持!
model = CustomChatModelAdvanced(n=3)
model.invoke(
[
HumanMessage(content="你好!"),
AIMessage(content="你好,人类!"),
HumanMessage(content="喵!"),
]
)
AIMessage(content='喵')
model.invoke("你好")
AIMessage(content='你好')
model.batch(["你好", "再见"])
for chunk in model.stream("cat"):
print(chunk.content, end="|")
c|a|t|
请查看模型中 _astream
的实现!如果您不实现它,那么将不会有任何输出流出来!
async for chunk in model.astream("cat"):
print(chunk.content, end="|")
c|a|t|
让我们尝试使用 astream 事件 API,这也有助于双重检查所有回调是否已实现!
async for event in model.astream_events("cat", version="v1"):
print(event)
{'event': 'on_chat_model_start', 'run_id': 'e03c0b21-521f-4cb4-a837-02fed65cf1cf', 'name': 'CustomChatModelAdvanced', 'tags': [], 'metadata': {}, 'data': {'input': 'cat'}}
{'event': 'on_chat_model_stream', 'run_id': 'e03c0b21-521f-4cb4-a837-02fed65cf1cf', 'tags': [], 'metadata': {}, 'name': 'CustomChatModelAdvanced', 'data': {'chunk': AIMessageChunk(content='c')}}
{'event': 'on_chat_model_stream', 'run_id': 'e03c0b21-521f-4cb4-a837-02fed65cf1cf', 'tags': [], 'metadata': {}, 'name': 'CustomChatModelAdvanced', 'data': {'chunk': AIMessageChunk(content='a')}}
{'event': 'on_chat_model_stream', 'run_id': 'e03c0b21-521f-4cb4-a837-02fed65cf1cf', 'tags': [], 'metadata': {}, 'name': 'CustomChatModelAdvanced', 'data': {'chunk': AIMessageChunk(content='t')}}
{'event': 'on_chat_model_end', 'name': 'CustomChatModelAdvanced', 'run_id': 'e03c0b21-521f-4cb4-a837-02fed65cf1cf', 'tags': [], 'metadata': {}, 'data': {'output': AIMessageChunk(content='cat')}}
/home/eugene/src/langchain/libs/core/langchain_core/_api/beta_decorator.py:86: LangChainBetaWarning: This API is in beta and may change in the future.
warn_beta(
识别参数
LangChain 具有回调系统,允许实现记录器来监视 LLM 应用程序的行为。
还记得之前提到的 _identifying_params
属性吗?
它被传递给回调系统,并可供用户指定的记录器访问。
接下来,我们将实现一个处理程序,只包含一个 on_chat_model_start
事件,以查看 _identifying_params
出现在哪里。
from typing import Union
from uuid import UUID
from langchain_core.callbacks import AsyncCallbackHandler
from langchain_core.outputs import (
ChatGenerationChunk,
ChatResult,
GenerationChunk,
LLMResult,
)
class SampleCallbackHandler(AsyncCallbackHandler):
"""处理来自 LangChain 的异步回调的处理程序。"""
async def on_chat_model_start(
self,
serialized: Dict[str, Any],
messages: List[List[BaseMessage]],
*,
run_id: UUID,
parent_run_id: Optional[UUID] = None,
tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> Any:
"""当聊天模型开始运行时运行。"""
print("---")
print("On chat model start.")
print(kwargs)
model.invoke("meow", stop=["woof"], config={"callbacks": [SampleCallbackHandler()]})
---
On chat model start.
{'invocation_params': {'n': 3, '_type': 'echoing-chat-model-advanced', 'stop': ['woof']}, 'options': {'stop': ['woof']}, 'name': None, 'batch_size': 1}
AIMessage(content='meo')
贡献
我们感谢所有聊天模型集成的贡献。
以下是一个检查清单,以确保您的贡献被添加到 LangChain 中:
文档:
- 模型包含所有初始化参数的文档字符串,因为这些将显示在 APIReference 中。
- 如果模型由服务提供支持,则模型的类文档字符串包含指向模型 API 的链接。
测试:
- 为重写的方法添加单元测试或集成测试。如果您已重写相应的代码,请验证
invoke
、ainvoke
、batch
、stream
是否正常工作。
流式处理(如果您正在实现):
- 通过
_astream
提供异步实现 - 确保调用
on_llm_new_token
回调 - 在生成块之前调用
on_llm_new_token
停止令牌行为:
- 应遵守停止令牌
- 停止令牌应包含在响应中
秘密 API 密钥:
- 如果您的模型连接到 API,则可能会接受 API 密钥作为初始化的一部分。使用 Pydantic 的
SecretStr
类型来处理秘密,以防止在打印模型时意外泄露。
4. 获取对数概率
某些聊天模型可以配置为返回标记级别的对数概率。本指南介绍了如何为多个模型获取对数概率。
OpenAI
安装 LangChain x OpenAI 软件包并设置您的 API 密钥
%pip install -qU langchain-openai
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
为了让 OpenAI API 返回对数概率,我们需要配置 logprobs=True
参数
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo-0125").bind(logprobs=True)
msg = llm.invoke(("human", "how are you today"))
对数概率包含在每个输出消息的 response_metadata
中:
msg.response_metadata["logprobs"]["content"][:5]
[{'token': 'As',
'bytes': [65, 115],
'logprob': -1.5358024,
'top_logprobs': []},
{'token': ' an',
'bytes': [32, 97, 110],
'logprob': -0.028062303,
'top_logprobs': []},
{'token': ' AI',
'bytes': [32, 65, 73],
'logprob': -0.009415812,
'top_logprobs': []},
{'token': ',', 'bytes': [44], 'logprob': -0.07371779, 'top_logprobs': []},
{'token': ' I',
'bytes': [32, 73],
'logprob': -4.298773e-05,
'top_logprobs': []}]
同时也包含在流式消息块中:
ct = 0
full = None
for chunk in llm.stream(("human", "how are you today")):
if ct < 5:
full = chunk if full is None else full + chunk
if "logprobs" in full.response_metadata:
print(full.response_metadata["logprobs"]["content"])
else:
break
ct += 1
[]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}, {'token': ' AI', 'bytes': [32, 65, 73], 'logprob': -0.0093033705, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}, {'token': ' AI', 'bytes': [32, 65, 73], 'logprob': -0.0093033705, 'top_logprobs': []}, {'token': ',', 'bytes': [44], 'logprob': -0.08852102, 'top_logprobs': []}]
5. 追踪令牌使用情况
本文档介绍如何跟踪特定调用的令牌使用情况。目前仅针对 OpenAI API 实现了此功能。
让我们首先看一个极其简单的示例,跟踪单个 Chat 模型调用的令牌使用情况。
from langchain.callbacks import get_openai_callback
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-4")
with get_openai_callback() as cb:
result = llm.invoke("Tell me a joke")
print(cb)
令牌使用情况:24
提示令牌:11
完成令牌:13
成功请求次数:1
总成本(美元):$0.0011099999999999999
在上下文管理器中的任何内容都将被跟踪。以下是一个使用它来跟踪连续多次调用的示例。
with get_openai_callback() as cb:
result = llm.invoke("Tell me a joke")
result2 = llm.invoke("Tell me a joke")
print(cb.total_tokens)
48
如果使用具有多个步骤的链或代理,它将跟踪所有这些步骤。
from langchain.agents import AgentType, initialize_agent, load_tools
from langchain_openai import OpenAI
tools = load_tools(["serpapi", "llm-math"], llm=llm)
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True)
with get_openai_callback() as cb:
response = agent.run(
"Who is Olivia Wilde's boyfriend? What is his current age raised to the 0.23 power?"
)
print(f"Total Tokens: {cb.total_tokens}")
print(f"Prompt Tokens: {cb.prompt_tokens}")
print(f"Completion Tokens: {cb.completion_tokens}")
print(f"Total Cost (USD): ${cb.total_cost}")
> 进入新的 AgentExecutor 链...
调用:使用 'Olivia Wilde 的当前男友' 进行搜索
['Olivia Wilde 迎来了黄金时期,因为这位女演员在与 Harry Styles 分手后重新踏入恋爱之旅 — 读取...', "“我不希望在 Olivia 当前伴侣的家中举行仪式,因为 Otis 和 Daisy 可能会在场,” Sudeikis 写道...", "2021 年 2 月:Olivia Wilde 称赞 Harry Styles 的谦逊。在这对新情侣引起轰动一个月后,Wilde 给予她的新男友极大的赞扬...", '一位知情人士向 People 透露,这对新情侣已经约会了一段时间。"他们上周末在加利福尼亚的蒙特西托参加了一场婚礼...', '一位消息人士去年告诉 People,尽管决定分手,但 Wilde 和 Styles 仍然是朋友。"他仍在巡回演出,现在...', '... 爱情生活。“他是你典型的普通人。”消息人士补充说,“她现在不想透露太多,希望保持这段关系...', "多位消息人士称两人因距离和不同优先事项而“休息”约会。“他仍在巡回演出,现在...", '评论。归类于。名人情侣 · 名人约会 · Harry Styles · Jason Sudeikis · Olivia Wilde... 现在具有更黑暗的含义 NYPost.', '... 在拍摄期间约会。尽管这位 39 岁的女演员与这位喜剧演员看起来非常亲密,但他的恋爱状况未知。Olivia...']
调用:使用 'Harry Styles 的当前年龄' 进行搜索
回答:Olivia Wilde 的当前男友是 Harry Styles。让我为您找出他的年龄。
答案:2.169459462491557
Harry Styles 的当前年龄(29 岁)的 0.23 次方约为 2.17。
> 链结束。
总令牌数:1929
提示令牌:1799
完成令牌:130
总成本(美元):$0.06176999999999999
6. 获取对数概率
某些聊天模型可以配置为返回标记级别的对数概率。本指南介绍了如何为多个模型获取对数概率。
OpenAI
安装 LangChain x OpenAI 软件包并设置您的 API 密钥
%pip install -qU langchain-openai
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
为了让 OpenAI API 返回对数概率,我们需要配置 logprobs=True
参数
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo-0125").bind(logprobs=True)
msg = llm.invoke(("human", "how are you today"))
对数概率包含在每个输出消息的 response_metadata
中:
msg.response_metadata["logprobs"]["content"][:5]
[{'token': 'As',
'bytes': [65, 115],
'logprob': -1.5358024,
'top_logprobs': []},
{'token': ' an',
'bytes': [32, 97, 110],
'logprob': -0.028062303,
'top_logprobs': []},
{'token': ' AI',
'bytes': [32, 65, 73],
'logprob': -0.009415812,
'top_logprobs': []},
{'token': ',', 'bytes': [44], 'logprob': -0.07371779, 'top_logprobs': []},
{'token': ' I',
'bytes': [32, 73],
'logprob': -4.298773e-05,
'top_logprobs': []}]
同时也包含在流式消息块中:
ct = 0
full = None
for chunk in llm.stream(("human", "how are you today")):
if ct < 5:
full = chunk if full is None else full + chunk
if "logprobs" in full.response_metadata:
print(full.response_metadata["logprobs"]["content"])
else:
break
ct += 1
[]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}, {'token': ' AI', 'bytes': [32, 65, 73], 'logprob': -0.0093033705, 'top_logprobs': []}]
[{'token': 'As', 'bytes': [65, 115], 'logprob': -1.7523563, 'top_logprobs': []}, {'token': ' an', 'bytes': [32, 97, 110], 'logprob': -0.019908238, 'top_logprobs': []}, {'token': ' AI', 'bytes': [32, 65, 73], 'logprob': -0.0093033705, 'top_logprobs': []}, {'token': ',', 'bytes': [44], 'logprob': -0.08852102, 'top_logprobs': []}]