- 将 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
53 KiB
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 + Flask(Phase 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 API,Gunicorn 部署 |
| 模板引擎 | Jinja2 | HTML 报告模板渲染 |
| 图表 | ECharts | 前端可视化(价格趋势/排行榜) |
| 调度 | APScheduler | Python 内置调度(辅助 cron) |
| 日志 | loguru | 结构化日志,低配置 |
| 日期处理 | pandas | 数据清洗、价格计算 |
| 货币换算 | forex-python | USD/CNY/EUR 汇率获取 |
2.3 数据库
Phase 1:PostgreSQL
- 与立交桥技术栈统一
- 利用 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(模型商)
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(模型)
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(运营商/云平台)
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(区域定价)
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(价格历史)
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(免费政策)
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(每日报告)
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(用户订阅)
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 内部采集 API(Collector → Server)
POST /api/v1/collect/push
采集器推送采集结果(Phase 2 分布式采集节点使用)
Request:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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/ 目录),统一接口输出:
# 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:
@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 将各厂商原始名称映射到标准名称:
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 统一调度,无外部消息队列依赖。
# /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
#!/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 失败重试 + 告警机制
# 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 2Phase 2Phase 2Phase 2Phase 2成Phase 2本Phase 2计Phase 2算Phase 2器Phase 2Phase 2Phase 2Phase 2Phase 2 Phase 2|Phase 2 Phase 2Phase 2/Phase 2cPhase 2aPhase 2lPhase 2cPhase 2uPhase 2lPhase 2aPhase 2tPhase 2oPhase 2rPhase 2.Phase 2hPhase 2tPhase 2mPhase 2lPhase 2Phase 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 2Phase 2Phase 2Phase 2Phase 2趋Phase 2势Phase 2图Phase 2Phase 2Phase 2Phase 2Phase 2 Phase 2|Phase 2 Phase 2Phase 2/Phase 2tPhase 2rPhase 2ePhase 2nPhase 2dPhase 2sPhase 2.Phase 2hPhase 2tPhase 2mPhase 2lPhase 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 2EPhase 2CPhase 2hPhase 2aPhase 2rPhase 2tPhase 2sPhase 2)Phase 2 Phase 2|Phase 2
Phase 2| 关于我们 | /about.html | 项目介绍、数据来源说明 |
6.3 与后端的数据交互
模式:纯前端 SPA(Single 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):
// 统一 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 模型浏览器页面结构
<!-- 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 配置
# 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 环境变量清单
# .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.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 前缀提取 provider(anthropic/claude-3.5-sonnet → Anthropic)
│ │ ├── 处理免费模型(id 包含 :free 后缀)
│ │ └── 错误处理(401/429/500)
│ ├── [ ] 实现 base collector 抽象类
│ ├── [ ] 实现数据清洗逻辑(去除异常价格、统一单位)
│ └── [ ] 验证:371 模型全部入库,无重复,数据正确
│
├── T1.3 数据映射 + Provider 标准化
│ ├── [ ] 建立 PROVIDER_NAME_MAP(OpenRouter id → 标准厂商名)
│ ├── [ ] 验证:所有 provider 名称统一(无别名)
│ └── [ ] 补充 provider logo_url / description
│
├── T1.4 初始数据导入
│ ├── [ ] 运行 OpenRouter 采集器,导入 371 模型
│ ├── [ ] 质量检查:随机抽 10 条数据,验证价格/上下文长度
│ └── [ ] 导出数据字典文档
│
└── T1.5 采集脚本 + cron 配置
├── [ ] 编写 scripts/run_openrouter_collect.sh
├── [ ] 配置 crontab(08: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.md(v0.3)、FEATURE_LIST.md(v1.0)、BUSINESS_MODEL.md(v1.0)、MARKET_ANALYSIS.md(v3.0)