LLM 推理服务平台完全教程
从零基础到企业级部署:Together AI、Fireworks.ai、Groq、Replicate 全面解析与实战
目录
- 第一章:LLM 推理服务概述
- 第二章:主流推理平台全景对比
- 第三章:API 接入实战
- 第四章:模型部署与托管
- 第五章:推理优化技术
- 第六章:成本控制策略
- 第七章:多模型路由与智能网关
- 第八章:企业级集成
- 第九章:实战项目——智能模型网关与成本优化系统
- 第十章:常见问题与故障排查
- 附录:资源与参考
第一章:LLM 推理服务概述
1.1 什么是 LLM 推理服务
LLM(大语言模型)推理服务是指将训练好的大语言模型部署为可通过 API 调用的在线服务。与训练阶段不同,推理阶段的核心任务是高效地将输入文本转换为模型输出。
一个完整的推理服务通常包含以下组件:
用户请求 → 负载均衡 → 推理引擎 → 模型权重 → 后处理 → 返回响应
推理过程分为两个阶段:
- 预填充阶段(Prefill):处理输入的所有 Token,计算初始的 Key-Value 缓存
- 解码阶段(Decode):逐个生成输出 Token,每个 Token 依赖之前生成的所有 Token
1.2 为什么需要推理服务平台
自建推理基础设施面临诸多挑战:
| 挑战 | 说明 |
|---|---|
| 硬件成本高 | 一块 A100 GPU 月租约 $1-2 万,70B 模型至少需要 2-4 块 |
| 运维复杂 | GPU 驱动、CUDA 版本、内存管理、故障恢复 |
| 弹性扩展难 | 流量波动时难以快速扩缩容 |
| 优化门槛高 | 量化、批处理、缓存等优化技术需要深厚专业知识 |
推理服务平台的价值在于:
- 降低门槛:无需管理 GPU 基础设施,API 调用即可使用
- 成本可控:按 Token 计费,用多少付多少
- 模型丰富:一站式访问数十种主流开源模型
- 性能优化:平台已做深度优化,性能通常优于自建
1.3 推理服务的核心指标
评估推理服务时,需要关注以下核心指标:
延迟指标:
- TTFT(Time to First Token):首 Token 延迟,影响用户感知的响应速度
- TPS(Tokens per Second):每秒生成 Token 数,影响流式输出的流畅度
- 端到端延迟:从发送请求到收到完整响应的总时间
吞吐指标:
- 并发请求数:同时处理的请求量
- 吞吐量(Tokens/s):系统整体每秒处理的 Token 总数
质量指标:
- 模型保真度:推理结果与原始模型的一致性
- 可用性:服务正常运行时间比例(SLA)
成本指标:
- 每百万 Token 成本:输入和输出分别计费
- 每月总支出:基于使用量的总成本
第二章:主流推理平台全景对比
2.1 Together AI
定位:全栈 AI 云平台,覆盖训练、微调、推理全链路
核心优势:
- 模型种类最丰富,支持 100+ 开源模型
- 提供微调服务,支持自定义模型部署
- 有 Turbo 系列优化推理端点,速度提升 2-5 倍
- 支持自定义 GPU 集群部署
定价参考(2024 年):
| 模型 | 输入价格 (\(/M tokens) | 输出价格 (\)/M tokens) | |
|---|---|---|
| Llama 3.1 70B Turbo | 0.88 | 0.88 |
| Llama 3.1 405B | 3.50 | 3.50 |
| Mixtral 8x22B | 1.20 | 1.20 |
| Qwen 2.5 72B | 1.20 | 1.20 |
适用场景: 需要模型多样性、有微调需求、追求性价比的团队。
2.2 Fireworks.ai
定位:高性能推理引擎,专注速度与效率
核心优势:
- 自研 FireAttention 引擎,推理速度业界领先
- 支持函数调用(Function Calling)优化
- 提供 FireAttention-v3 量化版本,精度损失极小
- 支持自定义模型上传与部署
特色功能:
- FireFunction:专门优化的函数调用模型
- JSON 模式:原生支持结构化 JSON 输出
- 批处理 API:支持大规模异步批处理
定价参考:
| 模型 | 输入价格 (\(/M tokens) | 输出价格 (\)/M tokens) | |
|---|---|---|
| Llama 3.1 70B | 0.90 | 0.90 |
| Mixtral 8x22B | 1.20 | 1.20 |
| FireFunction-v2 | 0.90 | 0.90 |
适用场景: 对延迟敏感的应用、需要函数调用能力、高吞吐量需求。
2.3 Groq
定位:极速推理平台,基于自研 LPU 芯片
核心优势:
- 极致速度:基于自研 LPU(Language Processing Unit),推理速度远超 GPU
- 极低延迟:TTFT 通常在 100ms 以内
- 稳定输出:TPS 波动极小,用户体验一致
技术特点:
- LPU 专为序列化推理设计,与 GPU 的并行计算范式不同
- 硬件级别的 Token 生成优化
- 内存带宽极高,消除推理瓶颈
定价参考:
| 模型 | 输入价格 (\(/M tokens) | 输出价格 (\)/M tokens) | |
|---|---|---|
| Llama 3.1 70B | 0.59 | 0.79 |
| Llama 3.1 8B | 0.05 | 0.08 |
| Mixtral 8x7B | 0.24 | 0.24 |
限制:
- 模型选择相对较少
- 不支持自定义模型部署
- 免费层有速率限制
适用场景: 对速度要求极高的实时应用、聊天机器人、交互式工具。
2.4 Replicate
定位:模型即服务平台,简化模型部署流程
核心优势:
- 模型生态丰富:不仅限于 LLM,支持图像、音频、视频等多模态模型
- 部署简单:一个命令即可部署自定义模型
- 按秒计费:真正的按使用量计费,不使用不收费
- 支持自定义容器:基于 Docker 的灵活部署
特色功能:
- Predictions API:异步推理接口,适合长耗时任务
- Webhooks:任务完成回调
- 模型权重缓存:冷启动优化
定价参考: Replicate 按 GPU 使用时间计费,而非 Token:
- Nvidia T4: $0.000225/秒
- Nvidia A40: $0.000575/秒
- Nvidia A100: $0.001150/秒
适用场景: 多模态应用、需要自定义模型、非标准推理任务。
2.5 综合对比表
| 特性 | Together AI | Fireworks.ai | Groq | Replicate |
|---|---|---|---|---|
| 模型数量 | 100+ | 50+ | 20+ | 数千(含社区) |
| 推理速度 | 快 | 极快 | 极极速 | 中等 |
| 自定义模型 | ✅ | ✅ | ❌ | ✅ |
| 微调服务 | ✅ | ❌ | ❌ | ✅ |
| 函数调用 | ✅ | ✅(优化) | ✅ | ✅ |
| 流式输出 | ✅ | ✅ | ✅ | ✅ |
| 定价模式 | 按 Token | 按 Token | 按 Token | 按 GPU 秒 |
| 免费层 | $5 额度 | 少量免费 | 有 | 少量免费 |
| 最佳场景 | 通用/微调 | 高性能推理 | 极速响应 | 多模态/自定义 |
第三章:API 接入实战
3.1 统一调用架构设计
在实际项目中,我们通常需要对接多个推理平台。设计一个统一的调用层至关重要:
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import AsyncIterator, Optional
import asyncio
@dataclass
class ChatMessage:
role: str # "system", "user", "assistant"
content: str
@dataclass
class ChatResponse:
content: str
model: str
usage: dict # {"prompt_tokens": int, "completion_tokens": int}
provider: str
latency_ms: float
@dataclass
class StreamChunk:
delta: str # 本次增量文本
finish_reason: Optional[str] = None
class BaseProvider(ABC):
"""推理平台基类"""
def __init__(self, api_key: str, base_url: str):
self.api_key = api_key
self.base_url = base_url
@abstractmethod
async def chat(
self,
messages: list[ChatMessage],
model: str,
temperature: float = 0.7,
max_tokens: int = 1024,
) -> ChatResponse:
pass
@abstractmethod
async def chat_stream(
self,
messages: list[ChatMessage],
model: str,
temperature: float = 0.7,
max_tokens: int = 1024,
) -> AsyncIterator[StreamChunk]:
pass
3.2 Together AI 接入
Together AI 提供 OpenAI 兼容的 API,接入非常简单:
import httpx
import time
import json
class TogetherProvider(BaseProvider):
"""Together AI 推理平台"""
def __init__(self, api_key: str):
super().__init__(api_key, "https://api.together.xyz/v1")
self.client = httpx.AsyncClient(
base_url=self.base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=60.0,
)
async def chat(
self,
messages: list[ChatMessage],
model: str = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
temperature: float = 0.7,
max_tokens: int = 1024,
) -> ChatResponse:
start = time.monotonic()
response = await self.client.post(
"/chat/completions",
json={
"model": model,
"messages": [{"role": m.role, "content": m.content} for m in messages],
"temperature": temperature,
"max_tokens": max_tokens,
},
)
response.raise_for_status()
data = response.json()
latency_ms = (time.monotonic() - start) * 1000
return ChatResponse(
content=data["choices"][0]["message"]["content"],
model=data["model"],
usage=data["usage"],
provider="together",
latency_ms=latency_ms,
)
async def chat_stream(
self,
messages: list[ChatMessage],
model: str = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
temperature: float = 0.7,
max_tokens: int = 1024,
) -> AsyncIterator[StreamChunk]:
async with self.client.stream(
"POST",
"/chat/completions",
json={
"model": model,
"messages": [{"role": m.role, "content": m.content} for m in messages],
"temperature": temperature,
"max_tokens": max_tokens,
"stream": True,
},
) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if line.startswith("data: "):
data_str = line[6:]
if data_str.strip() == "[DONE]":
break
data = json.loads(data_str)
delta = data["choices"][0]["delta"]
if "content" in delta:
yield StreamChunk(delta=delta["content"])
if data["choices"][0].get("finish_reason"):
yield StreamChunk(
delta="",
finish_reason=data["choices"][0]["finish_reason"],
)
# 使用示例
async def main():
provider = TogetherProvider(api_key="your-api-key")
messages = [
ChatMessage(role="system", content="你是一个有用的助手。"),
ChatMessage(role="user", content="用 Python 实现快速排序"),
]
# 非流式调用
response = await provider.chat(messages)
print(f"响应: {response.content}")
print(f"延迟: {response.latency_ms:.0f}ms")
print(f"Token 用量: {response.usage}")
# 流式调用
print("\n流式输出:")
async for chunk in provider.chat_stream(messages):
if chunk.delta:
print(chunk.delta, end="", flush=True)
print()
3.3 Fireworks.ai 接入
Fireworks.ai 同样兼容 OpenAI API 格式:
class FireworksProvider(BaseProvider):
"""Fireworks.ai 推理平台"""
def __init__(self, api_key: str):
super().__init__(api_key, "https://api.fireworks.ai/inference/v1")
self.client = httpx.AsyncClient(
base_url=self.base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=60.0,
)
async def chat(
self,
messages: list[ChatMessage],
model: str = "accounts/fireworks/models/llama-v3p1-70b-instruct",
temperature: float = 0.7,
max_tokens: int = 1024,
) -> ChatResponse:
start = time.monotonic()
response = await self.client.post(
"/chat/completions",
json={
"model": model,
"messages": [{"role": m.role, "content": m.content} for m in messages],
"temperature": temperature,
"max_tokens": max_tokens,
},
)
response.raise_for_status()
data = response.json()
return ChatResponse(
content=data["choices"][0]["message"]["content"],
model=data["model"],
usage=data["usage"],
provider="fireworks",
latency_ms=(time.monotonic() - start) * 1000,
)
async def chat_stream(self, messages, model=None, temperature=0.7, max_tokens=1024):
# 流式实现与 Together AI 类似,此处省略重复代码
# 关键区别在于模型名称格式和 base_url
...
# Fireworks 特色:函数调用
async def function_call_example():
provider = FireworksProvider(api_key="your-api-key")
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位",
},
},
"required": ["city"],
},
},
}
]
response = await provider.client.post(
"/chat/completions",
json={
"model": "accounts/fireworks/models/firefunction-v2",
"messages": [{"role": "user", "content": "北京今天天气怎么样?"}],
"tools": tools,
"tool_choice": "auto",
},
)
data = response.json()
tool_call = data["choices"][0]["message"]["tool_calls"][0]
print(f"函数调用: {tool_call['function']['name']}")
print(f"参数: {tool_call['function']['arguments']}")
3.4 Groq 接入
Groq 的 API 同样兼容 OpenAI 格式,但速度极快:
class GroqProvider(BaseProvider):
"""Groq 推理平台 - 极速推理"""
def __init__(self, api_key: str):
super().__init__(api_key, "https://api.groq.com/openai/v1")
self.client = httpx.AsyncClient(
base_url=self.base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=30.0, # Groq 响应很快,可以设置更短的超时
)
async def chat(
self,
messages: list[ChatMessage],
model: str = "llama-3.1-70b-versatile",
temperature: float = 0.7,
max_tokens: int = 1024,
) -> ChatResponse:
start = time.monotonic()
response = await self.client.post(
"/chat/completions",
json={
"model": model,
"messages": [{"role": m.role, "content": m.content} for m in messages],
"temperature": temperature,
"max_tokens": max_tokens,
},
)
response.raise_for_status()
data = response.json()
return ChatResponse(
content=data["choices"][0]["message"]["content"],
model=data["model"],
usage=data["usage"],
provider="groq",
latency_ms=(time.monotonic() - start) * 1000,
)
# Groq 速度对比测试
async def speed_comparison():
"""对比不同平台的响应速度"""
messages = [
ChatMessage(role="user", content="用一句话解释量子计算")
]
providers = {
"groq": GroqProvider(api_key="your-groq-key"),
"together": TogetherProvider(api_key="your-together-key"),
"fireworks": FireworksProvider(api_key="your-fireworks-key"),
}
results = {}
for name, provider in providers.items():
try:
resp = await provider.chat(messages, max_tokens=100)
results[name] = {
"latency_ms": resp.latency_ms,
"tps": resp.usage["completion_tokens"] / (resp.latency_ms / 1000),
}
except Exception as e:
results[name] = {"error": str(e)}
for name, r in results.items():
if "error" in r:
print(f"{name}: 错误 - {r['error']}")
else:
print(f"{name}: {r['latency_ms']:.0f}ms, {r['tps']:.0f} tokens/s")
3.5 Replicate 接入
Replicate 的 API 风格不同,采用异步预测模式:
import httpx
class ReplicateProvider:
"""Replicate 推理平台"""
def __init__(self, api_key: str):
self.api_key = api_key
self.client = httpx.AsyncClient(
base_url="https://api.replicate.com/v1",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
timeout=120.0,
)
async def run(
self,
model: str = "meta/meta-llama-3.1-405b-instruct",
prompt: str = "",
system_prompt: str = "",
temperature: float = 0.7,
max_tokens: int = 1024,
) -> dict:
"""运行模型预测(同步等待结果)"""
# 创建预测
response = await self.client.post(
f"/models/{model}/predictions",
json={
"input": {
"prompt": prompt,
"system_prompt": system_prompt,
"temperature": temperature,
"max_tokens": max_tokens,
}
},
)
response.raise_for_status()
prediction = response.json()
# 轮询等待完成
prediction_id = prediction["id"]
while prediction["status"] not in ("succeeded", "failed", "canceled"):
await asyncio.sleep(1)
resp = await self.client.get(f"/predictions/{prediction_id}")
prediction = resp.json()
if prediction["status"] == "succeeded":
return {
"output": "".join(prediction["output"]),
"model": model,
"metrics": prediction.get("metrics", {}),
}
else:
raise Exception(f"预测失败: {prediction.get('error', '未知错误')}")
async def run_stream(
self,
model: str = "meta/meta-llama-3.1-405b-instruct",
prompt: str = "",
system_prompt: str = "",
) -> AsyncIterator[str]:
"""流式运行模型"""
response = await self.client.post(
f"/models/{model}/predictions",
json={
"input": {
"prompt": prompt,
"system_prompt": system_prompt,
"stream": True,
},
"stream": True,
},
)
response.raise_for_status()
prediction = prediction = response.json()
# 使用 SSE 流式读取
stream_url = prediction.get("stream_url") or prediction.get("urls", {}).get("stream")
if stream_url:
async with self.client.stream("GET", stream_url) as stream:
async for line in stream.aiter_lines():
if line.startswith("data: "):
event = json.loads(line[6:])
if event.get("event") == "output":
yield event["data"]
3.6 流式输出实现
流式输出是提升用户体验的关键技术。以下是完整的流式处理框架:
import asyncio
from typing import AsyncIterator, Callable
class StreamProcessor:
"""流式输出处理器"""
def __init__(self):
self.buffer = ""
self.token_count = 0
self.start_time = None
async def process(
self,
stream: AsyncIterator[StreamChunk],
on_token: Callable[[str], None] | None = None,
on_complete: Callable[[str], None] | None = None,
) -> str:
"""处理流式输出"""
self.start_time = time.monotonic()
full_text = ""
async for chunk in stream:
if chunk.delta:
full_text += chunk.delta
self.token_count += 1
if on_token:
on_token(chunk.delta)
elapsed = time.monotonic() - self.start_time
tps = self.token_count / elapsed if elapsed > 0 else 0
if on_complete:
on_complete(full_text)
return full_text
def get_stats(self) -> dict:
"""获取流式处理统计"""
elapsed = time.monotonic() - self.start_time if self.start_time else 0
return {
"total_tokens": self.token_count,
"elapsed_seconds": elapsed,
"tokens_per_second": self.token_count / elapsed if elapsed > 0 else 0,
}
# Web 框架集成示例(FastAPI)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
@app.post("/api/chat/stream")
async def chat_stream(request: ChatRequest):
"""SSE 流式聊天接口"""
async def event_generator():
provider = get_provider(request.provider)
messages = [ChatMessage(role=m.role, content=m.content) for m in request.messages]
async for chunk in provider.chat_stream(messages, model=request.model):
if chunk.delta:
yield f"data: {json.dumps({'content': chunk.delta})}\n\n"
if chunk.finish_reason:
yield f"data: {json.dumps({'finish_reason': chunk.finish_reason})}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
},
)
第四章:模型部署与托管
4.1 模型选择策略
选择合适的模型是成本与效果的平衡艺术:
class ModelSelector:
"""智能模型选择器"""
# 模型能力矩阵
MODEL_PROFILES = {
"llama-3.1-8b": {
"tier": "light",
"cost_per_mtok": 0.05,
"max_context": 128000,
"strengths": ["简单问答", "文本分类", "摘要"],
"quality_score": 6,
},
"llama-3.1-70b": {
"tier": "standard",
"cost_per_mtok": 0.88,
"max_context": 128000,
"strengths": ["复杂推理", "代码生成", "创意写作"],
"quality_score": 8,
},
"llama-3.1-405b": {
"tier": "premium",
"cost_per_mtok": 3.50,
"max_context": 128000,
"strengths": ["高精度推理", "长文理解", "多语言"],
"quality_score": 9.5,
},
"mixtral-8x22b": {
"tier": "standard",
"cost_per_mtok": 1.20,
"max_context": 65536,
"strengths": ["多语言", "代码", "推理"],
"quality_score": 8.5,
},
}
@classmethod
def select(
cls,
task_complexity: str, # "simple", "medium", "complex"
budget_per_request: float,
context_length: int = 0,
priority: str = "balanced", # "cost", "quality", "speed"
) -> str:
"""根据任务特征选择最优模型"""
candidates = []
for model, profile in cls.MODEL_PROFILES.items():
# 过滤上下文长度
if context_length > profile["max_context"]:
continue
# 过滤预算
estimated_cost = (context_length / 1_000_000) * profile["cost_per_mtok"]
if estimated_cost > budget_per_request:
continue
candidates.append((model, profile))
if not candidates:
return "llama-3.1-8b" # 默认回退
# 根据优先级排序
if priority == "cost":
candidates.sort(key=lambda x: x[1]["cost_per_mtok"])
elif priority == "quality":
candidates.sort(key=lambda x: x[1]["quality_score"], reverse=True)
else: # balanced
candidates.sort(
key=lambda x: x[1]["quality_score"] / x[1]["cost_per_mtok"],
reverse=True,
)
return candidates[0][0]
4.2 自定义模型微调与部署
Together AI 支持上传微调后的模型:
# 微调流程示例
async def fine_tune_model():
"""使用 Together AI 进行模型微调"""
client = httpx.AsyncClient(
base_url="https://api.together.xyz/v1",
headers={"Authorization": "Bearer your-api-key"},
)
# 1. 上传训练数据
with open("training_data.jsonl", "rb") as f:
upload_resp = await client.post(
"/files",
files={"file": ("training_data.jsonl", f, "application/jsonl")},
data={"purpose": "fine-tune"},
)
file_id = upload_resp.json()["id"]
# 2. 创建微调任务
ft_resp = await client.post(
"/fine-tune",
json={
"model": "meta-llama/Meta-Llama-3.1-8B-Instruct",
"training_file": file_id,
"n_epochs": 3,
"learning_rate": 1e-5,
"batch_size": 4,
"suffix": "my-custom-model",
},
)
job_id = ft_resp.json()["id"]
# 3. 监控训练进度
while True:
status_resp = await client.get(f"/fine-tune/{job_id}")
status = status_resp.json()
print(f"状态: {status['status']}, 进度: {status.get('progress', 0)}%")
if status["status"] in ("succeeded", "failed"):
break
await asyncio.sleep(30)
if status["status"] == "succeeded":
model_name = status["model_name"]
print(f"微调完成!模型名称: {model_name}")
return model_name
else:
raise Exception(f"微调失败: {status.get('error')}")
4.3 模型版本管理
在生产环境中,模型版本管理至关重要:
class ModelRegistry:
"""模型版本注册中心"""
def __init__(self):
self.models: dict[str, dict] = {}
def register(
self,
name: str,
version: str,
provider: str,
model_id: str,
config: dict | None = None,
):
"""注册模型版本"""
if name not in self.models:
self.models[name] = {"versions": {}, "current": None}
self.models[name]["versions"][version] = {
"provider": provider,
"model_id": model_id,
"config": config or {},
"registered_at": time.time(),
"metrics": {"total_calls": 0, "avg_latency": 0, "error_rate": 0},
}
def set_current(self, name: str, version: str):
"""设置当前使用的版本"""
if name in self.models and version in self.models[name]["versions"]:
self.models[name]["current"] = version
def get_model(self, name: str, version: str | None = None) -> dict:
"""获取模型配置"""
if name not in self.models:
raise ValueError(f"模型 {name} 未注册")
version = version or self.models[name]["current"]
if not version or version not in self.models[name]["versions"]:
raise ValueError(f"模型 {name} 版本 {version} 不存在")
return self.models[name]["versions"][version]
def update_metrics(self, name: str, version: str, latency: float, success: bool):
"""更新模型指标"""
m = self.models[name]["versions"][version]["metrics"]
n = m["total_calls"]
m["avg_latency"] = (m["avg_latency"] * n + latency) / (n + 1)
m["total_calls"] = n + 1
if not success:
m["error_rate"] = (m["error_rate"] * n + 1) / (n + 1)
# 使用示例
registry = ModelRegistry()
# 注册模型
registry.register("chat-model", "v1", "groq", "llama-3.1-70b-versatile")
registry.register("chat-model", "v2", "together", "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo")
# 设置当前版本
registry.set_current("chat-model", "v2")
# 灰度发布:10% 流量切到新版本
import random
def get_model_with_canary(name: str, canary_ratio: float = 0.1) -> dict:
if random.random() < canary_ratio:
# 使用最新版本
versions = registry.models[name]["versions"]
latest = max(versions.keys())
return registry.get_model(name, latest)
return registry.get_model(name) # 使用当前稳定版本
第五章:推理优化技术
5.1 KV 缓存机制详解
KV(Key-Value)缓存是 Transformer 推理的核心优化技术:
传统方式(无缓存):
输入: [A, B, C] → 计算 K_A, V_A, K_B, V_B, K_C, V_C → 生成 D
输入: [A, B, C, D] → 重新计算 K_A, V_A, K_B, V_B, K_C, V_C, K_D, V_D → 生成 E
(每次都重新计算所有 Token 的 KV)
KV 缓存方式:
输入: [A, B, C] → 计算并缓存 K_A, V_A, K_B, V_B, K_C, V_C → 生成 D
输入: D → 仅计算 K_D, V_D → 从缓存读取 K_A..K_C → 生成 E
(只计算新 Token 的 KV,复用已缓存的)
KV 缓存的内存开销:
def calculate_kv_cache_size(
num_layers: int,
num_kv_heads: int,
head_dim: int,
max_seq_length: int,
dtype_bytes: int = 2, # FP16 = 2 bytes
) -> float:
"""计算 KV 缓存所需内存(GB)"""
# 每个 Token 的 KV 缓存 = 2(K+V) × 层数 × KV头数 × 头维度 × 数据类型字节数
bytes_per_token = 2 * num_layers * num_kv_heads * head_dim * dtype_bytes
total_bytes = bytes_per_token * max_seq_length
return total_bytes / (1024 ** 3)
# Llama 3.1 70B 的 KV 缓存计算
kv_size = calculate_kv_cache_size(
num_layers=80,
num_kv_heads=8, # GQA: 8 个 KV 头
head_dim=128,
max_seq_length=128000,
)
print(f"Llama 70B 128K 上下文 KV 缓存: {kv_size:.1f} GB")
# 输出: 约 30 GB
优化策略:
- GQA(Grouped Query Attention):减少 KV 头数,Llama 3.1 使用 8 个 KV 头而非 64 个
- PagedAttention:vLLM 使用的分页 KV 缓存管理,消除内存碎片
- KV 缓存压缩:对不重要的 KV 进行量化或丢弃
5.2 推测解码(Speculative Decoding)
推测解码通过"草稿-验证"机制加速推理:
class SpeculativeDecoder:
"""推测解码实现示意"""
def __init__(self, target_model, draft_model, gamma: int = 4):
"""
Args:
target_model: 大模型(精确但慢)
draft_model: 小模型(快但可能不精确)
gamma: 每次推测的 Token 数
"""
self.target = target_model
self.draft = draft_model
self.gamma = gamma
async def generate(self, prompt: str, max_tokens: int = 100) -> str:
"""推测解码生成"""
generated = []
while len(generated) < max_tokens:
# 步骤 1: 小模型快速生成 gamma 个候选 Token
draft_tokens = []
context = prompt + "".join(generated)
for _ in range(self.gamma):
token = await self.draft.generate_token(context + "".join(draft_tokens))
draft_tokens.append(token)
# 步骤 2: 大模型一次性验证所有候选 Token
# 大模型并行计算 gamma+1 个位置的概率分布
target_probs = await self.target.get_probs(
context, num_positions=self.gamma + 1
)
# 步骤 3: 逐个验证
accepted = 0
for i in range(self.gamma):
draft_prob = await self.draft.get_prob(
context + "".join(draft_tokens[:i]), draft_tokens[i]
)
target_prob = target_probs[i][draft_tokens[i]]
# 接受概率 = min(1, target_prob / draft_prob)
if random.random() < min(1, target_prob / max(draft_prob, 1e-10)):
generated.append(draft_tokens[i])
accepted += 1
else:
# 拒绝:从修正分布中采样一个 Token
corrected_token = self._resample(
target_probs[i], draft_prob, draft_tokens[i]
)
generated.append(corrected_token)
break
else:
# 所有候选都被接受,额外从第 gamma+1 个位置采样
extra_token = self._sample(target_probs[self.gamma])
generated.append(extra_token)
return "".join(generated)
推测解码的效果:
- 理论加速比:2-3 倍(取决于草稿模型与目标模型的一致性)
- 质量保证:输出分布与目标模型完全一致
- 适用场景:长文本生成、高延迟模型
5.3 量化技术
量化是降低推理成本的核心技术:
# 量化精度对比
QUANTIZATION_LEVELS = {
"FP32": {"bits": 32, "relative_size": 1.0, "quality_loss": "无"},
"FP16": {"bits": 16, "relative_size": 0.5, "quality_loss": "极小"},
"BF16": {"bits": 16, "relative_size": 0.5, "quality_loss": "极小"},
"INT8": {"bits": 8, "relative_size": 0.25, "quality_loss": "轻微"},
"INT4": {"bits": 4, "relative_size": 0.125, "quality_loss": "中等"},
"GPTQ-4bit": {"bits": 4, "relative_size": 0.125, "quality_loss": "较小"},
"AWQ-4bit": {"bits": 4, "relative_size": 0.125, "quality_loss": "较小"},
"GGUF-Q4_K_M": {"bits": 4, "relative_size": 0.13, "quality_loss": "较小"},
}
def calculate_model_memory(
params_billions: float,
quantization: str = "FP16",
) -> float:
"""计算模型所需 GPU 内存(GB)"""
info = QUANTIZATION_LEVELS[quantization]
bytes_per_param = info["bits"] / 8
total_bytes = params_billions * 1e9 * bytes_per_param
# 加上约 20% 的运行时开销
return total_bytes * 1.2 / (1024 ** 3)
# 各模型在不同量化下的内存需求
models = {
"Llama 3.1 8B": 8,
"Llama 3.1 70B": 70,
"Llama 3.1 405B": 405,
}
for name, params in models.items():
fp16_mem = calculate_model_memory(params, "FP16")
int4_mem = calculate_model_memory(params, "INT4")
print(f"{name}: FP16={fp16_mem:.0f}GB, INT4={int4_mem:.0f}GB")
5.4 批处理与连续批处理
批处理是提升吞吐量的关键:
class ContinuousBatcher:
"""连续批处理器(Continuous Batching)示意"""
def __init__(self, max_batch_size: int = 32, max_queue_size: int = 100):
self.max_batch_size = max_batch_size
self.queue: asyncio.Queue = asyncio.Queue(maxsize=max_queue_size)
self.active_requests: dict[str, dict] = {}
async def add_request(self, request_id: str, tokens: list[int]) -> str:
"""添加请求到队列"""
future = asyncio.get_event_loop().create_future()
await self.queue.put({
"id": request_id,
"tokens": tokens,
"generated": [],
"future": future,
})
return await future
async def run(self, model):
"""主循环:连续批处理推理"""
while True:
batch = []
# 从队列取出请求,组成批次
while len(batch) < self.max_batch_size and not self.queue.empty():
try:
req = self.queue.get_nowait()
self.active_requests[req["id"]] = req
batch.append(req)
except asyncio.QueueEmpty:
break
if not batch:
await asyncio.sleep(0.01)
continue
# 并行推理(关键:每个请求可能在不同生成阶段)
# 这就是"连续批处理"的核心:不同请求可以在同一批次中
# 处于不同的生成步骤
results = await model.batch_generate(batch)
# 处理结果
completed = []
for req, result in zip(batch, results):
req["generated"].append(result.token)
if result.is_finished:
req["future"].set_result("".join(req["generated"]))
completed.append(req["id"])
else:
# 未完成的请求放回队列
await self.queue.put(req)
# 清理已完成的请求
for rid in completed:
del self.active_requests[rid]
第六章:成本控制策略
6.1 定价模型分析
各平台的定价模型差异显著:
class CostCalculator:
"""推理成本计算器"""
# 各平台定价 ($/百万 Token)
PRICING = {
"together": {
"llama-3.1-8b": {"input": 0.10, "output": 0.10},
"llama-3.1-70b": {"input": 0.88, "output": 0.88},
"llama-3.1-405b": {"input": 3.50, "output": 3.50},
},
"fireworks": {
"llama-3.1-70b": {"input": 0.90, "output": 0.90},
"mixtral-8x22b": {"input": 1.20, "output": 1.20},
},
"groq": {
"llama-3.1-8b": {"input": 0.05, "output": 0.08},
"llama-3.1-70b": {"input": 0.59, "output": 0.79},
"mixtral-8x7b": {"input": 0.24, "output": 0.24},
},
}
@classmethod
def estimate_cost(
cls,
provider: str,
model: str,
input_tokens: int,
output_tokens: int,
) -> dict:
"""估算单次请求成本"""
pricing = cls.PRICING[provider][model]
input_cost = (input_tokens / 1_000_000) * pricing["input"]
output_cost = (output_tokens / 1_000_000) * pricing["output"]
total_cost = input_cost + output_cost
return {
"input_cost": input_cost,
"output_cost": output_cost,
"total_cost": total_cost,
"total_cost_cents": total_cost * 100,
}
@classmethod
def monthly_estimate(
cls,
daily_requests: int,
avg_input_tokens: int = 500,
avg_output_tokens: int = 200,
provider: str = "groq",
model: str = "llama-3.1-70b",
) -> dict:
"""估算月度成本"""
daily_cost = cls.estimate_cost(
provider, model,
avg_input_tokens * daily_requests,
avg_output_tokens * daily_requests,
)
monthly = daily_cost["total_cost"] * 30
return {
"daily_cost": daily_cost["total_cost"],
"monthly_cost": monthly,
"cost_per_request": daily_cost["total_cost"] / daily_requests,
}
# 对比不同场景的成本
scenarios = [
{"name": "轻量聊天机器人", "daily": 10000, "input": 200, "output": 100},
{"name": "客服系统", "daily": 50000, "input": 500, "output": 300},
{"name": "代码助手", "daily": 5000, "input": 2000, "output": 1000},
]
for scenario in scenarios:
print(f"\n场景: {scenario['name']}")
for provider in ["groq", "together", "fireworks"]:
result = CostCalculator.monthly_estimate(
daily_requests=scenario["daily"],
avg_input_tokens=scenario["input"],
avg_output_tokens=scenario["output"],
provider=provider,
model="llama-3.1-70b",
)
print(f" {provider}: ${result['monthly_cost']:.2f}/月")
6.2 Token 优化技巧
class TokenOptimizer:
"""Token 使用优化器"""
@staticmethod
def compress_system_prompt(prompt: str) -> str:
"""压缩系统提示词,去除冗余"""
# 移除多余空白
prompt = " ".join(prompt.split())
# 缩写常用指令
replacements = {
"请用中文回答": "中文",
"请用英文回答": "English",
"不要包含任何解释": "仅输出结果",
"请确保你的回答准确且详细": "精确详细",
}
for old, new in replacements.items():
prompt = prompt.replace(old, new)
return prompt
@staticmethod
def truncate_history(
messages: list[dict],
max_tokens: int = 4000,
keep_system: bool = True,
) -> list[dict]:
"""智能截断对话历史"""
# 粗略估计:1 个中文字符 ≈ 2 tokens
def estimate_tokens(text):
return len(text) * 2
result = []
total_tokens = 0
# 保留系统消息
if keep_system and messages and messages[0]["role"] == "system":
result.append(messages[0])
total_tokens += estimate_tokens(messages[0]["content"])
# 从最新的消息开始,向前保留
for msg in reversed(messages[1:]):
msg_tokens = estimate_tokens(msg["content"])
if total_tokens + msg_tokens > max_tokens:
break
result.insert(-len(result) + (1 if keep_system else 0), msg)
total_tokens += msg_tokens
return result
@staticmethod
def use_structured_output(prompt: str, schema: dict) -> str:
"""使用结构化输出减少冗余 Token"""
# 与其让模型输出长篇解释,不如直接要求 JSON
return f"""{prompt}
按以下 JSON 格式输出:
{json.dumps(schema, ensure_ascii=False, indent=2)}
仅输出 JSON,不要其他内容。"""
6.3 缓存策略
import hashlib
import json
from datetime import datetime, timedelta
class SemanticCache:
"""语义缓存系统"""
def __init__(self, similarity_threshold: float = 0.95):
self.cache: dict[str, dict] = {}
self.threshold = similarity_threshold
def _compute_key(self, messages: list[dict], model: str) -> str:
"""计算缓存键"""
content = json.dumps(messages, sort_keys=True) + model
return hashlib.sha256(content.encode()).hexdigest()
def _compute_semantic_key(self, text: str) -> str:
"""计算语义相似的缓存键(简化版)"""
# 实际项目中应使用嵌入模型计算向量相似度
normalized = text.lower().strip()
# 移除标点和多余空格
import re
normalized = re.sub(r'[^\w\s]', '', normalized)
normalized = " ".join(normalized.split())
return hashlib.md5(normalized.encode()).hexdigest()
def get(self, messages: list[dict], model: str) -> str | None:
"""查询缓存"""
key = self._compute_key(messages, model)
if key in self.cache:
entry = self.cache[key]
if datetime.now() < entry["expires_at"]:
entry["hits"] += 1
return entry["response"]
else:
del self.cache[key]
return None
def set(
self,
messages: list[dict],
model: str,
response: str,
ttl_hours: int = 24,
):
"""设置缓存"""
key = self._compute_key(messages, model)
self.cache[key] = {
"response": response,
"created_at": datetime.now(),
"expires_at": datetime.now() + timedelta(hours=ttl_hours),
"hits": 0,
}
def get_stats(self) -> dict:
"""缓存统计"""
total = len(self.cache)
total_hits = sum(e["hits"] for e in self.cache.values())
return {
"entries": total,
"total_hits": total_hits,
"memory_estimate_kb": total * 2, # 粗略估计
}
# 缓存代理:透明集成缓存
class CachedProvider:
"""带缓存的推理代理"""
def __init__(self, provider: BaseProvider, cache: SemanticCache):
self.provider = provider
self.cache = cache
self.stats = {"cache_hits": 0, "cache_misses": 0, "cost_saved": 0.0}
async def chat(self, messages, model, **kwargs) -> ChatResponse:
# 检查缓存
cached = self.cache.get(
[{"role": m.role, "content": m.content} for m in messages], model
)
if cached:
self.stats["cache_hits"] += 1
return ChatResponse(
content=cached,
model=model,
usage={"prompt_tokens": 0, "completion_tokens": 0},
provider="cache",
latency_ms=0,
)
# 调用实际 API
self.stats["cache_misses"] += 1
response = await self.provider.chat(messages, model, **kwargs)
# 写入缓存
self.cache.set(
[{"role": m.role, "content": m.content} for m in messages],
model,
response.content,
)
return response
6.4 预算告警与限制
class BudgetGuard:
"""预算守护系统"""
def __init__(self, monthly_budget: float):
self.monthly_budget = monthly_budget
self.spending: list[dict] = []
self.alerts: list[Callable] = []
def record_cost(self, cost: float, provider: str, model: str):
"""记录支出"""
self.spending.append({
"amount": cost,
"provider": provider,
"model": model,
"timestamp": time.time(),
})
self._check_alerts()
def get_monthly_total(self) -> float:
"""获取本月总支出"""
month_start = datetime.now().replace(day=1, hour=0, minute=0, second=0).timestamp()
return sum(
s["amount"] for s in self.spending if s["timestamp"] >= month_start
)
def get_daily_breakdown(self) -> dict:
"""获取每日支出明细"""
breakdown = {}
for s in self.spending:
day = datetime.fromtimestamp(s["timestamp"]).strftime("%Y-%m-%d")
if day not in breakdown:
breakdown[day] = 0
breakdown[day] += s["amount"]
return breakdown
def _check_alerts(self):
"""检查是否触发告警"""
monthly = self.get_monthly_total()
ratio = monthly / self.monthly_budget
if ratio >= 1.0:
raise BudgetExceededException(
f"月度预算已超支!当前: ${monthly:.2f}, 预算: ${self.monthly_budget:.2f}"
)
elif ratio >= 0.8:
self._send_alert(f"⚠️ 预算警告:已使用 {ratio*100:.0f}%")
elif ratio >= 0.5:
self._send_alert(f"📊 预算提醒:已使用 {ratio*100:.0f}%")
def _send_alert(self, message: str):
"""发送告警"""
for alert_fn in self.alerts:
alert_fn(message)
def add_alert_handler(self, handler: Callable):
self.alerts.append(handler)
class BudgetExceededException(Exception):
pass
第七章:多模型路由与智能网关
7.1 路由策略设计
智能路由是企业级 LLM 应用的核心:
from enum import Enum
class RoutingStrategy(Enum):
COST_OPTIMIZED = "cost_optimized" # 最低成本
LATENCY_OPTIMIZED = "latency_optimized" # 最低延迟
QUALITY_OPTIMIZED = "quality_optimized" # 最高质量
BALANCED = "balanced" # 平衡
class SmartRouter:
"""智能模型路由器"""
def __init__(self):
self.providers: dict[str, BaseProvider] = {}
self.route_table: dict[str, list[dict]] = {}
self.metrics: dict[str, dict] = {}
def add_provider(self, name: str, provider: BaseProvider):
"""添加推理提供者"""
self.providers[name] = provider
def configure_routes(self, task_type: str, candidates: list[dict]):
"""配置路由规则"""
self.route_table[task_type] = candidates
async def route(
self,
messages: list[ChatMessage],
task_type: str = "general",
strategy: RoutingStrategy = RoutingStrategy.BALANCED,
) -> ChatResponse:
"""智能路由请求"""
candidates = self.route_table.get(task_type, self.route_table.get("general", []))
if not candidates:
raise ValueError(f"未找到任务类型 {task_type} 的路由配置")
# 根据策略排序
if strategy == RoutingStrategy.COST_OPTIMIZED:
candidates.sort(key=lambda c: c.get("cost_weight", 1.0))
elif strategy == RoutingStrategy.LATENCY_OPTIMIZED:
candidates.sort(key=lambda c: self._get_avg_latency(c["provider"], c["model"]))
elif strategy == RoutingStrategy.QUALITY_OPTIMIZED:
candidates.sort(key=lambda c: c.get("quality_weight", 0.5), reverse=True)
else: # BALANCED
candidates.sort(key=lambda c: self._compute_score(c))
# 尝试候选者,支持故障转移
last_error = None
for candidate in candidates[:3]: # 最多尝试 3 个
try:
provider = self.providers[candidate["provider"]]
response = await provider.chat(
messages, model=candidate["model"],
temperature=candidate.get("temperature", 0.7),
max_tokens=candidate.get("max_tokens", 1024),
)
# 记录成功指标
self._record_success(candidate["provider"], candidate["model"], response)
return response
except Exception as e:
last_error = e
self._record_failure(candidate["provider"], candidate["model"], str(e))
continue
raise Exception(f"所有候选者均失败: {last_error}")
def _compute_score(self, candidate: dict) -> float:
"""计算综合评分"""
cost = candidate.get("cost_weight", 1.0)
quality = candidate.get("quality_weight", 0.5)
latency = self._get_avg_latency(candidate["provider"], candidate["model"])
# 归一化并加权
return quality * 0.4 + (1 / max(cost, 0.01)) * 0.3 + (1 / max(latency, 1)) * 0.3
def _get_avg_latency(self, provider: str, model: str) -> float:
"""获取平均延迟"""
key = f"{provider}:{model}"
if key in self.metrics and self.metrics[key]["count"] > 0:
return self.metrics[key]["total_latency"] / self.metrics[key]["count"]
return 1000.0 # 默认 1 秒
def _record_success(self, provider: str, model: str, response: ChatResponse):
key = f"{provider}:{model}"
if key not in self.metrics:
self.metrics[key] = {"count": 0, "total_latency": 0, "failures": 0}
self.metrics[key]["count"] += 1
self.metrics[key]["total_latency"] += response.latency_ms
def _record_failure(self, provider: str, model: str, error: str):
key = f"{provider}:{model}"
if key not in self.metrics:
self.metrics[key] = {"count": 0, "total_latency": 0, "failures": 0}
self.metrics[key]["failures"] += 1
7.2 负载均衡与故障转移
class LoadBalancer:
"""负载均衡器"""
def __init__(self):
self.backends: list[dict] = []
self.current_index = 0
self.health_status: dict[str, bool] = {}
def add_backend(self, provider: str, model: str, weight: int = 1):
"""添加后端"""
key = f"{provider}:{model}"
self.backends.append({
"provider": provider,
"model": model,
"weight": weight,
"key": key,
})
self.health_status[key] = True
def next(self) -> dict | None:
"""获取下一个可用后端(加权轮询)"""
available = [b for b in self.backends if self.health_status.get(b["key"], False)]
if not available:
return None
# 加权轮询
total_weight = sum(b["weight"] for b in available)
self.current_index = self.current_index % total_weight
cumulative = 0
for backend in available:
cumulative += backend["weight"]
if self.current_index < cumulative:
self.current_index += 1
return backend
return available[0]
async def health_check(self, providers: dict[str, BaseProvider]):
"""健康检查"""
for backend in self.backends:
try:
provider = providers[backend["provider"]]
start = time.monotonic()
await provider.chat(
[ChatMessage(role="user", content="ping")],
model=backend["model"],
max_tokens=1,
)
latency = (time.monotonic() - start) * 1000
self.health_status[backend["key"]] = latency < 5000 # 5 秒超时
except Exception:
self.health_status[backend["key"]] = False
class CircuitBreaker:
"""熔断器"""
def __init__(self, failure_threshold: int = 5, recovery_time: float = 60.0):
self.failure_threshold = failure_threshold
self.recovery_time = recovery_time
self.failures: dict[str, list[float]] = {}
self.state: dict[str, str] = {} # "closed", "open", "half-open"
def record_failure(self, key: str):
"""记录失败"""
if key not in self.failures:
self.failures[key] = []
self.failures[key].append(time.time())
# 清理旧记录
cutoff = time.time() - self.recovery_time
self.failures[key] = [t for t in self.failures[key] if t > cutoff]
if len(self.failures[key]) >= self.failure_threshold:
self.state[key] = "open"
def record_success(self, key: str):
"""记录成功"""
self.state[key] = "closed"
self.failures[key] = []
def is_available(self, key: str) -> bool:
"""检查是否可用"""
state = self.state.get(key, "closed")
if state == "closed":
return True
elif state == "open":
# 检查是否可以尝试恢复
if self.failures.get(key):
last_failure = max(self.failures[key])
if time.time() - last_failure > self.recovery_time:
self.state[key] = "half-open"
return True
return False
else: # half-open
return True # 允许一个请求通过
7.3 智能模型网关实现
class ModelGateway:
"""智能模型网关 - 核心路由引擎"""
def __init__(self):
self.router = SmartRouter()
self.load_balancer = LoadBalancer()
self.circuit_breaker = CircuitBreaker()
self.cost_tracker = BudgetGuard(monthly_budget=1000.0)
self.cache = SemanticCache()
self.logger = logging.getLogger("gateway")
async def handle_request(
self,
messages: list[ChatMessage],
task_type: str = "general",
strategy: RoutingStrategy = RoutingStrategy.BALANCED,
max_cost: float | None = None,
) -> ChatResponse:
"""处理推理请求"""
# 1. 检查预算
if max_cost:
current = self.cost_tracker.get_monthly_total()
if current + max_cost > self.cost_tracker.monthly_budget:
raise BudgetExceededException("预算不足")
# 2. 检查缓存
cached_response = self.cache.get(
[{"role": m.role, "content": m.content} for m in messages],
f"{task_type}:{strategy.value}",
)
if cached_response:
self.logger.info("缓存命中")
return cached_response
# 3. 路由选择
response = await self.router.route(messages, task_type, strategy)
# 4. 缓存结果
self.cache.set(
[{"role": m.role, "content": m.content} for m in messages],
f"{task_type}:{strategy.value}",
response,
)
# 5. 记录成本
cost = self._estimate_cost(response)
self.cost_tracker.record_cost(cost, response.provider, response.model)
return response
def _estimate_cost(self, response: ChatResponse) -> float:
"""估算请求成本"""
# 简化计算
input_cost = response.usage.get("prompt_tokens", 0) / 1_000_000 * 0.5
output_cost = response.usage.get("completion_tokens", 0) / 1_000_000 * 1.0
return input_cost + output_cost
def get_status(self) -> dict:
"""获取网关状态"""
return {
"budget": {
"monthly_budget": self.cost_tracker.monthly_budget,
"monthly_spent": self.cost_tracker.get_monthly_total(),
"remaining": self.cost_tracker.monthly_budget - self.cost_tracker.get_monthly_total(),
},
"cache": self.cache.get_stats(),
"providers": {
name: {
"healthy": self.load_balancer.health_status.get(f"{name}:*", True),
}
for name in self.router.providers
},
}
第八章:企业级集成
8.1 安全与合规
class SecurityMiddleware:
"""安全中间件"""
def __init__(self):
self.blocked_patterns = [
r"(?i)(api[_-]?key|secret|password|token)\s*[:=]\s*\S+",
r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b", # 信用卡号
r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", # 邮箱
]
self.content_filter = ContentFilter()
def sanitize_input(self, messages: list[ChatMessage]) -> list[ChatMessage]:
"""清理输入中的敏感信息"""
import re
sanitized = []
for msg in messages:
content = msg.content
for pattern in self.blocked_patterns:
content = re.sub(pattern, "[REDACTED]", content)
sanitized.append(ChatMessage(role=msg.role, content=content))
return sanitized
def filter_output(self, response: str) -> str:
"""过滤输出中的不当内容"""
return self.content_filter.check(response)
def log_request(self, request_id: str, messages: list, response: str, metadata: dict):
"""审计日志"""
log_entry = {
"request_id": request_id,
"timestamp": datetime.now().isoformat(),
"input_length": sum(len(m.content) for m in messages),
"output_length": len(response),
"provider": metadata.get("provider"),
"model": metadata.get("model"),
"cost": metadata.get("cost"),
# 不记录完整内容,保护隐私
}
logging.info(f"AUDIT: {json.dumps(log_entry)}")
class ContentFilter:
"""内容安全过滤器"""
def __init__(self):
self.blocked_keywords = set() # 可配置的关键词列表
def check(self, text: str) -> str:
"""检查并过滤内容"""
# 实际项目中应对接内容安全 API
return text
8.2 监控与可观测性
import time
from dataclasses import dataclass, field
from collections import defaultdict
@dataclass
class MetricPoint:
timestamp: float
value: float
labels: dict = field(default_factory=dict)
class MetricsCollector:
"""指标收集器"""
def __init__(self):
self.counters: dict[str, int] = defaultdict(int)
self.histograms: dict[str, list[float]] = defaultdict(list)
self.gauges: dict[str, float] = {}
def inc(self, name: str, value: int = 1, labels: dict | None = None):
"""增加计数器"""
key = f"{name}:{json.dumps(labels or {}, sort_keys=True)}"
self.counters[key] += value
def observe(self, name: str, value: float, labels: dict | None = None):
"""记录直方图值"""
key = f"{name}:{json.dumps(labels or {}, sort_keys=True)}"
self.histograms[key].append(value)
# 保留最近 1000 个值
if len(self.histograms[key]) > 1000:
self.histograms[key] = self.histograms[key][-1000:]
def set_gauge(self, name: str, value: float):
"""设置仪表盘值"""
self.gauges[name] = value
def get_summary(self) -> dict:
"""获取指标摘要"""
summary = {"counters": dict(self.counters), "gauges": dict(self.gauges)}
for key, values in self.histograms.items():
if values:
summary[f"histogram:{key}"] = {
"count": len(values),
"mean": sum(values) / len(values),
"p50": sorted(values)[len(values) // 2],
"p95": sorted(values)[int(len(values) * 0.95)],
"p99": sorted(values)[int(len(values) * 0.99)],
}
return summary
# 集成到网关中
class MonitoredGateway(ModelGateway):
"""带监控的模型网关"""
def __init__(self):
super().__init__()
self.metrics = MetricsCollector()
async def handle_request(self, messages, task_type="general", **kwargs):
start = time.monotonic()
self.metrics.inc("requests_total", labels={"task_type": task_type})
try:
response = await super().handle_request(messages, task_type, **kwargs)
latency = (time.monotonic() - start) * 1000
self.metrics.observe("request_duration_ms", latency)
self.metrics.inc("requests_success")
self.metrics.observe(
"tokens_generated",
response.usage.get("completion_tokens", 0),
)
return response
except Exception as e:
self.metrics.inc("requests_failed", labels={"error": type(e).__name__})
raise
8.3 高可用架构设计
class HighAvailabilityGateway:
"""高可用网关架构"""
def __init__(self):
self.primary_providers = [] # 主要提供者
self.fallback_providers = [] # 备用提供者
self.circuit_breakers = {}
self.health_checker = None
async def handle_with_fallback(self, messages, **kwargs) -> ChatResponse:
"""带降级的请求处理"""
# 尝试主要提供者
for provider_config in self.primary_providers:
key = provider_config["key"]
if not self._is_healthy(key):
continue
try:
response = await self._call_provider(provider_config, messages, **kwargs)
self._record_success(key)
return response
except Exception as e:
self._record_failure(key, e)
continue
# 主要提供者全部失败,使用备用
for provider_config in self.fallback_providers:
try:
response = await self._call_provider(provider_config, messages, **kwargs)
return response
except Exception:
continue
# 所有提供者都失败
raise Exception("所有推理提供者均不可用")
async def _call_provider(self, config, messages, **kwargs):
"""调用指定提供者"""
provider = config["provider"]
return await provider.chat(messages, model=config["model"], **kwargs)
def _is_healthy(self, key: str) -> bool:
cb = self.circuit_breakers.get(key)
return cb.is_available(key) if cb else True
def _record_success(self, key: str):
if key in self.circuit_breakers:
self.circuit_breakers[key].record_success(key)
def _record_failure(self, key: str, error: Exception):
if key not in self.circuit_breakers:
self.circuit_breakers[key] = CircuitBreaker()
self.circuit_breakers[key].record_failure(key)
第九章:实战项目——智能模型网关与成本优化系统
9.1 项目架构设计
我们将构建一个完整的智能模型网关系统,整合前面所有技术:
┌─────────────────────────────────────────────────┐
│ 客户端应用 │
└─────────────────────┬───────────────────────────┘
│ HTTP/SSE
┌─────────────────────▼───────────────────────────┐
│ API 网关层 (FastAPI) │
│ ┌─────────┐ ┌──────────┐ ┌───────────────┐ │
│ │认证鉴权 │ │速率限制 │ │ 请求/响应日志 │ │
│ └─────────┘ └──────────┘ └───────────────┘ │
└─────────────────────┬───────────────────────────┘
│
┌─────────────────────▼───────────────────────────┐
│ 智能路由层 │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │
│ │任务分类器 │ │模型选择器 │ │ 成本优化器 │ │
│ └──────────┘ └───────────┘ └──────────────┘ │
└─────────────────────┬───────────────────────────┘
│
┌─────────────────────▼───────────────────────────┐
│ 执行引擎层 │
│ ┌────────┐ ┌──────────┐ ┌───────────┐ │
│ │语义缓存 │ │负载均衡 │ │ 熔断器 │ │
│ └────────┘ └──────────┘ └───────────┘ │
└────────┬────────────┬────────────┬──────────────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌───▼─────┐
│Groq │ │Together │ │Fireworks│
│ │ │AI │ │ │
└─────────┘ └─────────┘ └─────────┘
9.2 核心模块实现
# gateway/main.py
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import uvicorn
app = FastAPI(title="智能模型网关", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# === 数据模型 ===
class Message(BaseModel):
role: str
content: str
class ChatRequest(BaseModel):
messages: list[Message]
model: str | None = None
provider: str | None = None
task_type: str = "general"
strategy: str = "balanced"
temperature: float = 0.7
max_tokens: int = 1024
stream: bool = False
class CostReport(BaseModel):
daily_costs: dict
monthly_total: float
budget_remaining: float
top_models: list[dict]
# === 全局实例 ===
gateway = ModelGateway()
metrics = MetricsCollector()
security = SecurityMiddleware()
# 初始化提供者
def setup_providers():
gateway.router.add_provider("groq", GroqProvider(api_key="..."))
gateway.router.add_provider("together", TogetherProvider(api_key="..."))
gateway.router.add_provider("fireworks", FireworksProvider(api_key="..."))
# 配置路由
gateway.router.configure_routes("general", [
{"provider": "groq", "model": "llama-3.1-70b-versatile",
"cost_weight": 0.3, "quality_weight": 0.8},
{"provider": "together", "model": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
"cost_weight": 0.5, "quality_weight": 0.85},
{"provider": "fireworks", "model": "accounts/fireworks/models/llama-v3p1-70b-instruct",
"cost_weight": 0.5, "quality_weight": 0.85},
])
gateway.router.configure_routes("code", [
{"provider": "fireworks", "model": "accounts/fireworks/models/llama-v3p1-70b-instruct",
"cost_weight": 0.5, "quality_weight": 0.9},
{"provider": "together", "model": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
"cost_weight": 0.5, "quality_weight": 0.85},
])
gateway.router.configure_routes("fast", [
{"provider": "groq", "model": "llama-3.1-8b-instant",
"cost_weight": 0.1, "quality_weight": 0.6},
])
# === API 端点 ===
@app.on_event("startup")
async def startup():
setup_providers()
@app.post("/v1/chat/completions")
async def chat_completions(request: ChatRequest):
"""聊天补全接口(OpenAI 兼容)"""
# 安全过滤
messages = [
ChatMessage(role=m.role, content=m.content)
for m in request.messages
]
messages = security.sanitize_input(messages)
# 选择策略
strategy_map = {
"cost": RoutingStrategy.COST_OPTIMIZED,
"latency": RoutingStrategy.LATENCY_OPTIMIZED,
"quality": RoutingStrategy.QUALITY_OPTIMIZED,
"balanced": RoutingStrategy.BALANCED,
}
strategy = strategy_map.get(request.strategy, RoutingStrategy.BALANCED)
try:
if request.stream:
return StreamingResponse(
_stream_response(messages, request, strategy),
media_type="text/event-stream",
)
else:
response = await gateway.handle_request(
messages, request.task_type, strategy=strategy,
)
return {
"choices": [{"message": {"content": response.content}}],
"usage": response.usage,
"model": response.model,
"provider": response.provider,
}
except BudgetExceededException:
raise HTTPException(status_code=429, detail="月度预算已超支")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
async def _stream_response(messages, request, strategy):
"""流式响应生成器"""
provider = gateway.router.providers.get("groq")
model = request.model or "llama-3.1-70b-versatile"
async for chunk in provider.chat_stream(messages, model=model):
if chunk.delta:
yield f"data: {json.dumps({'choices': [{'delta': {'content': chunk.delta}}]})}\n\n"
if chunk.finish_reason:
yield f"data: {json.dumps({'choices': [{'finish_reason': chunk.finish_reason}]})}\n\n"
yield "data: [DONE]\n\n"
@app.get("/v1/health")
async def health_check():
"""健康检查"""
return gateway.get_status()
@app.get("/v1/metrics")
async def get_metrics():
"""获取监控指标"""
return metrics.get_summary()
@app.get("/v1/costs", response_model=CostReport)
async def get_costs():
"""获取成本报告"""
return CostReport(
daily_costs=gateway.cost_tracker.get_daily_breakdown(),
monthly_total=gateway.cost_tracker.get_monthly_total(),
budget_remaining=(
gateway.cost_tracker.monthly_budget
- gateway.cost_tracker.get_monthly_total()
),
top_models=_get_top_models(),
)
def _get_top_models() -> list[dict]:
"""获取使用量最高的模型"""
model_usage: dict[str, dict] = {}
for entry in gateway.cost_tracker.spending:
key = f"{entry['provider']}:{entry['model']}"
if key not in model_usage:
model_usage[key] = {"provider": entry["provider"], "model": entry["model"], "cost": 0, "calls": 0}
model_usage[key]["cost"] += entry["amount"]
model_usage[key]["calls"] += 1
return sorted(model_usage.values(), key=lambda x: x["cost"], reverse=True)[:10]
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
9.3 成本监控面板
# gateway/dashboard.py
"""成本监控仪表板数据接口"""
class CostDashboard:
"""成本监控仪表板"""
def __init__(self, cost_tracker: BudgetGuard, metrics: MetricsCollector):
self.cost_tracker = cost_tracker
self.metrics = metrics
def get_overview(self) -> dict:
"""获取总览数据"""
monthly_total = self.cost_tracker.get_monthly_total()
daily_breakdown = self.cost_tracker.get_daily_breakdown()
return {
"monthly_budget": self.cost_tracker.monthly_budget,
"monthly_spent": monthly_total,
"budget_usage_pct": (monthly_total / self.cost_tracker.monthly_budget) * 100,
"daily_average": monthly_total / max(len(daily_breakdown), 1),
"projected_monthly": self._project_monthly(daily_breakdown),
"daily_breakdown": daily_breakdown,
}
def get_provider_comparison(self) -> list[dict]:
"""获取提供者对比数据"""
provider_stats: dict[str, dict] = {}
for entry in self.cost_tracker.spending:
p = entry["provider"]
if p not in provider_stats:
provider_stats[p] = {
"provider": p,
"total_cost": 0,
"total_calls": 0,
"avg_cost_per_call": 0,
}
provider_stats[p]["total_cost"] += entry["amount"]
provider_stats[p]["total_calls"] += 1
for stats in provider_stats.values():
if stats["total_calls"] > 0:
stats["avg_cost_per_call"] = stats["total_cost"] / stats["total_calls"]
return sorted(provider_stats.values(), key=lambda x: x["total_cost"], reverse=True)
def get_optimization_recommendations(self) -> list[str]:
"""获取成本优化建议"""
recommendations = []
overview = self.get_overview()
# 检查是否超预算
if overview["budget_usage_pct"] > 80:
recommendations.append("⚠️ 月度预算使用超过 80%,建议检查高成本调用")
# 检查是否可以使用更便宜的模型
for stats in self.get_provider_comparison():
if stats["avg_cost_per_call"] > 0.01:
recommendations.append(
f"💡 {stats['provider']} 平均调用成本较高"
f"(${stats['avg_cost_per_call']:.4f}),"
f"考虑对简单任务使用更小的模型"
)
# 检查缓存命中率
cache_stats = self.metrics.get_summary().get("counters", {})
hits = cache_stats.get("cache_hits:{}", 0)
misses = cache_stats.get("cache_misses:{}", 0)
if hits + misses > 0:
hit_rate = hits / (hits + misses) * 100
if hit_rate < 30:
recommendations.append(
f"📊 缓存命中率仅 {hit_rate:.0f}%,"
f"建议增加缓存 TTL 或调整相似度阈值"
)
return recommendations
def _project_monthly(self, daily_breakdown: dict) -> float:
"""预测月度总成本"""
if not daily_breakdown:
return 0
avg_daily = sum(daily_breakdown.values()) / len(daily_breakdown)
days_in_month = 30
return avg_daily * days_in_month
9.4 部署与测试
# docker-compose.yml
version: '3.8'
services:
gateway:
build: .
ports:
- "8000:8000"
environment:
- GROQ_API_KEY=${GROQ_API_KEY}
- TOGETHER_API_KEY=${TOGETHER_API_KEY}
- FIREWORKS_API_KEY=${FIREWORKS_API_KEY}
- MONTHLY_BUDGET=1000
volumes:
- ./data:/app/data
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/v1/health"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
volumes:
redis-data:
# tests/test_gateway.py
import pytest
import asyncio
from gateway.main import app
from httpx import AsyncClient, ASGITransport
@pytest.fixture
def client():
transport = ASGITransport(app=app)
return AsyncClient(transport=transport, base_url="http://test")
@pytest.mark.asyncio
async def test_chat_completions(client):
"""测试聊天补全接口"""
response = await client.post(
"/v1/chat/completions",
json={
"messages": [{"role": "user", "content": "你好"}],
"max_tokens": 50,
},
)
assert response.status_code == 200
data = response.json()
assert "choices" in data
assert len(data["choices"]) > 0
assert "usage" in data
@pytest.mark.asyncio
async def test_cost_optimized_routing(client):
"""测试成本优化路由"""
response = await client.post(
"/v1/chat/completions",
json={
"messages": [{"role": "user", "content": "1+1=?"}],
"strategy": "cost",
"task_type": "fast",
"max_tokens": 10,
},
)
assert response.status_code == 200
data = response.json()
# 成本优化应选择更便宜的模型
assert data["provider"] == "groq"
@pytest.mark.asyncio
async def test_health_endpoint(client):
"""测试健康检查"""
response = await client.get("/v1/health")
assert response.status_code == 200
data = response.json()
assert "budget" in data
assert "cache" in data
@pytest.mark.asyncio
async def test_cost_report(client):
"""测试成本报告"""
response = await client.get("/v1/costs")
assert response.status_code == 200
data = response.json()
assert "monthly_total" in data
assert "budget_remaining" in data
运行测试:
# 安装依赖
pip install fastapi uvicorn httpx pytest pytest-asyncio
# 运行测试
pytest tests/test_gateway.py -v
# 启动服务
python -m gateway.main
第十章:常见问题与故障排查
Q1: 如何选择推理平台?
决策流程:
需要极速响应?
├── 是 → Groq(LPU 硬件加速,延迟最低)
└── 否 → 需要自定义模型?
├── 是 → Together AI(支持微调和自定义部署)
│ 或 Replicate(Docker 容器灵活部署)
└── 否 → 对函数调用有要求?
├── 是 → Fireworks.ai(Function Calling 优化)
└── 否 → 按性价比选择
├── 预算紧张 → Groq(价格最低)
└── 模型多样性 → Together AI(模型最全)
Q2: API 调用返回 429 错误怎么办?
import asyncio
from functools import wraps
def retry_with_backoff(max_retries: int = 3, base_delay: float = 1.0):
"""指数退避重试装饰器"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return await func(*args, **kwargs)
except httpx.HTTPStatusError as e:
if e.response.status_code == 429:
# 从响应头获取重试时间
retry_after = e.response.headers.get("Retry-After")
if retry_after:
delay = float(retry_after)
else:
delay = base_delay * (2 ** attempt)
print(f"速率限制,等待 {delay:.1f} 秒后重试...")
await asyncio.sleep(delay)
else:
raise
raise Exception(f"重试 {max_retries} 次后仍然失败")
return wrapper
return decorator
@retry_with_backoff(max_retries=3, base_delay=2.0)
async def call_with_retry(provider, messages, model):
return await provider.chat(messages, model=model)
Q3: 如何处理长上下文窗口?
class ContextManager:
"""上下文管理器"""
@staticmethod
def sliding_window(messages: list[dict], max_tokens: int = 4000) -> list[dict]:
"""滑动窗口:保留最新的消息"""
result = []
total = 0
# 保留系统消息
if messages and messages[0]["role"] == "system":
result.append(messages[0])
total += len(messages[0]["content"]) * 2
# 从后向前保留
for msg in reversed(messages[1:]):
msg_tokens = len(msg["content"]) * 2
if total + msg_tokens > max_tokens:
break
result.insert(1, msg)
total += msg_tokens
return result
@staticmethod
def summarize_older(messages: list[dict], keep_recent: int = 5) -> list[dict]:
"""对旧消息进行摘要"""
if len(messages) <= keep_recent + 1:
return messages
system = messages[0] if messages[0]["role"] == "system" else None
rest = messages[1:] if system else messages
older = rest[:-keep_recent]
recent = rest[-keep_recent:]
# 生成摘要(实际应调用 LLM)
summary_text = f"[历史对话摘要:共 {len(older)} 条消息,讨论了多个话题]"
result = []
if system:
result.append(system)
result.append({"role": "user", "content": summary_text})
result.append({"role": "assistant", "content": "好的,我已了解之前的对话内容。"})
result.extend(recent)
return result
Q4: 流式输出中断怎么办?
class ResilientStreamProcessor:
"""带断点续传的流式处理器"""
def __init__(self, provider: BaseProvider):
self.provider = provider
self.buffer = ""
self.completed_tokens = []
async def stream_with_recovery(
self,
messages: list[ChatMessage],
model: str,
on_token: Callable | None = None,
) -> str:
"""带恢复的流式输出"""
max_retries = 3
for attempt in range(max_retries):
try:
async for chunk in self.provider.chat_stream(messages, model=model):
if chunk.delta:
self.buffer += chunk.delta
self.completed_tokens.append(chunk.delta)
if on_token:
on_token(chunk.delta)
return self.buffer
except (httpx.ReadTimeout, httpx.RemoteProtocolError) as e:
if attempt < max_retries - 1:
# 将已生成的内容作为上下文继续
messages = messages + [
ChatMessage(role="assistant", content=self.buffer)
]
await asyncio.sleep(1)
continue
else:
# 返回已生成的部分内容
return self.buffer
return self.buffer
Q5: 多个 API Key 如何管理?
class KeyRotator:
"""API Key 轮换管理器"""
def __init__(self):
self.keys: dict[str, list[dict]] = {} # provider -> [{key, usage, limit, reset_at}]
def add_key(self, provider: str, api_key: str, daily_limit: float = 10.0):
"""添加 API Key"""
if provider not in self.keys:
self.keys[provider] = []
self.keys[provider].append({
"key": api_key,
"usage": 0.0,
"limit": daily_limit,
"reset_at": time.time() + 86400,
"errors": 0,
})
def get_key(self, provider: str) -> str | None:
"""获取可用的 API Key"""
if provider not in self.keys:
return None
# 重置过期的使用量
now = time.time()
for key_info in self.keys[provider]:
if now > key_info["reset_at"]:
key_info["usage"] = 0
key_info["reset_at"] = now + 86400
key_info["errors"] = 0
# 选择使用量最低且未超限的 Key
available = [
k for k in self.keys[provider]
if k["usage"] < k["limit"] and k["errors"] < 5
]
if not available:
return None
# 选择使用量最低的
best = min(available, key=lambda k: k["usage"])
return best["key"]
def record_usage(self, provider: str, api_key: str, cost: float):
"""记录使用量"""
for key_info in self.keys.get(provider, []):
if key_info["key"] == api_key:
key_info["usage"] += cost
break
def record_error(self, provider: str, api_key: str):
"""记录错误"""
for key_info in self.keys.get(provider, []):
if key_info["key"] == api_key:
key_info["errors"] += 1
break
Q6: 如何降低推理成本?
核心策略清单:
- 模型分层:简单任务用小模型,复杂任务用大模型
- 语义缓存:相似请求直接返回缓存结果
- 提示词压缩:精简系统提示和上下文
- 批量处理:非实时任务使用批处理 API
- 预算守卫:设置每日/每月预算上限
- 多平台比价:同一模型在不同平台价格差异可达 2-3 倍
- 输出长度限制:合理设置 max_tokens,避免过长输出
- 提前终止:检测到完整答案时立即停止生成
Q7: 推理结果质量不稳定怎么办?
class QualityAssurance:
"""推理质量保障"""
@staticmethod
async def verify_with_consensus(
providers: list[BaseProvider],
messages: list[ChatMessage],
model: str,
consensus_threshold: int = 2,
) -> str:
"""多数投票验证"""
responses = []
for provider in providers:
try:
resp = await provider.chat(messages, model=model)
responses.append(resp.content)
except Exception:
continue
if not responses:
raise Exception("所有提供者均失败")
# 简单的相似度投票
from difflib import SequenceMatcher
scores = []
for i, r1 in enumerate(responses):
score = sum(
SequenceMatcher(None, r1, r2).ratio()
for j, r2 in enumerate(responses) if i != j
)
scores.append((score, r1))
scores.sort(reverse=True)
return scores[0][1]
附录:资源与参考
官方文档
- Together AI: https://docs.together.ai
- Fireworks.ai: https://docs.fireworks.ai
- Groq: https://console.groq.com/docs
- Replicate: https://replicate.com/docs
开源工具
- vLLM: 高性能推理引擎,支持 PagedAttention
- Ollama: 本地模型运行工具
- LiteLLM: 统一的 LLM API 调用库
- LangChain: LLM 应用开发框架
学习资源
- Papers With Code: 推理优化相关论文
- Hugging Face: 模型库与社区
- r/LocalLLaMA: Reddit 本地 LLM 社区
定价对比工具
建议定期检查各平台最新定价,因为价格变化很快。可以使用以下方式:
# 定期抓取各平台定价页面(仅用于个人参考)
PRICING_URLS = {
"together": "https://api.together.xyz/models/pricing",
"fireworks": "https://fireworks.ai/pricing",
"groq": "https://console.groq.com/docs/models",
"replicate": "https://replicate.com/pricing",
}
本教程到此结束。 掌握了这些知识,你已经具备了构建企业级 LLM 推理服务的能力。核心要点:选择合适的平台、设计智能路由、做好成本控制、保证高可用性。从今天开始,用这些技术让你的 AI 应用更高效、更可靠、更经济。