- 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表(向后兼容零改动)
260 lines
13 KiB
Markdown
260 lines
13 KiB
Markdown
# 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_b64,password_key 做密钥,对称可解密)。⚠️ 不能用 RC4:rc4.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;静态资源 any;load_path.py 注册各路径
|
||
4. **i18n**:zh/en 双语文案(模块内文案走 appcodes + i18n msg.txt)
|
||
5. **建表**:mysql.ddl.sql(json2ddl 生成 + 手写补齐),存量库 ALTER 幂等
|
||
6. **兼容**:旧 `llm` 表保留,`llm_bridge.py` 优先查新表(按 name),无则回退旧表——
|
||
现有调用零改动,迁移平滑
|
||
|
||
## 8. 分期
|
||
|
||
- **一期(本次交付)**:全部 10 张表 + F01-F03/F05-F14/F16-F18 + F04 内置 openai-chat + F15
|
||
- **二期(触发:第一个非 t2t 模型)**:异步任务形态适配模板、任务轮询端点
|
||
- **三期(触发:定价平台实时价接入)**:成本/消费侧单价自动读取
|