A03第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

服务端调用模型

服务端调用模型 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能在 Next.js 的 Route Handler 里,用 OpenAI 兼容接口完成一次真实的模型调用:密钥安全存放、错误显式处理、模型标识有据可查。

问题场景

你把调用代码写进了 React 组件,apiKey 就写在变量里,本地跑得好好的。代码推到仓库当晚,密钥被自动化脚本扫走,第二天账单多了几千块。问题不在「调用」本身,在「调用发生在哪」。

核心解释

本站的调用架构(对应 lib/ai/config.ts 与 lib/ai/adapter.ts)分两步。第一步,配置只放服务端环境文件:

# .env.local(在 .gitignore 里,绝不提交)
AI_BASE_URL=https://xxx/v1    # OpenAI 兼容服务地址
AI_API_KEY=sk-xxxxxxxx        # 密钥,只存在于服务端进程
AI_MODEL=glm-4.7              # 明确记录所用模型标识

第二步,Route Handler 在服务端读取配置并发起调用:

// app/api/ai/simple/route.ts(教学简化版)
import { getAiConfig } from "@/lib/ai/config";

export async function POST(req: Request) {
  const { baseUrl, apiKey, model } = getAiConfig();
  const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/chat/completions`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`, // 密钥只出现在这一行,永不返回给浏览器
    },
    body: JSON.stringify({
      model,
      messages: [{ role: "user", content: "用一句话介绍 SSE" }],
    }),
  });
  if (!res.ok) {
    return Response.json({ error: `上游返回 ${res.status}` }, { status: 502 });
  }
  const data = await res.json();
  const reply = data.choices?.[0]?.message?.content ?? "";
  return Response.json({ reply, model }); // 把模型标识带回,方便排查
}

浏览器只跟自己的 /api/ai/simple 说话,密钥物理上到不了前端。响应形状是 OpenAI 兼容约定:choices[0].message.content 是回答正文。

「必要的请求配置」至少记录三样:model(行为突变时先确认是不是模型换了)、接口地址、是否流式。本站更进一步:每次调用都写一行 ai_call 记录(模型、token 用量、耗时、状态),排查问题时这是唯一的真相来源。

示例拆解

先用 curl 验证通路,再写业务代码——调通接口是最省时间的顺序:

curl -s "$AI_BASE_URL/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AI_API_KEY" \
  -d '{"model":"'"$AI_MODEL"'","messages":[{"role":"user","content":"回复两个字:在线"}]}'

关键点:

  • 401/403 是密钥问题,429 是频率或额度受限,超时是地址或网络不对——分类看待比笼统「失败」有用(A12 课展开错误分类)。
  • .env.local 改动后必须重启 dev server 才生效,这是「我明明配了」的第一大来源。
  • res.ok 必须检查:上游失败时 HTTP 状态不是 200,跳过检查就会在 res.json() 处炸出一个不知所云的解析错误。

动手任务

  1. 本机配好 .env.local 三项,用 curl 跑通一次调用,确认拿到正文。
  2. 把教学版路由跑起来,在浏览器 Network 里确认:请求发给你自己的 /api/ai/simple,响应里搜不到任何密钥片段。
  3. 故意把 AI_API_KEY 改错一个字符重启,观察返回的状态码类别,再改回来。

常见错误

  • 密钥写进组件或提交进仓库:前端一切代码皆公开。密钥只活在 .env.local 和服务端环境里。
  • 不检查 res.ok 直接解析:上游报错时先炸在 JSON 解析,真实原因被掩盖。
  • 不记录模型标识:换了模型或提供方后行为变化,没有记录就无从对证。
  • 把 .env.local 提交了:它专为本机私密配置设计,务必留在 .gitignore;泄露后第一件事是作废密钥。

检查与复盘

自查清单:

  • 能说出密钥的完整路径:.env.local → 服务端进程 → 上游请求头,任何一环不经过浏览器
  • 能默写 OpenAI 兼容调用的三要素:服务地址、Authorization: Bearer 头、messages 数组
  • 知道去哪里查「上次调用用的什么模型、花了多少 token」

参考资料

检查题共 3 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

关于 AI 调用密钥(API Key)的存放与使用,符合本站工程口径的是?

Q2读代码中等

阅读服务端调用模型的路由代码。上游因密钥失效返回 401 时,这段代码会发生什么?

const res = await fetch(`${baseUrl}/chat/completions`, {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
  body: JSON.stringify({ model, messages }),
});
const data = await res.json();
const reply = data.choices?.[0]?.message?.content ?? "";
Q3单选中等

开发者在 .env.local 里改了 AI_API_KEY,保存后刷新浏览器页面重新发消息,服务端仍报 401,日志显示用的还是旧密钥。最可能的原因是?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...