TL;DR:我们宣布改进了回调系统,该系统支持日志记录、追踪、流式输出,以及一些出色的第三方集成。这将更好地支持具有独立回调的并发运行、LangChain 组件的深度嵌套树的追踪,以及作用域限定为单个请求的回调处理程序(这对于在服务器上部署 LangChain 非常有用)。
背景
最初,我们在 LangChain 中将回调机制设计为用于非异步 Python 应用程序。现在我们支持 asyncio
Python 用法以及 JavaScript/TypeScript 中的 LangChain,我们需要一些更好的抽象概念来适应这个新世界,在这个新世界中,许多并发的 LangChain 运行可以在同一线程或多个线程中进行。此外,很明显,在 Web 环境中使用 LangChain 的开发人员通常希望将回调限定为单个请求(例如,以便他们可以将其特定的句柄传递给 websocket)。
变更
我们对回调机制进行了一些更改,以解决这些问题
- 您现在可以在构造函数参数中声明您想要的回调(这适用于所有运行),或者直接将它们传递给启动运行的
run
/call
/apply
方法。构造函数回调 将用于对该对象进行的所有调用,并且其作用域仅限于该对象,即,如果您将处理程序传递给LLMChain
构造函数,则模型连接到该链时不会使用它。 - 请求回调 将仅用于该特定请求以及它包含的所有子请求(例如,调用 LLMChain 会触发对模型的调用,该模型使用在
call()
方法中传递的相同处理程序)。这些都是显式传递的。举一个更具体的例子:当通过run
将处理程序传递给AgentExecutor
时,它将用于与代理以及代理执行中涉及的所有对象相关的所有回调,在本例中,这些对象是Tools
、LLMChain
和LLM
。以前,要使用作用域限定为特定代理运行的回调,必须将该回调管理器附加到所有嵌套对象——这很繁琐、难看,并且难以重用对象。请参阅下面的 TypeScript 示例
// What had to be done before for run-scoped custom callbacks. Very tedious!
const executors = [];
for (let i = 0; i < 3; i += 1) {
const callbackManager = new CallbackManager();
callbackManager.addHandler(new ConsoleCallbackHandler());
callbackManager.addHandler(new LangChainTracer());
const model = new OpenAI({ temperature: 0, callbackManager });
const tools = [new SerpAPI(), new Calculator()];
for (const tool of tools) {
tool.callbackManager = callbackManager;
}
const executor = await initializeAgentExecutor(
tools,
model,
"zero-shot-react-description",
true,
callbackManager
);
executor.agent.llmChain.callbackManager = callbackManager;
executors.push(executor);
}
const results = await Promise.all(
executors.map((executor) => executor.call({ input }))
);
for (const result of results) {
console.log(`Got output ${result.output}`);
}
- Chains / LLMs / Chat Models / Agents / Tools 上的
_call
、_generate
、_run
和等效的异步方法现在接收一个名为runManager
的第二个参数,该参数绑定到该运行,并包含可供该对象使用的日志记录方法(即handleLLMNewToken
)。这在构建自定义链时非常有用,例如,您可以在此处找到更多信息。 verbose
参数现在仅用作在 JS 中添加ConsoleCallbackHandler
和在 Python 中添加StdOutCallbackHandler
的快捷方式,后者将事件打印到 stdout。它不控制其他回调。
追踪和其他回调现在只需在并发情况下工作。我们还添加了一个上下文管理器,使追踪特定运行变得更加容易。
重大更改和弃用
- 任何依赖于全局回调或 LangChain 外部的全局追踪器(即
SharedCallbackManager
、SharedTracer
)的代码在 Python 包的 >0.0.153 版本中都会中断。 - 现在已弃用将
CallbackManager
附加到对象,请使用callbacks
参数传入处理程序列表。 verbose
标志现在仅控制stdout
和console
回调,而不控制其他回调。
灵感
当我们实施这些回调改进时,我们研究了一些现有的解决方案,这些解决方案最终影响了最终的 API,值得一提的是
- Python
logging
模块(和其他模块),它提供了一个getChild
方法,该方法返回一个绑定到特定上下文的新记录器。这启发了新的runManager.getChild()
,您可以在实现自定义链时使用它来确保正确跟踪子运行。 - Web 服务器框架,如
express
,其中每个 HTTP 请求的所有特定于上下文的信息都作为函数参数显式传递,而不是作为某种全局变量提供。
我们还考虑了使用某种形式的异步上下文变量的替代方案,Python 和 Node.js 中存在其实现(但在其他 JS 环境中不存在)。最终,我们决定采用显式函数参数方法,因为它更易于调试,并且具有更好的跨平台兼容性(函数参数几乎可以在任何地方工作)。
如果您遇到任何问题,请告知我们,因为这是一项重大更改!