Files
012-kaopeilian/docs/规划/瑞小美AI接入规范.md
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

605 lines
19 KiB
Markdown
Raw Permalink 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.
# 瑞小美 AI 接入规范
> 适用于瑞小美全团队所有 AI 相关项目
> **最后更新**2026-01-20
---
## 核心原则
| 原则 | 要求 |
|------|------|
| **优先最强** | 所有 AI 任务**默认使用 Claude Opus 4.5**,失败后自动降级 |
| **智能降级** | Claude → Gemini Pro → Gemini Flash每级重试 2 次) |
| **多 Key 策略** | **通用 Key**Gemini/DeepSeek+ **Anthropic Key**Claude 专属) |
| **服务商策略** | **首选 4sapi.com → 备选 OpenRouter.ai**(自动降级) |
| **统一配置** | 从**门户系统**统一获取 Key各模块**禁止独立配置** |
| **统一服务** | 通过 `shared_backend.AIService` 调用,禁止直接请求 API |
### 瑞小美 SCRM 配置入口
- **配置管理**https://scrm.ireborn.com.cn → AI 配置
- **调用统计**:查看各模块 Token 使用量、成本、服务商分布
- **调用日志**:按模块、服务商、状态筛选历史调用
---
## 服务商配置
### 降级策略(强制)
```
请求流程4sapi.com → (失败) → OpenRouter.ai
```
| 优先级 | 服务商 | API 地址 | 说明 |
|--------|--------|----------|------|
| **1首选** | 4sapi.com | `https://4sapi.com/v1/chat/completions` | 国内优化,延迟低 |
| **2备选** | OpenRouter.ai | `https://openrouter.ai/api/v1/chat/completions` | 模型全,稳定性好 |
**降级触发条件**(宽松策略,首选失败就尝试备选):
- 连接超时(默认 30s
- 服务端错误5xx
- 客户端错误4xx余额不足、Key 无效、模型不存在等
- 网络异常
> ✅ **说明**:只要首选服务商调用失败,就会自动尝试备选服务商
### 4sapi.com 配置
**API 端点**
```
https://4sapi.com/v1/chat/completions
```
**测试阶段 Key**(仅限开发环境):
```
sk-9yMCXjRGANbacz20kJY8doSNy6Rf446aYwmgGIuIXQ7DAyBw
```
> ⚠️ **注意**
> - 测试 Key 仅用于开发调试,正式环境 Key 在门户后台配置
> - 通用 Key 不能调用 Anthropic 模型,生产环境需配置 Anthropic 专属 Key
**官方文档**
- [图片生成](https://4sapi.apifox.cn/359535008e0)
- [图片修改](https://4sapi.apifox.cn/359535009e0)
- [音频理解](https://4sapi.apifox.cn/359535011e0)
- [视频理解](https://4sapi.apifox.cn/359535012e0)
- [文档理解](https://4sapi.apifox.cn/359535013e0)
- [TTS 语音合成](https://4sapi.apifox.cn/382937873e0)
- [语音转文字](https://4sapi.apifox.cn/382936341e0)
- [Embeddings](https://4sapi.apifox.cn/359535014e0)
### OpenRouter.ai 配置(备选)
**API 端点**
```
https://openrouter.ai/api/v1/chat/completions
```
**测试阶段 Key**(仅限开发环境):
```
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
```
**官方文档**[Images](https://openrouter.ai/docs/guides/overview/multimodal/images) | [PDFs](https://openrouter.ai/docs/guides/overview/multimodal/pdfs) | [Audio](https://openrouter.ai/docs/guides/overview/multimodal/audio) | [Videos](https://openrouter.ai/docs/guides/overview/multimodal/videos)
---
## Key 管理规范
### ⚠️ 强制要求
1. **禁止**在代码中硬编码 API Key
2. **必须**从门户系统统一获取配置
3. **必须**同时配置两个服务商的 Key支持降级
> 测试阶段 Key 见上方「服务商配置」章节
### 配置架构(瑞小美 SCRM
```
┌───────────────────────────────────────────────────────────────┐
│ 门户系统 (scrm.ireborn.com.cn) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ AI 配置页面(仅超管可访问) │ │
│ │ - 首选服务商4sapi.comAPI Key + Base URL │ │
│ │ - 备选服务商OpenRouterAPI Key + Base URL │ │
│ │ - 默认模型 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ GET /api/ai/internal/config (内部 API无需鉴权
└──────────────────────────────┬────────────────────────────────┘
┌───────────────────────┼───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 会话存档 │ │ 智能回复 │ │ 撩回搭子 │
│ AIService │ │ AIService │ │ AIService │
└─────────────┘ └─────────────┘ └─────────────┘
```
### 配置 API门户系统已实现
**端点**`GET http://portal-backend:8000/api/ai/internal/config`
**返回格式**
```json
{
"code": 0,
"data": {
"primary": {
"provider": "4sapi",
"api_key": "sk-xxx...",
"base_url": "https://4sapi.com/v1"
},
"fallback": {
"provider": "openrouter",
"api_key": "sk-or-v1-xxx...",
"base_url": "https://openrouter.ai/api/v1"
},
"anthropic_api_key": "sk-xxx...",
"models": {
"primary": "claude-opus-4-5-20251101-thinking",
"standard": "gemini-3-pro-preview",
"fast": "gemini-3-flash-preview",
"image": "gemini-2.5-flash-image-preview",
"video": "veo3.1-pro"
}
}
}
```
**说明**
- 各模块通过 Docker 内网访问 `portal-backend:8000`
- 配置有 **5 分钟缓存**,避免频繁调用
- 如需自定义端点,设置环境变量:`PORTAL_CONFIG_API=http://...`
---
## 支持的能力
| 能力 | 方法 | 说明 |
|------|------|------|
| 文本聊天 | `chat()` | 基础对话,支持多轮 |
| 图片理解 | `vision()` | PNG/JPEG/WebP/GIF |
| PDF 分析 | `analyze_pdf()` | 文档理解、OCR |
| 音频分析 | `analyze_audio()` | 语音转文字 |
| 视频分析 | `analyze_video()` | 视频内容理解 |
| 图像生成 | `generate_image()` | 文生图 |
| 流式输出 | `chat_stream()` | 逐字返回 |
> 官方文档见上方「服务商配置」章节
---
## 模型策略(智能降级)
### 核心原则:优先使用最强模型
**所有 AI 调用默认使用 Claude Opus 4.5,失败后在 4sapi 内部降级4sapi 全部失败才切换 OpenRouter**
```
┌─────────────────────────────────────────────────────────────┐
│ 4sapi (首选服务商) │
│ Claude → 2次失败 → Gemini Pro → 2次失败 → Gemini Flash │
└─────────────────────────────────────────────────────────────┘
↓ 全部失败
┌─────────────────────────────────────────────────────────────┐
│ OpenRouter (备选服务商) - 不支持 Claude │
│ Gemini Pro → 2次失败 → Gemini Flash │
└─────────────────────────────────────────────────────────────┘
```
### 降级触发条件
| 条件 | 说明 |
|------|------|
| **首字超时** | 流式输出 **10 秒**内没有收到首字 |
| **请求失败** | 网络错误、API 错误、余额不足等 |
| **重试次数** | 每个模型最多 **2 次**,然后降级到下一个模型 |
| **服务商切换** | **4sapi 全部模型都失败后**才切换到 OpenRouter |
### 模型配置
| 等级 | 模型 | 4sapi | OpenRouter | 说明 |
|------|-----|-------|------------|------|
| 🥇 **首选** | `claude-opus-4-5-20251101-thinking` | ✅ | ❌ 不支持 | 所有任务首先尝试 |
| 🥈 **标准** | `gemini-3-pro-preview` | ✅ | ✅ | Claude 失败后降级 |
| 🥉 **快速** | `gemini-3-flash-preview` | ✅ | ✅ | 最终保底 |
| 🖼️ **生图** | `gemini-2.5-flash-image-preview` | ✅ | ✅ | 图像生成(不参与降级) |
| 🎬 **视频** | `veo3.1-pro` | ✅ | - | 视频生成(不参与降级) |
> ✅ 已验证可用2026-01-20
### 代码中使用
```python
from shared_backend.services.ai_service import (
MODEL_PRIMARY, # Claude Opus 4.5(默认)
MODEL_STANDARD, # Gemini 3 Pro
MODEL_FAST, # Gemini 3 Flash
MODEL_IMAGE, # 生图
MODEL_VIDEO, # 视频
DEFAULT_MODEL, # = MODEL_PRIMARY
)
# 默认调用(自动智能降级)
response = await ai.chat(messages, prompt_name="analysis")
# 4sapi: Claude → Gemini Pro → Gemini Flash
# 全部失败 → OpenRouter: Gemini Pro → Gemini Flash
# 指定从某个等级开始降级
response = await ai.chat(messages, model=MODEL_STANDARD, prompt_name="reply")
# 从 Gemini Pro 开始
# 禁用智能降级(只做简单服务商降级)
response = await ai.chat(messages, model=MODEL_FAST, auto_fallback=False, prompt_name="quick")
# 流式输出(同样支持智能降级 + 首字超时检测)
async for chunk in ai.chat_stream(messages, prompt_name="stream"):
print(chunk, end="", flush=True)
```
### 降级日志示例
```
[archive] 4sapi claude-opus-4-5-20251101-thinking 第1次失败: timeout
[archive] 4sapi claude-opus-4-5-20251101-thinking 第2次失败: timeout
[archive] 4sapi claude-opus-4-5-20251101-thinking 失败2次降级
[archive] 4sapi gemini-3-pro-preview 第1次失败: 502
[archive] 4sapi gemini-3-pro-preview 第2次失败: 502
[archive] 4sapi gemini-3-pro-preview 失败2次降级
[archive] 4sapi gemini-3-flash-preview 第1次失败: 502
[archive] 4sapi gemini-3-flash-preview 第2次失败: 502
[archive] 4sapi 全部失败,切换到 OpenRouter
[archive] OpenRouter google/gemini-3-pro-preview 调用成功
```
---
## 调用示例
### 基础用法
```python
from shared_backend.services.ai_service import AIService
# module_code 标识你的模块,用于统计
ai = AIService(module_code="your_module", db_session=db)
# Key 自动从系统后台获取,无需手动指定
response = await ai.chat(
messages=[
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"}
],
prompt_name="greeting" # 必填,用于调用统计
)
print(response.content)
```
### 图片理解
```python
response = await ai.vision(
prompt="描述这张图片",
images=["https://example.com/image.jpg"], # URL / base64 / bytes
prompt_name="image_analysis"
)
```
### PDF 分析
```python
response = await ai.analyze_pdf(
prompt="总结要点",
pdf="https://example.com/doc.pdf",
pdf_engine="pdf-text", # 免费 | "mistral-ocr" 收费
prompt_name="pdf_summary"
)
```
### 音频/视频
```python
# 音频
response = await ai.analyze_audio(
prompt="转录并总结", audio=audio_bytes, mime_type="audio/mp3"
)
# 视频
response = await ai.analyze_video(
prompt="描述内容", video="https://example.com/video.mp4"
)
```
### 图像生成
```python
response = await ai.generate_image(
prompt="一只橘猫",
model=MODEL_IMAGE, # 图像生成专用模型
prompt_name="cat_gen"
)
for img in response.images:
print(img)
```
### 流式输出
```python
async for chunk in ai.chat_stream(messages, prompt_name="stream_test"):
print(chunk, end="", flush=True)
```
---
## 多模态消息格式
```python
# 图片
{"type": "image_url", "image_url": {"url": "https://..." or "data:image/jpeg;base64,..."}}
# PDF
{"type": "file", "file": {"filename": "doc.pdf", "file_data": "..."}}
# 音频
{"type": "input_audio", "input_audio": {"url": "..."}}
# 视频
{"type": "input_video", "input_video": {"url": "..."}}
```
---
## 工具函数
```python
from shared_backend.services.ai_service import (
file_to_base64, # 文件转 base64
make_data_url, # 构建 data URL
get_mime_type, # 获取 MIME 类型
)
```
---
## 返回结构
所有调用返回 `AIResponse` 对象(对服务商原始响应的统一封装):
```python
@dataclass
class AIResponse:
content: str # ← choices[0].message.content
model: str # ← model
provider: str # ← 实际使用的服务商4sapi / openrouter
input_tokens: int # ← usage.prompt_tokens
output_tokens: int # ← usage.completion_tokens
total_tokens: int # ← 计算值
cost: float # ← usage.total_cost如有
latency_ms: int # ← 本地计算
raw_response: dict # ← 完整原始响应
images: List[str] # ← 图像生成结果
annotations: dict # ← PDF 解析注释
```
**使用示例**
```python
response = await ai.chat(messages, prompt_name="test")
print(response.content) # AI 回复
print(response.provider) # 实际服务商4sapi / openrouter
print(response.total_tokens) # 消耗 token
print(response.cost) # 费用(美元)
print(response.latency_ms) # 延迟(毫秒)
# 需要原始响应时
print(response.raw_response) # 服务商完整返回
```
---
## 提示词规范
### 文件位置(强制)
```
{模块}/后端服务/prompts/{功能名}_prompts.py
```
### 文件结构(强制)
```python
"""功能描述"""
PROMPT_META = {
"name": "policy_analysis", # 唯一标识,用于统计
"display_name": "政策解读", # 后台显示名称
"description": "解析政策文档", # 功能描述
"module": "your_module", # 所属模块
"variables": ["content"], # 变量列表
}
SYSTEM_PROMPT = """你是专业分析师..."""
USER_PROMPT = """请分析:{content}"""
```
### 元数据自动注册(可视化)
`PROMPT_META` 会**自动注册到数据库**,实现后台可视化管理:
```python
# 模块启动时扫描并注册
from shared_backend.services.ai_service import scan_and_register_prompts
scan_and_register_prompts(
module_path="/path/to/your_module",
module_code="your_module"
)
```
**注册流程**
```
prompts/*_prompts.py → PROMPT_META → ai_prompts 表 → 后台可视化
```
**后台功能**
- 查看所有已注册的提示词
- 按模块筛选
- 查看变量定义
- 点击"同步"手动刷新
> 提示词**内容**由开发维护Git 版本控制),后台**仅展示元数据**,不支持在线编辑
---
## 调用日志与统计
### ⚠️ 强制要求
**必须传入 `db_session`** 才能记录调用日志到 `ai_call_logs` 表:
```python
# ❌ 错误:无法记录日志,统计页面无数据
ai = AIService(module_code="my_module")
# ✅ 正确:日志会写入数据库
ai = AIService(module_code="my_module", db_session=db)
```
### 独立模块配置
如果模块运行在独立容器中,无法直接获取数据库会话,需配置环境变量:
```bash
# docker-compose.yml
environment:
- DATABASE_URL=mysql+pymysql://user:pass@scrm-mysql:3306/scrm_content?charset=utf8mb4
```
然后在代码中自动创建会话:
```python
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import os
def get_db_session():
database_url = os.getenv("DATABASE_URL")
if not database_url:
return None
engine = create_engine(database_url, pool_pre_ping=True)
Session = sessionmaker(bind=engine)
return Session()
# 使用
db = get_db_session()
ai = AIService(module_code="my_module", db_session=db)
```
### 查看统计
**入口**https://scrm.ireborn.com.cn → AI 配置 → 调用统计
**统计维度**
- 按模块各模块调用次数、Token 消耗、成本
- 按服务商4sapi / OpenRouter 使用分布(观察降级频率)
- 按日期:调用趋势图
**自动记录字段**`ai_call_logs` 表):
| 字段 | 说明 |
|------|------|
| `module_code` | 模块标识 |
| `prompt_name` | 提示词名称 |
| `provider` | **实际使用的服务商**4sapi / openrouter |
| `model` | 使用的模型 |
| `input_tokens` / `output_tokens` | Token 消耗 |
| `cost` | 费用(美元) |
| `latency_ms` | 响应延迟 |
| `status` | 调用状态success / error |
| `error_message` | 错误信息(失败时) |
| `created_at` | 调用时间 |
**降级监控**:通过 `provider` 字段筛选,可观察降级发生频率,评估首选服务商稳定性
---
## AI 响应解析规范2026-01-18 新增)
### 公共解析函数(强制使用)
各模块解析 AI JSON 响应时,**必须**使用公共函数,禁止自行编写解析逻辑:
```python
from shared_backend.services.ai_service import parse_ai_json_response, safe_parse_ai_json
# 方式1会抛异常需 try-catch
try:
result, thinking = parse_ai_json_response(ai_response.content)
except json.JSONDecodeError:
# 处理解析失败
pass
# 方式2不抛异常返回默认值
result, thinking = safe_parse_ai_json(ai_response.content, default={"status": "error"})
```
### 处理能力
| 输入格式 | 示例 | 能否处理 |
|---------|------|---------|
| thinking 标签 | `<thinking>分析中...</thinking>{"key": "value"}` | ✅ |
| JSON 代码块 | ` ```json {"key": "value"} ``` ` | ✅ |
| 普通代码块 | ` ``` {"key": "value"} ``` ` | ✅ |
| 混合文本 | `分析结果如下:{"key": "value"}` | ✅ |
| 纯 JSON | `{"key": "value"}` | ✅ |
### 返回值
```python
result, thinking = parse_ai_json_response(content)
# result: dict - 解析后的 JSON 对象
# thinking: str - thinking 标签中的内容(可用于调试/展示)
```
---
## 检查清单
### 配置检查(门户系统)
- [ ] 配置 **4sapi.com 通用 Key**(用于 Gemini/DeepSeek 等)
- [ ] 配置 **Anthropic 专属 Key**(用于 Claude 模型)
- [ ] 配置 **OpenRouter API Key**(备选服务商)
- [ ] 配置**按用途的模型**(测试/分析/创意/生图/视频)
- [ ] 确认所有 Key 都有效(门户页面显示"已配置"
### 代码检查(各模块)
- [ ] 使用 `shared_backend.AIService`,未直接调用 API
- [ ] 未硬编码 API Key
- [ ] 创建 `prompts/{功能}_prompts.py`
- [ ] 包含 `PROMPT_META`name, display_name, module, variables
- [ ] 调用时传入 `prompt_name`(用于统计)
- [ ] 初始化时传入 `db_session`(记录日志)
### 验证
```bash
# 检查门户配置 API 是否可访问
curl http://portal-backend:8000/api/ai/internal/config
# 检查 AI 调用日志是否记录
SELECT * FROM ai_call_logs ORDER BY created_at DESC LIMIT 10;
```
---
*瑞小美 AI 团队 · 2026-01-20*