LLM 推理服务平台完全教程

教程简介

零基础LLM推理服务平台完全教程,涵盖Together AI、Fireworks.ai、Groq、Replicate等主流推理平台对比、API接入实战、模型部署与托管、推理优化技术(推测解码、KV缓存)、成本控制策略、多模型路由、流式输出、企业级集成等核心技能,配有智能模型网关与成本优化系统实战项目,适合AI工程师和平台架构师系统学习。

LLM 推理服务平台完全教程

从零基础到企业级部署:Together AI、Fireworks.ai、Groq、Replicate 全面解析与实战


目录


第一章: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 版本、内存管理、故障恢复
弹性扩展难 流量波动时难以快速扩缩容
优化门槛高 量化、批处理、缓存等优化技术需要深厚专业知识

推理服务平台的价值在于:

  1. 降低门槛:无需管理 GPU 基础设施,API 调用即可使用
  2. 成本可控:按 Token 计费,用多少付多少
  3. 模型丰富:一站式访问数十种主流开源模型
  4. 性能优化:平台已做深度优化,性能通常优于自建

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: 如何降低推理成本?

核心策略清单:

  1. 模型分层:简单任务用小模型,复杂任务用大模型
  2. 语义缓存:相似请求直接返回缓存结果
  3. 提示词压缩:精简系统提示和上下文
  4. 批量处理:非实时任务使用批处理 API
  5. 预算守卫:设置每日/每月预算上限
  6. 多平台比价:同一模型在不同平台价格差异可达 2-3 倍
  7. 输出长度限制:合理设置 max_tokens,避免过长输出
  8. 提前终止:检测到完整答案时立即停止生成

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]

附录:资源与参考

官方文档

开源工具

  • 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 应用更高效、更可靠、更经济。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录