Felo API PlatformFelo API Platform

成本

隨著用量成長,讓模型開銷保持可預測

把每個任務都走同一個 OpenAI-compatible endpoint,路由到能完成任務的最便宜模型,並記錄每次回應回傳的 token 用量。

建置時間20 分鐘難度進階更新於2026-10-29
適合誰平台團隊API 產品負責人工程負責人

複製 prompt

把方括號裡的欄位替換掉,然後貼到 Claude Code、Codex 或任何能存取終端機的 AI。它會安裝 Felo skill、在花額度前詢問,並執行這套工作流程。

審查我們的模型支出,並為這些任務提出路由方案:[任務或功能清單]。

限制:月度預算 [金額]、延遲目標 [目標]、品質門檻 [門檻]。
在提出任何建議前,先讀取即時模型目錄。

輸出:
1. 任務 -> 模型推薦表。
2. 哪些任務可以降一階,以及品質風險。
3. 依任務和功能記錄的用量日誌方案。
4. 改動生產前需要衡量什麼。

規則:
- 從即時目錄讀取目前模型 ID 和價格;不要憑記憶引用費率。
- 不要改動生產設定或傳送真實流量。
- 在消耗額度做測試呼叫前先詢問。
- 保留回滾方案和依功能的支出上限。
- 將方案寫入 ./routing-plan.md,並在對話中總結。

複製的內容也包含安裝步驟和安全規則。

你會得到什麼

一次執行的範例輸出,不是即時生成。

路由方案範例(節錄)
任務目前建議風險
工單分類強模型快模型低
回覆草稿強模型強模型無
變更日誌摘要強模型快模型中

改動之前: 依任務記錄一週用量,並在 50 條抽樣輸出上比較品質。

實際呼叫

給開發者:同一套工作流程的可複製程式碼,依執行順序排列。

展開實際 API 呼叫
  1. 所有任務走同一個 endpoint

    統一用 OpenAI-compatible endpoint 和 Key,避免每家供應商一套 SDK。

    routing.ts
    const BASE_URL = "https://openapi.felo.ai/api/v1";
    const headers = {
      Authorization: `Bearer ${process.env.FELO_API_KEY}`,
      "Content-Type": "application/json",
    };
    
    type Completion = {
      choices: { message: { content: string } }[];
      usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
    };
    
    async function chat(model: string, prompt: string): Promise<Completion> {
      const res = await fetch(`${BASE_URL}/chat/completions`, {
        method: "POST",
        headers,
        body: JSON.stringify({ model, messages: [{ role: "user", content: prompt }] }),
      });
      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
      return res.json() as Promise<Completion>;
    }
    
    const summary = await chat(
      "claude-haiku-5-5",
      "Summarize this changelog in three bullets: …",
    );
    console.log(summary.choices[0].message.content);

    範例輸出

    {
      "choices": [{ "message": { "content": "• …" } }],
      "usage": { "prompt_tokens": 812, "completion_tokens": 96, "total_tokens": 908 }
    }
  2. 依任務路由模型

    依任務複雜度選擇快模型或強模型,先記錄目前基線。

    routing.ts
    type TaskKind = "classify" | "extract" | "summarize" | "draft" | "review";
    
    const MODEL_ROUTES: Record<TaskKind, string> = {
      classify: "claude-haiku-5-5",
      extract: "claude-haiku-5-5",
      summarize: "claude-haiku-5-5",
      draft: "claude-sonnet-5-5",
      review: "claude-sonnet-5-5",
    };
    
    export async function route(kind: TaskKind, prompt: string) {
      const model = MODEL_ROUTES[kind];
      const result = await chat(model, prompt);
      return {
        model,
        text: result.choices[0].message.content,
        usage: result.usage,
      };
    }

    範例輸出

    {
      "model": "claude-haiku-5-5",
      "text": "• …",
      "usage": { "prompt_tokens": 812, "completion_tokens": 96, "total_tokens": 908 }
    }
  3. 讀取並記錄用量

    從每次回應裡讀 usage,依功能記錄 token 和延遲。

    routing.ts
    type UsageRecord = {
      model: string;
      kind: TaskKind;
      promptTokens: number;
      completionTokens: number;
      totalTokens: number;
    };
    
    const usageLog: UsageRecord[] = [];
    
    function logUsage(kind: TaskKind, model: string, usage: Completion["usage"]) {
      const entry: UsageRecord = {
        model,
        kind,
        promptTokens: usage.prompt_tokens,
        completionTokens: usage.completion_tokens,
        totalTokens: usage.total_tokens,
      };
      usageLog.push(entry);
      console.log(JSON.stringify(entry)); // ship this line to your metrics store
      return entry;
    }
    
    export function tokensByModel() {
      return usageLog.reduce<Record<string, number>>((totals, entry) => {
        totals[entry.model] = (totals[entry.model] ?? 0) + entry.totalTokens;
        return totals;
      }, {});
    }
    
    const result = await route("summarize", "Summarize this changelog in three bullets: …");
    logUsage("summarize", result.model, result.usage);
    console.log(tokensByModel());

    範例輸出

    {"model":"claude-haiku-5-5","kind":"summarize","promptTokens":812,"completionTokens":96,"totalTokens":908}
    { "claude-haiku-5-5": 18234, "claude-sonnet-5-5": 4201 }
  4. 一個 Key 涵蓋所有供應商

    用同一個 Felo Key 呼叫目錄裡的模型,減少供應商管理。

    routing.ts
    async function chatOnChannel(model: string, prompt: string, channel?: string) {
      const res = await fetch(`${BASE_URL}/chat/completions`, {
        method: "POST",
        headers: {
          ...headers,
          ...(channel ? { "X-Felo-Channel": channel } : {}),
        },
        body: JSON.stringify({ model, messages: [{ role: "user", content: prompt }] }),
      });
      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
      return res.json() as Promise<Completion>;
    }
    
    // Leave `channel` undefined for the default supplier.
    export function routePinned(kind: TaskKind, prompt: string, channel?: string) {
      return chatOnChannel(MODEL_ROUTES[kind], prompt, channel);
    }
  5. 價格從即時目錄讀

    不要寫死費率;從模型目錄和價格頁讀取目前價格。

    routing.ts
    type Catalog = {
      data: { id: string; owned_by: string; model_kind: string }[];
    };
    
    export async function chatModelIds() {
      const res = await fetch(
        `${BASE_URL}/models?exclude_video_and_decisions=true`,
        { headers },
      );
      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
      const catalog = (await res.json()) as Catalog;
      return catalog.data
        .filter((model) => model.model_kind === "chat")
        .map((model) => model.id);
    }
    
    // Price `tokensByModel()` against https://openapi.felo.ai/pricing and
    // https://openapi.felo.ai/models. Keep the links, not the rates, in your code.

    範例輸出

    ["claude-haiku-5-5", "claude-sonnet-5-5"]

包含什麼

  • 統一 endpoint 和 Key
  • 依任務選擇快模型或強模型
  • 每次回應的 token 用量日誌
  • 從即時目錄讀取的價格,而不是寫死在程式碼裡

不做什麼

  • 不保證一定降低帳單
  • 不自動切換生產流量
  • 模型 id 和價格會變;始終讀即時目錄
  • 不替你評估品質,需要你自己的樣本
  • 不取代帳單或可觀測系統

常見問題

會降低帳單嗎?

它指出哪些任務可能可以降到更便宜的層級,但只有在你對比樣本輸出並記錄用量之後,節省才是真的。

會自動呼叫模型嗎?

只有你批准測試呼叫時才會。預設只讀目錄並寫建議,不碰生產流量。

怎麼知道哪個模型更便宜?

讀即時模型目錄和價格頁;工作流程不寫死費率,因為價格會變。

可以固定供應商嗎?

可以,當延遲比價格更重要時。固定是選擇,不是重試策略;fallback 需要你自己加。

應該記錄什麼?

每次呼叫記錄:任務、功能、模型、prompt token、completion token、總 token 和延遲。依功能聚合後再設預算。

一次 review 要花多少?

讀目錄不產生模型呼叫;測試呼叫按用量計費,prompt 會在呼叫前請求確認。

相關工作流程

改成你的版本

同一套配方,依你實際的交付方式調整。

先只記錄不切換

上線前先記錄一週用量,再決定是否降檔。

依功能設預算

給每個功能設定月度上限,超出時告警或降級。

固定供應商換延遲

當延遲比價格重要時,固定供應商並加自己的 fallback。

這套配方背後的參考頁面,以及下一步可以看什麼。