深入理解异步环境下的回调函数——全面指南

145 阅读3分钟
# 引言

在现代编程中,异步编程是一项关键技能,尤其是在处理I/O密集型任务和需要并发执行的环境中。本文将介绍如何在异步环境中使用回调函数,并提供实用的代码示例,帮助您更好地理解和应用这些技术。

# 主要内容

## 概念介绍

### 回调函数

回调函数是一种将函数作为参数传递给另一函数的方式,使得某些操作在事件触发后执行。在异步编程中,回调函数的设计可以帮助我们在不阻塞主线程的情况下处理事件响应。

### 自定义异步回调处理器

自定义异步回调处理器是指我们可以根据需要创建和使用自己的回调逻辑,以满足特定的应用需求。

### 避免阻塞事件

在异步API中,建议使用并扩展`AsyncCallbackHandler`来避免阻塞事件。

## 异步与同步处理器的比较

在使用异步方法运行LLM/Chain/Tool/Agent时,虽然可以使用`syncCallbackHandler`,但在后台会通过`run_in_executor`调用,这可能导致问题,尤其是在您的`CallbackHandler`不是线程安全的情况下。

## 兼容性注意

如果您使用Python版本小于等于3.10,在通过`RunnableLambda``RunnableGenerator``@tool`调用其他可运行对象时,需确保传递`config``callbacks`,否则回调将不会被正确传播。

# 代码示例

以下示例展示了如何在异步环境中创建和使用自定义回调处理器:

```python
import asyncio
from typing import Any, Dict, List
from langchain_anthropic import ChatAnthropic
from langchain_core.callbacks import AsyncCallbackHandler, BaseCallbackHandler
from langchain_core.messages import HumanMessage
from langchain_core.outputs import LLMResult

class MyCustomSyncHandler(BaseCallbackHandler):
    def on_llm_new_token(self, token: str, **kwargs) -> None:
        print(f"Sync handler being called in a `thread_pool_executor`: token: {token}")

class MyCustomAsyncHandler(AsyncCallbackHandler):
    """Async callback handler that can be used to handle callbacks from langchain."""
    async def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None:
        """Run when chain starts running."""
        print("zzzz....")
        await asyncio.sleep(0.3)
        class_name = serialized["name"]
        print("Hi! I just woke up. Your llm is starting")

    async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
        """Run when chain ends running."""
        print("zzzz....")
        await asyncio.sleep(0.3)
        print("Hi! I just woke up. Your llm is ending")

# 使用API代理服务提高访问稳定性
chat = ChatAnthropic(
    model="claude-3-sonnet-20240229",
    max_tokens=25,
    streaming=True,
    callbacks=[MyCustomSyncHandler(), MyCustomAsyncHandler()],
)

await chat.agenerate([[HumanMessage(content="Tell me a joke")]])

常见问题和解决方案

  1. 线程安全性:在编写同步的回调处理器时,确保其线程安全,以避免在thread_pool_executor中执行时出现问题。

  2. 回调传播:在旧版本的Python中,确保合适地传递configcallbacks,以确保回调的正确传播。

总结和进一步学习资源

本文介绍了在异步编程中使用回调函数的方法,以及如何编写自定义的异步回调处理器。对于希望深入理解异步编程的读者,建议参考以下资源:

参考资料

  1. Python AsyncIO 官方文档
  2. Langchain 文档

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

---END---