工具与 activity 机制:@activity 装饰器如何变成 LLM 可调的 schema
30 秒导读: LLM 只会输出文本,它没法直接"调用一个 Python 方法"。Griptape 的做法是:你在方法上贴一个
@activity装饰器,框架就自动把这个方法翻译成一段 LLM 看得懂的 JSON schema(方法能干嘛、要什么参数);LLM 照着 schema 生成一次调用意图,框架再把它 容错地 打回到那个真实方法上执行。本章讲清这条"方法 → schema → 调用 → 结果"的翻译链路。
本章覆盖三块源码:
| 文件 | 角色一句话 |
|---|---|
griptape/utils/decorators.py | @activity 装饰器本身:给方法打标记、挂配置 |
griptape/mixins/activity_mixin.py | 收集带标记的方法、渲染 schema、校验入参 |
griptape/tools/base_tool.py | 把多个 activity 拼成整个 Tool 的 schema、执行、容错、转存记忆 |
边界: LLM 输出的那段动作文本 怎么被解析成一次调用(ReAct/actions 子任务)属于 02;结果转存进的 Task Memory 内部怎么存 属于 05。本章只管"一个方法怎么暴露成工具动作、怎么被容错执行"。
1. 这是什么(零基础也能懂)
先理清两个词:Tool 和 Activity
Griptape 里工具是 两层 的,别混:
- Tool(工具) = 一个 Python 类,一个"技能包"。比如"计算器工具""网页抓取工具"。
- Activity(活动) = 工具类里 具体能做的一个动作,就是一个被
@activity装饰的方法。比如计算器里的calculate。
一个 Tool 可以有多个 Activity(一个"文件工具"可能有"读文件""写文件""列目录"三个动作)。LLM 面对的最小单位是 Activity,不是 Tool。
它解决什么问题
模型缺的是"手脚"。你想让 LLM 帮你算 (3+4)*5,但模型自己算数会错;正确做法是让它 调用真实的 Python 计算。难点不在"让模型说想算什么",而在:
- 模型只会吐文本,怎么让它知道"有个 calculate 动作,要传一个叫 expression 的字符串"?
- 模型吐回来的调用意图(一段 JSON),怎么 安全地 落到
calculate这个真实方法上,还不能因为它多传/少传参数就崩?
@activity + ActivityMixin + BaseTool 三件套就是干这个的。
用起来什么样(一个真实工具)
这是仓库里自带的计算器工具,是理解全章的锚:
# griptape/tools/calculator/tool.py — 真实源码(节选)
class CalculatorTool(BaseTool):
@activity(
config={
"description": "Can be used for computing simple numerical or algebraic calculations in Python",
"schema": Schema({
Literal("expression", description="Arithmetic expression parsable in pure Python..."): str,
}),
},
)
def calculate(self, params: dict) -> BaseArtifact:
expression = params["values"]["expression"]
return TextArtifact(numexpr.evaluate(expression))
你只写了两样东西:一句 description(告诉 LLM 这动作干嘛)、一个 schema(告诉 LLM 参数长啥样)。剩下的翻译、暴露、容错执行,全是框架自动做的。 本章就是拆开这个"自动"。
一句话直觉
把
@activity想成给方法贴 产品说明书:说明书(description + schema)是给 LLM 这个"顾客"看的;顾客照说明书下单(生成调用),框架照订单去仓库(真实方法)取货,取回来的东西还要 统一装箱(转成 Artifact)才交付。