Felo API PlatformFelo API Platform

成本

随着用量增长,让模型开销保持可预测

把每个任务都走同一个 OpenAI-compatible endpoint,路由到能完成任务的最便宜模型,并记录每次响应返回的 token 用量。

构建时间20 分钟难度进阶更新于2026-10-29
适合谁平台团队API 产品负责人工程负责人

复制 prompt

把方括号里的字段替换掉,然后粘贴到 Claude Code、Codex 或任何能访问终端的 AI。它会安装 Felo skill、在花额度前询问,并执行这套 workflow。

审查我们的模型支出,并为这些任务提出路由方案:[任务或功能列表]。

约束:月度预算 [金额]、延迟目标 [目标]、质量门槛 [门槛]。
在提出任何建议前,先读取实时模型目录。

输出:
1. 任务 -> 模型推荐表。
2. 哪些任务可以降一档,以及质量风险。
3. 按任务和功能记录的用量日志方案。
4. 改动生产前需要衡量什么。

规则:
- 从实时目录读取当前模型 ID 和价格;不要凭记忆引用费率。
- 不要改动生产配置或发送真实流量。
- 在消耗额度做测试调用前先询问。
- 保留回滚方案和按功能的支出上限。
- 将方案写入 ./routing-plan.md,并在对话中总结。

复制的内容也包含安装步骤和安全规则。

你会得到什么

一次运行的示例输出,不是实时生成。

路由方案示例(节选)
任务当前建议风险
工单分类强模型快模型低
回复草稿强模型强模型无
变更日志摘要强模型快模型中

改动之前: 按任务记录一周用量,并在 50 条抽样输出上比较质量。

实际调用

给开发者:同一套 workflow 的可复制代码,按执行顺序排列。

展开实际 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 和价格会变;始终读实时目录
  • 不替你评估质量,需要你自己的样本
  • 不替代账单或可观测系统

常见问题

会降低账单吗?

它指出哪些任务可能可以降到更便宜的档位,但只有在你对比样本输出并记录用量之后,节省才是真的。

会自动调用模型吗?

只有你批准测试调用时才会。默认只读目录并写建议,不碰生产流量。

怎么知道哪个模型更便宜?

读实时模型目录和价格页;workflow 不写死费率,因为价格会变。

可以固定供应商吗?

可以,当延迟比价格更重要时。固定是选择,不是重试策略;fallback 需要你自己加。

应该记录什么?

每次调用记录:任务、功能、模型、prompt token、completion token、总 token 和延迟。按功能聚合后再设预算。

一次 review 要花多少?

读目录不产生模型调用;测试调用按用量计费,prompt 会在调用前请求确认。

相关 workflow

改成你的版本

同一套配方,按你实际的交付方式调整。

先只记录不切换

上线前先记录一周用量,再决定是否降档。

按功能设预算

给每个功能设置月度上限,超出时告警或降级。

固定供应商换延迟

当延迟比价格重要时,固定供应商并加自己的 fallback。

这套配方背后的参考页面,以及下一步可以看什么。