mcpField
N
netlify-ai-gateway

netlify-ai-gateway

github.com/netlify开发473.1k 次安装6 天前更新
代码仓库

关于 netlify-ai-gateway

netlify-ai-gateway 是在 Netlify Functions 和 Edge Functions 中调用 AI 模型的技能,面向构建 Netlify 应用的开发者。它通过注入的提供商凭据配合官方 SDK 调用多家模型服务,并支持本地开发。

主要功能

  1. 01通过提供商官方 SDK 调用模型
  2. 02自动注入提供商凭据
  3. 03支持 OpenAI、Anthropic、Gemini 等
  4. 04通过实时端点查询可用模型
  5. 05使用 Netlify CLI 或 Vite 插件本地开发

适用场景

  1. 01在 Function 请求处理中生成 AI 内容
  2. 02在 Edge Function 中调用模型
  3. 03对表单提交进行分类或路由
  4. 04本地开发和测试网关调用

安装

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

$npx skills add https://github.com/netlify/context-and-tools --skill netlify-ai-gateway

安装后在 Claude Code 中输入/netlify-ai-gateway确认已加载。

详细介绍

Netlify AI 网关

使用提供商的官方 SDK,从 Netlify 计算环境调用 AI 模型。网关会自动注入提供商凭据,因此无需传入参数实例化 SDK 即可使用。

使用注入环境凭据的提供商 SDK。 不要针对网关 URL 手动编写原始 fetch() 请求,也不要默认将调用配置为使用 NETLIFY_AI_GATEWAY_KEY / NETLIFY_AI_GATEWAY_URL——这些仅供第三方/不受支持的库使用(见下文)。

易踩的坑(请先阅读)

  • 不能从浏览器调用。 网关调用应放在 Functions 或 Edge Functions 中,绝不能放在客户端代码中。浏览器没有注入的凭据。
  • 凭据仅在运行时可用。 绝不要从构建脚本、预渲染/SSG 或构建插件调用网关——这些环境没有凭据,调用会失败。请在请求时执行 AI 任务;如果输出需要看起来像是预先生成的,请缓存到 Netlify Blobs。
  • 同步超时为 60 秒。 在同步函数中调用网关受 60 秒函数超时限制。对于较长的生成任务,请使用流式传输(SDK 流式传输 + ReadableStream),或使用后台函数持久化输出,供客户端获取。绝不要让较慢的生成任务在不使用流式传输的情况下运行。
  • 需要先进行一次生产部署。 项目至少完成一次生产部署后,网关才会启用。即使是本地开发,也请先运行一次 netlify deploy --prod。
  • 不要硬编码模型列表。 可用模型会变化。请查询实时 providers 端点(https://api.netlify.com/api/v1/ai-gateway/providers/detailed),不要将静态列表写入代码。
  • OpenRouter SDK 需要 1.2.43 或更高版本。 更早的版本会忽略 OPENROUTER_BASE_URL,直接调用 openrouter.ai,并因 401 Missing Authentication header 而失败。

代码放在哪里

编写常规的 Function/处理程序代码——没有专用于 AI 的文件类型。位于 netlify/functions/joke.js、导出 config = { path: "/api/joke" } 的函数,在 netlify dev 和生产环境中都会通过 /api/joke 提供服务。

提供商 SDK(不传入参数实例化)

网关会注入各提供商自己的环境变量,因此官方 SDK 无需任何配置即可使用。

Anthropic Claude:

import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic(); // uses ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL

const message = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Hello!' }]
});

OpenAI:

import OpenAI from 'openai';
const openai = new OpenAI(); // uses OPENAI_API_KEY, OPENAI_BASE_URL

const completion = await openai.chat.completions.create({
  model: 'gpt-5',
  messages: [{ role: 'user', content: 'Hello!' }]
});

Google Gemini:

import { GoogleGenAI } from '@google/genai';
const genAI = new GoogleGenAI({}); // uses GEMINI_API_KEY, GOOGLE_GEMINI_BASE_URL

const result = await genAI.models.generateContent({
  model: 'gemini-2.5-pro',
  contents: 'Hello!'
});

TypeSafe (Jev) — 结构化决策(例如,路由/分类表单提交):

import type { Config, Context } from '@netlify/functions';
import { choice, TypeSafeClient } from '@typesafe-ai/sdk';

export default async (req: Request, context: Context) => {
  const body = await req.json().catch(() => undefined);
  if (body === undefined)
    return Response.json({ error: 'Request body must be valid JSON.' }, { status: 400 });

  const client = new TypeSafeClient(); // uses TYPESAFE_API_KEY, TYPESAFE_BASE_URL
  const { answers } = await client.systemOne({
    state: body,
    questions: {
      team: choice('Route this contact form submission', {
        sales: null,
        support: null,
        spam: null,
      }),
    },
  });

  return Response.json({ team: answers.team.choice, requestId: context.requestId });
};

export const config: Config = { path: '/api/route', method: 'POST' };

systemOne 默认使用 jev-latest 模型。每个问题都是一个 choice(prompt, options),用于将命名选项映射到 null;结果位于 answers.<question>.choice。请发送 JSON 请求体(例如 {"message":"Can someone help us upgrade to 200 seats?"}),并设置 Content-Type: application/json。

OpenRouter(需要 SDK 1.2.43 或更高版本——见易踩的坑):

import { OpenRouter } from '@openrouter/sdk';
const openRouter = new OpenRouter(); // uses OPENROUTER_API_KEY, OPENROUTER_BASE_URL

const result = await openRouter.chat.send({
  chatRequest: {
    model: 'x-ai/grok-4.5',
    messages: [{ role: 'user', content: 'Hello!' }]
  }
});

通过 OpenRouter 提供的模型既可以使用 OpenRouter SDK,也可以使用 OpenAI SDK 并采用 OpenRouter 模型 ID 格式来调用(例如 deepseek/deepseek-v4-flash-0731)——只需将该 ID 传给 model。

上方的模型 ID(gpt-5、claude-sonnet-4-5-20250929、gemini-2.5-pro、x-ai/grok-4.5 等)仅为示例,且会变化——请查询实时 providers 端点。

环境变量——该使用哪些

**默认情况:**受支持的提供商 SDK 会自动使用注入的提供商专用变量。按照上文所示,不带参数实例化 SDK(new OpenAI()、new Anthropic()、new GoogleGenAI({})、new TypeSafeClient()、new OpenRouter()),系统会为你读取相应的变量对:

  • OpenAI:OPENAI_API_KEY、OPENAI_BASE_URL
  • Anthropic:ANTHROPIC_API_KEY、ANTHROPIC_BASE_URL
  • Google Gemini:GEMINI_API_KEY、GOOGLE_GEMINI_BASE_URL
  • OpenRouter:OPENROUTER_API_KEY、OPENROUTER_BASE_URL
  • TypeSafe:TYPESAFE_API_KEY、TYPESAFE_BASE_URL

显式配置方式:NETLIFY_AI_GATEWAY_KEY 和 NETLIFY_AI_GATEWAY_URL 始终会注入,且不会与用户设置的提供商变量冲突。仅当第三方库或不受支持的库需要显式配置密钥和基础 URL 时,才使用这组变量——将它们作为构造函数参数传入。这不是默认方式;受支持的 SDK 应使用上文所列的提供商专用变量。

**优先级:**Netlify 不会覆盖你在项目或团队级别设置的密钥或基础 URL。如果你设置了自己的提供商密钥,网关会优先使用该密钥。对于 Gemini,如果设置了 GOOGLE_API_KEY 或 GOOGLE_VERTEX_BASE_URL,则不会注入变量(Vertex/Google API 密钥配置优先)。

若要停止注入所有变量,请禁用 AI Features:https://docs.netlify.com/build/build-with-ai/manage-ai-for-your-team/manage-ai-features/#disable-ai-features

完整示例(Vite + React + Function)

检查是否存在注入的变量,以检测网关是否可用,然后调用 SDK。

先安装客户端:npm install openai。然后创建 netlify/functions/joke.js:

import process from "process";
import OpenAI from "openai";

export default async () => {
  if (!process.env.OPENAI_BASE_URL)
    return Response.json({ error: "AI Gateway not active — deploy to prod once on a credit-based plan" });

  try {
    const client = new OpenAI();
    const res = await client.responses.create({
      model: "gpt-5-mini",
      input: [{ role: "user", content: "Give me a short dad joke about coffee" }],
      reasoning: { effort: "minimal" },
    });
    return Response.json({
      joke: res.output_text?.trim() || "Out of jokes",
      model: res.model,
      tokens: { input: res.usage.input_tokens, output: res.usage.output_tokens },
    });
  } catch (e) {
    return Response.json({ error: `${e}` }, { status: 500 });
  }
};

export const config = { path: "/api/joke" };

src/App.jsx 会获取 /api/joke:

import { useState } from "react";

export default function App() {
  const [joke, setJoke] = useState();
  const [loading, setLoading] = useState(false);

  const getJoke = async () => {
    setLoading(true);
    try {
      const res = await fetch("/api/joke");
      setJoke(res.ok ? await res.json() : { error: res.status });
    } finally {
      setLoading(false);
    }
  };

  return (
    <>
      <button onClick={getJoke} disabled={loading}>
        {loading ? "Thinking..." : "Get joke"}
      </button>
      <pre>{JSON.stringify(joke, null, 2)}</pre>
    </>
  );
}

本地开发

有两种方式——都需要至少先完成一次生产环境部署:

  1. Netlify CLI:netlify dev 可提供完整的网关支持。
  2. **Vite 插件:**无需运行 netlify dev,即可在本地访问网关。添加 @netlify/vite-plugin,并运行原生开发命令(npm run dev):
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import netlify from "@netlify/vite-plugin";

export default defineConfig({ plugins: [react(), netlify()] })

设置流程:

npm install -g netlify-cli@latest
netlify login
npm create vite@latest dad-jokes -- --template react --no-interactive
cd dad-jokes && npm install
netlify init
netlify deploy --prod --open   # required: activates the gateway

计费、限额和限制

  • **套餐:**仅适用于基于积分的套餐(Free、Personal、Pro)。Enterprise:请联系你的客户经理。旧版套餐必须先切换。默认启用,除非你禁用了 AI Features 或设置了自己的提供商密钥。
  • **费用:**令牌 → 美元(提供商公布的费率)→ 积分。1 美元 = 180 积分。
  • 速率限制(每分钟、每个团队、所有项目合计):Free 为 90,Personal 为 450,Pro 为 1,800,Enterprise 为 9,000 积分。
  • **上下文窗口:**输入限制为 200k 个令牌。
  • **提示词缓存:**Anthropic——仅支持默认的 5 分钟临时缓存;OpenAI——会为你设置账户级的 prompt_cache_key;Gemini——不支持显式上下文缓存。
  • 不支持透传请求头(无法启用受请求头控制的实验性功能)、批量推理和 OpenAI 优先处理。
  • **仅限 OpenRouter ZDR:**Netlify 只会将请求路由到实行零数据保留政策的提供商。OpenRouter 目录中列出的模型,如果没有实行 ZDR 的托管服务,则不会提供。浏览符合 ZDR 条件的模型:https://openrouter.ai/models?zdr=true
  • **隐私:**网关不会存储提示词或模型输出。

**费用控制:**为调用 AI 的 Functions/Edge Functions 设置速率限制规则,以防止访客滥用和费用失控:https://docs.netlify.com/manage/security/secure-access-to-sites/rate-limiting/ ——并配置自动充值或积分包:https://docs.netlify.com/manage/accounts-and-billing/billing/billing-for-credit-based-plans/configure-auto-recharge/ · https://docs.netlify.com/manage/accounts-and-billing/billing/billing-for-credit-based-plans/buy-credit-packs/

监控用量:https://docs.netlify.com/manage/accounts-and-billing/billing/billing-for-credit-based-plans/monitor-usage-for-credit-based-plans

参考资料

Netlify 内部规范(ai-gateway)

这些是组织惯例,而非文档事实——它们由 ctx-gen 合并到渲染后的技能中,绝不会自动生成。由技能维护者负责。

  1. 使用提供商 SDK 和注入的环境凭据——不要自行通过 fetch() 直接调用网关,即使原始 REST 也是受支持的调用方式。正文不得将原始 REST 或 NETLIFY_AI_GATEWAY_KEY / NETLIFY_AI_GATEWAY_URL 这一对变量作为推荐路径,但必须仍将它们作为事实说明:它们始终会被注入,不会与用户设置的提供商变量冲突;当第三方或不受支持的库需要显式配置时,它们是合适的选择。降低其推荐优先级,但保留相关信息。
  2. 网关不能在浏览器中调用:调用应放在函数或边缘函数中,绝不能放在客户端代码中。
  3. 模型可用性会变化:不要硬编码模型列表;请检查实时 providers 端点。
  4. 网关凭据仅在运行时可用:绝不要从构建脚本、预渲染/SSG 或构建插件中调用网关——这些调用不会获得凭据并会失败。应在请求时执行 AI 工作;如果必须让结果看起来像是预先计算好的,请缓存结果(例如存入 Netlify Blobs)。
  5. 同步函数中的网关调用受 60 秒超时限制:对于较长的生成任务,应进行流式传输(SDK 流式传输 + ReadableStream),或使用后台函数持久化输出,供客户端获取——绝不要让耗时的生成任务在不进行流式传输的情况下运行,并假定它能完成。
  6. 被问到应该使用哪些环境变量时——即使问题明确询问网关变量对——回答字面问题前也要先说明默认方案:受支持的提供商 SDK 使用注入的提供商专属变量(OPENAI_API_KEY/OPENAI_BASE_URL 等),并采用正文中展示的各提供商实例化方式——重述正文中的设置方式,不要在这里臆造构造函数细节。然后再将 NETLIFY_AI_GATEWAY_KEY / NETLIFY_AI_GATEWAY_URL 作为第三方或不受支持的库的显式配置方式提供。仅回答网关变量对会让人误以为手动配置是默认方式,事实并非如此。

评论

支持 Markdown