mcpField
O
openrouter-typescript-sdk

openrouter-typescript-sdk

github.com/openrouterarchived开发292.0k 次安装39 周前更新
代码仓库

关于 openrouter-typescript-sdk

面向开发者的 TypeScript SDK 技能,通过 OpenRouter 统一 API 调用模型,支持生成文本、流式响应和工具执行,适用于构建多轮 AI Agent 工作流。

主要功能

  1. 01类型安全的模型调用
  2. 02文本与多模态输入
  3. 03流式响应
  4. 04自动执行工具
  5. 05多轮停止条件

适用场景

  1. 01构建模型驱动的 TypeScript 应用
  2. 02向界面流式输出文本
  3. 03在对话中运行工具
  4. 04按对话上下文切换参数

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/openrouterarchived/agent-skills --skill openrouter-typescript-sdk

安装后在 Claude Code 中输入/openrouter-typescript-sdk确认已加载。

附带文件metadata.json

详细介绍

OpenRouter TypeScript SDK

一个功能全面的 TypeScript SDK,可用于调用 OpenRouter 的统一 API,通过单一、类型安全的接口访问 300 多种 AI 模型。此技能使 AI Agent 能够使用 callModel 模式进行文本生成、工具调用、流式处理和多轮对话。


安装

npm install @openrouter/sdk

设置

从 openrouter.ai/settings/keys 获取 API 密钥,然后进行初始化:

import OpenRouter from '@openrouter/sdk';

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

身份验证

SDK 支持两种身份验证方式:适用于服务端应用的 API 密钥,以及适用于面向用户的应用的 OAuth PKCE 流程。

API 密钥身份验证

主要身份验证方式是使用 OpenRouter 账户中的 API 密钥。

获取 API 密钥
  1. 访问 openrouter.ai/settings/keys
  2. 创建新的 API 密钥
  3. 将其安全地存储在环境变量中
环境设置
export OPENROUTER_API_KEY=sk-or-v1-your-key-here
客户端初始化
import OpenRouter from '@openrouter/sdk';

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

客户端会在之后的所有请求中自动使用此密钥:

// API key is automatically included
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello!'
});
获取当前密钥元数据

检索当前配置的 API 密钥信息:

const keyInfo = await client.apiKeys.getCurrentKeyMetadata();
console.log('Key name:', keyInfo.name);
console.log('Created:', keyInfo.createdAt);
API 密钥管理

以编程方式管理 API 密钥:

// List all keys
const keys = await client.apiKeys.list();

// Create a new key
const newKey = await client.apiKeys.create({
  name: 'Production API Key'
});

// Get a specific key by hash
const key = await client.apiKeys.get({
  hash: 'sk-or-v1-...'
});

// Update a key
await client.apiKeys.update({
  hash: 'sk-or-v1-...',
  requestBody: {
    name: 'Updated Key Name'
  }
});

// Delete a key
await client.apiKeys.delete({
  hash: 'sk-or-v1-...'
});
OAuth 身份验证(PKCE 流程)

对于需要让用户自行控制 API 密钥的面向用户应用,OpenRouter 支持使用 PKCE(Proof Key for Code Exchange,代码交换证明)的 OAuth。此流程允许用户通过浏览器授权流程生成 API 密钥,而无需应用处理他们的凭据。

createAuthCode

生成授权码和 URL,以启动 OAuth 流程:

const authResponse = await client.oAuth.createAuthCode({
  callbackUrl: 'https://myapp.com/auth/callback'
});

// authResponse contains:
// - authorizationUrl: URL to redirect the user to
// - code: The authorization code for later exchange

console.log('Redirect user to:', authResponse.authorizationUrl);

参数:

参数类型必填说明
callbackUrlstring是用户授权后,应用用于接收回调的 URL

浏览器重定向:

// In a browser environment
window.location.href = authResponse.authorizationUrl;

// Or in a server-rendered app, return a redirect response
res.redirect(authResponse.authorizationUrl);
exchangeAuthCodeForAPIKey

用户授权应用后,会带着授权码重定向回你的回调 URL。将此授权码兑换为 API 密钥:

// In your callback handler
const code = req.query.code;  // From the redirect URL

const apiKeyResponse = await client.oAuth.exchangeAuthCodeForAPIKey({
  code: code
});

// apiKeyResponse contains:
// - key: The user's API key
// - Additional metadata about the key

const userApiKey = apiKeyResponse.key;

// Store securely for this user's future requests
await saveUserApiKey(userId, userApiKey);

参数:

参数类型必填说明
codestring是OAuth 重定向返回的授权码
OAuth 流程完整示例
import OpenRouter from '@openrouter/sdk';
import express from 'express';

const app = express();
const client = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY  // Your app's key for OAuth operations
});

// Step 1: Initiate OAuth flow
app.get('/auth/start', async (req, res) => {
  const authResponse = await client.oAuth.createAuthCode({
    callbackUrl: 'https://myapp.com/auth/callback'
  });

  // Store any state needed for the callback
  req.session.oauthState = { /* ... */ };

  // Redirect user to OpenRouter authorization page
  res.redirect(authResponse.authorizationUrl);
});

// Step 2: Handle callback and exchange code
app.get('/auth/callback', async (req, res) => {
  const { code } = req.query;

  if (!code) {
    return res.status(400).send('Authorization code missing');
  }

  try {
    const apiKeyResponse = await client.oAuth.exchangeAuthCodeForAPIKey({
      code: code as string
    });

    // Store the user's API key securely
    await saveUserApiKey(req.session.userId, apiKeyResponse.key);

    res.redirect('/dashboard?auth=success');
  } catch (error) {
    console.error('OAuth exchange failed:', error);
    res.redirect('/auth/error');
  }
});

// Step 3: Use the user's API key for their requests
app.post('/api/chat', async (req, res) => {
  const userApiKey = await getUserApiKey(req.session.userId);

  // Create a client with the user's key
  const userClient = new OpenRouter({
    apiKey: userApiKey
  });

  const result = userClient.callModel({
    model: 'openai/gpt-5-nano',
    input: req.body.message
  });

  const text = await result.getText();
  res.json({ response: text });
});
安全最佳实践
  1. 环境变量:将 API 密钥存储在环境变量中,切勿写入代码
  2. 密钥轮换:使用密钥管理 API 定期轮换密钥
  3. 环境隔离:为开发、预发布和生产环境使用不同的密钥
  4. 面向用户的应用使用 OAuth:面向用户的应用应使用 OAuth PKCE 流程,避免处理用户凭据
  5. 安全存储:在数据库中加密存储用户 API 密钥
  6. 最小权限范围:创建仅包含所需权限的密钥

核心概念:callModel

callModel 函数是文本生成的主要接口。它提供了一种统一且类型安全的方式,可与任何受支持的模型交互。

基本用法
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Explain quantum computing in one sentence.',
});

const text = await result.getText();
主要优势
  • 类型安全的参数,支持 IDE 完整的自动补全
  • 根据 OpenAPI 规范自动生成——随新模型推出自动更新
  • 多种使用方式——文本、流式处理、结构化数据
  • 自动执行工具,支持多轮对话

输入格式

SDK 接受灵活的输入类型作为 input 参数:

字符串输入

简单字符串会转换为一条用户消息:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello, how are you?'
});
消息数组

用于多轮对话:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: [
    { role: 'user', content: 'What is the capital of France?' },
    { role: 'assistant', content: 'The capital of France is Paris.' },
    { role: 'user', content: 'What is its population?' }
  ]
});
多模态内容

包含图像和文本:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'What is in this image?' },
        { type: 'image_url', image_url: { url: 'https://example.com/image.png' } }
      ]
    }
  ]
});
系统指令

使用 instructions 参数提供系统级指导:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  instructions: 'You are a helpful coding assistant. Be concise.',
  input: 'How do I reverse a string in Python?'
});

响应方法

结果对象提供了多种方法来处理响应:

方法用途
getText()所有工具执行完成后获取完整文本
getResponse()获取包含 token 用量的完整响应对象
getTextStream()随文本增量到达而流式输出
getReasoningStream()流式输出推理 token(适用于 o1/推理模型)
getToolCallsStream()随工具调用完成而流式输出
getText()
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Write a haiku about coding'
});

const text = await result.getText();
console.log(text);
getResponse()
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello!'
});

const response = await result.getResponse();
console.log('Text:', response.text);
console.log('Token usage:', response.usage);
getTextStream()
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Write a short story'
});

for await (const delta of result.getTextStream()) {
  process.stdout.write(delta);
}

工具系统

使用 Zod 模式创建强类型工具,实现自动验证和类型推断。

定义工具
import { tool } from '@openrouter/sdk';
import { z } from 'zod';

const weatherTool = tool({
  name: 'get_weather',
  description: 'Get current weather for a location',
  inputSchema: z.object({
    location: z.string().describe('City name'),
    units: z.enum(['celsius', 'fahrenheit']).optional().default('celsius')
  }),
  outputSchema: z.object({
    temperature: z.number(),
    conditions: z.string(),
    humidity: z.number()
  }),
  execute: async (params) => {
    // Implement weather fetching logic
    return {
      temperature: 22,
      conditions: 'Sunny',
      humidity: 45
    };
  }
});
将工具与 callModel 搭配使用
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'What is the weather in Paris?',
  tools: [weatherTool]
});

const text = await result.getText();
// The SDK automatically executes the tool and continues the conversation
工具类型
常规工具

返回结果的标准执行函数:

const calculatorTool = tool({
  name: 'calculate',
  description: 'Perform mathematical calculations',
  inputSchema: z.object({
    expression: z.string()
  }),
  execute: async ({ expression }) => {
    return { result: eval(expression) };
  }
});
生成器工具

使用 eventSchema 生成进度事件:

const searchTool = tool({
  name: 'web_search',
  description: 'Search the web',
  inputSchema: z.object({ query: z.string() }),
  eventSchema: z.object({
    type: z.literal('progress'),
    message: z.string()
  }),
  outputSchema: z.object({ results: z.array(z.string()) }),
  execute: async function* ({ query }) {
    yield { type: 'progress', message: 'Searching...' };
    yield { type: 'progress', message: 'Processing results...' };
    return { results: ['Result 1', 'Result 2'] };
  }
});
手动工具

将 execute: false 设为由你自行处理工具调用:

const manualTool = tool({
  name: 'user_confirmation',
  description: 'Request user confirmation',
  inputSchema: z.object({ message: z.string() }),
  execute: false
});

带停止条件的多轮对话

使用停止条件控制自动工具执行:

import { stepCountIs, maxCost, hasToolCall } from '@openrouter/sdk';

const result = client.callModel({
  model: 'openai/gpt-5.2',
  input: 'Research this topic thoroughly',
  tools: [searchTool, analyzeTool],
  stopWhen: [
    stepCountIs(10),      // Stop after 10 turns
    maxCost(1.00),        // Stop if cost exceeds $1.00
    hasToolCall('finish') // Stop when 'finish' tool is called
  ]
});
可用停止条件
条件描述
stepCountIs(n)n 轮后停止
maxCost(amount)费用超过指定金额时停止
hasToolCall(name)调用指定工具时停止
自定义停止条件
const customStop = (context) => {
  return context.messages.length > 20;
};

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Complex task',
  tools: [myTool],
  stopWhen: customStop
});

动态参数

根据对话上下文计算参数:

const result = client.callModel({
  model: (ctx) => ctx.numberOfTurns > 3 ? 'openai/gpt-4' : 'openai/gpt-4o-mini',
  temperature: (ctx) => ctx.numberOfTurns > 1 ? 0.3 : 0.7,
  input: 'Hello!'
});
上下文对象属性
属性类型描述
numberOfTurnsnumber当前轮数
messagesarray目前为止的所有消息
instructionsstring当前系统指令
totalCostnumber累计费用

nextTurnParams:上下文注入

工具可以修改后续轮次的参数,从而支持技能和上下文感知行为:

const skillTool = tool({
  name: 'load_skill',
  description: 'Load a specialized skill',
  inputSchema: z.object({
    skill: z.string().describe('Name of the skill to load')
  }),
  nextTurnParams: {
    instructions: (params, context) => {
      const skillInstructions = loadSkillInstructions(params.skill);
      return `${context.instructions}\n\n${skillInstructions}`;
    }
  },
  execute: async ({ skill }) => {
    return { loaded: skill };
  }
});
nextTurnParams 的用例
  • 技能系统:动态加载专用能力
  • 上下文累积:跨多轮逐步构建上下文
  • 模式切换:在对话过程中更改模型行为
  • 记忆注入:将检索到的上下文添加到指令中

生成参数

使用以下参数控制模型行为:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Write a creative story',
  temperature: 0.7,        // Creativity (0-2, default varies by model)
  maxOutputTokens: 1000,   // Maximum tokens to generate
  topP: 0.9,               // Nucleus sampling parameter
  frequencyPenalty: 0.5,   // Reduce repetition
  presencePenalty: 0.5,    // Encourage new topics
  stop: ['\n\n']           // Stop sequences
});

流式传输

所有流式传输方法都支持从单个结果对象并发使用:

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Write a detailed explanation'
});

// Consumer 1: Stream text to console
const textPromise = (async () => {
  for await (const delta of result.getTextStream()) {
    process.stdout.write(delta);
  }
})();

// Consumer 2: Get full response simultaneously
const responsePromise = result.getResponse();

// Both run concurrently
const [, response] = await Promise.all([textPromise, responsePromise]);
console.log('\n\nTotal tokens:', response.usage.totalTokens);
流式输出工具调用
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Search for information about TypeScript',
  tools: [searchTool]
});

for await (const toolCall of result.getToolCallsStream()) {
  console.log(`Tool called: ${toolCall.name}`);
  console.log(`Arguments: ${JSON.stringify(toolCall.arguments)}`);
  console.log(`Result: ${JSON.stringify(toolCall.result)}`);
}

格式转换

在不同生态格式之间转换,以实现互操作性:

OpenAI 格式
import { fromChatMessages, toChatMessage } from '@openrouter/sdk';

// OpenAI messages → OpenRouter format
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: fromChatMessages(openaiMessages)
});

// Response → OpenAI chat message format
const response = await result.getResponse();
const chatMsg = toChatMessage(response);
Claude 格式
import { fromClaudeMessages, toClaudeMessage } from '@openrouter/sdk';

// Claude messages → OpenRouter format
const result = client.callModel({
  model: 'anthropic/claude-3-opus',
  input: fromClaudeMessages(claudeMessages)
});

// Response → Claude message format
const response = await result.getResponse();
const claudeMsg = toClaudeMessage(response);

Responses API 消息形态

SDK 使用 OpenResponses 格式表示消息。了解这些形态对于构建可靠的 Agent 至关重要。

消息角色

消息包含用于确定消息类型的 role 属性:

角色描述
user用户提供的输入
assistant模型生成的响应
system系统指令
developer开发者级指令
tool工具执行结果
文本消息

用户或助手发送的简单文本内容:

interface TextMessage {
  role: 'user' | 'assistant';
  content: string;
}
多模态消息(数组内容)

包含混合内容类型的消息:

interface MultimodalMessage {
  role: 'user';
  content: Array<
    | { type: 'input_text'; text: string }
    | { type: 'input_image'; imageUrl: string; detail?: 'auto' | 'low' | 'high' }
    | {
        type: 'image';
        source: {
          type: 'url' | 'base64';
          url?: string;
          media_type?: string;
          data?: string
        }
      }
  >;
}
工具函数调用消息

模型请求执行工具时:

interface ToolCallMessage {
  role: 'assistant';
  content?: null;
  tool_calls?: Array<{
    id: string;
    type: 'function';
    function: {
      name: string;
      arguments: string;  // JSON-encoded arguments
    };
  }>;
}
工具结果消息

工具执行后返回的结果:

interface ToolResultMessage {
  role: 'tool';
  tool_call_id: string;
  content: string;  // JSON-encoded result
}
非流式响应结构

通过 getResponse() 获取的完整响应对象:

interface OpenResponsesNonStreamingResponse {
  output: Array<ResponseMessage>;
  usage?: {
    inputTokens: number;
    outputTokens: number;
    cachedTokens?: number;
  };
  finishReason?: string;
  warnings?: Array<{
    type: string;
    message: string
  }>;
  experimental_providerMetadata?: Record<string, unknown>;
}
响应消息类型

响应数组中的输出消息:

// Text/content message
interface ResponseOutputMessage {
  type: 'message';
  role: 'assistant';
  content: string | Array<ContentPart>;
  reasoning?: string;  // For reasoning models (o1, etc.)
}

// Tool result in output
interface FunctionCallOutputMessage {
  type: 'function_call_output';
  call_id: string;
  output: string;
}
已解析的工具调用

从响应中解析工具调用时:

interface ParsedToolCall {
  id: string;
  name: string;
  arguments: unknown;  // Validated against inputSchema
}
工具执行结果

工具执行完成后:

interface ToolExecutionResult {
  toolCallId: string;
  toolName: string;
  result: unknown;                  // Validated against outputSchema
  preliminaryResults?: unknown[];   // From generator tools
  error?: Error;
}
步骤结果(用于停止条件)

可在自定义停止条件回调中使用:

interface StepResult {
  stepType: 'initial' | 'continue';
  text: string;
  toolCalls: ParsedToolCall[];
  toolResults: ToolExecutionResult[];
  response: OpenResponsesNonStreamingResponse;
  usage?: {
    inputTokens: number;
    outputTokens: number;
    cachedTokens?: number;
  };
  finishReason?: string;
  warnings?: Array<{ type: string; message: string }>;
  experimental_providerMetadata?: Record<string, unknown>;
}
TurnContext

可供工具和动态参数函数使用:

interface TurnContext {
  numberOfTurns: number;                     // Turn count (1-indexed)
  turnRequest?: OpenResponsesRequest;        // Current request being made
  toolCall?: OpenResponsesFunctionToolCall;  // Current tool call (in tool context)
}

事件类型

SDK 提供多种流式方法,可生成不同的事件类型。

响应流事件

getFullResponsesStream() 方法会生成以下事件类型:

type EnhancedResponseStreamEvent =
  | ResponseCreatedEvent
  | ResponseInProgressEvent
  | OutputTextDeltaEvent
  | OutputTextDoneEvent
  | ReasoningDeltaEvent
  | ReasoningDoneEvent
  | FunctionCallArgumentsDeltaEvent
  | FunctionCallArgumentsDoneEvent
  | ResponseCompletedEvent
  | ToolPreliminaryResultEvent;
事件类型参考
事件类型描述负载
response.created响应对象已初始化{ response: ResponseObject }
response.in_progress已开始生成{}
response.output_text.delta收到文本块{ delta: string }
response.output_text.done文本生成完成{ text: string }
response.reasoning.delta推理内容块(o1 模型){ delta: string }
response.reasoning.done推理完成{ reasoning: string }
response.function_call_arguments.delta工具参数块{ delta: string }
response.function_call_arguments.done工具参数完成{ arguments: string }
response.completed完整响应已完成{ response: ResponseObject }
tool.preliminary_result生成器工具的进度{ toolCallId: string; result: unknown }
文本增量事件
interface OutputTextDeltaEvent {
  type: 'response.output_text.delta';
  delta: string;
}
推理增量事件

用于推理模型(o1 等):

interface ReasoningDeltaEvent {
  type: 'response.reasoning.delta';
  delta: string;
}
函数调用参数增量事件
interface FunctionCallArgumentsDeltaEvent {
  type: 'response.function_call_arguments.delta';
  delta: string;
}
工具初步结果事件

由会生成进度的生成器工具发出:

interface ToolPreliminaryResultEvent {
  type: 'tool.preliminary_result';
  toolCallId: string;
  result: unknown;  // Matches the tool's eventSchema
}
响应完成事件
interface ResponseCompletedEvent {
  type: 'response.completed';
  response: OpenResponsesNonStreamingResponse;
}
工具流事件

getToolStream() 方法会生成:

type ToolStreamEvent =
  | { type: 'delta'; content: string }
  | { type: 'preliminary_result'; toolCallId: string; result: unknown };
示例:处理流事件
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Analyze this data',
  tools: [analysisTool]
});

for await (const event of result.getFullResponsesStream()) {
  switch (event.type) {
    case 'response.output_text.delta':
      process.stdout.write(event.delta);
      break;

    case 'response.reasoning.delta':
      console.log('[Reasoning]', event.delta);
      break;

    case 'response.function_call_arguments.delta':
      console.log('[Tool Args]', event.delta);
      break;

    case 'tool.preliminary_result':
      console.log(`[Progress: ${event.toolCallId}]`, event.result);
      break;

    case 'response.completed':
      console.log('\n[Complete]', event.response.usage);
      break;
  }
}
消息流事件

getNewMessagesStream() 会生成 OpenResponses 格式的更新:

type MessageStreamUpdate =
  | ResponsesOutputMessage        // Text/content updates
  | OpenResponsesFunctionCallOutput;  // Tool results
示例:跟踪新消息
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Research this topic',
  tools: [searchTool]
});

const allMessages: MessageStreamUpdate[] = [];

for await (const message of result.getNewMessagesStream()) {
  allMessages.push(message);

  if (message.type === 'message') {
    console.log('Assistant:', message.content);
  } else if (message.type === 'function_call_output') {
    console.log('Tool result:', message.output);
  }
}

API 参考

客户端方法

除了 callModel 以外,客户端还可访问其他 API 端点:

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

// List available models
const models = await client.models.list();

// Chat completions (alternative to callModel)
const completion = await client.chat.send({
  model: 'openai/gpt-5-nano',
  messages: [{ role: 'user', content: 'Hello!' }]
});

// Legacy completions format
const legacyCompletion = await client.completions.generate({
  model: 'openai/gpt-5-nano',
  prompt: 'Once upon a time'
});

// Usage analytics
const activity = await client.analytics.getUserActivity();

// Credit balance
const credits = await client.credits.getCredits();

// API key management
const keys = await client.apiKeys.list();

错误处理

SDK 提供带有可操作提示的特定错误类型:

try {
  const result = await client.callModel({
    model: 'openai/gpt-5-nano',
    input: 'Hello!'
  });
  const text = await result.getText();
} catch (error) {
  if (error.statusCode === 401) {
    console.error('Invalid API key - check your OPENROUTER_API_KEY');
  } else if (error.statusCode === 402) {
    console.error('Insufficient credits - add credits at openrouter.ai');
  } else if (error.statusCode === 429) {
    console.error('Rate limited - implement backoff retry');
  } else if (error.statusCode === 503) {
    console.error('Model temporarily unavailable - try again or use fallback');
  } else {
    console.error('Unexpected error:', error.message);
  }
}
错误状态码
代码含义操作
400请求无效检查请求参数
401未授权验证 API 密钥
402需要付款添加额度
429请求频率受限实现指数退避
500服务端错误采用退避策略重试
503服务不可用尝试其他模型

完整示例:带工具的 Agent

import OpenRouter, { tool, stepCountIs } from '@openrouter/sdk';
import { z } from 'zod';

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

// Define tools
const searchTool = tool({
  name: 'web_search',
  description: 'Search the web for information',
  inputSchema: z.object({
    query: z.string().describe('Search query')
  }),
  outputSchema: z.object({
    results: z.array(z.object({
      title: z.string(),
      snippet: z.string(),
      url: z.string()
    }))
  }),
  execute: async ({ query }) => {
    // Implement actual search
    return {
      results: [
        { title: 'Example', snippet: 'Example result', url: 'https://example.com' }
      ]
    };
  }
});

const finishTool = tool({
  name: 'finish',
  description: 'Complete the task with final answer',
  inputSchema: z.object({
    answer: z.string().describe('The final answer')
  }),
  execute: async ({ answer }) => ({ answer })
});

// Run agent
async function runAgent(task: string) {
  const result = client.callModel({
    model: 'openai/gpt-5-nano',
    instructions: 'You are a helpful research assistant. Use web_search to find information, then use finish to provide your final answer.',
    input: task,
    tools: [searchTool, finishTool],
    stopWhen: [
      stepCountIs(10),
      hasToolCall('finish')
    ]
  });

  // Stream progress
  for await (const toolCall of result.getToolCallsStream()) {
    console.log(`[${toolCall.name}] ${JSON.stringify(toolCall.arguments)}`);
  }

  return await result.getText();
}

// Usage
const answer = await runAgent('What are the latest developments in quantum computing?');
console.log('Final answer:', answer);

最佳实践

1. 优先使用 callModel,而非直接调用 API

callModel 模式可自动执行工具、提供类型安全并处理多轮交互。

2. 使用 Zod 定义工具架构

Zod 可提供运行时验证和出色的 TypeScript 类型推断:

import { z } from 'zod';

const schema = z.object({
  name: z.string().min(1),
  age: z.number().int().positive()
});
3. 设置停止条件

始终设置合理的限制,避免费用不断增加:

stopWhen: [stepCountIs(20), maxCost(5.00)]
4. 妥善处理错误

为暂时性故障实现重试逻辑:

async function callWithRetry(params, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await client.callModel(params).getText();
    } catch (error) {
      if (error.statusCode === 429 || error.statusCode >= 500) {
        await sleep(Math.pow(2, i) * 1000);
        continue;
      }
      throw error;
    }
  }
}
5. 对较长响应使用流式传输

流式传输能带来更好的用户体验,并允许提前终止:

for await (const delta of result.getTextStream()) {
  // Process incrementally
}

其他资源


SDK 状态:Beta - 请在 GitHub 上报告问题

评论

支持 Markdown