pipeline-llm/docs/design-spec.md
yumoqing 660ad21365 feat(pipeline-llm): 模型治理模块v1.0.0 — 供应商/端点目录/账号钱包/模型注册/组织容错策略/限额限流/双维度记账
- 8张llm_前缀表(手写幂等DDL): vendor/account/model/api_profile/org_policy/org_quota/user_quota/usage
- gateway.py 门禁链: 限流→限额→策略选模型(主备容错)→账号×端点轮转(偏好过滤+余额加权+冷却避让)→预授权-结算
- api_key AES单层加密(非RC4,盐不对称不可解); 限流Redis分钟窗口(db4) fail-open
- 双维度记账: 一次调用同行记cost(账号侧)+charge(组织池侧); 充值=recharge行
- CRUD+index.ui 8卡片导航+总览; appcodes字典5组; 设计规范+30测试用例
- 策略即开关: 机构无策略→__LEGACY__走旧llm表(向后兼容零改动)
2026-09-01 17:27:17 +08:00

260 lines
13 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.

# pipeline-llm 模块设计规范
> 版本1.0 | 日期2026-09-01 | 状态:待实施
> 宿主产线平台pipeline-app共享 `pipeline` 库) | 表前缀:`llm_`
## 1. 背景与目标
产线平台现有模型层是 `llm` 单表name/provider/model_id/api_base/api_key/capabilities
内嵌接入信息、单一 OpenAI-chat 调用形态、能力是标签不是契约。随着模型增多(非 t2t 能力、
多供应商、多账号),需要独立的模型治理模块。本模块解决五类问题:
1. **供给侧治理**:供应商协议/端点目录、账号(钱包)、模型注册、适配模板
2. **容量治理**:供应商限流 → 多账号轮转;多供应商 → 主备模型容错
3. **需求侧治理**:组织级模型容错策略、组织限额限流、个人限额限流
4. **计费**:双维度记账(成本侧记账号、消费侧记组织/个人),挂定价平台 `pricing_program.ppid`
5. **端点选择**供应商全球端点配置一次账号选用端点客户按区域偏好prefer/must
## 2. 架构总览
```
供给侧(容量) 需求侧(治理)
───────────── ─────────────
llm_vendor (协议+端点目录) llm_org_policy (组织容错策略)
└─ llm_account (钱包+选端点) llm_org_quota (组织限额限流)
└─ llm_model (模型注册) llm_user_quota (个人限额限流)
└─ llm_api_profile (适配)
llm_usage (用量流水,双维度)
```
**调用门禁链**(所有 LLM 调用收敛点 `llm_call`
```
① 个人限流窗口 → ② 组织限流窗口
→ ③ 个人额度余额 → ④ 组织池余额
→ ⑤ 组织容错策略选模型(主→备)
→ ⑥ 账号×端点候选池:区域偏好过滤 → 剩余额度加权轮转 + 冷却避让
→ ⑦ 预授权(冻结预估用量)→ 调用 → ⑧ 结算(实际用量,双维度记账)
任何一级不过:快速失败 + 明确原因(不挂起、不静默)
```
## 3. 数据表8 张,全部 `llm_` 前缀,建在 `pipeline` 库)
### 3.1 llm_vendor — 供应商
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| name | str(100) | 供应商名(如 阿里云百炼) |
| protocol | str(20) | openai_compat / dashscope_async / custom |
| endpoints | text(JSON) | 端点目录 `[{region, base_url, proxy, timeout, note}]` |
| description | text | |
| status | str(20) | active / disabled默认 active |
| org_id | str(32) | 0=系统级 |
| created_at/updated_at | str(30) | |
端点目录挂在 vendor 下(全球端点配置一次),账号选用。`endpoints` JSON 元素:
`region`domestic/international/europe/...)、`base_url``proxy`(可选,国外端点走 SOCKS
`timeout`(秒,默认 60`note`
### 3.2 llm_account — 供应商账号(钱包)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| vendor_id | str(32) | → llm_vendor.id |
| name | str(100) | 账号名 |
| api_key | str(500) | **AES 单层加密**appPublic.aes.aes_encode_b64password_key 做密钥,对称可解密)。⚠️ 不能用 RC4rc4.password()/unpassword() 盐不对称加密后永久不可解2026-08 实测坑)。改 key 须重启进程(有缓存) |
| endpoint_ids | text(JSON) | 选用的端点索引列表(对应 vendor.endpoints 下标),有序;空=不启用 |
| balance | decimal(14,4) | 余额(充值-消耗),单位与定价币种一致 |
| total_recharge | decimal(14,4) | 累计充值 |
| status | str(20) | active / suspended / exhausted默认 active |
| org_id | str(32) | |
| created_at/updated_at | str(30) | |
独立充值、独立记账。轮转限流时余额不足的账号直接跳过(不消耗)。
### 3.3 llm_model — 模型注册(旧 `llm` 表的替代)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| vendor_id | str(32) | → llm_vendor.id |
| account_id | str(32) | 默认账号(可空,运行时轮转选) |
| name | str(100) UNIQUE | 模型注册名(平台内唯一,调用方用此名) |
| vendor_model_id | str(100) | 供应商侧模型名(各家命名不一) |
| capability | str(20) | 能力类型,默认 t2t见 §6 能力标准) |
| sync_mode | str(10) | sync / async_task默认 sync |
| profile_id | str(32) | → llm_api_profile.id适配模板 |
| ppid | str(32) | → 定价平台 `pricing_program.id`(挂定价) |
| default_params | text(JSON) | 默认参数temperature/max_tokens 等) |
| status | str(20) | active / disabled |
| description | text | |
| org_id | str(32) | |
| created_at/updated_at | str(30) | |
### 3.4 llm_api_profile — 适配模板(协议×能力形态去重)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| name | str(100) | 如 openai-chat、dashscope-image-async |
| protocol | str(20) | 同 vendor.protocol |
| capability | str(20) | 能力类型 |
| request_template | text | Jinja2参数 → 供应商请求体 |
| response_template | text | Jinja2供应商响应 → 统一结果(含 usage |
| param_schema | text(JSON) | bricks input_fields 格式(参数表单定义) |
| status | str(20) | active / disabled |
| created_at/updated_at | str(30) | |
模板按「协议×能力形态」开,不按模型开——同一端点 N 个模型共用一条,杜绝重复记录。
一期只内置 `openai-chat`sync非 chat 形态模型进来时再加对应 profile。
### 3.5 llm_org_policy — 组织模型容错策略
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| org_id | str(32) UNIQUE | 一组织一策略 |
| primary_model_id | str(32) | → llm_model.id 主模型 |
| backup_model_ids | text(JSON) | 备模型 id 列表(有序) |
| endpoint_pref | str(20) | prefer_domestic / prefer_intl / must_domestic / must_intl / any默认 any |
| status | str(20) | active / disabled |
| created_at/updated_at | str(30) | |
生效优先级:会话/项目选择 > 组织策略 > 平台默认(与现有
`sd_projects.default_model > pipeline_agent_settings.default_llm_id` 链兼容,组织策略插中间)。
must 模式:候选端点耗尽宁可报错冒泡,绝不跨端点。
### 3.6 llm_org_quota — 组织限额限流
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| org_id | str(32) UNIQUE | |
| balance | decimal(14,4) | 组织池余额(充值-消耗) |
| total_recharge | decimal(14,4) | 累计充值 |
| rate_limit | int | 组织 QPM 上限0=不限) |
| status | str(20) | active / disabled |
| created_at/updated_at | str(30) | |
### 3.7 llm_user_quota — 个人限额限流
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| user_id | str(32) | |
| org_id | str(32) | 所属组织(额度挂组织池) |
| quota_limit | decimal(14,4) | 个人额度上限0=不限) |
| quota_used | decimal(14,4) | 个人已用 |
| rate_limit | int | 个人 QPM 上限0=不限) |
| status | str(20) | active / disabled |
| created_at/updated_at | str(30) | |
关系默认「共享池 + 个人上限」:组织充值一个池,个人上限防单人打爆池子。
个人额度耗尽不影响他人;组织池耗尽全员不可用(冒泡提醒充值)。
### 3.8 llm_usage — 用量流水(双维度记账事实表)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| org_id / user_id | str(32) | 消费侧维度 |
| model_id / account_id | str(32) | 供给侧维度model_id 空+source=recharge 时为充值记录) |
| endpoint_region | str(20) | 实际走的端点区域 |
| req_tokens / resp_tokens | int | 实际用量 |
| cost | decimal(12,6) | 成本侧金额(账号扣减,按定价) |
| charge | decimal(12,6) | 消费侧金额(组织池扣减,按产品定价) |
| ppid | str(32) | 定价项目(冗余自模型,便于对账) |
| task_ref | str(100) | 调用来源(任务/会话标识) |
| status | str(20) | ok / failed / recharge充值记录 |
| note | str(200) | 失败原因/充值备注 |
| created_at | str(30) | |
一次调用两条线:**成本侧** cost 记账号(供应商对账),**消费侧** charge 记组织池+个人
(客户计费)。充值是 balance 变动事件,记 `source=recharge`model_id/account_id 空)。
## 4. 核心机制
### 4.1 选择链与轮转gateway.py `llm_call`
```python
async def llm_call(prompt, model=None, org_id=None, user_id=None, task_ref='', **kw)
```
1. 解析模型:调用方指定 model → 直接用;否则查组织策略主备链
2. 候选对构建模型的账号池account.status=active× 账号选用端点
3. 区域偏好过滤must_* 只留对应区域端点对prefer_* 排序(匹配区域优先)
4. 加权轮转:按账号余额 + 端点窗口剩余配额加权;限流冷却中的对跳过
5. 调用 → 429 标记该(账号,端点)对冷却 → 换下一对;账号池耗尽 → 降备模型;
备模型也耗尽 → must 模式冒泡报错 / prefer 模式跨端点重试
6. 401/403 标记账号失效不轮转5xx 短暂重试(最多 2 次)
### 4.2 预授权-结算(防并发击穿余额)
- 调用前:按 max_tokens 预估(默认 2000 token × 单价Redis 原子冻结组织池+个人额度
- 调用后:按实际 token 多退少补,写双维度流水
- GeneratorExit/超时:结算兜底(按预估释放,写 failed 流水)
### 4.3 限流Redis 滑动窗口)
- key`llm_rl:{user|org}:{id}:{minute}`,原子 INCR + EXPIRE
- 超窗拒绝:返回明确错误(个人限流/组织限流),下窗口自动恢复,不涉及钱
- 供应商侧 429(账号,端点)对冷却 60s窗口内不再选它
### 4.4 定价对接ppid
- 模型挂 `pricing_program.id`(定价平台的定价项目)
- 消费侧单价从定价平台读取(跨库:定价模块库),一期支持手动设置 `price_per_1k`
兜底(组织策略页配置),二期接定价平台实时价
- 成本侧单价:账号维度配置(供应商结算价),一期也走手动配置
## 5. 功能点清单(共 18 项)
| # | 功能点 | 表/文件 | 优先级 |
|---|---|---|---|
| F01 | 供应商管理(含端点目录配置) | llm_vendor + CRUD | P0 |
| F02 | 账号管理(充值、余额、选端点) | llm_account + CRUD | P0 |
| F03 | 模型注册(挂能力、挂定价、挂适配) | llm_model + CRUD | P0 |
| F04 | 适配模板管理 | llm_api_profile + CRUD | P1一期内置 openai-chat |
| F05 | 组织容错策略(主备模型+端点偏好) | llm_org_policy + CRUD | P0 |
| F06 | 组织限额限流充值、QPM | llm_org_quota + CRUD | P0 |
| F07 | 个人限额限流 | llm_user_quota + CRUD | P0 |
| F08 | 调用门禁(①-④ 限流限额检查) | gateway.py | P0 |
| F09 | 主备容错(策略选模型→备链) | gateway.py | P0 |
| F10 | 账号×端点轮转(加权+冷却) | gateway.py | P0 |
| F11 | 区域偏好过滤prefer/must | gateway.py | P0 |
| F12 | 预授权-结算 | gateway.py | P0 |
| F13 | 双维度记账流水 | llm_usage | P0 |
| F14 | 充值操作(账号/组织池) | recharge dspy | P0 |
| F15 | 用量查询(按组织/模型/账号/时间) | usage 列表页 | P1 |
| F16 | 模型管理入口(主菜单) | index.ui 菜单项 | P0 |
| F17 | 与现有调用链兼容llm_bridge 切换) | llm_bridge.py 适配 | P0 |
| F18 | 机构未配置冒泡提示 | gateway 错误路径 | P0 |
## 6. 能力类型标准capabilities 取值,先行收敛)
| 值 | 含义 | 同步性 |
|---|---|---|
| t2t | 文本对话/生成 | sync |
| t2i | 文生图 | async_task |
| i2t | 图生文(多模态理解) | sync |
| t2v | 文生视频 | async_task |
| embedding | 文本向量化 | sync |
| mm-embedding | 多模态向量化 | sync |
| rerank | 重排序 | sync |
| mm-rerank | 多模态重排序 | sync |
| tts | 语音合成 | sync |
| asr | 语音识别 | sync |
新增能力类型走评审(改本文档 + init/data.json appcodes禁止野生标签。
## 7. 集成点
1. **宿主加载**`pipeline_app.py` init() 加 `load_llm()`try/except ImportError 兜底)
2. **菜单**`wwwroot/index.ui` 主菜单加「模型管理」项 → `/llm`TabPanel
3. **RBAC**`/llm/**` 注册到 superuser静态资源 anyload_path.py 注册各路径
4. **i18n**zh/en 双语文案(模块内文案走 appcodes + i18n msg.txt
5. **建表**mysql.ddl.sqljson2ddl 生成 + 手写补齐),存量库 ALTER 幂等
6. **兼容**:旧 `llm` 表保留,`llm_bridge.py` 优先查新表(按 name无则回退旧表——
现有调用零改动,迁移平滑
## 8. 分期
- **一期(本次交付)**:全部 10 张表 + F01-F03/F05-F14/F16-F18 + F04 内置 openai-chat + F15
- **二期(触发:第一个非 t2t 模型)**:异步任务形态适配模板、任务轮询端点
- **三期(触发:定价平台实时价接入)**:成本/消费侧单价自动读取