はじめに
AI エージェントには、Web 検索、データベース クエリ、コード実行など、外部の世界と対話するためのツールが必要です。ツール レジストリは、すべてのツールのライフサイクル (登録、検証、実行、セキュリティ) を管理します。
1. ToolHandler インターフェイス
// packages/core/src/tools/types.ts
export type ToolHandler = (
args: Record<string, unknown>,
context: ToolContext,
) => Promise<unknown>;
export interface ToolContext {
tenantId: string;
userId: string;
sessionId: string;
abortSignal?: AbortSignal;
}
ツール ハンドラーは、引数とコンテキストを受け取り、任意の結果を返す関数です。
2. ツールレジストリの実装
// packages/core/src/tools/tool-registry.ts
export class ToolRegistry {
private tools = new Map<string, {
definition: ToolDefinition;
handler: ToolHandler;
}>();
register(definition: ToolDefinition, handler: ToolHandler) {
if (this.tools.has(definition.name)) {
throw new Error(`Tool "${definition.name}" already registered`);
}
this.tools.set(definition.name, { definition, handler });
}
unregister(name: string) {
this.tools.delete(name);
}
getDefinitions(): ToolDefinition[] {
return Array.from(this.tools.values()).map(t => t.definition);
}
async execute(
name: string,
args: Record<string, unknown>,
context: ToolContext,
): Promise<ToolResult> {
const tool = this.tools.get(name);
if (!tool) {
return {
toolCallId: '',
success: false,
result: null,
error: `Unknown tool: ${name}`,
duration: 0,
};
}
const start = performance.now();
try {
const result = await tool.handler(args, context);
return {
toolCallId: '',
success: true,
result,
duration: performance.now() - start,
};
} catch (error) {
return {
toolCallId: '',
success: false,
result: null,
error: error instanceof Error ? error.message : String(error),
duration: performance.now() - start,
};
}
}
// Execute all tool calls from LLM response
async executeAll(
toolCalls: ToolCall[],
context: ToolContext,
): Promise<ToolResult[]> {
return Promise.all(
toolCalls.map(async (call) => {
const result = await this.execute(call.name, call.arguments, context);
return { ...result, toolCallId: call.id };
}),
);
}
}
3. 組み込みツール
// packages/core/src/tools/builtin/web-search.ts
export const webSearchTool: ToolDefinition = {
name: 'web_search',
description: 'Search the web for current information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
maxResults: { type: 'number', description: 'Max results (1-10)' },
},
required: ['query'],
},
};
export const webSearchHandler: ToolHandler = async (args) => {
const { query, maxResults = 5 } = args as { query: string; maxResults?: number };
const results = await fetch(`https://api.search.example/search?q=${encodeURIComponent(query)}&limit=${maxResults}`);
return results.json();
};
// packages/core/src/tools/builtin/code-interpreter.ts
export const codeInterpreterTool: ToolDefinition = {
name: 'execute_code',
description: 'Execute JavaScript/TypeScript code in a sandbox',
parameters: {
type: 'object',
properties: {
code: { type: 'string', description: 'Code to execute' },
language: { type: 'string', enum: ['javascript', 'typescript', 'python'] },
},
required: ['code'],
},
sandbox: { required: true },
};
4. サンドボックスでの実行
// packages/core/src/tools/sandbox.ts
import { runInNewContext } from 'node:vm';
export interface SandboxToolExecutor {
execute(code: string): Promise<unknown>;
}
export class VMSandbox implements SandboxToolExecutor {
async execute(code: string): Promise<unknown> {
const context = {
console: { log: (...args: unknown[]) => args },
Math,
JSON,
Date,
// NO access to: fs, process, require, import, fetch
};
return runInNewContext(code, context, {
timeout: 5000, // 5s max
displayErrors: true,
});
}
}
セキュリティ原則: マークが付いたツール sandbox: { required: true } VM サンドボックス内でのみ実行されます。ファイル システム、ネットワーク、プロセスにはアクセスできません。
5. まとめ
- ToolHandler — 単純な関数インターフェイス:
(args, context) => Promise<result> - ToolRegistry — 登録/登録解除/実行のレジストリ パターン
- 並列実行 —
executeAll()ツールを並行して実行する - サンドボックス — 信頼できないコードに対する VM ベースの分離
次の記事: ビルディング エージェント クラス — プラットフォームの調整センター。