- json/llm_call_trace_list.json: noedit只读列表;exclouded隐藏id/req_path/resp_path/org_id;
url/note加宽;kind/method内联枚举(含gen同步生成,此前草稿引用的get_llm_kind_options.dspy不存在);
删color_mapping(bricks全库0消费的死配置);toolbar查看IO+binds urlwidget→PopupWindow(85%)
url不带?id=${id}$——生成期ArgsConvert会eval成<built-in function id>垃圾串,
行数据id经_add_event_data自动并入params(dapi/llmage生产同款)
- api/llm_call_trace_io.dspy: 薄代理(get_user门禁+get_userorgid机构隔离)→env.llm_call_trace_io;
修草稿4处实锤bug:get_user未await/user.get当dict/sor块外使用/返回PopupWindow套娃
- init.py: llm_call_trace_io读表+同批次+llm_usage上下文(块内读全拷dict)→读文件
(trace.trace_root realpath前缀校验防穿越)→TabPanel三tab(摘要Html+pre escape防注入/
请求/响应CodeEditor readonly mode=null);单侧150K截断提示盘上路径;env注册
- api/llm_project_cost_query.dspy: 改{total,rows}契约接Tabular data_url(PageDataLoader);
wwwroot/llm_project_cost/index.ui: InlineForm(date_from/date_to/status)+Tabular;
script内getWidgetById起点bricks.app(弹窗DOM挂body下,app.root搜不到)
- card_popup.dspy: titles加llm_call_trace;新增page_targets白名单挂按项目费用页;
index.ui挂两张卡片(调用原文追踪/按项目费用)
- accounting.py: _maybe_cleanup_traces每日一次接cleanup_expired(trace.py铁律3
文档承诺worker每日调但循环没接,补齐);放抢锁前(清理幂等多进程同日无害)
- scripts/load_path.py: 注释说明/**通配已覆盖新页面(rbac check_roles_path前缀匹配)
- models/mysql.ddl/README/design-spec: 9张表同步(kind补gen枚举;3.9节;F19/F20)
- scripts/test_llm_call_trace_io.py: stub harness 22断言全过(隔离/穿越/转义/截断/每日门控)
- .gitignore: wwwroot/llm_call_trace/生成目录不入库
15 KiB
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 能力、
多供应商、多账号),需要独立的模型治理模块。本模块解决五类问题:
- 供给侧治理:供应商协议/端点目录、账号(钱包)、模型注册、适配模板
- 容量治理:供应商限流 → 多账号轮转;多供应商 → 主备模型容错
- 需求侧治理:组织级模型容错策略、组织限额限流、个人限额限流
- 计费:双维度记账(成本侧记账号、消费侧记组织/个人),挂定价平台
pricing_program.ppid - 端点选择:供应商全球端点配置一次,账号选用端点,客户按区域偏好(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. 数据表(9 张,全部 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) | 实际走的端点区域 |
| usages | text | 用量原文JSON(计费唯一事实源:token计数+按量因子,全量不截断) |
| cost | decimal(12,6) | 成本侧金额(账号扣减,按定价) |
| charge | decimal(12,6) | 消费侧金额(组织池扣减,按产品定价) |
| ppid | str(32) | 定价项目(冗余自模型,便于对账) |
| project_id | str(32) NOT NULL DEFAULT '0' | 项目ID(sd_projects.id;'0'=非项目调用哨兵值——按项目统计费用,查询不传=不区分项目,2026-09-10;token 绑定 project_id 全链透传) |
| task_ref | str(100) | 调用来源(任务/会话标识) |
| call_id | str(32) | 调用批次ID(关联 llm_call_trace 原文追踪,一次调用=N个往返;2026-09-10) |
| status | str(20) | 统一枚举 SUCCEEDED/FAILED/PENDING/RUNNING(其他如 recharge 充值记录) |
| note | str(200) | 失败原因/充值备注 |
| created_at | str(30) |
一次调用两条线:成本侧 cost 记账号(供应商对账),消费侧 charge 记组织池+个人
(客户计费)。充值是 balance 变动事件,记 source=recharge 行(model_id/account_id 空)。
3.9 llm_call_trace — 模型调用原文追踪(上行/下行)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | str(32) PK | |
| call_id | str(32) | 调用批次ID(与 llm_usage.call_id 同值;一次 chat_inference=N 个往返) |
| seq | int | 往返序号(同步重试 0..2;异步提交 0,轮询 1..N) |
| kind | str(20) | chat 对话 / gen 同步生成 / submit 异步提交 / query 任务轮询 |
| url | str(500) | 上游 URL(去 query 防签名泄露) |
| method | str(10) | HTTP 方法 |
| status_code / elapsed_ms | int | 响应状态码 / 耗时 |
| req_path / resp_path | str(300) | 原文文件路径(filesroot 相对;headers 已脱敏) |
| org_id | str(32) | 机构ID(查看端点机构隔离用) |
| note | str(200) | 错误摘要 |
| created_at | timestamp |
原文落盘(唯一收敛点 inference._post_upstream / _http_request 层各记一次):
filesroot/llm_trace/<YYYYMMDD>/<call_id>_<seq>.req.json / .resp.json。铁律:
脱敏后落盘(Authorization 等敏感 header 值 → [REDACTED]);先文件后库行;
record_roundtrip 永不抛(审计不反噬计费/调用链);保留 90 天,过期清理挂记账
worker 循环(accounting._maybe_cleanup_traces 每日一次);文件需纳入部署备份
(dbackup 只备库)。查看入口:列表页「查看IO」→ api/llm_call_trace_io.dspy →
env.llm_call_trace_io(TabPanel 三页:上下文摘要/请求原文/响应原文,CodeEditor
readonly;realpath 前缀校验不逃逸 llm_trace 目录;机构隔离 org_id='0' 平台可看全部)。
4. 核心机制
4.1 选择链与轮转(gateway.py llm_call)
async def llm_call(prompt, model=None, org_id=None, user_id=None, task_ref='', **kw)
- 解析模型:调用方指定 model → 直接用;否则查组织策略主备链
- 候选对构建:模型的账号池(account.status=active)× 账号选用端点
- 区域偏好过滤:must_* 只留对应区域端点对;prefer_* 排序(匹配区域优先)
- 加权轮转:按账号余额 + 端点窗口剩余配额加权;限流冷却中的对跳过
- 调用 → 429 标记该(账号,端点)对冷却 → 换下一对;账号池耗尽 → 降备模型; 备模型也耗尽 → must 模式冒泡报错 / prefer 模式跨端点重试
- 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. 功能点清单(共 20 项)
| # | 功能点 | 表/文件 | 优先级 |
|---|---|---|---|
| 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 |
| F19 | 调用原文追踪(上行/下行脱敏落盘+查看IO弹窗+90天保留) | llm_call_trace + trace.py | P1 |
| F20 | 按项目费用统计(project_id 分组汇总) | llm_usage.project_id + llm_project_cost_query | P1 |
6. 能力类型标准(capabilities 取值,先行收敛)
| 值 | 含义 | 同步性 |
|---|---|---|
| t2t | 文本对话/生成 | sync |
| t2i | 文生图 | async_task |
| i2t | 图生文(多模态理解) | sync |
| m2t | 多媒体生文(视频/音频等多媒体输入→文本) | sync |
| t2v | 文生视频 | async_task |
| embedding | 文本向量化 | sync |
| mm-embedding | 多模态向量化 | sync |
| rerank | 重排序 | sync |
| mm-rerank | 多模态重排序 | sync |
| tts | 语音合成 | sync |
| asr | 语音识别 | sync |
新增能力类型走评审(改本文档 + init/data.json appcodes),禁止野生标签。
7. 集成点
- 宿主加载:
pipeline_app.pyinit() 加load_llm()(try/except ImportError 兜底) - 菜单:
wwwroot/index.ui主菜单加「模型管理」项 →/llm(TabPanel) - RBAC:
/llm/**注册到 superuser;静态资源 any;load_path.py 注册各路径 - i18n:zh/en 双语文案(模块内文案走 appcodes + i18n msg.txt)
- 建表:mysql.ddl.sql(json2ddl 生成 + 手写补齐),存量库 ALTER 幂等
- 兼容:旧
llm表保留,llm_bridge.py优先查新表(按 name),无则回退旧表—— 现有调用零改动,迁移平滑
8. 分期
- 一期(本次交付):全部 10 张表 + F01-F03/F05-F14/F16-F18 + F04 内置 openai-chat + F15
- 二期(触发:第一个非 t2t 模型):异步任务形态适配模板、任务轮询端点
- 三期(触发:定价平台实时价接入):成本/消费侧单价自动读取