Tool Calling Runtime 设计:工具注册、发现与动态调度
全面解析 Tool Calling Runtime 的核心架构,包括工具注册中心、能力发现机制和动态调度策略的工程实现。
Tool Calling Runtime 是 Agent 系统中连接 LLM 推理和外部操作的桥梁。它不仅要管理工具的注册和发现,还要处理调用路由、权限控制和结果格式化。一个设计良好的 Runtime 可以让 Agent 高效、安全地使用各种工具。
工具注册中心
注册机制
interface ToolDefinition {
name: string;
description: string;
inputSchema: JSONSchema;
outputSchema?: JSONSchema;
metadata: {
category: string;
tags: string[];
version: string;
author: string;
permissions: string[];
};
}
class ToolRegistry {
private tools: Map<string, ToolDefinition> = new Map();
private handlers: Map<string, ToolHandler> = new Map();
register(definition: ToolDefinition, handler: ToolHandler): void {
this.validateDefinition(definition);
this.tools.set(definition.name, definition);
this.handlers.set(definition.name, handler);
}
unregister(name: string): void {
this.tools.delete(name);
this.handlers.delete(name);
}
get(name: string): ToolDefinition | undefined {
return this.tools.get(name);
}
list(filter?: ToolFilter): ToolDefinition[] {
let tools = Array.from(this.tools.values());
if (filter?.category) {
tools = tools.filter(t => t.metadata.category === filter.category);
}
if (filter?.tags) {
tools = tools.filter(t =>
filter.tags!.some(tag => t.metadata.tags.includes(tag))
);
}
return tools;
}
private validateDefinition(definition: ToolDefinition): void {
if (!definition.name || !definition.description) {
throw new Error('Tool must have name and description');
}
if (!definition.inputSchema) {
throw new Error('Tool must have input schema');
}
}
}
动态注册
支持在运行时动态注册和注销工具:
class DynamicToolRegistry extends ToolRegistry {
private sources: Map<string, ToolSource> = new Map();
async addSource(source: ToolSource): Promise<void> {
this.sources.set(source.id, source);
// 从源加载工具
const tools = await source.discover();
for (const tool of tools) {
this.register(tool.definition, tool.handler);
}
}
async refreshSource(sourceId: string): Promise<void> {
const source = this.sources.get(sourceId);
if (!source) return;
// 注销旧工具
const oldTools = this.list({ source: sourceId });
for (const tool of oldTools) {
this.unregister(tool.name);
}
// 重新加载
const tools = await source.discover();
for (const tool of tools) {
this.register(tool.definition, tool.handler);
}
}
}
能力发现
语义搜索
当工具数量很多时,使用语义搜索找到最相关的工具:
class ToolDiscovery {
private embeddings: Map<string, number[]> = new Map();
async findRelevant(query: string, limit: number = 5): Promise<ToolDefinition[]> {
const queryEmbedding = await this.embed(query);
const scored: Array<{ tool: ToolDefinition; score: number }> = [];
for (const [name, embedding] of this.embeddings) {
const score = this.cosineSimilarity(queryEmbedding, embedding);
const tool = this.registry.get(name);
if (tool) {
scored.push({ tool, score });
}
}
scored.sort((a, b) => b.score - a.score);
return scored.slice(0, limit).map(s => s.tool);
}
private cosineSimilarity(a: number[], b: number[]): number {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
动态调度
负载均衡
class ToolLoadBalancer {
private instances: Map<string, ToolInstance[]> = new Map();
private roundRobinCounters: Map<string, number> = new Map();
select(toolName: string): ToolInstance {
const instances = this.instances.get(toolName) || [];
if (instances.length === 0) {
throw new Error(`No instances available for tool ${toolName}`);
}
// 轮询选择
const counter = this.roundRobinCounters.get(toolName) || 0;
const selected = instances[counter % instances.length];
this.roundRobinCounters.set(toolName, counter + 1);
return selected;
}
}
熔断机制
class CircuitBreaker {
private failures: Map<string, number> = new Map();
private states: Map<string, 'closed' | 'open' | 'half-open'> = new Map();
async execute<T>(toolName: string, fn: () => Promise<T>): Promise<T> {
const state = this.states.get(toolName) || 'closed';
if (state === 'open') {
throw new Error(`Circuit breaker open for ${toolName}`);
}
try {
const result = await fn();
this.onSuccess(toolName);
return result;
} catch (error) {
this.onFailure(toolName);
throw error;
}
}
private onSuccess(toolName: string): void {
this.failures.set(toolName, 0);
this.states.set(toolName, 'closed');
}
private onFailure(toolName: string): void {
const failures = (this.failures.get(toolName) || 0) + 1;
this.failures.set(toolName, failures);
if (failures >= 5) {
this.states.set(toolName, 'open');
setTimeout(() => this.states.set(toolName, 'half-open'), 60000);
}
}
}
结果格式化
class ResultFormatter {
format(result: any, schema?: JSONSchema): FormattedResult {
// 根据输出 schema 格式化结果
if (schema) {
const validated = this.validate(result, schema);
return { content: [{ type: 'text', text: JSON.stringify(validated) }] };
}
// 默认格式化
if (typeof result === 'string') {
return { content: [{ type: 'text', text: result }] };
}
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
}
}
常见问题(FAQ)
如何处理工具版本不兼容?
在工具注册时声明版本,Runtime 在调用时检查版本兼容性。使用语义化版本号(SemVer)。
工具的热更新如何实现?
通过动态注册机制,注销旧版本工具、注册新版本。使用中的工具调用不受影响。
如何限制工具的调用频率?
在 Runtime 层实现速率限制器,按工具名或用户 ID 进行限流。
总结
Tool Calling Runtime 是 Agent 系统的核心组件。通过注册中心管理工具定义,通过能力发现帮助 LLM 选择工具,通过动态调度保证调用的可靠性和性能。