关键代码验证
AI研究 Tool Calling 代码实现
提供跨模型Tool Calling兼容的关键代码示例和实现方案
统一工具调用接口设计
TypeScript 接口定义
// 统一的工具定义接口
interface UnifiedToolDefinition {
name: string;
description: string;
parameters: JSONSchema;
}
// 统一的工具调用请求
interface UnifiedToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
// 统一的工具调用结果
interface UnifiedToolResult {
callId: string;
content: string;
isError?: boolean;
}
模型适配器实现
OpenAI 适配器
class OpenAIAdapter implements ToolCallingAdapter {
// 转换工具定义
toModelFormat(tools: UnifiedToolDefinition[]): OpenAITool[] {
return tools.map(tool => ({
type: 'function',
function: {
name: tool.name,
description: tool.description,
parameters: tool.parameters
}
}));
}
// 解析模型响应
parseToolCalls(response: OpenAIResponse): UnifiedToolCall[] {
if (!response.tool_calls) return [];
return response.tool_calls.map(tc => ({
id: tc.id,
name: tc.function.name,
// 关键:OpenAI 返回的是 JSON 字符串,需要解析
arguments: JSON.parse(tc.function.arguments)
}));
}
// 构建结果消息
buildToolResultMessage(result: UnifiedToolResult): OpenAIMessage {
return {
role: 'tool',
tool_call_id: result.callId,
content: result.content
};
}
}
Anthropic 适配器
class AnthropicAdapter implements ToolCallingAdapter {
// 转换工具定义
toModelFormat(tools: UnifiedToolDefinition[]): AnthropicTool[] {
return tools.map(tool => ({
name: tool.name,
description: tool.description,
// 关键差异:使用 input_schema 而非 parameters
input_schema: tool.parameters
}));
}
// 解析模型响应
parseToolCalls(response: AnthropicResponse): UnifiedToolCall[] {
const toolUseBlocks = response.content.filter(
block => block.type === 'tool_use'
);
return toolUseBlocks.map(block => ({
id: block.id,
name: block.name,
// 关键:Anthropic 直接返回对象,无需解析
arguments: block.input
}));
}
// 构建结果消息
buildToolResultMessage(result: UnifiedToolResult): AnthropicMessage {
return {
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: result.callId,
content: result.content
}]
};
}
}
MCP 兼容转换
// MCP 工具转换为 Claude 格式
function mcpToClaudeTools(mcpTools: MCPTool[]): ClaudeTool[] {
return mcpTools.tools.map(tool => ({
name: tool.name,
description: tool.description || '',
// 关键转换:inputSchema -> input_schema
input_schema: tool.inputSchema
}));
}
// 处理 MCP 工具调用结果
async function handleMCPToolCall(
mcpClient: MCPClient,
toolCall: UnifiedToolCall
): Promise<UnifiedToolResult> {
const result = await mcpClient.callTool({
name: toolCall.name,
arguments: toolCall.arguments
});
return {
callId: toolCall.id,
content: result.content
.filter(c => c.type === 'text')
.map(c => c.text)
.join('\n'),
isError: result.isError
};
}
错误恢复机制
参数解析容错
function safeParseArguments(
rawArgs: string | Record<string, unknown>
): Record<string, unknown> {
// 如果已经是对象,直接返回
if (typeof rawArgs === 'object') {
return rawArgs;
}
// 如果是字符串,尝试解析
if (typeof rawArgs === 'string') {
try {
return JSON.parse(rawArgs);
} catch (e) {
// 解析失败,返回空对象而非抛出异常
console.error('Failed to parse tool arguments:', e);
return {};
}
}
return {};
}
响应格式验证
interface ToolCallValidator {
validate(response: unknown): ToolCallValidationResult;
}
class GenericToolCallValidator implements ToolCallValidator {
validate(response: unknown): ToolCallValidationResult {
// 检测 OpenAI 格式
if (this.isOpenAIFormat(response)) {
return { format: 'openai', toolCalls: response.tool_calls };
}
// 检测 Anthropic 格式
if (this.isAnthropicFormat(response)) {
const toolUseBlocks = response.content?.filter(
(b: { type: string }) => b.type === 'tool_use'
) || [];
return { format: 'anthropic', toolCalls: toolUseBlocks };
}
// 检测 Gemini 格式
if (this.isGeminiFormat(response)) {
const functionCalls = response.candidates?.[0]?.content?.parts
?.filter((p: { functionCall?: unknown }) => p.functionCall)
.map((p: { functionCall: { name: string; args: Record<string, unknown> } }) => ({
name: p.functionCall.name,
arguments: p.functionCall.args
})) || [];
return { format: 'gemini', toolCalls: functionCalls };
}
return { format: 'unknown', toolCalls: [] };
}
private isOpenAIFormat(r: unknown): r is { tool_calls: unknown[] } {
return typeof r === 'object' && r !== null && 'tool_calls' in r;
}
private isAnthropicFormat(r: unknown): r is { content: unknown[] } {
return typeof r === 'object' && r !== null && 'content' in r &&
Array.isArray((r as { content: unknown[] }).content);
}
private isGeminiFormat(r: unknown): r is { candidates: unknown[] } {
return typeof r === 'object' && r !== null && 'candidates' in r;
}
}
OpenCode 集成建议
配置层设计
# opencode.yaml
providers:
- name: openai
adapter: openai
models:
- gpt-4
- gpt-3.5-turbo
- name: anthropic
adapter: anthropic
models:
- claude-opus-4-6
- claude-sonnet-4-6
- name: glm
adapter: openai # GLM 兼容 OpenAI 格式
models:
- glm-5
config:
# 特殊处理配置
strictArgumentParsing: false
retryOnFormatError: true
运行时错误处理
async function executeToolCall(
model: string,
toolCall: UnifiedToolCall,
tools: Map<string, ToolExecutor>
): Promise<UnifiedToolResult> {
const executor = tools.get(toolCall.name);
if (!executor) {
return {
callId: toolCall.id,
content: `Error: Unknown tool "${toolCall.name}"`,
isError: true
};
}
try {
const result = await executor.execute(toolCall.arguments);
return {
callId: toolCall.id,
content: JSON.stringify(result)
};
} catch (error) {
// 捕获执行异常,避免中断整个流程
return {
callId: toolCall.id,
content: `Error: ${error.message}`,
isError: true
};
}
}
最佳实践总结
1. 格式转换三原则
- 输入统一 —— 无论模型如何,工具定义使用统一格式
- 输出适配 —— 根据模型特性转换响应
- 错误隔离 —— 工具调用失败不应导致整体崩溃
2. GLM 模型特殊处理
// GLM 模型的已知问题和解决方案
const glmConfig = {
// 问题:有时返回非标准 JSON
// 解决:宽松解析 + 默认值
argumentParsing: 'lenient',
// 问题:复杂 Schema 可能导致异常
// 解决:简化 Schema 定义
schemaSimplification: true,
// 问题:并行调用支持不稳定
// 解决:串行执行工具调用
parallelCalls: false
};
3. 调试技巧
// 启用详细日志
const DEBUG = process.env.TOOL_CALL_DEBUG === 'true';
function logToolCall(model: string, direction: 'in' | 'out', data: unknown) {
if (DEBUG) {
console.error(`[ToolCall][${model}][${direction}]`, JSON.stringify(data, null, 2));
}
}