videos1.0/AIProviderConfig.md
Your Name 83737090bf docs: 新增 AI 厂商动态配置架构设计
- 新增 AIProviderConfig.md:详细设计 AI 厂商动态配置系统
  - 数据库存储配置(而非环境变量)
  - 运行时动态加载,支持热更新
  - 多租户隔离,支持品牌方独立配置
  - API Key 加密存储
  - 故障转移机制

- 更新 DevelopmentPlan.md (V1.4):
  - 在 AI 模型选型章节添加动态配置说明
  - 添加 AIProviderConfig.md 到相关文档

- 更新 FeatureSummary.md (V1.3):
  - 新增系统管理模块 (F-47~F-50)
  - F-47: AI 厂商动态配置 (P0)
  - F-48: AI 厂商连通性测试 (P0)
  - F-49: 多租户 AI 配置隔离 (P1)
  - F-50: API Key 轮换管理 (P1)

- 更新 RequirementsDoc.md 和 PRD.md:
  - 在技术架构概述中添加 AI 配置管理说明

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:31:29 +08:00

913 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AIProviderConfig.md - AI 厂商动态配置架构设计
| 文档类型 | **Technical Design (技术设计文档)** |
| --- | --- |
| **项目名称** | SmartAudit (AI 营销内容合规审核平台) |
| **版本号** | V1.0 |
| **日期** | 2026-02-02 |
| **侧重** | AI 厂商动态配置、多租户隔离、运行时热更新 |
---
## 版本历史 (Version History)
| 版本 | 日期 | 作者 | 变更说明 |
| --- | --- | --- | --- |
| V1.0 | 2026-02-02 | Claude | 初稿AI 厂商动态配置架构设计 |
---
## 1. 设计背景与目标
### 1.1 问题陈述
传统方案将 AI 模型的 API Key 和 Base URL 写死在环境变量中,存在以下问题:
1. **灵活性差:** 切换 AI 厂商需要修改环境变量并重启服务
2. **多租户困难:** 无法支持不同品牌方使用不同的 AI 厂商
3. **安全隐患:** 环境变量容易泄露,难以细粒度管理
4. **运维成本高:** 密钥轮换需要重新部署
### 1.2 设计目标
实现**商业 SaaS 级别的 AI 厂商动态配置系统**
| 目标 | 描述 |
| --- | --- |
| **动态配置** | 管理员在后台配置 AI 厂商,无需修改代码或重启服务 |
| **多厂商支持** | 支持 DeepSeek、OpenAI、阿里云、OneAPI 中转等多种厂商 |
| **多租户隔离** | 不同品牌方可配置独立的 AI 厂商和配额 |
| **热更新** | 配置变更即时生效,无需重启服务 |
| **安全存储** | API Key 加密存储,支持密钥轮换 |
| **故障转移** | 主厂商不可用时自动切换到备用厂商 |
---
## 2. 系统架构
### 2.1 架构概览
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 管理后台 (Admin Portal) │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ AI 厂商配置页面:添加/编辑/删除/测试连通性 │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ API 层 (FastAPI) │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ POST /admin/ai-providers - 创建 AI 厂商配置 │ │
│ │ GET /admin/ai-providers - 获取厂商列表 │ │
│ │ PUT /admin/ai-providers/{id} - 更新配置 │ │
│ │ POST /admin/ai-providers/{id}/test - 测试连通性 │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ AI 客户端工厂 (AIClientFactory) │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ • 根据配置动态创建 AI 客户端实例 │ │
│ │ • 支持连接池和客户端复用 │ │
│ │ • 配置变更时自动刷新客户端 │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ DeepSeek │ │ OpenAI │ │ OneAPI │
│ Client │ │ Client │ │ (中转) │
└─────────────┘ └─────────────┘ └─────────────┘
```
### 2.2 核心组件
| 组件 | 职责 |
| --- | --- |
| **AIProviderConfig** | 数据模型,存储厂商配置 |
| **AIClientFactory** | 工厂类,根据配置创建客户端 |
| **AIClientRegistry** | 注册表,缓存和管理客户端实例 |
| **ConfigWatcher** | 监听配置变更,触发客户端刷新 |
| **SecretsManager** | 加密存储和解密 API Key |
---
## 3. 数据模型设计
### 3.1 AI 厂商配置表 (ai_provider_configs)
```sql
CREATE TABLE ai_provider_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- 基础信息
name VARCHAR(100) NOT NULL, -- 配置名称,如 "生产环境 DeepSeek"
provider_type VARCHAR(50) NOT NULL, -- 厂商类型deepseek/openai/oneapi/aliyun/...
description TEXT, -- 配置说明
-- 连接配置
base_url VARCHAR(500) NOT NULL, -- API Base URL
api_key_encrypted BYTEA NOT NULL, -- 加密后的 API Key
-- 模型配置
default_model VARCHAR(100), -- 默认模型,如 "deepseek-chat"
available_models JSONB DEFAULT '[]', -- 可用模型列表
-- 能力标签
capabilities JSONB DEFAULT '[]', -- 支持的能力:["chat", "vision", "embedding"]
-- 使用场景
use_cases JSONB DEFAULT '[]', -- 适用场景:["brief_parsing", "script_review", "video_audit"]
-- 租户隔离
tenant_id UUID, -- 所属租户品牌方NULL 表示全局配置
-- 优先级与状态
priority INT DEFAULT 100, -- 优先级,数字越小优先级越高
is_enabled BOOLEAN DEFAULT true, -- 是否启用
is_default BOOLEAN DEFAULT false, -- 是否为默认配置
-- 限流配置
rate_limit_rpm INT DEFAULT 60, -- 每分钟请求限制
rate_limit_tpm INT DEFAULT 100000, -- 每分钟 Token 限制
-- 故障转移
fallback_provider_id UUID, -- 备用厂商配置 ID
-- 扩展配置
extra_config JSONB DEFAULT '{}', -- 厂商特定配置
-- 元数据
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
created_by UUID,
-- 约束
CONSTRAINT uk_tenant_default UNIQUE (tenant_id, is_default)
WHERE is_default = true
);
-- 索引
CREATE INDEX idx_provider_tenant ON ai_provider_configs(tenant_id);
CREATE INDEX idx_provider_type ON ai_provider_configs(provider_type);
CREATE INDEX idx_provider_enabled ON ai_provider_configs(is_enabled);
CREATE INDEX idx_provider_use_cases ON ai_provider_configs USING GIN(use_cases);
```
### 3.2 厂商类型枚举
```python
from enum import Enum
class AIProviderType(str, Enum):
"""支持的 AI 厂商类型"""
# 国内厂商
DEEPSEEK = "deepseek" # DeepSeek
QWEN = "qwen" # 阿里云通义千问
DOUBAO = "doubao" # 字节豆包
ZHIPU = "zhipu" # 智谱 GLM
BAICHUAN = "baichuan" # 百川
MOONSHOT = "moonshot" # Moonshot (Kimi)
# 海外厂商(需注意合规)
OPENAI = "openai" # OpenAI
ANTHROPIC = "anthropic" # Anthropic Claude
# 中转服务
ONEAPI = "oneapi" # OneAPI 中转
OPENROUTER = "openrouter" # OpenRouter
# 本地部署
OLLAMA = "ollama" # Ollama 本地
VLLM = "vllm" # vLLM 部署
# ASR/OCR 专用
ALIYUN_ASR = "aliyun_asr" # 阿里云 ASR
ALIYUN_OCR = "aliyun_ocr" # 阿里云 OCR
PADDLEOCR = "paddleocr" # PaddleOCR 本地
WHISPER = "whisper" # OpenAI Whisper
class AICapability(str, Enum):
"""AI 能力标签"""
CHAT = "chat" # 对话/文本生成
VISION = "vision" # 图像理解
EMBEDDING = "embedding" # 向量嵌入
ASR = "asr" # 语音识别
OCR = "ocr" # 文字识别
TTS = "tts" # 语音合成
class AIUseCase(str, Enum):
"""AI 使用场景"""
BRIEF_PARSING = "brief_parsing" # Brief 解析
SCRIPT_REVIEW = "script_review" # 脚本预审
VIDEO_AUDIT = "video_audit" # 视频审核
CONTEXT_CLASSIFICATION = "context_classification" # 语境分类
SENTIMENT_ANALYSIS = "sentiment_analysis" # 情感分析
LOGO_DETECTION = "logo_detection" # Logo 检测
ASR_TRANSCRIPTION = "asr_transcription" # 语音转写
OCR_EXTRACTION = "ocr_extraction" # 文字提取
```
### 3.3 使用日志表 (ai_usage_logs)
```sql
CREATE TABLE ai_usage_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
provider_id UUID NOT NULL REFERENCES ai_provider_configs(id),
tenant_id UUID,
-- 请求信息
use_case VARCHAR(50) NOT NULL,
model VARCHAR(100),
-- 用量统计
prompt_tokens INT DEFAULT 0,
completion_tokens INT DEFAULT 0,
total_tokens INT DEFAULT 0,
-- 性能指标
latency_ms INT,
status VARCHAR(20), -- success/error/timeout
error_message TEXT,
-- 时间
created_at TIMESTAMPTZ DEFAULT NOW(),
-- 分区键
created_date DATE DEFAULT CURRENT_DATE
) PARTITION BY RANGE (created_date);
-- 按月分区
CREATE TABLE ai_usage_logs_2026_02 PARTITION OF ai_usage_logs
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
```
---
## 4. 核心代码设计
### 4.1 配置模型 (Pydantic)
```python
# app/models/ai_provider.py
from pydantic import BaseModel, Field, SecretStr
from typing import Optional, List
from uuid import UUID
from datetime import datetime
from enum import Enum
class AIProviderCreate(BaseModel):
"""创建 AI 厂商配置请求"""
name: str = Field(..., max_length=100)
provider_type: AIProviderType
description: Optional[str] = None
base_url: str
api_key: SecretStr # 接收时为明文,存储时加密
default_model: Optional[str] = None
available_models: List[str] = []
capabilities: List[AICapability] = []
use_cases: List[AIUseCase] = []
tenant_id: Optional[UUID] = None
priority: int = 100
is_enabled: bool = True
is_default: bool = False
rate_limit_rpm: int = 60
rate_limit_tpm: int = 100000
fallback_provider_id: Optional[UUID] = None
extra_config: dict = {}
class AIProviderResponse(BaseModel):
"""AI 厂商配置响应"""
id: UUID
name: str
provider_type: AIProviderType
description: Optional[str]
base_url: str
# 注意:不返回 api_key
default_model: Optional[str]
available_models: List[str]
capabilities: List[AICapability]
use_cases: List[AIUseCase]
tenant_id: Optional[UUID]
priority: int
is_enabled: bool
is_default: bool
rate_limit_rpm: int
rate_limit_tpm: int
fallback_provider_id: Optional[UUID]
extra_config: dict
created_at: datetime
updated_at: datetime
```
### 4.2 AI 客户端工厂
```python
# app/services/ai/client_factory.py
from abc import ABC, abstractmethod
from typing import Dict, Optional, Type
from functools import lru_cache
import asyncio
from openai import AsyncOpenAI
from app.models.ai_provider import AIProviderType
from app.services.secrets_manager import SecretsManager
class BaseAIClient(ABC):
"""AI 客户端基类"""
def __init__(self, config: dict):
self.config = config
self.base_url = config["base_url"]
self.api_key = config["api_key"]
self.default_model = config.get("default_model")
@abstractmethod
async def chat(self, messages: list, model: str = None, **kwargs) -> dict:
"""对话接口"""
pass
@abstractmethod
async def health_check(self) -> bool:
"""健康检查"""
pass
class OpenAICompatibleClient(BaseAIClient):
"""OpenAI 兼容客户端 (适用于 DeepSeek, OneAPI, Moonshot 等)"""
def __init__(self, config: dict):
super().__init__(config)
self.client = AsyncOpenAI(
api_key=self.api_key,
base_url=self.base_url,
)
async def chat(self, messages: list, model: str = None, **kwargs) -> dict:
model = model or self.default_model
response = await self.client.chat.completions.create(
model=model,
messages=messages,
**kwargs
)
return {
"content": response.choices[0].message.content,
"usage": {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens,
},
"model": response.model,
}
async def health_check(self) -> bool:
try:
await self.client.models.list()
return True
except Exception:
return False
class AIClientFactory:
"""AI 客户端工厂"""
# 厂商类型到客户端类的映射
_client_classes: Dict[AIProviderType, Type[BaseAIClient]] = {
AIProviderType.DEEPSEEK: OpenAICompatibleClient,
AIProviderType.OPENAI: OpenAICompatibleClient,
AIProviderType.ONEAPI: OpenAICompatibleClient,
AIProviderType.QWEN: OpenAICompatibleClient,
AIProviderType.MOONSHOT: OpenAICompatibleClient,
AIProviderType.ZHIPU: OpenAICompatibleClient,
# 可扩展更多厂商...
}
def __init__(self, secrets_manager: SecretsManager):
self.secrets_manager = secrets_manager
self._client_cache: Dict[str, BaseAIClient] = {}
self._cache_lock = asyncio.Lock()
async def get_client(self, provider_config: dict) -> BaseAIClient:
"""获取或创建 AI 客户端"""
cache_key = f"{provider_config['id']}:{provider_config['updated_at']}"
if cache_key in self._client_cache:
return self._client_cache[cache_key]
async with self._cache_lock:
# 双重检查
if cache_key in self._client_cache:
return self._client_cache[cache_key]
# 解密 API Key
api_key = await self.secrets_manager.decrypt(
provider_config["api_key_encrypted"]
)
config = {
**provider_config,
"api_key": api_key,
}
# 创建客户端
provider_type = AIProviderType(provider_config["provider_type"])
client_class = self._client_classes.get(provider_type)
if not client_class:
raise ValueError(f"Unsupported provider type: {provider_type}")
client = client_class(config)
# 缓存客户端
self._client_cache[cache_key] = client
# 清理旧缓存
self._cleanup_old_cache(provider_config['id'])
return client
def _cleanup_old_cache(self, provider_id: str):
"""清理同一 provider 的旧缓存"""
keys_to_remove = [
k for k in self._client_cache.keys()
if k.startswith(f"{provider_id}:")
]
# 保留最新的一个
for key in keys_to_remove[:-1]:
del self._client_cache[key]
def invalidate_cache(self, provider_id: str = None):
"""使缓存失效"""
if provider_id:
keys_to_remove = [
k for k in self._client_cache.keys()
if k.startswith(f"{provider_id}:")
]
for key in keys_to_remove:
del self._client_cache[key]
else:
self._client_cache.clear()
```
### 4.3 AI 服务路由器
```python
# app/services/ai/router.py
from typing import Optional, List
from uuid import UUID
from app.models.ai_provider import AIUseCase, AICapability
from app.repositories.ai_provider_repo import AIProviderRepository
from app.services.ai.client_factory import AIClientFactory, BaseAIClient
class AIServiceRouter:
"""AI 服务路由器 - 根据场景选择合适的 AI 厂商"""
def __init__(
self,
provider_repo: AIProviderRepository,
client_factory: AIClientFactory,
):
self.provider_repo = provider_repo
self.client_factory = client_factory
async def get_client_for_use_case(
self,
use_case: AIUseCase,
tenant_id: Optional[UUID] = None,
required_capabilities: List[AICapability] = None,
) -> BaseAIClient:
"""
根据使用场景获取合适的 AI 客户端
优先级:
1. 租户专属配置 (tenant_id 匹配)
2. 全局默认配置 (tenant_id = NULL)
3. 按 priority 排序
"""
# 查询符合条件的配置
configs = await self.provider_repo.find_by_use_case(
use_case=use_case,
tenant_id=tenant_id,
capabilities=required_capabilities,
enabled_only=True,
)
if not configs:
raise ValueError(
f"No AI provider configured for use case: {use_case}"
)
# 选择优先级最高的配置
selected_config = configs[0]
# 创建并返回客户端
client = await self.client_factory.get_client(selected_config)
# 健康检查,失败则尝试备用
if not await client.health_check():
if selected_config.get("fallback_provider_id"):
fallback_config = await self.provider_repo.get_by_id(
selected_config["fallback_provider_id"]
)
if fallback_config:
client = await self.client_factory.get_client(fallback_config)
return client
async def chat(
self,
messages: list,
use_case: AIUseCase,
tenant_id: Optional[UUID] = None,
model: str = None,
**kwargs
) -> dict:
"""统一的对话接口"""
client = await self.get_client_for_use_case(
use_case=use_case,
tenant_id=tenant_id,
required_capabilities=[AICapability.CHAT],
)
return await client.chat(messages, model=model, **kwargs)
```
### 4.4 管理后台 API
```python
# app/api/v1/endpoints/admin/ai_providers.py
from fastapi import APIRouter, Depends, HTTPException, status
from typing import List, Optional
from uuid import UUID
from app.models.ai_provider import (
AIProviderCreate,
AIProviderUpdate,
AIProviderResponse,
)
from app.services.ai_provider_service import AIProviderService
from app.api.deps import get_current_admin_user
router = APIRouter()
@router.post("", response_model=AIProviderResponse, status_code=status.HTTP_201_CREATED)
async def create_ai_provider(
request: AIProviderCreate,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""创建 AI 厂商配置(仅管理员)"""
return await service.create(request, created_by=current_user.id)
@router.get("", response_model=List[AIProviderResponse])
async def list_ai_providers(
tenant_id: Optional[UUID] = None,
provider_type: Optional[str] = None,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""获取 AI 厂商配置列表"""
return await service.list(tenant_id=tenant_id, provider_type=provider_type)
@router.get("/{provider_id}", response_model=AIProviderResponse)
async def get_ai_provider(
provider_id: UUID,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""获取单个 AI 厂商配置"""
provider = await service.get_by_id(provider_id)
if not provider:
raise HTTPException(status_code=404, detail="Provider not found")
return provider
@router.put("/{provider_id}", response_model=AIProviderResponse)
async def update_ai_provider(
provider_id: UUID,
request: AIProviderUpdate,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""更新 AI 厂商配置"""
provider = await service.update(provider_id, request)
if not provider:
raise HTTPException(status_code=404, detail="Provider not found")
return provider
@router.delete("/{provider_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_ai_provider(
provider_id: UUID,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""删除 AI 厂商配置"""
success = await service.delete(provider_id)
if not success:
raise HTTPException(status_code=404, detail="Provider not found")
@router.post("/{provider_id}/test")
async def test_ai_provider(
provider_id: UUID,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""测试 AI 厂商连通性"""
result = await service.test_connection(provider_id)
return {
"success": result.success,
"latency_ms": result.latency_ms,
"error": result.error,
}
@router.post("/{provider_id}/rotate-key", response_model=AIProviderResponse)
async def rotate_api_key(
provider_id: UUID,
new_api_key: str,
service: AIProviderService = Depends(),
current_user = Depends(get_current_admin_user),
):
"""轮换 API Key"""
return await service.rotate_api_key(provider_id, new_api_key)
```
---
## 5. 安全设计
### 5.1 API Key 加密存储
```python
# app/services/secrets_manager.py
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import os
class SecretsManager:
"""密钥管理器 - 负责加密/解密敏感信息"""
def __init__(self, master_key: str):
"""
初始化密钥管理器
Args:
master_key: 主密钥,从安全存储(如 Vault、KMS获取
"""
# 从主密钥派生加密密钥
salt = os.environ.get("ENCRYPTION_SALT", "smartaudit").encode()
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(master_key.encode()))
self.fernet = Fernet(key)
async def encrypt(self, plaintext: str) -> bytes:
"""加密明文"""
return self.fernet.encrypt(plaintext.encode())
async def decrypt(self, ciphertext: bytes) -> str:
"""解密密文"""
return self.fernet.decrypt(ciphertext).decode()
```
### 5.2 权限控制
| 操作 | 系统管理员 | 品牌方管理员 | 代理商 | 达人 |
| --- | --- | --- | --- | --- |
| 创建全局配置 | ✅ | ❌ | ❌ | ❌ |
| 创建租户配置 | ✅ | ✅ (仅自己租户) | ❌ | ❌ |
| 查看配置列表 | ✅ (全部) | ✅ (仅自己租户) | ❌ | ❌ |
| 修改配置 | ✅ | ✅ (仅自己租户) | ❌ | ❌ |
| 删除配置 | ✅ | ✅ (仅自己租户) | ❌ | ❌ |
| 查看 API Key | ❌ | ❌ | ❌ | ❌ |
| 轮换 API Key | ✅ | ✅ (仅自己租户) | ❌ | ❌ |
---
## 6. 配置热更新
### 6.1 更新机制
```python
# app/services/ai/config_watcher.py
import asyncio
from datetime import datetime
from typing import Callable, List
from app.repositories.ai_provider_repo import AIProviderRepository
from app.services.ai.client_factory import AIClientFactory
class ConfigWatcher:
"""配置变更监听器"""
def __init__(
self,
provider_repo: AIProviderRepository,
client_factory: AIClientFactory,
poll_interval: int = 30, # 秒
):
self.provider_repo = provider_repo
self.client_factory = client_factory
self.poll_interval = poll_interval
self._last_check = datetime.min
self._running = False
self._callbacks: List[Callable] = []
def on_config_change(self, callback: Callable):
"""注册配置变更回调"""
self._callbacks.append(callback)
async def start(self):
"""启动监听"""
self._running = True
while self._running:
await self._check_for_changes()
await asyncio.sleep(self.poll_interval)
async def stop(self):
"""停止监听"""
self._running = False
async def _check_for_changes(self):
"""检查配置变更"""
changed_configs = await self.provider_repo.find_updated_since(
self._last_check
)
if changed_configs:
self._last_check = datetime.utcnow()
# 使相关缓存失效
for config in changed_configs:
self.client_factory.invalidate_cache(config["id"])
# 触发回调
for callback in self._callbacks:
await callback(changed_configs)
```
### 6.2 应用启动集成
```python
# app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.services.ai.config_watcher import ConfigWatcher
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时
config_watcher = ConfigWatcher(
provider_repo=app.state.provider_repo,
client_factory=app.state.client_factory,
)
asyncio.create_task(config_watcher.start())
yield
# 关闭时
await config_watcher.stop()
app = FastAPI(lifespan=lifespan)
```
---
## 7. 使用示例
### 7.1 在业务代码中使用
```python
# app/services/brief_parser.py
from app.services.ai.router import AIServiceRouter
from app.models.ai_provider import AIUseCase
class BriefParserService:
"""Brief 解析服务"""
def __init__(self, ai_router: AIServiceRouter):
self.ai_router = ai_router
async def parse_brief(self, content: str, tenant_id: UUID = None) -> dict:
"""解析 Brief 文档"""
messages = [
{"role": "system", "content": "你是一个专业的 Brief 解析助手..."},
{"role": "user", "content": f"请解析以下 Brief 内容:\n{content}"},
]
# 自动选择合适的 AI 厂商
result = await self.ai_router.chat(
messages=messages,
use_case=AIUseCase.BRIEF_PARSING,
tenant_id=tenant_id,
)
return self._parse_response(result["content"])
```
### 7.2 管理员配置流程
```
1. 管理员登录后台
2. 进入「系统设置 → AI 厂商管理」
3. 点击「添加厂商」
4. 填写配置:
- 名称:生产环境 DeepSeek
- 厂商类型DeepSeek
- Base URLhttps://api.deepseek.com/v1
- API Keysk-xxx
- 默认模型deepseek-chat
- 适用场景Brief 解析、脚本预审
- 优先级10
5. 点击「测试连通性」
6. 保存配置
7. 配置立即生效,无需重启服务
```
---
## 8. 监控与告警
### 8.1 监控指标
| 指标 | 说明 | 告警阈值 |
| --- | --- | --- |
| `ai_request_total` | AI 请求总数 | - |
| `ai_request_latency_p99` | P99 延迟 | > 10s |
| `ai_request_error_rate` | 错误率 | > 5% |
| `ai_token_usage_total` | Token 使用量 | 接近配额 80% |
| `ai_provider_health` | 厂商健康状态 | 连续失败 > 3 次 |
### 8.2 告警规则
```yaml
# prometheus/alerts/ai_provider.yml
groups:
- name: ai_provider
rules:
- alert: AIProviderHighErrorRate
expr: rate(ai_request_errors_total[5m]) / rate(ai_request_total[5m]) > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "AI 厂商 {{ $labels.provider }} 错误率过高"
- alert: AIProviderDown
expr: ai_provider_health == 0
for: 1m
labels:
severity: critical
annotations:
summary: "AI 厂商 {{ $labels.provider }} 不可用"
```
---
## 9. 相关文档
| 文档 | 说明 |
| --- | --- |
| DevelopmentPlan.md | 开发计划(已更新 AI 配置章节) |
| RequirementsDoc.md | 需求文档 |
| FeatureSummary.md | 功能清单 |
| API 接口规范 | 待编写 |