API 与本地部署
🎯 学习目标
- 理解远程 API 与本地推理(Ollama / vLLM)的适用场景与权衡
- 使用 Node.js 或 Python 完成一次 OpenAI 兼容的 Chat Completions 调用
- 掌握 API Key、baseURL、环境变量与错误处理的基本模式
- 设计可切换 Provider 的配置结构
引言
动手写代码之前,先定部署形态:远程 API(OpenAI、DeepSeek、通义、Anthropic 等)让你分钟级接入最强模型,按 Token 付费,运维由云厂商承担;本地部署(Ollama、vLLM、llama.cpp)把权重跑在自己的 GPU/CPU 上,数据不出内网,长期高 QPS 可能更省,但要自己负责显卡、扩容与版本升级。
对大多数团队,路径是 开发期远程 API 快速验证 → 生产按合规与成本选 VPC API 或私有化。本节用 OpenAI 兼容接口 演示——国内多数厂商支持同一套 /v1/chat/completions 路径,换 baseURL 即可迁移。你会完成从环境变量读取 Key、发起请求、打印回复与 usage 的全流程,并看到 Ollama 本地调用的等价写法。
章节正文
第 1 步:远程 API:环境与第一次请求(Node.js)
创建项目目录,安装官方 SDK:
npm init -y
npm install openai dotenv根目录新建 .env(勿提交 Git):
OPENAI_API_KEY=sk-...
# 若用 DeepSeek 等兼容厂商,再设:
# OPENAI_BASE_URL=https://api.deepseek.com
# OPENAI_MODEL=deepseek-chatchat.mjs 示例:
import OpenAI from 'openai'
import 'dotenv/config'
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL, // 可选,默认 OpenAI 官方
})
const response = await client.chat.completions.create({
model: process.env.OPENAI_MODEL ?? 'gpt-4o-mini',
messages: [
{ role: 'system', content: '你是简洁的中文技术助手。' },
{ role: 'user', content: '用一句话解释什么是 Token。' },
],
max_tokens: 200,
})
console.log(response.choices[0].message.content)
console.log('usage:', response.usage)运行 node chat.mjs。成功时你会看到模型回复及 prompt_tokens、completion_tokens。生产代码务必 不要硬编码 Key,并捕获 401(Key 无效)、429(限流)、5xx(上游故障) 做重试或降级(见 1.5 备用模型)。
第 2 步:Python 等价实现与 httpx 裸调用
Python 同样推荐官方 SDK:
pip install openai python-dotenvchat.py:
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ.get("OPENAI_BASE_URL"),
)
resp = client.chat.completions.create(
model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
messages=[
{"role": "system", "content": "你是简洁的中文技术助手。"},
{"role": "user", "content": "用一句话解释什么是 Token。"},
],
max_tokens=200,
)
print(resp.choices[0].message.content)
print("usage:", resp.usage)若不想依赖 SDK,可用 httpx 理解 HTTP 形态(便于调试 curl 转代码):
import os, httpx
r = httpx.post(
f"{os.environ.get('OPENAI_BASE_URL', 'https://api.openai.com/v1')}/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
json={
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "hello"}],
},
timeout=60.0,
)
r.raise_for_status()
data = r.json()
print(data["choices"][0]["message"]["content"])timeout 必须设置:LLM 响应可能数十秒,默认过短会导致客户端先断开。
第 3 步:Ollama 本地部署:零 Key 开发循环
安装 Ollama 后拉取模型并启动服务(默认 http://localhost:11434):
ollama pull qwen2.5:7b
ollama run qwen2.5:7bOllama 提供 OpenAI 兼容 端点,Node 侧只需改 baseURL 与 model:
const client = new OpenAI({
apiKey: 'ollama', // 任意非空字符串
baseURL: 'http://localhost:11434/v1',
})
const response = await client.chat.completions.create({
model: 'qwen2.5:7b',
messages: [{ role: 'user', content: '本地模型是否正常?' }],
})适用场景:离线开发、敏感原型、演示无外网环境。局限:模型能力弱于顶级 API、并发与长上下文受本机 GPU/内存限制;生产高 QPS 常用 vLLM 而非 Ollama。
对比记忆:
| 维度 | 远程 API | Ollama 本地 |
|---|---|---|
| 数据出境 | 视厂商协议 | 不出机器 |
| 运维 | 低 | 自备 GPU/驱动 |
| 能力 | 旗舰模型 | 取决于所拉权重 |
| 计费 | 按 Token | 电费 + 硬件 |
第 4 步:抽象 Provider:一套代码切换云与本地
避免业务层散落 if (deepseek) ... else ...。推荐 配置驱动:
// config/models.mjs
export const providers = {
openai: {
baseURL: 'https://api.openai.com/v1',
apiKey: process.env.OPENAI_API_KEY,
defaultModel: 'gpt-4o-mini',
},
deepseek: {
baseURL: 'https://api.deepseek.com',
apiKey: process.env.DEEPSEEK_API_KEY,
defaultModel: 'deepseek-chat',
},
ollama: {
baseURL: 'http://localhost:11434/v1',
apiKey: 'ollama',
defaultModel: 'qwen2.5:7b',
},
}
export function createClient(name = 'openai') {
const p = providers[name]
if (!p?.apiKey) throw new Error(`Missing config for provider: ${name}`)
return new OpenAI({ apiKey: p.apiKey, baseURL: p.baseURL })
}业务只调用:
const client = createClient(process.env.LLM_PROVIDER ?? 'openai')
const model = providers[process.env.LLM_PROVIDER].defaultModel配合 1.5 节 fallback:捕获 429/5xx 时换 createClient('fallback') 重试一次,并记录 metrics。
第 5 步:安全、观测与上线检查清单
安全
- Key 仅存环境变量或密钥管理服务(Vault、云 KMS)
- 后端代理 API,禁止前端浏览器直连 Key
- 企业 VPC / 私有化 endpoint 走专线,日志脱敏
观测
- 每次请求记录:
provider、model、usage、latency、HTTP status - 对 429 做 指数退避;对重复相同 system+context 探索 Prompt Cache
上线前自测
# 连通性
curl "$OPENAI_BASE_URL/models" -H "Authorization: Bearer $OPENAI_API_KEY"
# 最小 completion
node chat.mjs确认 模型 ID 字符串 与控制台一致(大小写、后缀 -instruct 等),避免 404 model not found。
动手练习
- 用 Node 或 Python 跑通一家远程 API,打印 content 与 usage;再改用 Ollama 跑同一 prompt 对比质量与延迟。
- 实现
createClient(providerName),通过环境变量LLM_PROVIDER切换 deepseek 与 ollama,业务代码零改动。 - 为 chat 函数添加 try/catch:401 抛明确错误,429 等待 2s 重试一次,5xx 记录日志并抛给上层降级。
- 写
.env.example(无真实 Key)与.gitignore规则,说明团队如何分发密钥。
常见问题
Q:baseURL 末尾要不要加 /v1?
OpenAI SDK 期望 baseURL 含 /v1(如 https://api.deepseek.com 或 https://api.deepseek.com/v1,视厂商文档而定)。以厂商示例为准,404 时先检查这一路径。
Q:本地 Ollama 能在团队 CI 里跑吗?
可以但慢且 GPU runner 稀缺。CI 更适合 mock 响应或调用固定 stub;集成测试可夜间跑小模型冒烟。
Q:同一项目能混用 OpenAI 和国产 Embedding 吗?
可以,但 Chat 与 Embedding 建议分别抽象 Provider。RAG 检索链路对 Embedding 模型切换更敏感,需重建向量库。
本节小结
远程 API 适合快速迭代与旗舰能力;Ollama 适合本地开发与数据隔离。OpenAI 兼容 SDK + baseURL 切换是国内多厂商接入的通用模式。通过 Provider 抽象、环境变量管理 Key、timeout 与重试,可为第 2 章后续参数、流式与结构化输出打下基础。