Composio × Google GenAI(Gemini)集成实战:从工具获取到函数调用的完整链路
【免费下载链接】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
导读
本文基于仓库中的 ts/examples/google 示例,完整演示如何在 TypeScript 项目中用 Composio SDK 与 Google 的 GenAI(Gemini)模型集成:包括环境准备、依赖安装、工具获取,以及把 Composio 工具包装为 Gemini 的 Function Declaration 并执行函数调用的完整链路。读完本文,你将掌握GoogleProvider的底层工作原理(工具 Schema 转换、参数归一化、工具执行),并能独立搭建一个"任务输入 → Gemini 决策 → Composio 执行工具 → 返回结果"的可用 Agent 程序,同时了解如何通过会话(Session)与 MCP 端点做进阶集成。
示例概览:Composio 如何与 Gemini 协同工作
Composio 本身提供 1000+ 工具集成,但 Gemini 这类模型并不知道这些工具的存在。示例的核心思路是**"格式适配 + 回调执行"**:
- 通过 Composio SDK 获取工具定义;
- 由
GoogleProvider把工具定义转换成 Gemini 能识别的FunctionDeclaration(函数声明)格式; - 把声明随提示词一起交给 Gemini,模型在需要时返回
functionCalls; - 程序把函数调用转交
composio.provider.executeToolCall实际执行,并把结果回传给模型。
整个示例位于 ts/examples/google/src/index.ts,目录结构如下:
ts/examples/google/ ├── .env.example # 环境变量模板 ├── README.md # 官方示例说明 ├── package.json # 依赖与运行脚本 ├── tsconfig.json # TypeScript 配置 ├── CHANGELOG.md # 版本变更记录 └── src/ ├── index.ts # 主示例:函数调用流程 └── experimental.mcp.ts # 进阶:基于 Session + MCP 的示例第一步:安装依赖
示例使用 pnpm 工作区管理依赖,进入示例目录后执行:
pnpm install从 ts/examples/google/package.json 可以看到本示例的核心依赖:
| 依赖包 | 作用 |
|---|---|
@composio/core | Composio SDK 核心,提供Composio客户端 |
@composio/google | Google GenAI Provider,负责工具格式适配与执行 |
@google/genai | Google 官方 GenAI SDK,用于调用 Gemini 模型 |
dotenv | 从.env文件加载环境变量 |
示例脚本同样定义在 package.json 中:start使用 Bun 直接运行src/index.ts,dev则开启文件监听模式,二者均在仓库根目录的 pnpm workspace 环境下可用。
第二步:配置环境变量
复制环境变量模板并填入密钥:
cp .env.example .env根据 ts/examples/google/.env.example,需要配置两个变量:
| 变量 | 说明 |
|---|---|
COMPOSIO_API_KEY | 在 Composio 控制台(app.composio.dev)创建,用于身份认证与工具访问 |
GEMINI_API_KEY | 在 Google AI Studio(aistudio.google.com)创建,用于调用 Gemini 模型 |
注意:@composio/google的 README(ts/packages/providers/google/README.md)中提到的变量名为GOOGLE_API_KEY,而本示例使用GEMINI_API_KEY——两者指向同一个密钥,只是命名习惯不同。实际项目中请与你的代码读取方式保持一致。
第三步:运行示例
# 运行示例 pnpm start # 开发模式(文件变更自动重启) pnpm dev示例运行时,控制台会依次输出:初始化信息 → 获取到的工具数量 → 待执行任务 → Gemini 的响应 → 工具调用名称 → 最终执行结果。
深入主流程:Composio × Gemini 函数调用全解析
ts/examples/google/src/index.ts 完整展示了上述链路,下面拆解每一步。
1. 初始化两个客户端
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY, }); const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, provider: new GoogleProvider(), });关键点在于Composio构造函数中传入的provider: new GoogleProvider()。GoogleProvider是 ts/packages/providers/google/src/index.ts 中定义的核心适配器,它继承了BaseNonAgenticProvider,负责"把 Composio 工具翻译成 Gemini 的 Function Declaration"以及"执行 Gemini 返回的函数调用"两个方向的工作。
2. 获取 Composio 工具
const tools = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); console.log(`✅ Found ${tools.length} tools`);composio.tools.get按名称获取工具定义,此处获取的是 Hacker News 的HACKERNEWS_GET_USER工具(用于查询指定用户的信息)。工具定义中包含了slug、description、inputParameters(JSON Schema 格式的参数描述)等字段,这些正是后续转换 Function Declaration 所需的原始素材。
3. 把工具声明交给 Gemini
const task = "Fetch the details of the user 'pg'"; const response = await ai.models.generateContent({ model: 'gemini-2.0-flash-001', contents: task, config: { tools: [{ functionDeclarations: tools }], }, });tools数组被放进config.tools[0].functionDeclarations,这正是 Google GenAI 函数调用(Function Calling)的标准用法。模型会基于声明判断:完成该任务是否需要调用工具、调用哪个、参数传什么。
4. 执行模型返回的函数调用
if (response.functionCalls) { const functionCall = { name: response.functionCalls[0].name || '', args: response.functionCalls[0].args || {}, }; const result = await composio.provider.executeToolCall('default', functionCall); console.log(JSON.parse(result).data); } else { console.log(response.text); }这是"非 Agent 型(Non-Agentic)"集成的典型形态:模型只负责"决策",不负责"执行"。当response.functionCalls存在时,程序取出函数名与参数,交给composio.provider.executeToolCall('default', functionCall)执行,返回的 JSON 字符串中.data字段即工具执行结果(此处为 Hacker News 用户pg的详情)。
源码级剖析:GoogleProvider 如何完成双向适配
理解了主流程后,再看 ts/packages/providers/google/src/index.ts 中GoogleProvider的三个关键方法,就能彻底明白示例背后的原理。
wrapTool:Composio 工具 → Gemini Function Declaration
wrapTool(tool: Tool): GoogleTool { const inputParameters = ensureObjectTypeOnProperties( deduplicateJsonSchemaRequiredArrays( dereferenceJsonSchema(tool.inputParameters ?? { type: 'object', properties: {} }, { onUnresolved: 'sentinel', }) ) ); return { name: tool.slug, description: tool.description || '', parameters: { type: 'object', description: tool.description || '', properties: inputParameters?.properties || {}, required: inputParameters?.required || [], } as unknown as Schema, }; }这里完成了一次关键的Schema 管道处理,保证转换后的参数描述能被 Gemini 正确解析:
dereferenceJsonSchema:把 JSON Schema 中的$ref引用解析为内联定义(onUnresolved: 'sentinel'表示无法解析时保留哨兵值,避免报错中断);deduplicateJsonSchemaRequiredArrays:去重required数组中重复的字段名;ensureObjectTypeOnProperties:确保带properties的对象节点显式声明type: 'object'(Gemini 的 Schema 校验对此有严格要求)。
转换后工具的name取tool.slug,parameters则直接映射为 Gemini 的Schema。对应测试见 ts/packages/providers/google/test/google.test.ts,其中验证了"工具被包装为 Function Declaration 格式"以及"无inputParameters的工具也能安全处理"两种场景。
executeToolCall:Gemini 函数调用 → Composio 工具执行
async executeToolCall( userId: string, tool: GoogleGenAIFunctionCall, options?: ExecuteToolFnOptions, modifiers?: ExecuteToolModifiers ): Promise<string> { const payload: ToolExecuteParams = { // Models occasionally emit tool args as a JSON string rather than an object (issue #2406). arguments: normalizeToolArguments(tool.args, tool.name), connectedAccountId: options?.connectedAccountId, customAuthParams: options?.customAuthParams, customConnectionData: options?.customConnectionData, userId: userId, }; const result = await this.executeTool(tool.name, payload, modifiers); return JSON.stringify(result); }executeToolCall的入参tool即 Gemini 返回的{ name, args }。值得注意的两点:
normalizeToolArguments(tool.args, tool.name):模型偶尔会把参数以 JSON字符串而非对象的形式返回(源码注释中标注了 issue #2406),该工具函数负责把字符串参数归一化为对象,确保下游执行不会因类型不符而失败;- 返回结果统一
JSON.stringify为字符串,方便回传给模型作为functionResponse,也便于上层JSON.parse(result)使用。
此外,options支持connectedAccountId(指定已连接账号)、customAuthParams(自定义认证参数)、customConnectionData(自定义连接数据),这些是接入需鉴权工具(如 Gmail、GitHub)时的关键扩展点。
_isAgentic = false:为什么需要手动循环
GoogleProvider是"非 Agent 型"Provider(ts/packages/providers/google/test/google.test.ts 中明确断言provider._isAgentic为false)。这意味着 Composio 不会替模型自动调度多轮工具调用,"模型返回函数调用 → 程序执行 → 结果回传 → 模型再决策"的循环必须由你编写。这也是示例主流程只处理一次functionCalls的原因——它演示的是单次调用;生产环境需要的是下面这种完整循环。
实战升级:完整的 Agentic Loop 写法
ts/packages/providers/google/README.md 给出了生产可用的多轮循环模板,它比示例更进一步:先把工具绑定到会话,再用while循环反复执行,直到模型输出纯文本:
import { Composio } from '@composio/core'; import { GoogleProvider } from '@composio/google'; import { GoogleGenAI, type Part } from '@google/genai'; const composio = new Composio({ provider: new GoogleProvider(), }); const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY! }); // 为你的用户创建会话,绑定工具 const session = await composio.create('user_123'); const tools = await session.tools(); const chat = ai.chats.create({ model: 'gemini-3-pro-preview', config: { tools: [{ functionDeclarations: tools }], }, }); let response = await chat.sendMessage({ message: "Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'", }); // Agentic loop:不断执行工具调用,直到模型以文本作答 while (response.functionCalls && response.functionCalls.length > 0) { const parts: Part[] = []; for (const fc of response.functionCalls) { const result = await composio.provider.executeToolCall('user_123', { name: fc.name || '', args: (fc.args || {}) as Record<string, unknown>, }); parts.push({ functionResponse: { id: fc.id, name: fc.name, response: JSON.parse(result), }, }); } response = await chat.sendMessage({ message: parts }); } console.log(response.text);与示例主流程相比,这个版本有两个重要差异:
- 会话(Session)抽象:
composio.create('user_123')为指定用户创建会话并挂载工具,executeToolCall的第一个参数即该用户 ID——这是多用户场景下隔离连接与权限的正确姿势; - 多轮循环:
while循环支持一次任务需要连续调用多个工具(例如"查邮件 → 写摘要 → 发消息")的情况;functionResponse携带id与 Gemini 返回的函数调用一一对应,保证多工具并发时结果不错位。
进阶路径:通过 Session + MCP 端点集成
如果不想手动做 Schema 转换,ts/examples/google/src/experimental.mcp.ts 展示了一条更"声明式"的路径:让 Composio 托管一个 MCP(Model Context Protocol)端点,再通过标准 MCP 客户端把工具交给 Gemini。
核心步骤:
// 1. 创建绑定 Gmail 工具包的会话,暴露托管 MCP 端点 const session = await composio.sessions.create(externalUserId, { toolkits: ['gmail'], manageConnections: false, // 关闭连接管理工具,本示例用不到 mcp: true, }); // 2. 用 Streamable HTTP 传输连接 MCP 端点 const serverParams = new StreamableHTTPClientTransport(new URL(session.mcp.url), { requestInit: { headers: session.mcp.headers }, // 端点凭据 }); const mcpClient = new MCPClient({ name: 'composio-mcp-client', version: '1.0.0' }); await mcpClient.connect(serverParams); // 3. 用 @google/genai 提供的 mcpToTool 把 MCP 工具转成 Gemini 工具 const tools = [mcpToTool(mcpClient)]; // 4. 交给 Gemini 流式执行 const stream = await gemini.models.generateContentStream({ model: 'gemini-2.5-flash', contents: `Fetch the latest 2 emails and provide a detailed summary...`, config: { tools }, });几个值得注意的实现细节:
sessions.create的mcp: true让会话返回session.mcp.url与session.mcp.headers,前者是端点地址,后者携带访问凭据,缺一不可;manageConnections: false会关闭自动注入的连接管理工具,避免无关工具干扰模型;- MCP 客户端对象必须保持存活(不能被 GC 回收),直到从它上面取完工具;
- 该文件注释为"experimental",说明 Session + MCP 属于演进中的能力,接入时建议锁定所依赖的
@modelcontextprotocol/sdk版本。
这种方式的好处是:工具集的获取、鉴权、生命周期由 Composio 托管,客户端代码只需关心"连接 MCP → 转工具 → 调模型",适合接入 Gmail 这类需要 OAuth 鉴权的重型工具包。
自定义与继续探索
按 ts/examples/google/README.md 的指引,你可以基于此示例做三类改造:
- 更换工具:把
HACKERNEWS_GET_USER换成其他工具 slug,或改用session.tools()/ 工具包(toolkit)方式批量挂载,例如 Gmail、GitHub、Slack 等; - 实现业务逻辑:在
main()中扩展多轮对话、结果格式化、持久化等逻辑; - 增强健壮性:为
executeToolCall增加错误捕获与重试,处理connectedAccountId、customAuthParams等鉴权参数。
仓库中还提供了同源对比示例,帮助你理解不同 Provider 的适配差异:
- OpenAI 示例:展示与 OpenAI 的集成方式;
- LangChain 示例:展示与 LangChain 框架的集成方式;
- 更多示例:浏览完整的示例集合,包括工具路由(tool-router)、触发器(triggers)、会话管理(session-management)等场景。
如果你要深入源码,建议按以下顺序阅读:先看 示例主文件 理解使用形态,再读 GoogleProvider 实现 掌握 Schema 管道与执行细节,最后对照 Provider 测试 验证各行为的预期,即可完整把握 Composio 与 Gemini 的集成全貌。
【免费下载链接】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),仅供参考