sage_datamart/README.md
yumoqing 8a97ea4a86 refactor: rename sage_datamark → sage_datamart + add ETL cron + stat UIs
- Rename package directory and all references from sage_datamark to sage_datamart
- Add 3 ETL cron dspy files (sync/aggregate/provider_cost) for scheduled data pipelines
- Add 5 stat UI files (total_users/fail_calls/success_rate/avg_ttft/total_calls)
- Register j2_ template functions in init.py for UI rendering
- Move j2_avg_ttft SQL to dashboards.py for DRY
- Clean unused imports (getConfig/DBPools/exception)
- Update git remote to sage_datamart.git
2026-07-20 10:49:15 +08:00

149 lines
5.3 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.

# Sage DataMart — 数据集市模块
模型性能监控、供应商性价比分析、多维度数据下钻。
## 数据来源
| 数据源 | 模块 | 表 | 说明 |
|--------|------|-----|------|
| 模型调用记录 | llmage | `llmusage` | 每次 API 调用详细日志 |
| 模型定义 | llmage | `llm` | 模型名、catelog、供应商、owner |
| 定价 | pricing | `pricing_program_timing` | 各模型供应商定价数据 |
| 供应链关系 | supplychain | `distribution_agreements` | 分销商(reseller) → 二级分销商(sub_reseller) |
| 供应链关系 | supplychain | `product_org_auth` | 产品授权 org 关联链 |
| 组织架构 | rbac | `organization` | org 基础信息名称、类型、parentid |
### 维度层级(从供应链模块获取)
```
分销商(distributor, org_type='reseller')
└─ 供应商/二级分销商(sub_reseller, org_type='sub_reseller')
└─ 客户(customer, org_type='customer')
```
- 分销商与二级分销商关系:`distribution_agreements`(reseller_id → sub_reseller_id)
- 二级分销商与客户关系:`organization.parentid``product_org_auth` 授权链
- **注意**: 不能用 `organization.parentid` 递归到顶来获取分销商,因为 parentid 是组织管理层级,不代表供应链业务关系。分销商归属必须从 `supplychain` 模块获取。
## 集市表
| 表名 | 说明 | 粒度 |
|------|------|------|
| `dm_model_call_fact` | 模型调用事实表 | 每次调用 |
| `dm_model_perf_daily` | 模型性能天汇总 | 天/模型/组织 |
| `dm_provider_cost_daily` | 供应商性价比天汇总 | 天/模型/供应商 |
## 取数频率
| ETL 任务 | 频率 | 说明 |
|----------|------|------|
| `sync_call_fact` | 每 5 分钟 | 增量同步 llmusage → dm_model_call_fact |
| `aggregate_daily_perf` | 每小时 | 聚合当天 dm_model_call_fact → dm_model_perf_daily |
| `aggregate_provider_cost` | 每天 1 次 | 聚合同模型供应商性价比 → dm_provider_cost_daily |
## 业务指标
### 性能指标
- **TTFT** (Time To First Token): 首 Token 响应时间,来源 `llmusage.responsed_seconds`
- **TTOT** (Time To Output Total): 完成时间,来源 `llmusage.finish_seconds`
- **成功率**: `status='SUCCEEDED'` / 总调用数
- **失败率**: `status='FAILED'` / 总调用数
- **并发度**: 同一时间窗口内活跃请求数
### 性价比
- **单价**: `total_amount / total_tokens`,按模型+供应商维度对比
- **TTFT × 单价**:综合性价比排序
### 多维度
- **分销商(reseller)** → **二级分销商(sub_reseller)****客户(customer)**
- 层级关系来自 `supplychain` 模块:`distribution_agreements` + `product_org_auth`
- 每层独立统计:用量、模型分布、性能、费用
## Dashboard 看板
### 统一数据看板 `/sage_datamart/index.ui`
- 6 个统计卡片今日调用、今日金额、成功率、失败数、平均TTFT、总用户
- 模型性能排行 + 供应商性价比对比(双栏)
- 热门模型 / 用户排行 / 供应商排行(三栏)
- 数据来源全部从集市表 `dm_model_call_fact` / `dm_model_perf_daily` / `dm_provider_cost_daily`
### 供应商性价比看板 `/sage_datamart/provider_roi.ui`
- 同模型不同供应商对比:单价(¥/token)、TTFT、调用次数、总费用
## API 接口
| 端点 | 说明 | 替代 dashboard_for_sage |
|------|------|------------------------|
| `/sage_datamart/api/stats.dspy` | 统计卡片 | get_today_usage/amount, active_users |
| `/sage_datamart/api/model_perf.dspy` | 模型性能 | - (新增) |
| `/sage_datamart/api/provider_roi.dspy` | 供应商性价比 | - (新增) |
| `/sage_datamart/api/top_models.dspy` | 热门模型排行 | get_top_models |
| `/sage_datamart/api/top_providers.dspy` | 供应商排行 | get_top_providers_combined |
| `/sage_datamart/api/top_users.dspy` | 用户排行 | get_top_users_combined |
| `/sage_datamart/api/customer_models.dspy` | 客户今日+当月用量 | get_customer_daily/monthly_models |
| `/sage_datamart/api/daily_trend.dspy` | 7天趋势 | - (新增) |
| `/sage_datamart/api/hourly_concurrency.dspy` | 每小时并发 | - (新增) |
### model_perf.dspy
```json
// 请求
{"date": "2026-07-17", "model": "qwen3-max"}
// 响应
[{
"model": "qwen3-max",
"avg_ttft_ms": 320.5,
"avg_ttot_ms": 2100.3,
"success_rate": 98.5,
"total_calls": 12340,
"success_calls": 12155,
"fail_calls": 185,
"total_amount": 246.80
}]
```
### provider_roi.dspy
```json
// 请求
{"model": "qwen3-max"}
// 响应
[{
"model": "qwen3-max",
"providerid": "aliyun",
"total_calls": 5000,
"total_tokens": 5000000,
"total_amount": 10.00,
"unit_price": 0.000002,
"avg_ttft_ms": 280.5
}]
```
## 部署
```bash
cd ~/sage/pkgs
git clone git@git.opencomputing.cn:yumoqing/sage_datamart.git
cd sage_datamart && pip install .
py3/bin/python scripts/load_path.py
# 添加 ETL cron 任务(在 Sage start.sh 或系统 cron
# 每 5 分钟同步
*/5 * * * * cd ~/sage && py3/bin/python -c "
from sage_datamart.etl import run_etl_sync
from sqlor.dbpools import DBPools
from appPublic.jsonConfig import getConfig
import asyncio
config = getConfig('.')
db = DBPools(config.databases)
async def go():
env_dbname = ServerEnv().get_module_dbname('sage_datamart')
async with db.sqlorContext(env_dbname) as sor:
await run_etl_sync(sor)
asyncio.run(go())
"
# 重启 Sage访问 /sage_datamart/index.ui
```