33 KiB
33 KiB
GEO 平台技术详细设计文档
文档版本: v1.0 创建日期: 2026-05-01 状态: 待评审 依据文档:
目录
1. 系统架构设计
1.1 整体架构
┌─────────────────────────────────────────────────────────────────┐
│ 用户层 (Client) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web App │ │ Mobile Web │ │ API │ │
│ │ (Next.js) │ │ (响应式) │ │ (REST) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 网关层 (Gateway) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Nginx / API Gateway │ │
│ │ - 负载均衡 - SSL终止 - 限流 - 认证 │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 前端服务 │ │ 后端服务 │ │
│ │ (Next.js) │ │ (FastAPI) │ │
│ │ Port: 3000 │ │ Port: 8000 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │
│ ┌──────────┴──────────┐ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Worker 1 │ │ Worker 2 │ │
│ │ (Scheduler) │ │ (Scheduler) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 数据层 (Data) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ Redis │ │ MinIO │ │
│ │ 主数据库 │ │ 缓存/队列 │ │ 文件存储 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 外部服务 (External) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ LLM APIs │ │ Email API │ │ 短信 API │ │
│ │ (DeepSeek等) │ │ (SendGrid) │ │ (阿里云) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.2 技术栈总览
| 层级 | 技术选型 | 版本 | 说明 |
|---|---|---|---|
| 前端框架 | Next.js | 14.x | App Router, React 18 |
| UI组件 | shadcn/ui | latest | Radix UI + TailwindCSS |
| 图表库 | Recharts | 2.x | 数据可视化 |
| 后端框架 | FastAPI | 0.109+ | 异步高性能 |
| ORM | SQLAlchemy | 2.0+ | 异步ORM |
| 数据库 | PostgreSQL | 15+ | 主数据存储 |
| 缓存 | Redis | 7+ | 缓存/队列 |
| 任务调度 | APScheduler | 3.10+ | 定时任务 |
| LLM SDK | OpenAI SDK | 1.x | 统一LLM调用 |
| 文档 | Alembic | - | 数据库迁移 |
1.3 模块架构
backend/
├── app/
│ ├── api/ # API路由层
│ │ ├── v1/
│ │ │ ├── auth.py # 认证接口
│ │ │ ├── brands.py # 品牌接口
│ │ │ ├── competitors.py # 竞品接口
│ │ │ ├── citations.py # 引用接口
│ │ │ ├── reports.py # 报告接口
│ │ │ └── admin.py # 管理接口
│ │ └── deps.py # 依赖注入
│ │
│ ├── core/ # 核心模块
│ │ ├── config.py # 配置管理
│ │ ├── security.py # 安全认证
│ │ └── exceptions.py # 异常定义
│ │
│ ├── models/ # 数据模型
│ │ ├── user.py
│ │ ├── brand.py
│ │ ├── competitor.py
│ │ ├── citation.py
│ │ └── query_task.py
│ │
│ ├── schemas/ # Pydantic模型
│ │ ├── auth.py
│ │ ├── brand.py
│ │ ├── citation.py
│ │ └── report.py
│ │
│ ├── services/ # 业务逻辑层
│ │ ├── auth_service.py
│ │ ├── brand_service.py
│ │ ├── citation_service.py
│ │ ├── scoring_service.py # 评分计算
│ │ └── report_service.py
│ │
│ ├── workers/ # 后台任务
│ │ ├── scheduler.py # 任务调度
│ │ ├── citation_engine.py # 引用检测引擎
│ │ ├── adapters/ # 平台适配器
│ │ │ ├── base.py # 基类
│ │ │ ├── llm_adapter.py # LLM适配器
│ │ │ └── platforms/ # 各平台实现
│ │ └── cache.py # 缓存管理
│ │
│ ├── db/ # 数据库
│ │ ├── session.py
│ │ └── repositories/
│ │
│ └── main.py # 应用入口
│
├── alembic/ # 数据库迁移
│ └── versions/
│
├── tests/ # 测试
│
└── requirements.txt
2. 核心模块设计
2.1 引用检测引擎 (CitationEngine)
模块职责: 统一管理品牌引用检测的核心业务逻辑
设计原则:
- 单一职责: 仅负责引用检测流程编排
- 开闭原则: 新增平台只需实现适配器
- 依赖倒置: 通过抽象接口调用平台
核心类图:
# 伪代码描述,实际代码见实现文件
class CitationEngine:
"""引用检测引擎核心类"""
def __init__(self):
self.llm_adapter: LLMAdapter # LLM调用适配器
self.platform_adapters: dict # 平台适配器映射
self.cache: CacheService # 缓存服务
self.scoring_service: ScoringService # 评分服务
async def query_brand(
self,
keyword: str,
brand: Brand,
platforms: list[str]
) -> QueryResult:
"""执行品牌查询"""
pass
async def calculate_score(
self,
citations: list[Citation]
) -> BrandScore:
"""计算品牌评分"""
pass
核心流程:
query_brand 流程:
1. 检查缓存
└── 命中 → 返回缓存结果
2. 构建查询任务
3. 遍历平台适配器
├── LLMAdapter.query() # 主路径
└── PlaywrightAdapter.query() # 兜底
4. 解析响应
5. 品牌匹配检测
6. 竞品识别
7. 计算评分
8. 存储结果
9. 更新缓存
10. 返回结果
2.2 LLM适配器 (LLMAdapter)
模块职责: 统一封装LLM API调用
LLM API定价依据 (来源: DeepSeek官方API文档 [1], OpenAI官方 [2]):
| LLM | 输入价格 | 输出价格 | 单次成本估算 |
|---|---|---|---|
| DeepSeek-V3 | ¥0.002/千tokens | ¥0.008/千tokens | ¥0.006/次 |
| GPT-4o-mini | ¥0.001/千tokens | ¥0.004/千tokens | ¥0.003/次 |
单次成本计算依据:
- 输入Prompt: ~1000 tokens (品牌名、别名、查询词)
- 输出响应: ~500 tokens (JSON格式检测结果)
- DeepSeek-V3: 1000×¥0.002 + 500×¥0.008 = ¥0.006
- GPT-4o-mini: 1000×¥0.001 + 500×¥0.004 = ¥0.003
成本优化建议:
- 使用DeepSeek-V3作为主LLM (成本最低)
- 启用Prompt缓存进一步降低成本 (~50%节省)
- 批量查询合并请求减少API调用次数
Prompt模板设计:
# 品牌引用检测Prompt
BRAND_CITATION_PROMPT = """
你是一个专业的品牌分析助手。请分析以下查询中是否提到了目标品牌。
查询关键词: {keyword}
目标品牌: {brand_name}
品牌别名: {brand_aliases}
请分析AI平台对上述品牌的提及情况,并返回JSON格式的结果:
{{
"cited": true/false, // 是否被提及
"position": 1, // 提及位置(1-based)
"citation_text": "...", // 引用文本片段(最多200字)
"sentiment": "positive/neutral/negative", // 情感倾向
"confidence": 0.95 // 置信度
}}
请仅返回JSON,不要有其他内容。
"""
错误处理策略:
| 错误类型 | 处理策略 | 重试次数 |
|---|---|---|
| API超时 | 重试 | 3次 |
| 配额超限 | 切换LLM/排队 | - |
| 响应格式错误 | 使用备用Parser | 1次 |
| 网络错误 | 重试 | 3次 |
2.3 平台适配器 (PlatformAdapter)
模块职责: 封装各AI平台的查询接口
适配器结构:
class BasePlatformAdapter(ABC):
"""平台适配器基类"""
platform_name: str
platform_code: str
api_endpoint: str
@abstractmethod
async def query(
self,
keyword: str,
context: QueryContext
) -> PlatformResponse:
"""执行平台查询"""
pass
def build_prompt(
self,
keyword: str,
brand_name: str,
brand_aliases: list[str]
) -> str:
"""构建查询Prompt"""
return BRAND_CITATION_PROMPT.format(
keyword=keyword,
brand_name=brand_name,
brand_aliases=", ".join(brand_aliases)
)
class WenxinAdapter(BasePlatformAdapter):
"""文心一言适配器"""
platform_name = "文心一言"
platform_code = "wenxin"
api_endpoint = "https://api.deepseek.com/v1/chat/completions"
async def query(self, keyword, context):
# 实现文心一言特定逻辑
pass
支持的平台:
| 平台 | 代码 | API类型 | 备注 |
|---|---|---|---|
| 文心一言 | wenxin | LLM API | 优先 |
| Kimi | kimi | LLM API | 优先 |
| 通义千问 | tongyi | LLM API | 优先 |
| 豆包 | doubao | LLM API | 优先 |
| 讯飞星火 | xinghuo | LLM API | 优先 |
| 天工 | tiangong | LLM API | 优先 |
| 清言 | qingyan | LLM API | 优先 |
2.4 评分服务 (ScoringService)
模块职责: 计算品牌AI曝光度评分
评分算法 (依据 Visibili.ai模型):
class ScoringService:
"""评分计算服务"""
WEIGHTS = {
"mention_rate": 0.30, # 提及率权重
"sov": 0.40, # SOV权重
"quality": 0.30, # 引用质量权重
}
async def calculate_brand_score(
self,
brand_id: UUID,
time_range: TimeRange
) -> BrandScore:
"""计算品牌综合评分"""
# 1. 获取引用数据
citations = await self.citation_repo.get_by_brand_and_time(
brand_id, time_range
)
# 2. 计算提及率
mention_rate = self._calc_mention_rate(citations)
# 3. 计算SOV
sov = await self._calc_sov(brand_id, citations)
# 4. 计算引用质量
quality = self._calc_quality(citations)
# 5. 综合评分 (加权求和后取上限100)
overall = min(100, (
mention_rate * self.WEIGHTS["mention_rate"] +
sov * self.WEIGHTS["sov"] +
quality * self.WEIGHTS["quality"]
))
return BrandScore(
overall=round(overall, 1),
mention_rate=mention_rate,
sov=sov,
quality=quality,
platform_breakdown=..., # 平台分别评分
trend=..., # 趋势变化
)
def _calc_mention_rate(self, citations: list[Citation]) -> float:
"""计算提及率 = 被引用次数 / 总查询次数"""
if not citations:
return 0.0
cited_count = sum(1 for c in citations if c.cited)
return (cited_count / len(citations)) * 100
def _calc_sov(self, brand_id: UUID, citations: list[Citation]) -> float:
"""计算SOV = 品牌引用次数 / (品牌+竞品引用次数)"""
# 获取竞品引用数据
competitor_citations = await self._get_competitor_citations(brand_id)
total_citations = len(citations) + len(competitor_citations)
if total_citations == 0:
return 0.0
return (len(citations) / total_citations) * 100
def _calc_quality(self, citations: list[Citation]) -> float:
"""计算引用质量"""
if not citations:
return 0.0
total_quality = 0.0
for c in citations:
# 情感系数
sentiment_score = {"positive": 1.0, "neutral": 0.6, "negative": 0.2}.get(c.sentiment, 0.5)
# 位置系数
position_score = self._get_position_score(c.position)
# 长度系数
length_score = self._get_length_score(c.citation_text)
quality = sentiment_score * 0.4 + position_score * 0.3 + length_score * 0.3
total_quality += quality
return (total_quality / len(citations)) * 100
def _get_position_score(self, position: int) -> float:
"""获取位置系数"""
if position == 1:
return 1.0
elif position <= 3:
return 0.8
elif position <= 5:
return 0.6
else:
return 0.4
def _get_length_score(self, text: str) -> float:
"""获取引用长度系数"""
if not text:
return 0.5
length = len(text)
if length > 100:
return 1.0
elif length >= 50:
return 0.8
else:
return 0.6
2.5 缓存服务 (CacheService)
模块职责: 管理Redis缓存
缓存策略:
| 缓存键 | 数据 | TTL | 说明 |
|---|---|---|---|
| brand:{id}:score | 品牌评分 | 5min | 实时性要求高 |
| brand:{id}:stats | 统计数据 | 5min | 聚合数据 |
| platform:config | 平台配置 | 1h | 相对稳定 |
| user:{id}:session | 会话信息 | 24h | 用户会话 |
| rate_limit:{id} | 限流计数 | 1min | 滑动窗口 |
缓存操作示例:
class CacheService:
"""缓存服务"""
def __init__(self, redis: Redis):
self.redis = redis
async def get_brand_score(self, brand_id: UUID) -> Optional[BrandScore]:
"""获取品牌评分缓存"""
key = f"brand:{brand_id}:score"
data = await self.redis.get(key)
if data:
return BrandScore.parse_raw(data)
return None
async def set_brand_score(
self,
brand_id: UUID,
score: BrandScore,
ttl: int = 300
):
"""设置品牌评分缓存"""
key = f"brand:{brand_id}:score"
await self.redis.setex(key, ttl, score.json())
async def invalidate_brand(self, brand_id: UUID):
"""失效品牌相关缓存"""
pattern = f"brand:{brand_id}:*"
async for key in self.redis.scan_iter(match=pattern):
await self.redis.delete(key)
3. 接口设计
3.1 API设计规范
版本控制: URL路径包含版本号 /api/v1/
认证方式: Bearer Token (JWT)
请求格式:
{
"content-type": "application/json"
}
响应格式:
{
"code": 200,
"message": "success",
"data": {}
}
错误格式:
{
"code": 400,
"message": "错误描述",
"detail": "详细错误信息"
}
3.2 认证接口
POST /api/v1/auth/register
请求:
{
"email": "user@example.com",
"password": "Password123",
"confirm_password": "Password123"
}
响应 (201):
{
"code": 201,
"message": "注册成功",
"data": {
"id": "uuid",
"email": "user@example.com",
"plan": "free"
}
}
POST /api/v1/auth/login
请求:
{
"email": "user@example.com",
"password": "Password123"
}
响应 (200):
{
"code": 200,
"message": "登录成功",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 86400,
"user": {
"id": "uuid",
"email": "user@example.com",
"plan": "free"
}
}
}
3.3 品牌接口
GET /api/v1/brands/
查询参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| skip | int | 0 | 偏移量 |
| limit | int | 20 | 每页数量 |
响应 (200):
{
"code": 200,
"message": "success",
"data": {
"items": [
{
"id": "uuid",
"name": "示例品牌",
"platforms": ["wenxin", "kimi"],
"frequency": "daily",
"score": {
"overall": 78,
"trend": "+3"
},
"last_queried_at": "2026-05-01T00:00:00Z"
}
],
"total": 1
}
}
POST /api/v1/brands/
请求:
{
"name": "示例品牌",
"aliases": ["示例", "example"],
"platforms": ["wenxin", "kimi"],
"frequency": "daily"
}
响应 (201):
{
"code": 201,
"message": "品牌创建成功",
"data": {
"id": "uuid",
"name": "示例品牌",
"status": "active"
}
}
POST /api/v1/brands/{brand_id}/query/
请求:
{
"platforms": ["wenxin", "kimi"]
}
响应 (202):
{
"code": 202,
"message": "查询任务已加入队列",
"data": {
"task_id": "uuid",
"estimated_time": 30
}
}
3.4 引用数据接口
GET /api/v1/citations/
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| brand_id | UUID | 品牌ID |
| platform | string | 平台筛选 |
| start_date | date | 开始日期 |
| end_date | date | 结束日期 |
| skip | int | 偏移量 |
| limit | int | 每页数量 |
响应 (200):
{
"code": 200,
"message": "success",
"data": {
"items": [
{
"id": "uuid",
"platform": "wenxin",
"cited": true,
"citation_text": "示例品牌是...",
"position": 1,
"sentiment": "positive",
"queried_at": "2026-05-01T00:00:00Z"
}
],
"total": 100
}
}
GET /api/v1/citations/stats/
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| brand_id | UUID | 品牌ID |
响应 (200):
{
"code": 200,
"message": "success",
"data": {
"overall_score": 78,
"mention_rate": 65.5,
"sov": 45.2,
"quality": 72.3,
"platform_breakdown": {
"wenxin": {"score": 85, "cited": 45, "total": 50},
"kimi": {"score": 72, "cited": 36, "total": 50}
},
"sentiment_breakdown": {
"positive": 60,
"neutral": 30,
"negative": 10
},
"trend": [
{"date": "2026-04-01", "score": 72},
{"date": "2026-04-02", "score": 75}
]
}
}
3.5 报告接口
GET /api/v1/reports/export/csv
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| brand_id | UUID | 品牌ID |
| start_date | date | 开始日期 |
| end_date | date | 结束日期 |
响应: CSV文件下载
GET /api/v1/reports/export/pdf
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| brand_id | UUID | 品牌ID |
| type | string | weekly/monthly/custom |
响应: PDF文件下载
4. 数据模型设计
4.1 实体关系图
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ User │ │ Brand │ │ Competitor │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ id (PK) │───1:N─│ id (PK) │───1:N─│ id (PK) │
│ email │ │ user_id (FK) │ │ brand_id(FK)│
│ password_hash │ │ name │ │ name │
│ plan │ │ aliases │ │ aliases │
│ max_queries │ │ platforms │ └──────────────┘
│ is_active │ │ frequency │
│ created_at │ │ status │
└──────────────┘ │ score │
│ │ last_queried │
│ └──────────────┘
│ │
│ ┌─────┴─────┐
│ │ │
│ ┌────────────┐ ┌────────────┐
│ │CitationRec.│ │ QueryTask │
│ ├────────────┤ ├────────────┤
│ │ id (PK) │ │ id (PK) │
└───────────│ brand_id(FK)│ │ brand_id(FK)│
│ platform │ │ platform │
│ cited │ │ status │
│ sentiment │ │ scheduled │
│ position │ │ started_at │
│ text │ │ completed │
│ confidence │ └────────────┘
│ match_type │
│ queried_at │
└────────────┘
4.2 数据库表结构
users
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(100),
plan VARCHAR(20) DEFAULT 'free',
max_queries INTEGER DEFAULT 5,
is_active BOOLEAN DEFAULT TRUE,
email_verified BOOLEAN DEFAULT FALSE,
verification_code VARCHAR(6),
verification_code_expires TIMESTAMP,
reset_token VARCHAR(255),
reset_token_expires TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_plan ON users(plan);
brands
CREATE TABLE brands (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(50) UNIQUE NOT NULL,
aliases JSONB DEFAULT '[]',
website VARCHAR(500),
industry VARCHAR(50),
platforms JSONB NOT NULL DEFAULT '["wenxin", "kimi"]',
frequency VARCHAR(20) DEFAULT 'weekly',
status VARCHAR(20) DEFAULT 'active',
last_queried_at TIMESTAMP,
next_query_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_brands_user_id ON brands(user_id);
CREATE INDEX idx_brands_status ON brands(status);
CREATE INDEX idx_brands_next_query ON brands(next_query_at);
competitors
CREATE TABLE competitors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
name VARCHAR(50) NOT NULL,
aliases JSONB DEFAULT '[]',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_competitors_brand_id ON competitors(brand_id);
citation_records
CREATE TABLE citation_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
platform VARCHAR(50) NOT NULL,
cited BOOLEAN NOT NULL DEFAULT FALSE,
citation_text TEXT,
citation_position INTEGER,
sentiment VARCHAR(20),
competitor_brands JSONB DEFAULT '[]',
raw_response TEXT,
confidence FLOAT,
match_type VARCHAR(20),
queried_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_citations_brand_id ON citation_records(brand_id);
CREATE INDEX idx_citations_platform ON citation_records(platform);
CREATE INDEX idx_citations_queried_at ON citation_records(queried_at);
CREATE INDEX idx_citations_brand_platform ON citation_records(brand_id, platform);
query_tasks
CREATE TABLE query_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
platform VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
error_message TEXT,
scheduled_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP,
completed_at TIMESTAMP
);
CREATE INDEX idx_query_tasks_brand_id ON query_tasks(brand_id);
CREATE INDEX idx_query_tasks_status ON query_tasks(status);
CREATE INDEX idx_query_tasks_scheduled ON query_tasks(scheduled_at);
5. 安全设计
5.1 认证与授权
JWT Token结构:
{
"sub": "user_id",
"email": "user@example.com",
"plan": "free",
"exp": 1717200000,
"iat": 1717113600
}
Token配置:
- 有效期: 24小时
- 刷新策略: 支持刷新Token
- 存储: HttpOnly Cookie
权限控制:
| 角色 | 品牌数限制 | 竞品数/品牌 | 每日查询次数 |
|---|---|---|---|
| free用户 | 1个品牌 | 2个竞品 | 5次 |
| pro用户 | 5个品牌 | 5个竞品 | 100次 |
| admin | 无限制 | 无限制 | 无限制 |
5.2 数据安全
传输加密: TLS 1.2+
存储加密:
- 密码: bcrypt
- 敏感字段: AES-256
数据隔离:
- 租户级隔离 (user_id)
- API级别鉴权
5.3 API安全
限流策略:
| 接口 | 限制 |
|---|---|
| 登录/注册 | 5次/分钟 |
| 查询接口 | 100次/分钟 |
| 全局 | 1000次/分钟 |
输入校验: Pydantic模型自动校验
SQL注入防护: SQLAlchemy参数化查询
XSS防护: 输出编码
6. 部署架构
6.1 Docker Compose架构
version: '3.8'
services:
# 前端服务
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://backend:8000
depends_on:
- backend
volumes:
- ./frontend:/app
- /app/node_modules
# 后端服务
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/geo_platform
- REDIS_URL=redis://redis:6379/0
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
depends_on:
- db
- redis
volumes:
- ./backend:/app
# 数据库
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=geo_platform
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres123
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
6.2 环境配置
.env配置示例:
# 数据库
DATABASE_URL=postgresql+asyncpg://postgres:postgres123@db:5432/geo_platform
# Redis
REDIS_URL=redis://redis:6379/0
# JWT
JWT_SECRET=your-super-secret-key-change-in-production
JWT_EXPIRE_HOURS=24
# LLM API
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
# CORS
CORS_ORIGINS=http://localhost:3000,https://geo.example.com
7. 监控与运维
7.1 监控指标
系统指标:
- CPU使用率
- 内存使用率
- 磁盘使用率
- 网络IO
应用指标:
- API响应时间 (P50/P95/P99)
- API错误率
- 活跃用户数
- 查询任务完成率
业务指标:
- 品牌数量
- 查询次数
- 引用成功率
7.2 日志管理
日志级别:
- DEBUG: 开发环境
- INFO: 正常日志
- WARNING: 警告
- ERROR: 错误
日志格式:
{
"timestamp": "2026-05-01T00:00:00Z",
"level": "INFO",
"service": "backend",
"message": "Query completed",
"context": {
"brand_id": "uuid",
"platform": "wenxin",
"duration_ms": 1234
}
}
7.3 告警策略
| 告警类型 | 触发条件 | 通知方式 |
|---|---|---|
| 服务宕机 | HTTP 5xx > 5% | 邮件+钉钉 |
| 响应超时 | P99 > 2s | 邮件 |
| 数据库连接 | 连接数 > 80% | 邮件 |
| API配额 | 使用率 > 90% | 邮件 |
附录
A. 变更记录
| 版本 | 日期 | 变更内容 | 作者 |
|---|---|---|---|
| v1.0 | 2026-05-01 | 初稿创建 | 技术团队 |
| v1.1 | 2026-05-01 | 修正LLM成本估算,增加定价依据来源 | 技术团队 |
B. 参考资料
- FastAPI最佳实践
- SQLAlchemy 2.0文档
- Next.js 14文档
- Redis缓存设计模式
- DeepSeek API定价 - 官方文档 - 2026-05核实
- OpenAI GPT-4o-mini定价 - 官方定价页 - 2026-05核实