pricing 模块开发文档

模块概述

pricing 是 Hermes Agent 平台的通用定价引擎模块,提供基于 YAML 规则表的灵活定价计算能力。支持多维度定价条件匹配between、in、=、>、<、>=、<= 等)、公式计算、数据映射、分时段定价和批量导入导出。

在 Sage 系统中的作用:为 llmage大模型管理提供计费定价服务。每个 LLM 模型通过 llm.ppid 字段关联到 pricing 模块的定价项目pricing_program调用结束后由 llmage 的 llm_charging() 通过 buffered_charging(ppid, usages) 计算费用。

核心职责:

  • 定价项目管理创建定价项目定义定价规格pricing_spec
  • 定价项目时序支持按时间段定义不同定价策略enabled_date / expired_date
  • 定价数据管理:通过 Excel 批量导入/导出定价表,自动生成下拉菜单
  • 定价计算引擎:根据输入数据匹配定价规则,通过公式计算费用
  • 定价测试:提供测试界面验证定价规则是否正确

目录结构

pricing/
├── pyproject.toml              # 构建配置
├── setup.cfg                   # 包元数据name=pricing
├── README.md
├── pricing/                    # Python 源码包
│   ├── __init__.py             # 空
│   ├── init.py                 # 模块初始化,注册函数到 ServerEnv
│   ├── pricing.py              # 定价引擎核心PricingProgram 类 + 匹配逻辑
│   └── write_pattern.py        # Excel 定价模版生成与解析
├── json/                       # CRUD 定义bricks-framework
│   ├── pricing_program.json    # 定价项目管理
│   └── pricing_program_timing.json  # 定价项目时序
├── models/                     # 表定义xlsx 格式)
│   ├── pricing_program.xlsx
│   └── pricing_program_timing.xlsx
├── wwwroot/                    # Web 前端资源
│   ├── menu.ui                 # 菜单
│   ├── test_pricing_program.ui # 定价测试窗口
│   ├── test_pricing_program.dspy # 定价测试逻辑
│   ├── pricing_test.ui         # 定价验证窗口
│   ├── get_all_pricing_programs.dspy # 获取所有定价项目
│   ├── pi_get_all_specs.dspy   # 获取所有规格
│   ├── upload_pricing_data.dspy   # 上传定价数据
│   ├── download_pricing_data.dspy   # 下载定价数据
│   ├── download_pricing_pattern.dspy # 下载定价模版
│   ├── load_pricing_data.ui    # 加载定价数据界面
│   ├── get_platform_providers.dspy # 获取平台供应商
│   ├── imgs/                   # 图标
│   └── pricing_item/           # 定价项管理
│       ├── index.ui
│       ├── add_pricing_item.dspy
│       ├── update_pricing_item.dspy
│       ├── delete_pricing_item.dspy
│       ├── get_pricing_item.dspy
│       └── get_spec_fields_by_psid.dspy
└── script/
    └── perms.json              # RBAC 权限配置

数据库表结构

表关系

pricing_program (定价项目) ──1:N──> pricing_program_timing (定价项目时序)
pricing_program_timing ──1:N──> pricing_item (定价项)

核心表说明

表名 说明 关键字段
pricing_program 定价项目定义 id, name, ownerid, providerid, pricing_spec(YAML格式), pricing_belong, discount
pricing_program_timing 定价项目时序(分时段定价) id, ppid, name, enabled_date, expired_date, pricing_data(YAML格式)
pricing_item 定价项 id, pptid, name, formula, spec_values

pricing_spec定价规格定义格式

pricing_program.pricing_spec 字段是 YAML 格式,定义定价的维度和字段:

model:
  type: str
  label: "模型"
  options:
    - "viduq3-pro"
    - "viduq3-turbo"

resolution:
  type: str
  label: "分辨率"
  options:
    - "1024p"
    - "720p"
    - "540p"

duration:
  type: int
  label: "时长"

off_peak:
  type: int
  label: "错峰执行"
  options:
    - 0  # 正常时段
    - 1  # 错峰

price:
  type: float
  label: 单价

字段属性说明:

  • type: 字段类型str、int、float、bool、factor
  • label: 显示名称
  • options: 可选值列表(用于前端下拉菜单和定价匹配)
  • value_mode: 匹配模式(可选,默认 =
    • = 或省略:精确匹配
    • between: 区间匹配(格式:小值 ~ 大值,可加 = 表示包含端点)
    • in: 枚举匹配(格式:值1 值2 ...
    • >>=<<=: 比较运算

必须有 price 字段,其 type 必须为 float

pricing_data定价数据格式

pricing_program_timing.pricing_data 是 YAML 格式,包含 fieldspricings 两部分:

fields:
  model:
    type: str
    label: "模型"
  resolution:
    type: str
    label: "分辨率"
  formula:
    type: str
    label: "计算公式"
  price:
    type: float
    label: "单价"

pricings:
  - model: viduq3-pro
    resolution: 1024p
    price: 0.5
    formula: price * 1

  - model: viduq3-pro
    resolution: 720p
    price: 0.3
    formula: price * 1

  - model: viduq3-turbo
    resolution: 1024p
    price: 0.2
    formula: price * 1

定价匹配规则

调用 get_pricing_from_ymalstr(config_data, yamlstr) 时:

  1. 遍历 pricings 中的每条定价规则
  2. 对每条规则的每个字段,用 check_value() 判断输入数据是否匹配:
    • value_mode == '=': 精确匹配 data_value == spec_value
    • value_mode == 'between': 区间匹配(如 0 ~ 10000 表示 0 <= value < 10000
    • value_mode == 'in': 枚举匹配(如 gpt-4 gpt-3.5 表示其中之一)
    • value_mode == '>'/'<'>='/<=': 比较运算
  3. 所有字段都匹配的定价规则才会被选中
  4. 执行 formula 字段中定义的计算公式,得到 amount
  5. 返回所有匹配的定价项列表

数据映射mappings

model_mappings:  # 模型名称映射
  "doubao-seed-2-0-pro-260215": "doubao-seed-2-0-pro"
  "doubao-seed-2-0-lite-260215": "doubao-seed-2-0-lite"

输入数据中的值会先通过 mappings 转换,再与定价规则匹配。


Python API

模块初始化

def load_pricing():
    env = ServerEnv()
    env.get_pricing_program = get_pricing_program
    env.write_pricing_patten = PricingProgram.write_pricing_patten
    env.pricing_program_charging = PricingProgram.charging
    env.buffered_charging = PricingProgram.buffered_charging
    env.load_pricing_data = PricingProgram.load_pricing_data
    env.get_pricing_program = PricingProgram.get_pricing_program
    env.test_pricing = test_pricing

其他模块的 .dspy 文件可通过 globals() 直接使用这些函数。

PricingProgram 类pricing.py

定价计算

# 带缓存的计费推荐llmage 使用此方法)
pp = PricingProgram()
prices = await pp.buffered_charging(ppid, data)
# data: {'prompt_tokens': 52, 'completion_tokens': 1416, 'model': 'xxx', ...}
# 返回: [DictObject(amount=0.001, cost=0.001*discount, data={...}, formula=...)]

# 不带缓存的计费(传入 sor
prices = await PricingProgram.charging(sor, ppid, data)

定价数据管理

# 获取定价项目(带缓存,按日期)
d = await PricingProgram.get_ppid_pricing(ppid)
# 返回: DictObject(name, ownerid, providerid, pricing_belong, discount, pricing_data)

# 获取定价项目定义
pp = await PricingProgram.get_pricing_program(ppid)

# 从 Excel 文件加载定价数据
await PricingProgram.load_pricing_data(pptid, webpath_xlsx)

# 生成定价 Excel 模版
fpath = await PricingProgram.write_pricing_patten(request, ppid)

YAML/DB 转换

# DB 到应用YAML 字符串 → dict
PricingProgram.pp_db2app(pp)         # pricing_program
PricingProgram.ppt_db2app(ppt)       # pricing_program_timing

# 应用到 DBdict → YAML 字符串)
PricingProgram.pp_app2db(pp)
PricingProgram.ppt_app2db(ppt)

独立函数

# 测试定价(给定 pptid 和输入数据,返回总金额)
amount = await test_pricing(pptid, data)

# 获取定价项目定义
pp = await get_pricing_program(ppid)

核心匹配引擎

# 静态方法,直接在内存 YAML 上匹配
prices = PricingProgram.get_pricing_from_ymalstr(config_data, yamlstr)
# config_data: 输入数据字典
# yamlstr: pricing_data 的 YAML 字符串
# 返回: 匹配的定价项列表,每项包含 amount通过 formula 计算)

前端页面

pricing_program.json — 定价项目管理

  • ownerid(登录用户所在组织)过滤
  • pricing_spec 字段不在浏览器中显示YAML 格式复杂)
  • 子表:pricing_program_timing(定价项目时序)
  • 工具栏:测试 按钮,弹出测试窗口

pricing_program_timing.json — 定价项目时序

  • ownerid 过滤
  • 子表:pricing_item(定价细项)
  • 工具栏:
    • 定价模版:下载 Excel 模版
    • 上传定价数据:上传 Excel 定价文件
    • 下载定价数据:下载当前定价数据
    • 验证定价:测试定价规则

Excel 模版与数据导入

生成模版

通过 write_pricing_patten(request, ppid) 生成 Excel 模版:

  • 根据 pricing_spec 中的字段生成列头
  • options 字段自动生成 Excel 下拉菜单DataValidation
  • bool 类型生成 TRUE/FALSE 下拉
  • factor 类型字段不参与定价(仅作为因子)

导入定价数据

通过 load_pricing_data(pptid, webpath_xlsx) 从 Excel 导入:

  1. 解析 Excel 第一行作为字段名
  2. 将 label 映射回字段名Excel 列头是 label需映射回 spec 中的 key
  3. 转换为 YAML 格式存入 pricing_program_timing.pricing_data

在 Sage 系统中的协同关系

llmage (模型管理) ──→ llm.ppid ──→ pricing_program.id
                                        │
                                        ├── pricing_spec (定价维度定义)
                                        │
                                        └── pricing_program_timing ──→ pricing_data (定价表)
                                                                        │
                                                                        └── pricings[] ──→ formula 计算金额

调用流程

  1. llmage 调用外部 LLM API获取 token 用量prompt_tokens, completion_tokens
  2. 记录到 llmusageaccounting_status='created'
  3. 后台记账任务 backend_accounting() 每 10 秒轮询
  4. 调用 llm_charging(ppid, llmusage)buffered_charging(ppid, usages)
  5. pricing 模块匹配定价规则,计算 amountcost
  6. 更新 llmusage.amountllmusage.cost
  7. 调用 llm_accounting() 记账到 accounting 模块

HTTP API 接口

获取定价展示数据

GET /pricing/api/get_pricing_display.dspy?ppid={ppid}
Authorization: Bearer {token}

说明: 返回指定定价项目的可读定价数据,供前端展示。自动解析 pricing_data 字段,转换为人类可读的价格表。

返回示例Token 定价):

{
  "status": "ok",
  "data": {
    "ppid": "5i1JIpqERgCWqKQ4DCegD",
    "name": "通义千问 qwen3.7-max",
    "pricing_type": "per_use",
    "items": [
      {
        "filters": {"model": "qwen3.7-max"},
        "filter_labels": {"模型": "qwen3.7-max"},
        "price_factors": [
          {
            "factor": "uncache_tokens",
            "label": "非缓存tokens",
            "unit_price": 6.0,
            "unit": "百万",
            "unit_label": "元/百万"
          }
        ]
      },
      {
        "filters": {"model": "qwen3.7-max"},
        "filter_labels": {"模型": "qwen3.7-max"},
        "price_factors": [
          {
            "factor": "cached_tokens",
            "label": "缓存tokens",
            "unit_price": 1.2,
            "unit": "百万",
            "unit_label": "元/百万"
          }
        ]
      }
    ],
    "display_text": "【通义千问 qwen3.7-max】定价:\n  - 非缓存tokens: 6.0 元/百万\n  - 缓存tokens: 1.2 元/百万"
  }
}

返回示例(视频生成定价):

{
  "status": "ok",
  "data": {
    "ppid": "vidu_video_pricing",
    "name": "viduq3视频定价",
    "pricing_type": "per_use",
    "items": [
      {
        "filters": {"model": "viduq3-turbo", "resolution": "1080p", "off_peak": "0"},
        "filter_labels": {"模型": "viduq3-turbo", "分辨率": "1080p", "错峰": "0"},
        "price_factors": [
          {
            "factor": "duration",
            "label": "时长",
            "unit_price": 0.56,
            "unit": "秒",
            "unit_label": "元/秒"
          }
        ]
      }
    ],
    "display_text": "【viduq3视频定价】定价:\n  - 时长: 0.56 元/秒 [model=viduq3-turbo, resolution=1080p, off_peak=0]"
  }
}

返回字段说明:

  • ppid: 定价项目 ID
  • name: 定价项目名称
  • pricing_type: 定价类型(per_use 按次计费)
  • items: 定价项列表
    • filters: 过滤条件(如适用模型、分辨率等)
    • filter_labels: 过滤条件的中文标签
    • price_factors: 计价因子列表
      • factor: 因子字段名
      • label: 因子中文名称
      • unit_price: 单位价格(已转换为展示价,无需再乘)
      • unit: 单位(百万、秒、次等)
      • unit_label: 单位标签(元/百万、元/秒等)
      • tiered: 阶梯定价(仅当价格不同时展示)
  • display_text: 人类可读的价格表文本

pricing_data 使用指南

两种格式

pricing_data 支持两种格式:

1. 新格式推荐price_factors + unit_prices + unit

适用于简单定价场景,直接指定计价因子和单位价格。

示例 1Token 定价(文本生成)

unit_values:
  百万: 1000000
fields:
  model:
    type: str
    role: filter
    label: 模型
  uncache_tokens:
    type: float
    role: factor
    label: 非缓存tokens
  cached_tokens:
    type: float
    role: factor
    label: 缓存tokens
  completion_tokens:
    type: float
    role: factor
    label: 输出tokens
pricings:
  # 非缓存输入定价
  - price_factors: uncache_tokens
    unit_prices: 6.0
    unit: 百万
    filters:
      - model: qwen3.7-max
  
  # 缓存输入定价
  - price_factors: cached_tokens
    unit_prices: 1.2
    unit: 百万
    filters:
      - model: qwen3.7-max
  
  # 输出定价
  - price_factors: completion_tokens
    unit_prices: 18.0
    unit: 百万
    filters:
      - model: qwen3.7-max

示例 2视频生成定价多维度过滤

unit_values:
  : 1
fields:
  model:
    type: str
    role: filter
    label: 模型
  resolution:
    type: str
    role: filter
    label: 分辨率
  duration:
    type: int
    role: filter
    label: 时长(秒)
    value_mode: between
  off_peak:
    type: str
    role: filter
    label: 错峰执行
  flat:
    type: float
    role: factor
    label: 固定费用
pricings:
  - price_factors: flat
    unit_prices: 85.0
    unit: 
    filters:
      - model: viduq2-pro
      - resolution: 1080p
      - duration: '1'
      - off_peak: false
  
  - price_factors: flat
    unit_prices: 43.0
    unit: 
    filters:
      - model: viduq2-pro
      - resolution: 1080p
      - duration: '1'
      - off_peak: true
  
  - price_factors: duration
    unit_prices: 0.56
    unit: 
    filters:
      - model: viduq3-turbo
      - resolution: 1080p
      - off_peak: 0

2. 旧格式formula公式计算

适用于复杂定价场景,使用 Python 表达式计算金额。

示例:按 Token 数量计算

fields:
  model:
    type: str
    role: filter
    label: 模型
  prompt_tokens:
    type: float
    role: factor
    label: 输入tokens
  completion_tokens:
    type: float
    role: factor
    label: 输出tokens
pricings:
  - model: gpt-4
    formula: (3.2 * prompt_tokens + 16 * completion_tokens) / 1000000.0
  
  - model: gpt-3.5
    formula: (0.5 * prompt_tokens + 1.5 * completion_tokens) / 1000000.0

关键规则

  1. unit_values 必须定义:在 pricing_data 顶部定义 unit_values,如 百万: 1000000,用于单位换算
  2. unit_prices 存储展示价:新格式中 unit_prices 直接存储展示价格(如 6.0 表示 6.0 元/百万),不需要再乘以单位值
  3. filters 支持两种格式
    • 列表格式:filters: [{model: xxx}, {resolution: yyy}]
    • 字典格式:直接作为字段写在 pricing 条目中
  4. value_mode 默认精确匹配:不指定时做 = 精确匹配,可指定 betweenin><
  5. formula 可引用任意字段:旧格式中 formula 可引用 config_data 中的任意字段名
  6. 多条件匹配:如果多条定价规则都匹配,全部返回(不是只返回第一条)

关键设计要点

  1. YAML 驱动定价规则:所有定价逻辑用 YAML 描述,支持字段定义、选项、匹配模式、计算公式
  2. 多维度条件匹配:支持 between、in、=、>、<、>=、<= 等多种匹配模式
  3. 公式计算:每条定价规则可定义独立的计算公式(如 (3.2 * prompt_tokens + 16 * completion_tokens) / 1000000
  4. 数据映射mappings:输入值可预先映射(如 doubao-seed-2-0-pro-260215doubao-seed-2-0-pro
  5. 分时段定价pricing_program_timing 支持按 enabled_date/expired_date 定义不同时期的定价
  6. 折扣支持pricing_program.discount 字段控制折扣,最终 cost = amount * discount
  7. Excel 批量操作:通过 Excel 导入导出定价数据,自动生成下拉菜单,降低维护门槛
  8. 内存缓存PricingProgram.pricing_data{ppid}.{date} 键缓存,避免重复查询
  9. 定价项pricing_item:可单独管理定价细项,用于更复杂的定价结构

依赖关系

pricing
├── sqlor          # 数据库 ORM
├── apppublic      # 工具库日志、唯一ID、时间工具等
├── ahserver       # Web 服务器框架
├── openpyxl       # Excel 文件读写(依赖安装)
└── pyyaml         # YAML 解析(依赖安装)

开发注意事项

  1. pricing_spec 必须包含 price 字段:且 type 为 float否则定价匹配会失败
  2. formula 字段:每条定价规则的 formula 是 Python 表达式,通过 eval() 执行,可引用 config_data 中的任意字段
  3. value_mode 默认值为 =:不指定时做精确匹配
  4. between 区间格式0 ~ 100 表示 0 <= value < 1000 =~ 100 表示 0 <= value <= 100
  5. factor 类型字段:在 Excel 模版中被跳过(不生成列),仅作为计算因子使用
  6. pricing_data 结构:必须是 {fields: {...}, pricings: [...]} 格式,缺少任一部分都会报错
  7. 缓存过期策略:缓存按日期键管理,每个 ppid 只保留最近 2 天的缓存
  8. discount 应用discount 在 buffered_charging()charging() 中乘以 amount 得到 cost
  9. Excel 列头是 label:导入时通过 label 映射回 spec 中的 key 字段名label 必须完全匹配
  10. 多匹配规则:如果多条定价规则都匹配输入数据,全部返回(不是只返回第一条),调用方需自行处理
Description
定价模块
Readme 1.3 MiB
Languages
Python 99.9%
Shell 0.1%