webagent — LLM Providers

設計原則

  • BYOK(Bring Your Own Key) — 套件不代管 API key
  • Provider 抽象 — host 換 provider 不用改 agent 邏輯
  • v1 支援 OpenAI + Google AI Studio 兩家(Google 同一個 key 可同時呼叫 Gemini + Gemma),其他先不上
  • Function calling 走 OpenAI 格式為內部標準(Google 用 adapter 轉)
  • Per-role 模型分流 — 透過 LLMRouter 讓不同 role 用不同 provider/model,省成本

LLMProvider 介面

interface LLMProvider {
  readonly name: string;

  complete(opts: CompleteOptions): Promise<CompleteResult>;
}

interface CompleteOptions {
  messages: LLMMessage[];
  tools?: ToolDefinition[];
  temperature?: number;
  maxTokens?: number;
  model?: string;
  signal?: AbortSignal;
}

interface CompleteResult {
  content: string;
  toolCalls?: ToolCall[];
  usage?: { promptTokens: number; completionTokens: number };
  finishReason: 'stop' | 'tool_calls' | 'length' | 'content_filter';
}

interface LLMMessage {
  role: 'system' | 'user' | 'assistant' | 'tool';
  content: string | ContentPart[];
  toolCallId?: string;  // role=tool 時
  toolCalls?: ToolCall[];  // role=assistant 時
}

interface ContentPart {
  type: 'text' | 'image';
  text?: string;
  image?: string;  // URL 或 base64
}

interface ToolDefinition {
  name: string;
  description: string;
  parameters: JSONSchema;  // 標準 JSON Schema
}

interface ToolCall {
  id: string;
  name: string;
  arguments: Record<string, any>;
}

OpenAI Provider

import { OpenAIProvider } from '@perhapxin/webagent';

const llm = new OpenAIProvider({
  apiKey: 'sk-...',
  model: 'gpt-5.5',         // 預設 'gpt-5.4-mini'
  baseURL?: 'https://api.openai.com/v1',  // 可改自架 / 反代理
  organization?: string,
});

支援所有 OpenAI 相容 endpoint(Azure OpenAI、自架 OpenRouter、Cloudflare AI Gateway)。

gpt-5.x / o-series reasoning models 的特殊處理

新一代 reasoning models(gpt-5.xo1o3o4不接受

  • max_tokens — 改用 max_completion_tokens
  • 自訂 temperature — 必須是 default 1

Provider 內部 isReasoningModel 會偵測 model 前綴自動切:

Model 前綴 token field temperature
gpt-5.x, o[1-9], gpt-1[0-9].x max_completion_tokens 省略(用 default)
其他 max_tokens 套用 opts.temperature ?? 0.7

Host 不需要關心 — 換 model 名稱字串就好。

建議 model(2026 年中)

用途 model
flagship gpt-5.5
中等便宜 gpt-5.4-mini
最便宜最快 gpt-5.4-nano

Google Provider(Gemini + Gemma 共用)

import { GoogleProvider } from '@perhapxin/webagent';

const llm = new GoogleProvider({
  apiKey: '...',                         // Google AI Studio key
  model: 'gemini-3.1-pro-preview',       // 預設
});

同一個 Google AI Studio key 可以打 Gemini 跟 Gemma 兩個 model family(只差 model id)。內部把 OpenAI 格式的 tool definitions 轉成 Google function declarations;把 Google 回應的 function call 轉回 OpenAI ToolCall。

建議 model(2026 年中)

用途 model
flagship gemini-3.1-pro-preview
中等 gemini-2.5-pro
快 / 便宜 gemini-3.1-flash-lite-preview
Open-weight (Gemma) gemma-4-31b-itgemma-4-26b-a4b-it

Per-role 模型分流(LLMRouter)

不一定要全部一個 model,可以每個 role 配不同 provider/model

import { OpenAIProvider, GoogleProvider, type LLMRouter } from '@perhapxin/webagent';

const router: LLMRouter = {
  webagent: new OpenAIProvider({ apiKey: openai, model: 'gpt-5.5' }),
  webagentWithSelection: new OpenAIProvider({ apiKey: openai, model: 'gpt-5.4-mini' }),
  select: new GoogleProvider({ apiKey: google, model: 'gemini-3.1-flash-lite-preview' }),
  voiceCleanup: new GoogleProvider({ apiKey: google, model: 'gemini-3.1-flash-lite-preview' }),
};

new WebAgent({ llm: router });  // accepts LLMProvider | LLMRouter

未設的 role 自動 fallback 到 webagent(必填)。

Roles:

Role 何時用 推薦特性
webagent webagent 主迴圈,無 selection smart, 大 model
webagentWithSelection 開 palette 時有選取 → webagent 中等(task 範圍受限)
select inline 工具列、translate/summarize 等短任務 快、便宜
voiceCleanup 語音 STT 結果 post-processing 最便宜

多 provider 切換策略

Host 可以根據用戶設定 / 環境變數 / cost 動態挑:

const provider = userPlan === 'pro'
  ? new OpenAIProvider({ apiKey: openaiKey, model: 'gpt-5.5' })
  : new GoogleProvider({ apiKey: googleKey, model: 'gemini-3.1-flash-lite-preview' });

const agent = new WebAgent({ llm: provider });

API key 哪裡放

不要 hardcode 在 client bundle。三個建議:

  1. 用戶自己填(推薦) — 提供 settings UI 讓用戶輸入,存 localStorage / IndexedDB
  2. 反代理 — 自己架一個 worker / edge function 當代理,把 key 放 server env(見 ProxyProvider
  3. OAuth — 用 Google OAuth + AI Studio 的 user-scoped key(適合企業場景)

預設假設 1。

Image 支援

兩家都支援 vision,介面統一:

const messages: LLMMessage[] = [{
  role: 'user',
  content: [
    { type: 'text', text: '這張圖是什麼?' },
    { type: 'image', image: 'data:image/png;base64,...' },
  ],
}];

await llm.complete({ messages });

Provider 內部處理格式差異。

Streaming

v1 做 streaming(agent 是 turn-based,每一步要完整 tool call 才能執行)。

v2 考慮 streaming 用在 show_subtitle 場景(讓 subtitle 邊打邊出)。

自己寫 provider

class MyProvider implements LLMProvider {
  readonly name = 'my-provider';

  async complete(opts: CompleteOptions): Promise<CompleteResult> {
    // 你的實作
  }
}

只要實作 complete() 就行,agent 不在乎你怎麼接。

v1 不支援的(明確列出來,避免被問)

  • ❌ Anthropic Claude(v2 加)
  • ❌ Ollama / 本機模型(v2 加)
  • ❌ Embedding(不在 webagent 範圍)
  • ❌ Fine-tune API(不在範圍)
  • ❌ Streaming(v2 看情況)