Files
llm-intelligence/TECHNICAL_DESIGN.md
Your Name ba054f04cf feat(phase1): OpenRouter采集器接入PostgreSQL,数据链路闭环
- 将 fetch_openrouter.go 的 summarize() 实现为 PostgreSQL upsert
- 新增 -db 参数和 DATABASE_URL 环境变量支持
- 打通 models + model_prices 表的最小可运行链路
- 创建 llm_intelligence 数据库并运行 migration
- 前端 Explorer 验证 T-3.2~T-3.5 全部通过
- 日报生成器正常产出 Markdown 和 latest_models.json
2026-05-08 13:49:12 +08:00

1502 lines
53 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.
# LLM Intelligence Hub — 技术设计文档 v1.0
> 文档版本v1.0
> 日期2026-05-04
> 负责人宰相AI 辅助)
> 状态:初稿
---
## 一、系统架构概览
### 1.1 整体架构
```
┌──────────────────────────────────────────────────────────────────────┐
│ LLM Intelligence Hub │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ 报告 │ │ Web UI │ │ AI Agent / MCP Client │ │
│ │ Phase 2才推送│ │ (Explorer+报告) │ │ (REST API / MCP) │ │
│ └──────┬──────┘ └──────┬──────┘ └────────────┬────────────┘ │
│ │ │ │ │
│ ┌──────▼──────────────────▼────────────────────────▼────────────┐ │
│ │ Service Layer (Python) │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ │ │
│ │ │ Report │ │ API │ │ Scheduler │ │ Notifier │ │ │
│ │ │ Generator │ │ Server │ │ (cron) │ │ (告警) │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └──────────┘ │ │
│ └───────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼────────────────────────────────────┐ │
│ │ Data Access Layer (SQLAlchemy ORM) │ │
│ └───────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼────────────────────────────────────┐ │
│ │ Storage Layer (PostgreSQL) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Data Collection Layer │ │
│ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ OpenRouter │ │ Phase 2才扩充厂商/中转平台 │ │ 中转平台采集器 │ │ │
│ │ │ Collector │ │ (10家厂商) │ │ (硅基流动等) │ │ │
│ │ └─────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```
### 1.2 各层职责
| 层级 | 职责 | 技术选型 |
| **数据采集层** | 从 OpenRouter 抓取模型元数据、定价 | Python + requests |
| **存储层** | 结构化数据持久化 + 数据库内任务队列 | PostgreSQL与立交桥技术栈统一 |
| **服务层** | 报告生成、API 服务、调度 | Python 3.11 + FlaskPhase 1 API+ Jinja2报告模板 |
| **前端层** | 静态 Web 页面展示Explorer / 报告) | 纯 HTML/CSS/JS + Bootstrap 5 + ECharts 5 |
### 1.3 技术架构决策(与立交桥技术栈统一)
**核心约束**与立交桥技术栈保持一致Phase 1 直接使用 PostgreSQL。
| 决策 | 选型 | 理由 |
| 数据库 | **PostgreSQL** | 与立交桥统一;支持 JSONB/数组类型;数据库内队列替代第三方消息组件 |
| API 框架 | **Flask** | 轻量1 进程运行 |
| 前端 | **纯静态 HTML** | 无需 Node.js 服务端渲染 |
| 爬虫框架 | **requests + BeautifulSoup** | 成熟稳定 |
| 调度 | **系统 cron + Python script** | 无需 Celery/RQ |
| 日志/监控 | **文件系统日志** | loguru 写入文件 |
| 告警 | **Phase 2** | 钉钉/飞书 Webhook 推送 |
**Phase 1 单机部署拓扑**
```
Phase 1 单机部署
├── PostgreSQL DB
├── 采集脚本cron 触发,直写 DB
├── 日报生成命令Markdown 输出)
└── 备份脚本Phase 2 才推送至 OSS
```
---
## 二、技术选型详解
### 2.1 语言与运行时
| 组件 | 选型 | 版本 | 依据 |
| 主力语言 | **Python** | 3.11+ | 爬虫/数据处理生态最成熟Flask/Jinja2 配套完善 |
| 前端 | **Vanilla JS + HTML5** | — | 无需 Node.js 构建链,静态文件 CDN 托管,降低成本 |
### 2.2 框架与工具库
| 用途 | 库/工具 | 用途说明 |
| HTTP 请求 | `requests` | 数据采集主库,轻量稳定 |
| HTML 解析 | `BeautifulSoup4` | 静态页面解析 |
| 动态页面 | `playwright` | JavaScript 渲染页(如某些国内定价页) |
| ORM | `SQLAlchemy` | 数据库抽象,直接对接 PostgreSQL |
| API 框架 | `Flask` | 轻量 REST APIGunicorn 部署 |
| 模板引擎 | `Jinja2` | HTML 报告模板渲染 |
| 图表 | `ECharts` | 前端可视化(价格趋势/排行榜) |
| 调度 | `APScheduler` | Python 内置调度(辅助 cron |
| 日志 | `loguru` | 结构化日志,低配置 |
| 日期处理 | `pandas` | 数据清洗、价格计算 |
| 货币换算 | `forex-python` | USD/CNY/EUR 汇率获取 |
### 2.3 数据库
**Phase 1PostgreSQL**
- 与立交桥技术栈统一
- 利用 PostgreSQL JSONB 存储灵活字段(如 capabilities 数组)
- 使用 PostgreSQL job queue 表实现异步任务队列,无须第三方消息组件
- Schema 设计直接以 PostgreSQL 语法编写
### 2.4 为什么不用这些
| 未选方案 | 原因 |
| SQLite | 不符合与立交桥技术栈统一的要求 |
| FastAPI | Swagger UI 增加包体积Flask 在 Phase 1 足够轻量 |
| Scrapy | 重量级框架Phase 1 采集规模不需要分布式 |
| Celery + RabbitMQ | 增加运维复杂度,用 PostgreSQL job queue 替代 |
| React/Vue | 需要 Node.js 构建链,增加部署复杂度 |
| Deno/Bun | 生态不如 Python 成熟,数据处理库少 |
---
## 三、数据库设计DDL
> 以下 DDL 以 PostgreSQL 语法编写,与立交桥技术栈统一。
### 3.1 model_provider模型商
```sql
CREATE TABLE model_provider (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE, -- "OpenAI", "百度", "DeepSeek"
name_cn TEXT, -- 中文名:"百度智能云"
country TEXT NOT NULL, -- "US" / "CN" / "EU"
website TEXT, -- 官网 URL
founded_year INTEGER, -- 成立年份
description TEXT, -- 简介
logo_url TEXT, -- 厂商 Logo
status TEXT NOT NULL DEFAULT 'active', -- active / deprecated
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_provider_country ON model_provider(country);
CREATE INDEX idx_provider_status ON model_provider(status);
```
### 3.2 model模型
```sql
CREATE TABLE model (
id BIGSERIAL PRIMARY KEY,
provider_id INTEGER NOT NULL,
name TEXT NOT NULL, -- "GPT-4o", "ERNIE-4.0"
version TEXT, -- "2025-12", "V3.2"
modality TEXT NOT NULL, -- text / vision / audio / video / code
context_length INTEGER NOT NULL DEFAULT 0, -- 上下文窗口0=未知
capabilities TEXT, -- JSON数组: ["function_calling","vision"]
release_date DATE, -- 发布日期
status TEXT NOT NULL DEFAULT 'active', -- active / deprecated / discontinued
parent_model_id INTEGER, -- 父模型ID区分 Turbo/Lite 变体)
elo_score REAL, -- ELO 分数OpenRouter
benchmark_scores TEXT, -- JSON: {"mmlu": 88.5, "humaneval": 90.2}
source_url TEXT, -- 来源 URL
data_confidence TEXT DEFAULT 'official', -- official / inferred / unverified
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (provider_id) REFERENCES model_provider(id) ON DELETE CASCADE,
UNIQUE(provider_id, name, version)
);
CREATE INDEX idx_model_provider ON model(provider_id);
CREATE INDEX idx_model_modality ON model(modality);
CREATE INDEX idx_model_status ON model(status);
CREATE INDEX idx_model_name ON model(name);
```
### 3.3 operator运营商/云平台)
```sql
CREATE TABLE operator (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE, -- "AWS Bedrock", "硅基流动"
name_cn TEXT, -- 中文名
type TEXT NOT NULL, -- cloud / reseller / official
country TEXT NOT NULL, -- 运营主体国籍
website TEXT, -- 控制台地址
api_endpoint TEXT, -- API 基础 URL
auth_type TEXT NOT NULL, -- api_key / oauth / sts
is_cn_accessible BOOLEAN DEFAULT 1, -- 国内是否可访问
stability_grade TEXT DEFAULT 'B', -- A/B/C/D 稳定性评级
status TEXT NOT NULL DEFAULT 'active', -- active / deprecated
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_operator_type ON operator(type);
CREATE INDEX idx_operator_country ON operator(country);
```
### 3.4 region_pricing区域定价
```sql
CREATE TABLE region_pricing (
id BIGSERIAL PRIMARY KEY,
operator_id INTEGER NOT NULL,
model_id INTEGER NOT NULL,
region TEXT NOT NULL DEFAULT 'GLOBAL', -- CN / US / EU / GLOBAL
currency TEXT NOT NULL, -- CNY / USD / EUR
input_price_per_mtok REAL NOT NULL, -- 元/百万Token
output_price_per_mtok REAL NOT NULL,
unit TEXT DEFAULT 'per_mtok', -- per_mtok / per_1k / per_token
free_tier_id INTEGER, -- 关联 free_tier 表
rate_limit TEXT, -- JSON: {"rpm": 60, "tpm": 100000}
free_limitations TEXT, -- JSON数组: ["仅限国内IP","新用户专享"]
last_updated DATE NOT NULL,
source_url TEXT,
data_confidence TEXT DEFAULT 'official', -- official / inferred / expired
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (operator_id) REFERENCES operator(id) ON DELETE CASCADE,
FOREIGN KEY (model_id) REFERENCES model(id) ON DELETE CASCADE,
UNIQUE(operator_id, model_id, region, currency)
);
CREATE INDEX idx_pricing_operator ON region_pricing(operator_id);
CREATE INDEX idx_pricing_model ON region_pricing(model_id);
CREATE INDEX idx_pricing_region ON region_pricing(region);
CREATE INDEX idx_pricing_currency ON region_pricing(currency);
CREATE INDEX idx_pricing_input_cost ON region_pricing(input_price_per_mtok);
```
### 3.5 pricing_history价格历史
```sql
CREATE TABLE pricing_history (
id BIGSERIAL PRIMARY KEY,
region_pricing_id INTEGER NOT NULL,
model_id INTEGER NOT NULL,
operator_id INTEGER NOT NULL,
region TEXT NOT NULL,
currency TEXT NOT NULL,
old_input_price REAL,
new_input_price REAL NOT NULL,
old_output_price REAL,
new_output_price REAL NOT NULL,
change_pct REAL, -- 变动百分比(自动计算)
change_type TEXT NOT NULL, -- increase / decrease / new_model / discontinued
recorded_at DATE NOT NULL, -- 记录日期
source_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (region_pricing_id) REFERENCES region_pricing(id) ON DELETE CASCADE,
FOREIGN KEY (model_id) REFERENCES model(id) ON DELETE CASCADE,
FOREIGN KEY (operator_id) REFERENCES operator(id) ON DELETE CASCADE
);
CREATE INDEX idx_history_model ON pricing_history(model_id);
CREATE INDEX idx_history_operator ON pricing_history(operator_id);
CREATE INDEX idx_history_recorded ON pricing_history(recorded_at);
CREATE INDEX idx_history_change_type ON pricing_history(change_type);
```
### 3.6 free_tier免费政策
```sql
CREATE TABLE free_tier (
id BIGSERIAL PRIMARY KEY,
operator_id INTEGER NOT NULL,
model_id INTEGER, -- NULL表示该平台全部免费额度
free_model_name TEXT, -- 免费模型名称(展示用)
quota_type TEXT NOT NULL, -- daily / monthly / one_time / unlimited
quota_amount REAL, -- 配额数量
quota_unit TEXT, -- requests / tokens / minutes
tpm_limit INTEGER, -- tokens per minute 限制
rpm_limit INTEGER, -- requests per minute 限制
daily_req_limit INTEGER, -- 每日请求上限
monthly_req_limit INTEGER, -- 每月请求上限
token_limit_per_req INTEGER, -- 单次请求Token上限
requires_credit_card BOOLEAN DEFAULT 0, -- 是否需要绑定信用卡
requires_verification BOOLEAN DEFAULT 0, -- 是否需要实名认证
region_restrictions TEXT, -- JSON: ["仅限部分地区"]
applicable_scenarios TEXT, -- JSON: ["仅限新用户"]
special_notes TEXT, -- 特殊说明
effective_from DATE,
effective_until DATE, -- NULL表示长期有效
last_updated DATE,
source_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (operator_id) REFERENCES operator(id) ON DELETE CASCADE,
FOREIGN KEY (model_id) REFERENCES model(id) ON DELETE SET NULL
);
CREATE INDEX idx_free_operator ON free_tier(operator_id);
CREATE INDEX idx_free_model ON free_tier(model_id);
CREATE INDEX idx_free_quota_type ON free_tier(quota_type);
```
### 3.7 daily_report每日报告
```sql
CREATE TABLE daily_report (
id BIGSERIAL PRIMARY KEY,
report_date DATE NOT NULL UNIQUE,
new_models TEXT, -- JSON数组新上线模型
price_changes TEXT, -- JSON数组价格变动
free_changes TEXT, -- JSON数组免费政策变更
top_recommendations TEXT, -- JSON对象场景推荐
cost_alerts TEXT, -- JSON数组成本告警
html_content TEXT, -- 完整HTML报告内容
summary_md TEXT, -- Markdown摘要
status TEXT NOT NULL DEFAULT 'generated', -- generated / failed / partial
generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
error_message TEXT
);
CREATE INDEX idx_report_date ON daily_report(report_date);
CREATE INDEX idx_report_status ON daily_report(status);
```
### 3.8 user_subscription用户订阅
```sql
CREATE TABLE user_subscription (
id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL, -- 统一用户ID
email TEXT,
phone TEXT,
subscription_tier TEXT NOT NULL DEFAULT 'free', -- free / pro / team / enterprise
subscription_start DATE,
subscription_end DATE,
notify_channels TEXT, -- JSON: ["feishu","email","dingtalk"]
feishu_webhook TEXT,
dingtalk_webhook TEXT,
email_webhook TEXT,
model_watchlist TEXT, -- JSON数组关注模型
operator_watchlist TEXT, -- JSON数组关注平台
price_alert_threshold REAL DEFAULT 10.0, -- 告警阈值(%
monthly_token_limit INTEGER, -- 月度Token限制
monthly_token_used INTEGER DEFAULT 0,
stripe_customer_id TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(email)
);
CREATE INDEX idx_sub_user ON user_subscription(user_id);
CREATE INDEX idx_sub_tier ON user_subscription(subscription_tier);
```
---
## 四、API 设计
### 4.1 内部采集 APICollector → Server
#### POST /api/v1/collect/push
采集器推送采集结果Phase 2 分布式采集节点使用)
**Request:**
```json
{
"batch": [
{
"provider_name": "OpenAI",
"model_name": "GPT-4o",
"version": "2025-01",
"operator_name": "OpenRouter",
"region": "GLOBAL",
"currency": "USD",
"input_price": 2.50,
"output_price": 10.0,
"context_length": 128000,
"capabilities": ["vision", "function_calling", "json_mode"],
"free_tier": null,
"source_url": "https://openrouter.ai/api/v1/models"
}
],
"collected_at": "2026-05-04T08:00:00+08:00"
}
```
**Response:**
```json
{
"status": "ok",
"inserted": 365,
"updated": 12,
"errors": 0
}
```
---
### 4.2 对外 REST API
#### GET /api/v1/models
查询模型列表
**Query Parameters:**
| 参数 | 类型 | 默认值 | 说明 |
| `provider` | string | — | 模型商名称过滤 |
| `modality` | string | — | text/vision/audio/video/code |
| `min_context` | int | — | 最小上下文长度 |
| `max_input_price` | float | — | 最大输入价格(/MTok |
| `has_free` | bool | false | 仅显示有免费额的模型 |
| `search` | string | — | 关键词搜索(模型名/capabilities |
| `sort` | string | `input_price` | 排序字段 |
| `order` | string | `asc` | asc/desc |
| `page` | int | 1 | 页码 |
| `page_size` | int | 20 | 每页数量max 100 |
**Response:**
```json
{
"total": 523,
"page": 1,
"page_size": 20,
"models": [
{
"id": 42,
"name": "DeepSeek V4-Flash",
"provider": "DeepSeek",
"provider_cn": "深度求索",
"modality": "text",
"context_length": 1048576,
"capabilities": ["function_calling", "json_mode"],
"status": "active",
"lowest_price": {
"operator": "硅基流动",
"currency": "CNY",
"input": 0.14,
"output": 0.028,
"region": "CN"
}
}
]
}
```
#### GET /api/v1/models/{id}
查询单个模型详情
**Response:**
```json
{
"id": 42,
"name": "DeepSeek V4-Flash",
"provider": {
"id": 5,
"name": "DeepSeek",
"country": "CN"
},
"version": "V4-Flash",
"modality": "text",
"context_length": 1048576,
"capabilities": ["function_calling", "json_mode"],
"release_date": "2026-04-15",
"status": "active",
"elo_score": 1382.5,
"pricing": [
{
"operator": "硅基流动",
"region": "CN",
"currency": "CNY",
"input": 0.14,
"output": 0.028,
"source_url": "https://siliconflow.cn"
},
{
"operator": "OpenRouter",
"region": "GLOBAL",
"currency": "USD",
"input": 0.02,
"output": 0.004
}
],
"free_tier": {
"quota_type": "monthly",
"quota_amount": 5000000,
"quota_unit": "tokens",
"requires_credit_card": false
}
}
```
#### GET /api/v1/cost
成本计算器
**Query Parameters:**
| 参数 | 类型 | 必填 | 说明 |
| `input_tokens` | int | 是 | 输入 Token 数 |
| `output_tokens` | int | 否 | 输出 Token 数(默认=input_tokens×0.3 |
| `modality` | string | 否 | 模态过滤 |
| `region` | string | 否 | 区域CN/US/GLOBAL |
| `currency` | string | CNY | 显示货币 |
| `top_n` | int | 10 | 返回前N个最低价 |
**Response:**
```json
{
"input_tokens": 1000000,
"output_tokens": 300000,
"currency": "CNY",
"results": [
{
"rank": 1,
"model": "DeepSeek V4-Flash",
"provider": "DeepSeek",
"operator": "硅基流动",
"input_cost": 0.14,
"output_cost": 0.0084,
"total_cost": 0.1484,
"total_cost_usd": 0.020
},
{
"rank": 2,
"model": "Kimi K2.5",
"provider": "Moonshot",
"operator": "硅基流动",
"input_cost": 0.23,
"output_cost": 0.021,
"total_cost": 0.251,
"total_cost_usd": 0.034
}
]
}
```
#### GET /api/v1/recommend
模型推荐
**Query Parameters:**
| 参数 | 类型 | 必填 | 说明 |
| `use_case` | string | 是 | 场景coding/writing/reasoning/free/vision |
| `min_context` | int | — | 最小上下文需求 |
| `budget` | float | — | 预算上限(/MTok input |
| `region` | string | CN | 区域偏好 |
| `limit` | int | 5 | 返回数量 |
**Response:**
```json
{
"use_case": "coding",
"recommendations": [
{
"rank": 1,
"model": "Kimi K2.6",
"provider": "Moonshot",
"reason": "SWE-Bench Pro 超越 GPT-5.4,编码能力最强",
"input_price": 0.95,
"currency": "CNY",
"free_option": null
},
{
"rank": 2,
"model": "GLM-5.1",
"provider": "智谱",
"reason": "编码能力接近 Opus 4.6,性价比高",
"input_price": 1.40,
"currency": "CNY",
"free_option": null
}
]
}
```
#### GET /api/v1/reports
每日报告列表
**Query Parameters:**
| 参数 | 类型 | 默认值 | 说明 |
| `from` | date | 30天前 | 开始日期 |
| `to` | date | 今天 | 结束日期 |
| `page` | int | 1 | 页码 |
**Response:**
```json
{
"total": 30,
"reports": [
{
"id": 30,
"report_date": "2026-05-04",
"status": "generated",
"summary": "新上线3个模型价格变动2项免费政策更新1项",
"generated_at": "2026-05-04T08:00:45+08:00"
}
]
}
```
#### GET /api/v1/reports/{date}
获取指定日期报告内容
**Response:**
```json
{
"id": 30,
"report_date": "2026-05-04",
"html_content": "<html>...</html>",
"new_models": [
{"name": "xAI Grok 4.1 Fast", "provider": "xAI", "input_price": 0.20, "currency": "USD"}
],
"price_changes": [
{
"model": "Claude Opus 4.6",
"operator": "Anthropic",
"old_price": 15.0,
"new_price": 5.0,
"change_pct": -66.7,
"currency": "USD"
}
],
"free_changes": [
{
"model": "Gemini 2.5 Pro",
"operator": "Google",
"change": "免费层下线,需付费使用"
}
],
"top_recommendations": {
"coding": {"model": "Kimi K2.6", "provider": "Moonshot"},
"writing": {"model": "GLM-5.1", "provider": "智谱"},
"free": {"model": "DeepSeek R1", "provider": "DeepSeek"},
"cheapest": {"model": "Step 3.5 Flash", "provider": "字节"}
}
}
```
#### GET /api/v1/health
健康检查
**Response:**
```json
{
"status": "ok",
"version": "1.0.0",
"db_record_count": {
"models": 523,
"providers": 22,
"operators": 31,
"pricing_records": 1847
},
"last_collect_time": "2026-05-04T08:00:12+08:00",
"last_report_time": "2026-05-04T08:00:45+08:00"
}
```
---
## 五、数据采集 Pipeline
### 5.1 OpenRouter 采集流程
```
┌─────────────────┐
│ 每日 08:00 │
│ cron 触发 │
└────────┬────────┘
┌─────────────────────────────────────────────────┐
│ GET https://openrouter.ai/api/v1/models │
│ Headers: Authorization: Bearer <OPENROUTER_KEY>│
└────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 解析响应 JSON │
│ 字段映射: │
│ id → model.name (如 "anthropic/claude-3.5-sonnet")│
│ name → display_name │
│ pricing.input * 1e6 → input_price_per_mtok │
│ pricing.output * 1e6 → output_price_per_mtok│
│ context_length → context_length │
│ supported_parameters → capabilities │
│ opensource → modality (text/vision/etc) │
└────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 识别 provider_name (从 id 前缀提取) │
│ 示例: "anthropic/claude-3.5-sonnet" → │
│ provider="Anthropic", model="Claude 3.5 Sonnet"│
└────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Upsert: │
│ INSERT OR REPLACE INTO model_provider (...) │
│ INSERT OR REPLACE INTO model (...) │
│ INSERT OR REPLACE INTO region_pricing (...) │
└────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 检测价格变动: │
│ SELECT old_price FROM pricing_history │
│ WHERE model_id = x AND operator_id = y │
│ IF new_price != old_price: │
│ INSERT INTO pricing_history (...) │
│ IF abs(change_pct) > 5%: 标记为高亮变动 │
└─────────────────────────────────────────────────┘
```
### 5.2 国内厂商采集流程 — Phase 2
每个国内厂商独立采集器(`collectors/` 目录),统一接口输出:
```python
# collectors/base.py
class BaseCollector:
def collect(self) -> List[ModelRecord]:
"""返回标准化采集记录"""
raise NotImplementedError
def get_schedule(self) -> str:
"""返回 cron 表达式,如 "0 8 * * *" """
return "0 8 * * *"
def get_timeout(self) -> int:
"""超时秒数"""
return 60
def get_retry(self) -> int:
"""重试次数"""
return 3
```
#### 采集器清单Phase 1
#### 统一字段映射
每个采集器输出标准化 `CollectedRecord`:
```python
@dataclass
class CollectedRecord:
provider_name: str # "DeepSeek"
provider_name_cn: str # "深度求索"
model_name: str # "V4-Flash"
model_version: str # "V4-Flash"
modality: str # "text"
context_length: int # 1048576
capabilities: List[str] # ["function_calling", "vision"]
operator_name: str # "硅基流动"
operator_type: str # "reseller"
region: str # "CN" / "US" / "GLOBAL"
currency: str # "CNY" / "USD"
input_price_per_mtok: float
output_price_per_mtok: float
free_tier: Optional[FreeTierRecord] = None
source_url: str
collected_at: datetime
```
统一 `ProviderMapper` 将各厂商原始名称映射到标准名称:
```python
PROVIDER_NAME_MAP = {
# DeepSeek
"deepseek-ai/DeepSeek-V3": {"provider": "DeepSeek", "model": "V3.2", "version": "2026-03"},
"deepseek-ai/DeepSeek-V4": {"provider": "DeepSeek", "model": "V4", "version": "2026-04"},
"deepseek-ai/DeepSeek-R1": {"provider": "DeepSeek", "model": "R1", "version": "2026-01"},
# 阿里
"qwen/Qwen3-VL-32B": {"provider": "阿里云", "model": "Qwen3-VL-32B", "version": "2026-03"},
"qwen/Qwen3-VL-8B": {"provider": "阿里云", "model": "Qwen3-VL-8B", "version": "2026-03"},
# Moonshot
"moonshotai/Kimi-K2.6": {"provider": "Moonshot", "model": "K2.6", "version": "2026-04"},
"moonshotai/Kimi-K2.5": {"provider": "Moonshot", "model": "K2.5", "version": "2026-03"},
# 智谱
"zhipuai/GLM-5.1": {"provider": "智谱", "model": "GLM-5.1", "version": "2026-03"},
"zhipuai/GLM-4.7": {"provider": "智谱", "model": "GLM-4.7", "version": "2025-12"},
# ... 其他厂商
}
```
### 5.3 每日调度设计
**调度策略**:系统 cron 统一调度,无外部消息队列依赖。
```crontab
# /etc/crontab
# 每日 08:00 触发全量采集 + 报告生成
0 8 * * * root /opt/llm-hub/scripts/run_daily.sh >> /var/log/llm-hub/daily.log 2>&1
# 每日 09:00 触发数据备份
0 9 * * * root /opt/llm-hub/scripts/backup.sh >> /var/log/llm-hub/backup.log 2>&1
```
```bash
#!/bin/bash
# run_daily.sh
set -e
LOG_FILE="/var/log/llm-hub/daily.log"
echo "[$(date)] 开始每日采集任务" >> $LOG_FILE
# 1. 采集 OpenRouter海外模型优先级最高
cd /opt/llm-hub
python -m collectors.openrouter >> $LOG_FILE 2>&1
# 2. Phase 2 才并行采集国内厂商DeepSeek/阿里/Kimi/智谱等)
echo "[$(date)] 采集完成,开始生成报告" >> $LOG_FILE
# 3. 生成每日报告
python -m services.report_generator >> $LOG_FILE 2>&1
# 4. Phase 2 才检测价格变动并告警
echo "[$(date)] 每日任务完成" >> $LOG_FILE
```
### 5.4 失败重试 + 告警机制
```python
# services/retry_handler.py
import time
import loguru
from functools import wraps
from typing import Callable, Any
logger = loguru.logger
def retry(max_attempts: int = 3, delay: int = 5, backoff: float = 2.0):
"""指数退避重试装饰器"""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
attempt = 0
while attempt < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempt += 1
wait = delay * (backoff ** (attempt - 1))
logger.warning(
f"Attempt {attempt}/{max_attempts} failed for {func.__name__}: {e}. "
f"Retrying in {wait}s..."
)
if attempt >= max_attempts:
logger.error(f"All {max_attempts} attempts failed for {func.__name__}")
raise
time.sleep(wait)
return wrapper
return decorator
# 采集器调用示例
@retry(max_attempts=3, delay=10, backoff=2.0)
def collect_with_retry(collector_name: str):
collector = get_collector(collector_name)
records = collector.collect()
save_to_db(records)
logger.info(f"{collector_name}: collected {len(records)} records")
# 告警触发逻辑
def check_and_alert_price_change(model_id: int, operator_id: int, new_price: float):
old_price = get_last_price(model_id, operator_id)
if old_price is None:
return # 首 次录入,不告警
change_pct = (new_price - old_price) / old_price * 100
if abs(change_pct) > 10:
alert_msg = (
f"⚠️ 价格变动告警\n"
f"模型: {get_model_name(model_id)}\n"
f"平台: {get_operator_name(operator_id)}\n"
f"原价: {old_price}\n"
f"新价: {new_price}\n"
f"变动: {change_pct:+.1f}%"
)
send_dingtalk_alert(alert_msg)
send_feishu_alert(alert_msg)
logger.warning(alert_msg)
```
**告警规则:**
| 条件 | 动作 |
| 单个采集器失败 | 记录日志,保留旧数据,发送低优先级告警 |
| 连续 3 天同一采集器失败 | 发送高优先级告警(钉钉/飞书) |
| 价格变动 > 10% | 立即触发告警 |
| 价格变动 > 20% | 立即触发告警 + 暂停该平台数据(人工确认) |
| 报告生成失败 | 发送告警,保留前一天报告 |
| 数据库写入失败 | 立即告警,回滚事务 |
---
## 六、前端架构
### 6.1 技术栈
| 组件 | 选型 | 理由 |
| **页面框架** | 纯 HTML5 + Bootstrap 5 | 无需 Node.js 构建CDN 托管,零运维 |
| **图表库** | ECharts 5 | 免费,功能全面,支持中文,体积小(~1MB |
| **图标** | Bootstrap Icons | 与 Bootstrap 5 原生集成 |
| **搜索** | 前端 Fuse.js | 轻量模糊搜索,< 100KB无需服务端 |
| **布局** | Bootstrap 5 响应式网格 | 移动端适配 |
| **构建** | 无(纯静态文件) | Phase 2才引入 Nginx静态 CDN 托管 |
### 6.2 页面清单
| 页面 | 路径 | 功能说明 |
| **首页 / 报告列表** | `/` | 展示最新每日报告入口,显示近期报告摘要 |
| **报告详情** | `/reports/{date}.html` | 单日报告完整内容(新模型/价格变动/推荐) |
| **模型浏览器** | `/explorer.html` | 组合筛选 + 卡片/表格视图 + 搜索 |
| **模型详情** | `/model/{id}.html` | 模型完整信息 + 全平台定价对比 |
Phase 2|Phase 2 Phase 2~Phase 2~Phase 2*Phase 2*Phase 2成Phase 2本Phase 2计Phase 2算Phase 2器Phase 2*Phase 2*Phase 2~Phase 2~Phase 2 Phase 2|Phase 2 Phase 2`Phase 2/Phase 2cPhase 2aPhase 2lPhase 2cPhase 2uPhase 2lPhase 2aPhase 2tPhase 2oPhase 2rPhase 2.Phase 2hPhase 2tPhase 2mPhase 2lPhase 2`Phase 2 Phase 2|Phase 2 Phase 2TPhase 2oPhase 2kPhase 2ePhase 2nPhase 2 Phase 2用Phase 2量Phase 2 Phase 2→Phase 2 Phase 2多Phase 2平Phase 2台Phase 2成Phase 2本Phase 2对Phase 2比Phase 2排Phase 2行Phase 2 Phase 2|Phase 2
Phase 2Phase 2|Phase 2 Phase 2~Phase 2~Phase 2*Phase 2*Phase 2趋Phase 2势Phase 2图Phase 2*Phase 2*Phase 2~Phase 2~Phase 2 Phase 2|Phase 2 Phase 2`Phase 2/Phase 2tPhase 2rPhase 2ePhase 2nPhase 2dPhase 2sPhase 2.Phase 2hPhase 2tPhase 2mPhase 2lPhase 2`Phase 2 Phase 2|Phase 2 Phase 2价Phase 2格Phase 2/Phase 2模Phase 2型Phase 2能Phase 2力Phase 2历Phase 2史Phase 2趋Phase 2势Phase 2Phase 2EPhase 2CPhase 2hPhase 2aPhase 2rPhase 2tPhase 2sPhase 2Phase 2 Phase 2|Phase 2
Phase 2| **关于我们** | `/about.html` | 项目介绍、数据来源说明 |
### 6.3 与后端的数据交互
**模式**:纯前端 SPASingle Page Application通过 Fetch API 调用后端 REST API。
```
前端静态文件Phase 2才 Nginx 托管)
├── GET /api/v1/models → Flask API 返回 JSON
├── GET /api/v1/models/{id} → 模型详情 JSON
├── GET /api/v1/cost → 成本计算 JSON
├── GET /api/v1/recommend → 推荐结果 JSON
└── GET /api/v1/reports/{date} → 报告 JSON
```
**前端数据层dataService.js**
```javascript
// 统一 API 调用封装
const API_BASE = '/api/v1';
async function apiGet(endpoint, params = {}) {
const url = new URL(`${API_BASE}${endpoint}`, window.location.origin);
Object.entries(params).forEach(([k, v]) => v != null && url.searchParams.set(k, v));
const resp = await fetch(url);
if (!resp.ok) throw new Error(`API error: ${resp.status}`);
return resp.json();
}
// 主要接口封装
const api = {
models: {
list: (params) => apiGet('/models', params),
detail: (id) => apiGet(`/models/${id}`)
},
cost: {
calculate: (params) => apiGet('/cost', params)
},
recommend: (params) => apiGet('/recommend', params),
reports: {
list: (params) => apiGet('/reports', params),
get: (date) => apiGet(`/reports/${date}`)
}
};
```
### 6.4 模型浏览器页面结构
```html
<!-- explorer.html -->
<!-- 筛选栏 -->
<div class="row mb-3">
<div class="col-md-2">
<select id="filter-provider" class="form-select">
<option value="">全部厂商</option>
<option value="DeepSeek">DeepSeek</option>
<option value="阿里云">阿里云</option>
<!-- ... -->
</select>
</div>
<div class="col-md-2">
<select id="filter-modality" class="form-select">
<option value="">全部模态</option>
<option value="text">文字</option>
<option value="vision">视觉</option>
<option value="code">代码</option>
</select>
</div>
<div class="col-md-2">
<input type="number" id="filter-max-price" class="form-control"
placeholder="最大输入价(¥/MT)">
</div>
<div class="col-md-3">
<input type="text" id="search-keyword" class="form-control"
placeholder="搜索模型名称...">
</div>
<div class="col-md-3">
<div class="btn-group" role="group">
<button class="btn btn-outline-primary active" data-view="card">卡片</button>
<button class="btn btn-outline-primary" data-view="table">表格</button>
</div>
</div>
</div>
<!-- 结果区域 -->
<div id="results" class="row">
<!-- 动态渲染卡片或表格 -->
</div>
<!-- 分页 -->
<nav><ul class="pagination" id="pagination"></ul></nav>
```
---
## 七、部署架构
### 7.1 Docker 配置
```yaml
# docker-compose.yml
version: '3.8'
services:
# --- Phase 1 核心服务 ---
collector:
build:
context: .
dockerfile: Dockerfile.collector
volumes:
- ./data:/opt/llm-hub/data # PostgreSQL 数据持久化
- ./logs:/var/log/llm-hub # 日志持久化
- ./reports:/opt/llm-hub/reports # 报告输出
env_file:
- .env
restart: unless-stopped
networks:
- llm-hub-net
api:
build:
context: .
dockerfile: Dockerfile.api
ports:
- "5000:5000"
volumes:
- ./data:/opt/llm-hub/data
- ./reports:/opt/llm-hub/reports
env_file:
- .env
restart: unless-stopped
depends_on:
- collector
networks:
- llm-hub-net
# --- Phase 2 才引入 Nginx内网访问 + 静态文件服务)---
networks:
llm-hub-net:
driver: bridge
```
### 7.2 内网部署要求
**部署前提**
- 一台可访问外网的服务器(境外更好,便于访问 OpenRouter
- 域名(可选,用于 HTTPS + 钉钉/飞书 Webhook 回调)
- Docker + Docker Compose
**网络访问需求**
| 目的地 | 用途 | 协议 |
| `openrouter.ai` | 采集海外模型数据 | HTTPS |
| `api.deepseek.com` | 采集 DeepSeek 定价 | HTTPS |
| `dashscope.aliyuncs.com` | 采集阿里云定价 | HTTPS |
| `api.moonshot.cn` | 采集 Kimi 定价 | HTTPS |
| `open.bigmodel.cn` | 采集智谱定价 | HTTPS |
| `api.siliconflow.cn` | 采集硅基流动定价 | HTTPS |
| `oapi.dingtalk.com` | Phase 2 钉钉告警 | HTTPS |
| `open.feishu.cn` | Phase 2 飞书告警 | HTTPS |
| **无需访问** | 国内云厂商定价页(如阿里云控制台) | — |
### 7.3 环境变量清单
```bash
# .env 文件Phase 1 最小配置)
# === 数据库 ===
DATABASE_URL=postgresql://user:pass@localhost:5432/llmhub
# === OpenRouter ===
OPENROUTER_API_KEY=sk-or-v1-xxxxx
# === 国内厂商 API Keys ===
DEEPSEEK_API_KEY=sk-xxxxx
DASHSCOPE_API_KEY=sk-xxxxx
MOONSHOT_API_KEY=sk-xxxxx
ZHIPU_API_KEY=xxxxx
MINIMAX_API_KEY=xxxxx
VOLCENGINE_API_KEY=xxxxx
VOLCENGINE_SECRET_KEY=xxxxx
TENCENT_SECRET_ID=xxxxx
TENCENT_SECRET_KEY=xxxxx
BAIDU_QIANFAN_API_KEY=xxxxx
BAIDU_QIANFAN_SECRET_KEY=xxxxx
SILICONFLOW_API_KEY=sk-xxxxx
# === 告警配置Phase 2 才启用)===
# DINGTALK_WEBHOOK=https://oapi.dingtalk.com/robot/send?access_token=xxxxx
# FEISHU_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/xxxxx
ALERT_THRESHOLD_PCT=10
ALERT_THRESHOLD_CRITICAL_PCT=20
# === 邮件配置(可选)===
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@example.com
SMTP_PASS=xxxxx
# === 备份配置 ===
BACKUP_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
BACKUP_OSS_BUCKET=llm-hub-backup
BACKUP_OSS_KEY=xxxxx
BACKUP_OSS_SECRET=xxxxx
# === 系统 ===
LOG_LEVEL=INFO
TZ=Asia/Shanghai
```
### 7.4 Nginx 配置
```nginx
# nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# --- 静态文件服务(前端)---
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# 前端静态页面
location / {
try_files $uri $uri/ /index.html;
}
# 每日报告 HTML
location /reports/ {
alias /usr/share/nginx/html/reports/;
expires 7d;
add_header Cache-Control "public, immutable";
}
# --- API 反向代理 ---
location /api/ {
proxy_pass http://api:5000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 60s;
}
# 健康检查(无需认证)
location /health {
proxy_pass http://api:5000/api/v1/health;
proxy_set_header Host $host;
}
}
}
```
---
## 八、Phase 1 技术路线3个月
### 8.1 Sprint 划分
| Sprint | 周期 | 目标 | 交付物 |
| **Sprint 0** | Week 1 | 技术方案确认 + 环境搭建 | `TECHNICAL_DESIGN.md` 终稿;开发环境就绪 |
| **Sprint 1** | Week 2-3 | OpenRouter 采集器 + 数据库 Schema | 371 海外模型入库;数据库 DDL 可执行 |
| **Sprint 2** | Week 4-5 | PostgreSQL migration + 日报生成器 | 三张表落地Markdown 报告输出到 reports/daily/ |
| **Sprint 2** | Week 4-5 | 每日报告生成 + Explorer 页面 | Markdown 报告生成Explorer 页面上线Markdown 报告可输出到 reports/daily/ |
| **Sprint 4** | Week 8-9 | 模型浏览器 + 搜索筛选 | `/explorer.html` 上线;卡片/表格视图 |
| **Sprint 5** | Week 10-11 | Explorer 页面完善 + Dashboard 占位图 | 表格/筛选排序;价格趋势占位图 |
| **Sprint 6** | Week 12 | 收尾 + 部署 + 验证脚本 | Docker Compose 部署文档;验证脚本;备份策略 |
### 8.2 Sprint 1 详细任务OpenRouter 采集器)
```
Sprint 1 目标:从 OpenRouter API 采集 371 模型,建立基础数据库
任务分解:
├── T1.1 数据库 Schema 部署
│ ├── [ ] 创建所有 DDL 表model_provider/model/operator/region_pricing/...
│ ├── [ ] 编写 PostgreSQL Schema 部署脚本deploy.sh
│ └── [ ] 验证:查询所有表,返回空表,数量正确
├── T1.2 OpenRouter 采集器实现
│ ├── [ ] 实现 collectors/openrouter.py
│ │ ├── 调用 GET https://openrouter.ai/api/v1/models
│ │ ├── 解析 id/name/pricing/context_length/capabilities
│ │ ├── 从 id 前缀提取 provideranthropic/claude-3.5-sonnet → Anthropic
│ │ ├── 处理免费模型id 包含 :free 后缀)
│ │ └── 错误处理401/429/500
│ ├── [ ] 实现 base collector 抽象类
│ ├── [ ] 实现数据清洗逻辑(去除异常价格、统一单位)
│ └── [ ] 验证371 模型全部入库,无重复,数据正确
├── T1.3 数据映射 + Provider 标准化
│ ├── [ ] 建立 PROVIDER_NAME_MAPOpenRouter id → 标准厂商名)
│ ├── [ ] 验证:所有 provider 名称统一(无别名)
│ └── [ ] 补充 provider logo_url / description
├── T1.4 初始数据导入
│ ├── [ ] 运行 OpenRouter 采集器,导入 371 模型
│ ├── [ ] 质量检查:随机抽 10 条数据,验证价格/上下文长度
│ └── [ ] 导出数据字典文档
└── T1.5 采集脚本 + cron 配置
├── [ ] 编写 scripts/run_openrouter_collect.sh
├── [ ] 配置 crontab08:00 每日执行)
├── [ ] 编写失败重试逻辑
└── [ ] 验证:手动运行脚本成功,数据入库
```
### 8.3 Phase 1 关键技术决策记录
| 决策 | 选型 | 记录时间 | 理由 |
| Phase 1 数据库用 PostgreSQL | ✅ 确认 | Sprint 0 | 与立交桥技术栈统一;支持 JSONB/数组类型;数据库内队列 |
| 数据采集用 Python requests | ✅ 确认 | Sprint 0 | 生态成熟,内存占用低 |
| 报告生成用 Jinja2 模板 | ✅ 确认 | Sprint 0 | 模板复用,减少前端维护成本 |
| 告警用 Webhook 直推 | ✅ 确认 | Sprint 0 | 无需消息队列,降低复杂度 |
| OpenRouter ELO 数据暂不采集 | ⚠️ 延期 | Sprint 1 | ELO API 可能收费Phase 1 跳过 |
| 国内厂商优先级DeepSeek > 阿里 > Kimi > 智谱 > MiniMax > 火山 > 腾讯 > 百度 | ✅ 确认 | Sprint 2 | 按市场热度排序 |
### 8.4 质量检查清单Phase 1 上线前)
#### 功能验证
- [ ] OpenRouter 371 模型全部入库,覆盖率 100%
# (Phase 2 才采集国内厂商)
- [ ] 每日 08:00 cron 触发采集,报告自动生成
- [ ] 报告内容包含:新模型、价格变动(>5% 高亮)、场景推荐
- [ ] `/explorer.html` 搜索响应 < 500ms
# (Phase 2 才实现告警推送)
#### 数据质量验证
- [ ] 每条数据有 `source_url` 来源标注
- [ ] 置信度分级标注official / inferred / expired
- [ ] 价格单位统一为 ¥/MTok 或 $/MTok
- [ ] 同模型多源价格差异 > 20% 时标注"待核实"
- [ ] 采集失败写入日志,保留旧数据
#### 部署验证
- [ ] `docker-compose up` 可正常启动所有服务
- [ ] PostgreSQL 数据库持久化到 `data/` 目录
- [ ] 报告 HTML 生成到 `reports/` 目录
# Phase 2 才引入 Nginx
- [ ] API `/api/v1/health` 返回 200
- [ ] 备份脚本每日推送至 OSS 成功
#### 性能验证
- [ ] 371 模型采集完成 < 5 分钟
- [ ] 报告生成 < 30 秒
- [ ] API 查询响应 < 500ms/models, 20 条)
- [ ] 并发 10 个采集器同时运行,内存 < 2GB
---
## 附录:目录结构
```
llm-intelligence/
├── TECHNICAL_DESIGN.md # 本文档
├── PRD.md # 产品需求文档
├── FEATURE_LIST.md # 功能清单
├── BUSINESS_MODEL.md # 商业模式
├── MARKET_ANALYSIS.md # 市场调研
├── Dockerfile.collector # 采集器镜像
├── Dockerfile.api # API 服务镜像
├── docker-compose.yml # 容器编排
├── .env.example # 环境变量模板
├── nginx.conf # Nginx 配置
├── collectors/ # 数据采集器
│ ├── __init__.py
│ ├── base.py # 采集器基类
│ ├── openrouter.py # OpenRouter 采集器
│ ├── deepseek.py # DeepSeek 采集器
│ ├── aliyun.py # 阿里云 DashScope 采集器
│ ├── kimi.py # Moonshot/Kimi 采集器
│ ├── zhipu.py # 智谱 BigModel 采集器
│ ├── minimax.py # MiniMax 采集器
│ ├── volcengine.py # 火山引擎采集器
│ ├── tencent.py # 腾讯云采集器
│ ├── baidu.py # 百度 Qianfan 采集器
│ └── siliconflow.py # 硅基流动采集器
├── services/ # 服务层
│ ├── __init__.py
│ ├── database.py # SQLAlchemy 数据库连接
│ ├── models.py # ORM 模型定义
│ ├── report_generator.py # 每日报告生成器
│ ├── price_alert.py # 价格告警服务
│ ├── notifier.py # 钉钉/飞书推送
│ └── recommendation.py # 模型推荐引擎
├── api/ # REST API
│ ├── __init__.py
│ ├── app.py # Flask 应用入口
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── models.py # /models 路由
│ │ ├── cost.py # /cost 路由
│ │ ├── recommend.py # /recommend 路由
│ │ └── reports.py # /reports 路由
│ └── schemas.py # Pydantic 请求/响应模型
├── static/ # 前端静态文件
│ ├── index.html # 首页/报告列表
│ ├── explorer.html # 模型浏览器
│ ├── calculator.html # 成本计算器
│ ├── trends.html # 趋势分析
│ ├── css/
│ │ └── style.css
│ └── js/
│ ├── dataService.js # API 调用封装
│ ├── explorer.js # 模型浏览器逻辑
│ ├── calculator.js # 计算器逻辑
│ └── charts.js # ECharts 封装
├── templates/ # Jinja2 报告模板
│ └── report.html # 每日报告 HTML 模板
├── reports/ # 生成的报告 HTML 输出
│ └── 2026-05-04.html
├── scripts/ # 运维脚本
│ ├── run_daily.sh # 每日采集 + 报告脚本
│ ├── backup.sh # 数据库备份脚本
│ ├── migrate.sh # PostgreSQL Schema 部署脚本
│ └── init_db.py # 数据库初始化脚本
├── tests/ # 单元测试
│ ├── test_collectors.py
│ ├── test_api.py
│ └── test_report.py
├── data/ # PostgreSQL 数据目录(运行时生成)
│ └── llm_intelligence.db
└── logs/ # 日志文件(运行时生成)
├── collector.log
├── api.log
└── backup.log
```
---
**文档状态:** 设计修订完成 ✅
**修订内容2026-05-06**
- SQLite → PostgreSQL与立交桥技术栈统一
- 移除第三方消息组件依赖,改用 PostgreSQL 数据库内队列
- 技术架构简洁化
**下一步行动:**
- [ ] 技术负责人评审架构设计
- [ ] 确认数据库选型(已确定为 PostgreSQL
- [ ] 确认 OpenRouter API Key 获取方式
- [ ] Sprint 1 任务分配
---
_文档编制宰相AI 辅助)_
_基于 PRD.mdv0.3、FEATURE_LIST.mdv1.0、BUSINESS_MODEL.mdv1.0、MARKET_ANALYSIS.mdv3.0_