Skip to content

OpenRouter SDK 的 tool() 函数让你用 Zod schema 创建类型安全的 tool,支持自动执行(SDK 自动处理多轮循环)、generator tool(边执行边产出进度)、manual tool(手动处理调用)三种模式。本页涵盖 tool 定义、类型推导、execute 上下文、tool 间共享状态,以及并发执行和错误处理的最佳实践。

tool() 函数

tool() 函数创建带 Zod schema 验证的类型安全 tool:

typescript
import { OpenRouter, tool } from '@openrouter/agent';
import { z } from 'zod';

const weatherTool = tool({
  name: 'get_weather',
  description: 'Get the current weather for a location',
  inputSchema: z.object({
    location: z.string().describe('City name, e.g., "San Francisco, CA"'),
  }),
  outputSchema: z.object({
    temperature: z.number(),
    conditions: z.string(),
  }),
  execute: async (params) => {
    // params 类型自动推导为 { location: string }
    const weather = await fetchWeather(params.location);
    return {
      temperature: weather.temp,
      conditions: weather.description,
    };
  },
});

Tool 类型

SDK 支持三种 tool,由配置自动判断:

普通 Tool

带 execute 函数的标准 tool:

typescript
const calculatorTool = tool({
  name: 'calculate',
  description: 'Perform a mathematical calculation',
  inputSchema: z.object({
    expression: z.string().describe('Math expression like "2 + 2"'),
  }),
  outputSchema: z.object({ result: z.number() }),
  execute: async (params) => {
    const result = eval(params.expression); // 生产环境请用更安全的实现
    return { result };
  },
});

Generator Tool

执行过程中持续产出进度更新。添加 eventSchema 即启用 generator 模式:

typescript
const searchTool = tool({
  name: 'search_database',
  description: 'Search documents with progress updates',
  inputSchema: z.object({
    query: z.string(),
    limit: z.number().default(10),
  }),
  // eventSchema 触发 generator 模式
  eventSchema: z.object({
    progress: z.number().min(0).max(100),
    message: z.string(),
  }),
  outputSchema: z.object({
    results: z.array(z.string()),
    totalFound: z.number(),
  }),
  // execute 变成 async generator
  execute: async function* (params) {
    yield { progress: 0, message: 'Starting search...' };

    const results = [];
    for (let i = 0; i < 5; i++) {
      yield { progress: (i + 1) * 20, message: `Searching batch ${i + 1}...` };
      results.push(...await searchBatch(params.query, i));
    }

    return {
      results: results.slice(0, params.limit),
      totalFound: results.length,
    };
  },
});

进度事件通过 getToolStream()getFullResponsesStream() 推送给消费者。

Manual Tool

不自动执行,由你处理 tool 调用:

typescript
const manualTool = tool({
  name: 'send_email',
  description: 'Send an email (requires user confirmation)',
  inputSchema: z.object({
    to: z.string().email(),
    subject: z.string(),
    body: z.string(),
  }),
  execute: false, // 手动处理
});

通过 getToolCalls() 获取 manual tool 的调用信息。

类型推导

SDK 提供工具类型提取:

typescript
import type { InferToolInput, InferToolOutput, InferToolEvent } from '@openrouter/agent';

type WeatherInput = InferToolInput<typeof weatherTool>;
// { location: string }

type WeatherOutput = InferToolOutput<typeof weatherTool>;
// { temperature: number; conditions: string }

type SearchEvent = InferToolEvent<typeof searchTool>;
// { progress: number; message: string }

在 callModel 中使用 Tool

单个 Tool

typescript
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'What is the weather in Tokyo?',
  tools: [weatherTool],
});

// Tool 自动执行
const text = await result.getText();
// "The weather in Tokyo is 22°C and sunny."

使用 as const 获得完整类型推导

typescript
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'What is the weather?',
  tools: [weatherTool, searchTool] as const,
  maxToolRounds: 0,
});

for await (const toolCall of result.getToolCallsStream()) {
  if (toolCall.name === 'get_weather') {
    // toolCall.arguments 类型自动推导为 { location: string }
    console.log('Weather for:', toolCall.arguments.location);
  }
}

Execute 上下文

tool execute 函数的第二个参数包含运行上下文:

typescript
const contextAwareTool = tool({
  name: 'context_tool',
  inputSchema: z.object({ data: z.string() }),
  outputSchema: z.object({ result: z.string() }),
  execute: async (params, context) => {
    console.log('Turn:', context.numberOfTurns);
    console.log('History:', context.turnRequest?.input);
    console.log('Model:', context.turnRequest?.model);

    return { result: `Processed on turn ${context.numberOfTurns}` };
  },
});

上下文属性

属性类型说明
numberOfTurnsnumber当前轮次(从 1 开始)
turnRequestOpenResponsesRequest | undefined当前请求对象
toolCallOpenResponsesFunctionToolCall | undefined当前 tool 调用
localReadonly本 tool 的上下文(只读)
setContext(partial) => void修改本 tool 的上下文
sharedReadonly所有 tool 共享的上下文
setSharedContext(partial) => void修改共享上下文

Tool 上下文(contextSchema)

contextSchema 声明 tool 需要的类型化持久上下文:

typescript
const weatherTool = tool({
  name: 'get_weather',
  inputSchema: z.object({ location: z.string() }),
  outputSchema: z.object({ temperature: z.number() }),
  contextSchema: z.object({
    apiKey: z.string(),
    units: z.enum(['celsius', 'fahrenheit']),
  }),
  execute: async (params, context) => {
    const { apiKey, units } = context.local;
    const weather = await fetchWeather(params.location, apiKey, units);
    return { temperature: weather.temp };
  },
});

// 在 callModel 中传入上下文
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'What is the weather in Tokyo?',
  tools: [weatherTool, dbTool] as const,
  context: {
    get_weather: { apiKey: 'sk-...', units: 'celsius' },
    db_query: { connectionString: 'postgres://...' },
  },
});

用 setContext 修改上下文

上下文变更在 tool 轮次间持久保留:

typescript
const authTool = tool({
  name: 'auth',
  inputSchema: z.object({ action: z.string() }),
  contextSchema: z.object({
    token: z.string(),
    refreshCount: z.number(),
  }),
  execute: async (params, context) => {
    const { token } = context.local;

    if (isExpired(token)) {
      const newToken = await refreshToken(token);
      context.setContext({
        token: newToken,
        refreshCount: context.local.refreshCount + 1,
      });
    }

    return { success: true };
  },
});

Tool 执行机制

自动执行流程

  1. 模型接收 prompt,生成 tool 调用
  2. SDK 提取 tool 调用并验证参数
  3. 执行 tool 的 execute 函数
  4. 将结果格式化后返回模型
  5. 模型生成最终回复(或继续调用 tool)
  6. 重复直到模型完成

控制执行轮次

typescript
// 限制最多 3 轮
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Research this topic',
  tools: [searchTool, analyzeTool],
  maxToolRounds: 3,
});

// 动态控制
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Research and analyze',
  tools: [searchTool],
  maxToolRounds: (context) => context.numberOfTurns < 5,
});

maxToolRounds: 0 禁用自动执行,只获取 tool 调用。

并行 Tool 执行

模型同时调用多个 tool 时,SDK 并行执行:

typescript
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Get weather in Paris, Tokyo, and New York simultaneously',
  tools: [weatherTool],
});

// 三个城市的请求并行执行
const text = await result.getText();

错误处理

execute 函数中的异常会被捕获并发回模型:

typescript
const riskyTool = tool({
  name: 'risky_operation',
  inputSchema: z.object({ input: z.string() }),
  outputSchema: z.object({ result: z.string() }),
  execute: async (params) => {
    if (params.input === 'fail') {
      throw new Error('Operation failed: invalid input');
    }
    return { result: 'success' };
  },
});

// 模型收到错误信息后能做出相应处理
const text = await result.getText();
// "I tried the operation but it failed with: Operation failed: invalid input"

最佳实践

清晰的 name 和 description——让模型知道何时调用:

typescript
const tool1 = tool({
  name: 'search_knowledge_base',
  description: 'Search the company knowledge base for documents, FAQs, and policies. Returns relevant articles with snippets.',
  // ...
});

.describe() 帮模型理解参数

typescript
const inputSchema = z.object({
  query: z.string().describe('Natural language search query'),
  maxResults: z.number().min(1).max(100).default(10)
    .describe('Maximum number of results to return (1-100)'),
});

常见问题

Q: generator tool 和普通 tool 在费用上有差异吗?

A: 没有。generator tool 只是在本地执行过程中 yield 进度更新,不会产生额外的 API 调用费用。

Q: 多个 tool 能共享状态吗?

A: 可以。通过 sharedSchemasetSharedContext() 在 tool 间共享类型化的状态,在多工具协作场景(如维持一个 session ID)非常实用。

Q: as const 是必须的吗?

A: 不是必须的,但强烈推荐。不加 as const 时,toolCall.arguments 的类型会退化为 unknown,失去精确类型推导。