世界上有一种痛苦叫"API文档写得像天书"。
我曾经为了一个OAuth2.0流程,和官方文档对视了整整三天三夜。最后发现,是文档写错了。
这就是工具集成的现实——你以为在写代码,其实是在破案。
📚 什么是 Tool Integration?
Tool Integration(工具集成)是指将外部API、服务或系统连接到AI Agent的能力体系,让Agent能够调用现实世界的功能。它是AI从"聊天机器人"升级为"智能助手"的关键一步。
集成层次
| 层次 | 说明 | 示例 |
|---|---|---|
| L1 - 简单API | 无认证或简单Key,RESTful接口 | 天气查询、汇率转换 |
| L2 - 标准OAuth | OAuth2.0流程,用户授权 | Google Calendar、GitHub |
| L3 - 企业级 | 复杂认证、SDK、Webhook | Salesforce、SAP |
| L4 - 私有系统 | 内部系统、数据库、微服务 | 内部ERP、数据仓库 |
🏗️ 工具集成架构
核心组件
┌─────────────────────────────────────────────────────────┐
│ AI Agent Core │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Intent │ │ Planning │ │ Tool Selection │ │
│ │ Parser │→ │ Engine │→ │ Router │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Tool Integration Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Auth │ │ Request │ │ Cache │ │ Retry │ │
│ │ Manager │ │ Handler │ │ Layer │ │ Handler │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ External Services / APIs │
│ Slack Gmail Salesforce Database Custom │
└─────────────────────────────────────────────────────────┘
🔐 认证管理实战
1. API Key 认证
// 最简单的认证方式
const weatherTool = {
name: 'get_weather',
async execute(params) {
const response = await fetch('https://api.weather.com/v1/current', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.WEATHER_API_KEY}`,
'Content-Type': 'application/json'
},
params: {
city: params.city,
units: 'metric'
}
});
if (!response.ok) {
throw new Error(`Weather API error: ${response.status}`);
}
return await response.json();
}
};
2. OAuth2.0 用户授权
import { OAuth2Client } from 'google-auth-library';
class GoogleCalendarIntegration {
constructor() {
this.oauth2Client = new OAuth2Client({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
redirectUri: 'https://miaoquai.com/oauth/callback'
});
}
// Step 1: 生成授权URL
getAuthUrl(userId) {
const scopes = [
'https://www.googleapis.com/auth/calendar.readonly',
'https://www.googleapis.com/auth/calendar.events'
];
return this.oauth2Client.generateAuthUrl({
access_type: 'offline', // 获取refresh_token
scope: scopes,
state: userId, // 用于回调时识别用户
prompt: 'consent' // 强制显示授权页面
});
}
// Step 2: 处理回调,交换code获取token
async handleCallback(code) {
const { tokens } = await this.oauth2Client.getToken(code);
// 保存token到数据库(加密存储!)
await this.saveTokens(tokens);
return tokens;
}
// Step 3: 使用token调用API
async createEvent(userId, eventData) {
const tokens = await this.getTokens(userId);
this.oauth2Client.setCredentials(tokens);
// token过期时自动刷新
if (tokens.expiry_date && tokens.expiry_date < Date.now()) {
const { credentials } = await this.oauth2Client.refreshAccessToken();
await this.updateTokens(userId, credentials);
}
const calendar = google.calendar({ version: 'v3', auth: this.oauth2Client });
const response = await calendar.events.insert({
calendarId: 'primary',
requestBody: eventData
});
return response.data;
}
}
⚠️ 安全警告
- 永远不要将API Key硬编码在代码中
- 使用环境变量或密钥管理服务(AWS KMS、HashiCorp Vault)
- 数据库中的token必须加密存储
- 设置token过期时间,支持自动刷新
⚠️ 错误处理与重试
class ToolExecutor {
constructor() {
this.maxRetries = 3;
this.timeout = 30000;
}
async executeWithRetry(tool, params) {
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
return await this.execute(tool, params, attempt);
} catch (error) {
lastError = error;
console.error(`Attempt ${attempt} failed:`, error.message);
// 判断是否可重试
if (!this.isRetryable(error)) {
throw error;
}
// 指数退避
if (attempt < this.maxRetries) {
await this.sleep(Math.pow(2, attempt) * 1000);
}
}
}
// 所有重试都失败
throw new ToolExecutionError(
`Tool ${tool.name} failed after ${this.maxRetries} attempts`,
lastError
);
}
isRetryable(error) {
// 网络错误、超时、服务端错误可重试
const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 502, 503, 504];
return retryableCodes.includes(error.code) ||
retryableCodes.includes(error.status);
}
async execute(tool, params, attempt) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.timeout);
try {
const result = await tool.execute(params, { signal: controller.signal });
clearTimeout(timeout);
return result;
} catch (error) {
clearTimeout(timeout);
// 转换为统一错误格式
throw new ToolError(tool.name, {
message: error.message,
code: error.code,
status: error.status,
attempt,
timestamp: new Date().toISOString()
});
}
}
}
🚀 OpenClaw 工具注册实战
import { Tool, ToolRegistry } from '@openclaw/core';
// 注册自定义工具
const registry = new ToolRegistry();
// 添加天气工具
registry.register(new Tool({
name: 'weather',
description: '查询指定城市的天气',
parameters: {
city: { type: 'string', required: true, description: '城市名称' }
},
execute: async (params, context) => {
const weatherData = await callExternalAPI({
url: 'https://api.weather.com/v1/forecast',
params: { city: params.city }
});
return {
city: params.city,
temperature: weatherData.temp,
condition: weatherData.condition,
humidity: weatherData.humidity
};
}
}));
// 添加Slack通知工具
registry.register(new Tool({
name: 'slack_notify',
description: '发送Slack消息到指定频道',
parameters: {
channel: { type: 'string', required: true },
message: { type: 'string', required: true },
mentions: { type: 'array', description: '@用户列表' }
},
execute: async (params, context) => {
// 使用OAuth token
const token = await context.getOAuthToken('slack');
return await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
channel: params.channel,
text: params.message,
link_names: true
})
});
}
}));
// Agent中使用工具
const agent = new Agent({
tools: registry.list()
});
// 用户: "上海明天天气怎么样?"
// Agent自动调用: weather({ city: '上海' })
// 返回: { city: '上海', temperature: 18, condition: '晴', humidity: 65 }
💡 最佳实践
工具设计原则
- 单一职责 - 每个工具只做一件事
- 幂等性 - 同一参数调用结果一致
- 自描述性 - 名称和描述让LLM能理解
- 错误可处理 - 返回清晰的错误信息
安全建议
- 使用最小权限原则(OAuth scopes)
- 实现工具调用频率限制
- 敏感操作需要二次确认
- 记录所有工具调用日志