青训营X豆包MarsCode 技术训练营第一课 &LangChain 实战课&关于回调函数的认识

87 阅读4分钟

回调函数:在 AI 应用中引入异步通信机制

本文将围绕一个常见的应用场景——使用回调函数和异步通信机制来管理与 OpenAI API 的交互,结合 LangChain 框架进行深入分析,帮助大家理解如何在 AI 应用中高效地处理对话、计算和 token 计数等复杂任务。


一、背景与需求

在自然语言处理(NLP)任务中,尤其是与大语言模型(如 OpenAI 的 GPT 系列模型)进行交互时,我们通常需要处理以下几个方面的挑战:

  1. 对话状态的管理:如何在多轮对话中保持上下文信息,避免丢失关键信息。
  2. Token 管理:由于 OpenAI API 会根据输入和输出的 tokens 数量收费,如何实时监控和控制 token 的使用。
  3. 异步交互:为了提高应用的响应速度和处理能力,如何异步执行多个任务并及时收集结果。

我们通过 LangChain 框架实现这些功能,其中回调函数和异步通信机制是解决这些问题的关键。


二、回调函数与异步通信概念

1. 回调函数 (Callback Function)

回调函数是一种编程模式,它允许在某个操作完成后触发另一个操作。在 AI 应用中,回调函数通常用于处理异步事件,比如数据处理、API 请求的结果回调等。

例如,在与 OpenAI API 交互时,我们希望在每次请求完成后立即记录下使用的 tokens 数量。通过使用回调函数,我们能够在 API 响应后获取有关调用的详细信息。

2. 异步通信 (Asynchronous Communication)

异步通信是指在执行某些任务时,程序不需要等待任务完成,可以继续执行其他任务,任务完成时会通过回调函数、事件或其他方式通知程序。这种方式有效地提高了应用的并发处理能力,避免了阻塞操作。

在与 OpenAI API 交互时,异步通信可以帮助我们同时发起多个请求并并行处理响应,而不必等待每个请求逐个完成。


三、代码解析

在本篇代码示例中,我们结合了回调函数和异步通信机制,展示如何在 OpenAI API 的应用中实现高效的 token 计数和状态管理。

1. 初始化和配置

首先,我们配置了 OpenAI API 密钥,并初始化了 LangChain 的大语言模型(LLM)和对话链。

pythonCopy Code
import os
from langchain.chains.conversation.base import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain_community.callbacks.manager import get_openai_callback
from langchain_openai import ChatOpenAI  # ChatOpenAI模型
from langchain_core.messages.human import HumanMessage
import asyncio

# 初始化大语言模型
llm = ChatOpenAI(
    model=os.environ["LLM_MODELEND"],
    temperature=0.5,
)

# 初始化对话链
conversation = ConversationChain(llm=llm, memory=ConversationBufferMemory())

这里,ChatOpenAI 类代表我们要与之交互的 GPT 模型,而 ConversationChain 则是用于管理对话状态的链式结构,它结合了 ConversationBufferMemory 以实现对话的上下文记忆。

2. 使用回调函数进行 Token 计数

接下来,我们使用 get_openai_callback 获取一个回调对象,该回调对象用于在 OpenAI API 的交互过程中实时记录 tokens 的使用情况。

pythonCopy Code
# 使用context manager进行token counting
with get_openai_callback() as cb:
    # 第一天的对话
    conversation("我姐姐明天要过生日,我需要一束生日花束。")
    print("第一次对话后的记忆:", conversation.memory.buffer)

    # 回合2
    conversation("她喜欢粉色玫瑰,颜色是粉色的。")
    print("第二次对话后的记忆:", conversation.memory.buffer)

    # 回合3 (第二天的对话)
    conversation("我又来了,还记得我昨天为什么要来买花吗?")
    print("/n第三次对话后时提示:/n", conversation.prompt.template)
    print("/n第三次对话后的记忆:/n", conversation.memory.buffer)

# 输出使用的tokens
print("\n总计使用的tokens:", cb.total_tokens)

通过 with get_openai_callback() as cb 的上下文管理器,我们可以在每次与模型交互后,自动记录使用的 tokens 数量。在这个例子中,我们进行三轮对话,每次调用 conversation() 方法时,模型都会更新其对话状态,并返回更新后的记忆信息。

3. 异步交互

最后,我们演示了如何通过异步任务来并行发起多个请求,并利用回调函数收集每次请求的结果。asyncio 库帮助我们管理异步操作,避免了阻塞和性能瓶颈。

pythonCopy Code
# 进行更多的异步交互和token计数
async def additional_interactions():
    with get_openai_callback() as cb:
        await asyncio.gather(
            *[
                llm.agenerate(
                    messages=[[HumanMessage(content="我姐姐喜欢什么颜色的花?")]]
                )
                for _ in range(3)
            ]
        )
    print("\n另外的交互中使用的tokens:", cb.total_tokens)

# 运行异步函数
asyncio.run(additional_interactions())

在这个部分,我们使用 asyncio.gather 同时发起了三个异步任务,每个任务都请求一次 OpenAI 模型生成响应。这样,程序无需等待每个任务逐个完成,而是同时处理多个请求,大大提高了效率。