Composio SDK 工具修饰器(Modifiers)实战:用 modifySchema / beforeExecute / afterExecute 精确掌控 Agent 工具
【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio
工具修饰器(Modifiers)是 Composio SDK 提供给开发者的一组生命周期钩子,让你能够在工具被 LLM 使用之前、执行之中与执行之后分别改写其Schema(定义)、入参(params)与执行结果(result)。本文以仓库中的 ts/examples/modifiers 示例为骨架,结合 modifiers.types.ts 的类型定义与 Tools.ts 的底层实现,完整讲解如何通过这三种修饰器对 HackerNews 等工具进行细粒度定制,读完即可在非 Agentic 与 Agentic(Vercel AI SDK)两种模式下落地使用。
一、示例定位:Composio 工具修饰器的最小可运行演示
ts/examples/modifiers/README.md 将本示例定位为“Composio SDK + Vercel AI SDK”的集成演示,核心诉求是让 AI 应用能够调用 HackerNews 数据。不过,与仓库中其他示例不同,该目录真正落地的 src/index.ts 是一份修饰器(modifiers)专题演示:它没有实现流式聊天界面,而是聚焦展示modifySchema、beforeExecute、afterExecute三个钩子在“非 Agentic”与“Agentic”两种 Provider 模式下的用法,并以console.log(tools)输出最终装配好的工具对象供开发者直接观察。
目录结构非常精简:
ts/examples/modifiers/ ├── README.md # 示例说明(本文所述关联文档) ├── package.json # 依赖与运行脚本 ├── tsconfig.json # TypeScript 配置 └── src/index.ts # 修饰器演示源码从 package.json 可以看到,示例依赖@composio/core(核心 SDK)与@composio/vercel(Vercel 集成),并通过catalog:引用 workspace 中的ai与@ai-sdk/openai;运行脚本为bun src/index.ts(Bun 运行时),类型检查脚本为tsc --noEmit -p ./tsconfig.json。
二、环境准备与快速运行
按照 README 的 Getting Started 章节,需要准备:
| 前置条件 | 说明 |
|---|---|
| Node.js | 建议使用最新 LTS 版本 |
| pnpm | v10.8.0 及以上;若使用bun src/index.ts则需 Bun 运行时 |
| Composio API Key | 用于初始化Composio客户端 |
| OpenAI API Key | Agentic 模式下供 Vercel AI SDK 调用 GPT-4 等模型 |
启动步骤(在当前仓库根目录下执行):
# 1. 进入示例目录 cd ts/examples/modifiers # 2. 安装依赖 pnpm install # 3. 复制环境变量模板 cp .env.example .env注意:当前仓库中该示例目录没有随附
.env.example文件,README 中的.env配置项(COMPOSIO_API_KEY、OPENAI_API_KEY)需要你自行创建:
COMPOSIO_API_KEY=your_composio_api_key OPENAI_API_KEY=your_openai_api_key随后即可运行:
bun src/index.ts # 或 pnpm start pnpm typecheck # 仅做类型检查,不输出产物三、两种 Provider 模式:非 Agentic 与 Agentic 的修饰器差异
src/index.ts 的核心价值在于用同一把工具HACKERNEWS_GET_USER对比展示了两种获取工具的方式:
1. 非 Agentic 模式—— 直接使用Composio客户端,只能传入modifySchema:
const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, }); const tools = await composio.tools.get('default', 'HACKERNEWS_GET_USER', { modifySchema: ({ toolSlug, toolkitSlug, schema }) => { if (toolSlug === 'HACKERNEWS_GET_USER') { schema = { ...schema, inputParameters: { type: 'object', properties: { ...schema.inputParameters?.properties, userId: { type: 'string', description: 'The user ID to get the user for', }, }, }, }; } return schema; }, }); console.log(tools);2. Agentic 模式—— 传入provider: new VercelProvider()后,三种修饰器全部可用:
const vercel = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, provider: new VercelProvider(), // 将工具包装为 Vercel AI SDK 的 Tool }); const agenticTools = await vercel.tools.get( 'default', { tools: ['HACKERNEWS_GET_USER'] }, { afterExecute: ({ toolSlug, toolkitSlug, result }) => { // 修改执行结果 return result; }, beforeExecute: ({ toolSlug, toolkitSlug, params }) => { // 修改执行参数 return params; }, modifySchema: ({ toolSlug, toolkitSlug, schema }) => { // 修改工具 schema return schema; }, } ); console.log(agenticTools);注意两处关键差异(源码中注释亦明确标注):
- 获取方式:非 Agentic 模式第二个参数直接传工具 slug 字符串;Agentic 模式则传
{ tools: ['HACKERNEWS_GET_USER'] }对象; - 能力边界:非 Agentic 模式(
ToolOptions)只支持modifySchema(以及beforeFileUpload);Agentic 模式(AgenticToolOptions)额外支持beforeExecute与afterExecute。
这种差异并非人为约定,而是由类型系统强制保证的,详见下文第五节。
四、modifySchema:在工具暴露给 LLM 之前改写其定义
modifySchema是TransformToolSchemaModifier类型的回调,签名定义于 modifiers.types.ts:
export type TransformToolSchemaModifier = (context: { toolSlug: string; toolkitSlug: string; schema: Tool; }) => Tool | Promise<Tool>;它的用途包括:自定义输入/输出参数的描述、增删改参数以满足业务需求、改写工具名称与描述使其更贴合应用上下文、实现 Schema 的版本化或特性开关等。类型注释中给出了比示例更完整的改造样例,例如为HACKERNEWS_GET_USER补充带校验约束的userId参数、includeSubmissions布尔开关与submissionLimit数值范围:
const modifySchema = ({ schema, toolSlug, toolkitSlug }) => { if (toolSlug === 'HACKERNEWS_GET_USER') { return { ...schema, name: 'Get HackerNews User Profile', description: 'Retrieve detailed user information from HackerNews', inputParameters: { ...schema.inputParameters, userId: { type: 'string', description: 'The HackerNews username to retrieve information for', required: true, minLength: 2, maxLength: 15, pattern: '^[a-zA-Z0-9_-]+$' }, submissionLimit: { type: 'number', description: 'Maximum number of submissions to return', default: 10, minimum: 1, maximum: 100 } } }; } return schema; };底层实现:在 Tools.ts 中,get、getRawComposioToolBySlug、getRawComposioTools等方法都会在拿到后端返回的原始工具后调用applySchemaModifiers将用户提供的modifySchema逐工具应用(参见 Tools.ts 与 Tools.ts)。测试 modifiers.test.ts 验证了两点:单个工具获取时修饰器恰好被调用一次,且回调收到的上下文包含toolSlug、toolkitSlug与schema;批量获取多个工具(TOOL1、TOOL2)时修饰器会被逐个调用并分别改写name字段。
五、beforeExecute / afterExecute:拦截执行前后的请求与响应
5.1 签名与参数
两个执行期修饰器定义于 modifiers.types.ts:
export type beforeExecuteModifier = (context: { toolSlug: string; toolkitSlug: string; params: ToolExecuteParams; }) => Promise<ToolExecuteParams> | ToolExecuteParams; export type afterExecuteModifier = (context: { toolSlug: string; toolkitSlug: string; result: ToolExecuteResponse; }) => Promise<ToolExecuteResponse> | ToolExecuteResponse;beforeExecute:在工具真正执行前拿到即将发送的参数params,典型场景包括注入认证参数/请求头、转换输入数据格式、追加上下文信息、做请求校验与归一化。类型注释给出了为所有请求追加X-API-Key、X-Request-ID等头部的完整示例。afterExecute:在工具执行完成后拿到响应result(含data、error、successful、logId等字段),典型场景包括把响应转换为更便于下游消费的结构、统一错误处理与日志/埋点、为成功响应补充派生数据。
5.2 底层调用链(以 Session 执行为例)
在 Tools.ts 中可以看到beforeExecute的插入位置:它在文件上传预处理(applyFileUploadModifiers)之后才被调用,因此回调中看到的params与真正发送到 Tool Router 的参数完全一致;修改后的参数会写入executePayload.arguments发出。执行完成后,Tools.ts 再调用afterExecute,将修饰器返回的结果作为最终ToolExecuteResponse返回给调用方。两条钩子均支持返回 Promise,也支持抛错中断流程(非函数类型的修饰器会触发ComposioInvalidModifierError,见 Tools.ts 与 Tools.ts)。
5.3 测试佐证
modifiers.test.ts 的 Execution Modifiers 用例验证了完整行为:
beforeExecute把参数{ limit: 5 }改写为{ limit: 10 }后,mockClient.tools.execute收到的实际参数即为改写后的值(L72-L104);afterExecute在响应data上追加{ enhanced: true },最终result.data确认包含该字段(L106-L131);- Combined Modifiers 用例(L134-L296)同时注入三种修饰器,验证
modifySchema改写描述与标签、beforeExecute触发埋点并注入tracking参数、afterExecute在成功结果上追加processed字段且不触发错误日志。
六、类型体系:修饰器如何随 Provider 自动区分能力
修饰器能力的差异由 modifiers.types.ts 中的类型组合精确建模:
| 类型 | 包含的钩子 | 适用场景 |
|---|---|---|
ToolOptions(L377-L383) | modifySchema、beforeFileUpload | 非 Agentic Provider |
ExecuteToolModifiers(L412-L424) | beforeExecute、afterExecute、beforeFileUpload | 单次工具执行 |
AgenticToolOptions(L587) | ToolOptions & ExecuteToolModifiers | Agentic Provider(OpenAI、Vercel 等) |
SessionExecuteMetaModifiers(L537-L549) | 会话版beforeExecute/afterExecute | Tool Router 会话执行 |
SessionMetaToolOptions(L628) | ToolOptions & SessionExecuteMetaModifiers | 会话工具获取 |
其中的ProviderOptions<TProvider>(L658-L663)利用条件类型:当 Provider 是BaseAgenticProvider的子类(如VercelProvider)时自动解析为AgenticToolOptions,否则解析为ToolOptions。这正是示例源码中“非 Agentic 只能传modifySchema,Agentic 可以传三个钩子”这一行为在编译期的保证——写错配置会直接得到类型错误(示例源码注释中也留下了// what is the type error i am getting here?这类排查提示)。
七、进阶能力:会话上下文修饰器与文件上传钩子
除示例展示的三个钩子外,同一类型文件中还定义了面向更复杂场景的修饰器:
1. 会话上下文修饰器:beforeExecuteMetaModifier/afterExecuteMetaModifier(L459-L498)专用于 Tool Router 会话执行,回调额外携带sessionId,且params类型为MetaToolArguments(非空的工具参数,见 L431)。helper 工具使用composiotoolkit slug,预加载的应用工具使用各自 toolkit slug。典型用法是为会话内工具注入sessionMetadata、统计startTime或在结果上附加sessionInfo。
2. 文件上传钩子:beforeFileUploadModifier(L204-L209)在 SDK 读取并上传file_uploadable值之前触发,context.source区分三种输入——'path'(本地文件系统路径)、'url'(http(s)://链接)、'file'(File对象,此时path仅为文件名)。返回字符串可替换上传输入(重写路径、重定向 URL、把File换成本地路径上传);返回false或抛错则中止上传并抛出ComposioFileUploadAbortedError。
八、Agentic 模式内部机制:VercelProvider 如何包装工具
在 Agentic 模式下,provider: new VercelProvider()负责把 Composio 工具翻译成 Vercel AI SDK 的tool()格式,其核心逻辑在 ts/packages/providers/vercel/src/index.ts 的wrapTool方法(L113-L169)中:
- Schema 规范化:先通过
deduplicateJsonSchemaRequiredArrays去重required数组;若构造时开启strict: true,再用toStrictJsonSchema将 JSON Schema 重写为结构化输出所需的严格形态——所有属性必填(可选参数变为可空),无法表达的构造会保留原 Schema 并输出警告日志; - Zod 转换:
jsonSchemaToZodSchema(dereferenceJsonSchema(...))把展开$ref后的 Schema 转为 Vercel AI SDK 所需的 Zod Schema(Zod 转换器不跟随$ref,因此需预先内联定义); - 执行桥接:
execute回调里先用normalizeToolArguments处理模型偶尔把工具入参输出成 JSON 字符串的情况,再调用composio.tools.execute;strict 模式下还会用omitNullToolArguments剔除“值为 null 表示省略”的可选参数。
wrapTools(L234-L239)则把多个工具按 slug 组装成ToolSet字典返回。也就是说,示例中vercel.tools.get(...)返回的agenticTools,本质上就是可以直接喂给 Vercel AI SDK 的tools参数。
九、最佳实践与注意事项
综合示例、类型注释与源码实现,总结以下实践要点:
- 按 Provider 类型选择钩子:非 Agentic 场景(如直接用 SDK 调用工具)只有
modifySchema;需要beforeExecute/afterExecute时使用VercelProvider等 Agentic Provider,编译期类型会给出正确约束。 modifySchema是纯定义变换:它发生在工具暴露给消费者之前,不影响后端工具本身,适合做参数裁剪、描述增强、命名定制;注意保持返回对象为合法 JSON Schema(inputParameters需为type: 'object'结构)。beforeExecute看到的是最终参数:文件上传预处理先于它执行(Tools.ts),因此不要在前置钩子里重复处理文件上传逻辑。afterExecute的返回值即最终结果:无论成功失败都会进入该钩子,可统一在此处做日志埋点与错误增强;修改data时建议基于result.data展开合并,避免丢失successful、logId等字段。- 异步与错误处理:三个修饰器都支持返回
Promise;beforeExecute抛错会中止执行,afterExecute抛错则中断结果返回,可据此实现校验失败、鉴权刷新等分支逻辑。 - 示例为最小演示:README 中描述的 HackerNews 头条摘要(GPT-4 + 流式响应)属于该示例的设计目标;实际 src/index.ts 以
console.log输出装配后的工具对象为主,用于快速验证修饰器链路,落地完整 Agent 应用时可在其基础上接入ai与@ai-sdk/openai构建流式对话接口。
十、延伸阅读
- 示例入口与运行脚本:ts/examples/modifiers/src/index.ts、ts/examples/modifiers/package.json
- 修饰器全部类型定义与示例代码:ts/packages/core/src/types/modifiers.types.ts
- 修饰器底层调用链实现:ts/packages/core/src/models/Tools.ts
- 修饰器单元测试:ts/packages/core/test/tools/modifiers.test.ts
- Vercel Provider 工具包装实现:ts/packages/providers/vercel/src/index.ts
【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考