# 路线 6:模型资源与费用
代码依据:2026-09-30 当前工作区。学习场景:多个商家的咨询和记忆提取同时等待模型,平台需要分配并发额度,并保存每次调用的 token 与人民币费用。
图中的后端路径相对 `src/zhifu/`。连续箭头表示当前进程内的调用或标明条件的执行步骤;间断箭头标注框架回调、网络请求或持久记录之间的联系。框架负责调用 `BudgetCallback`,阅读时注意调用来源。返回[路线目录](README.md)。
## 图 6A:工作进程申请并发额度
```mermaid
flowchart TD
A1["6A-1 modules/agent_runtime/worker.py
main → execute_one
开始一次任务处理"]
A2["6A-2 modules/agent_runtime/business_wait.py
reconcile
检查业务等待事件"]
A3["6A-3 modules/agent_runtime/worker.py
claim
开始领取事务"]
A4["6A-4 modules/agent_runtime/capacity.py
reservations
锁定并发策略并读取商家预留"]
A5["6A-5 modules/agent_runtime/worker.py
claim 内部
选择租户、重新统计并发、锁定运行"]
A6["6A-6 modules/agent_runtime/capacity.py
acquire
检查全局、交互、后台与租户预留"]
A7["Redis state
CAPACITY_SCRIPT 原子更新有效额度"]
A8["6A-8 modules/agent_runtime/worker.py
claim 内部
RUNNING、lease_token、lease_until"]
A9["6A-9 modules/agent_runtime/runtime.py
build_graph
由 execute_one 调用 graph.ainvoke"]
A10["6A-10 modules/agent_runtime/capacity.py
release
execute_one 的 finally 释放额度"]
A11["本次没有取得任务
execute_one 返回 False"]
A1 --> A2
A1 -->|reconcile 返回后| A3
A3 --> A4
A4 -->|返回预留配置,继续 claim| A5
A5 -->|存在有效候选| A6
A5 -->|没有候选或租户已满| A11
A6 -.->|Redis eval| A7
A7 -.->|取得额度| A8
A7 -.->|额度不足| A11
A8 -->|领取事务提交后返回运行与租约| A9
A9 -->|成功、失败或取消后的 finally| A10
A11 -->|finally| A10
```
源码导航:
- 6A-1、6A-3、6A-5、6A-8:[worker.main](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:210)、[execute_one](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:137)、[claim](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:28)。关注 `TenantDispatch.selected_at`、`TenantQuota` 与 `AgentRun.lease_token`。
- 6A-2:[business_wait.reconcile](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/business_wait.py),具体业务恢复见[路线 2](02-agent-runtime.md)。
- 6A-4、6A-6、6A-10:[reservations](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/capacity.py:62)、[acquire](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/capacity.py:75)、[release](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/capacity.py:110)。沿 `state.eval` 阅读同文件的 `CAPACITY_SCRIPT` 与 `RELEASE_SCRIPT`。
- 6A-9:[build_graph](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:244)。额度取得之后才进入图执行。
租户调度优先照顾尚未用完预留额度的商家,随后参考上次领取时间;租户内部按照创建时间与运行标识选择任务。`claim` 取得租户锁后重新检查并发数量。Redis 将全局、交互、后台和租户预留放在一次 Lua 调用内判断。
记忆任务从 [memory.worker.execute_one](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/memory/worker.py:205) 进入,同样调用 `reservations` 和 `acquire`,使用 `background` 分类;只有成功取得额度才进入 `execute_claimed`。取得的额度在 `finally` 释放。Redis 额度还有有效期限,工作进程中断后的记录由后续申请清理。
## 图 6B:一次模型调用的预占与结算
```mermaid
flowchart TD
B1["6B-1 modules/agent_runtime/runtime.py
build_graph 内部 model
保存 ModelAttempt,调用 ainvoke"]
B2["6B-2 modules/agent_runtime/budget_callback.py
BudgetCallback.on_chat_model_start
估算输入与最大输出"]
B3["6B-3 modules/agent_runtime/budget.py
reserve 内部依次调用 lock_tenant、account
保存 token 预占"]
B4["6B-4 modules/billing/ledger.py
reserve_money
按已批准价格保存金额预占"]
B5["供应商模型服务
实际请求与 usage"]
B6["6B-6 modules/agent_runtime/budget_callback.py
BudgetCallback.on_llm_end
读取 usage_metadata"]
B7["6B-7 modules/agent_runtime/budget_callback.py
BudgetCallback.on_llm_error
记录未知用量"]
B8["6B-8 modules/agent_runtime/budget.py
change_entry
结算 token 或保留 UNKNOWN"]
B9["6B-9 modules/billing/ledger.py
settle_money → charge
结算金额或保留 UNKNOWN"]
B10["6B-10 modules/agent_runtime/runtime.py
model 返回处理
独立保存 ModelAttempt.usage,继续业务检查"]
B11["6B-11 modules/agent_runtime/budget.py
mark_pending_unknown
取消或恢复时检查未结算尝试"]
B12["预占事务终止
预算错误向上传播"]
B1 -.->|LangChain 请求前回调| B2
B2 --> B3 --> B4
B4 -.->|预占事务成功后,SDK 发送 HTTP| B5
B3 -->|token 不足| B12
B4 -->|金额不足或缺少已批准价格| B12
B5 -.->|LangChain 成功回调| B6
B5 -.->|LangChain 错误回调| B7
B6 --> B8
B7 --> B8
B8 --> B9
B9 -->|成功回调结束,ainvoke 返回| B10
B1 -->|外层执行被取消或发生异常| B11
B11 --> B8
```
源码导航:
- 6B-1、6B-10:[build_graph.model](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:284)。寻找 `attempt_id`、`callbacks`、`max_retries=0` 与返回后的独立事务。
- 6B-2、6B-6、6B-7:[on_chat_model_start](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget_callback.py:49)、[on_llm_end](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget_callback.py:74)、[on_llm_error](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget_callback.py:82)。`raise_error` 与 `run_inline` 让预算检查在发送之前完成。
- 6B-3、6B-8、6B-11:[reserve](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget.py:70)、[change_entry](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget.py:106)、[mark_pending_unknown](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/budget.py:168)。
- 6B-4、6B-9:[reserve_money](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/ledger.py:46)、[settle_money](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/ledger.py:77)、[charge](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/ledger.py:32)。
`reserve` 使用独立事务,在租户锁保护下同时处理 token 与金额。存在 `MoneyPolicy` 时才启用人民币预算;已启用金额预算却没有对应 `BillingRoute` 会拒绝调用。价格保存在每次 `MoneyEntry.price` 中,结算读取该次调用的价格快照。
输入预估使用序列化字节数与协议余量,输出使用配置上限。实际结算使用供应商 `usage_metadata`,缓存 token 单独参与金额计算。金额使用整数微元,每次请求汇总后向上取整。
没有 usage 时保留预占并登记 `UNKNOWN`。`change_entry` 对重复的相同 usage 返回原状态;不同 usage 产生冲突。人工释放后收到有效迟到 usage,仍然记录已经发生的消耗。模型用量的事务先完成,之后的业务权限复验仍可能拒绝答复提交。
## 图 6C:账单差异与费用审批
```mermaid
flowchart TD
C1["6C-1 modules/billing/api.py
import_statement
授权并检查导入幂等标识"]
C2["6C-2 modules/billing/statements.py
compare
按费用记录比较金额、token 和模型"]
C3["BillingStatement
保存账单,返回比较结果"]
C4["6C-4 modules/billing/api.py
submit
员工提交 RECONCILE 申请"]
C5["6C-5 modules/billing/statements.py
validate_evidence
核对账单明细与申请"]
C6["BillingChange
PENDING,保存申请人与原目标版本"]
C7["6C-7 modules/billing/api.py
decide
另一员工审批并复验权限、版本及期限"]
C8["6C-8 modules/billing/api.py
apply_change → validate_evidence
再次核对证据"]
C9["6C-9 modules/billing/ledger.py
reconcile
更新费用、账户与审计"]
C10["BillingChange
APPROVED 或 REJECTED"]
C1 --> C2 -->|比较通过后继续 import_statement| C3
C3 -.->|员工查看差异,另一次 HTTP 提交| C4
C4 --> C5 -->|返回后保存申请| C6
C6 -.->|审批员工发起独立 HTTP 请求| C7
C7 -->|批准| C8
C8 -->|证据校验成功后| C9
C9 -->|返回 decide,同一事务保存审批结果| C10
C7 -->|拒绝| C10
```
源码导航:
- 6C-1、6C-4、6C-7、6C-8:[import_statement](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/api.py:371)、[submit](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/api.py:74)、[decide](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/api.py:191)、[apply_change](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/api.py:136)。每个入口都经由 [authorize](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/api.py:46) 检查对应权限。
- 6C-2、6C-5:[compare](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/statements.py:28)、[validate_evidence](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/statements.py:87)。
- 6C-9:[ledger.reconcile](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/ledger.py:113)。预算和价格申请也通过 `submit`、`decide`,分别进入 `apply_change` 的 `BUDGET` 与 `PRICE` 分支。
账单比较产生 `MATCHED`、`DIFFERENT`、`UNKNOWN`、`UNLINKED` 或 `ROUTE_MISMATCH`,比较操作只返回差异。费用变更由独立审批请求触发,申请人不能审批自己的申请。批准操作、费用调整和审计记录共用事务。
## 关键数据与状态
- [AgentRun、ModelAttempt、TokenAccount、TokenEntry](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/models.py):运行可以包含多次模型尝试。`ModelAttempt.id` 传给回调作为 `reference_id`;LangChain 回调的 `run_id.bytes` 用作 `TokenEntry.id` 和 `MoneyEntry.id`。
- [MoneyPolicy、BillingRoute、MoneyAccount、MoneyEntry](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/billing/models.py):分别记录金额预算、已批准价格、当日账户和单次调用金额。
- 并发额度在 Redis 记录,同时数据库保存任务租约;token 与金额账户分别记录 `reserved` 和 `consumed`。并发额度释放与费用结算具有各自的记录和条件。
## 阅读检查
1. 在 `claim` 中找到租户选择、重新统计并发和 Redis 申请的位置,解释不同锁与额度检查分别保护什么。
2. 从 `ModelAttempt.id` 跟踪到 `TokenEntry.reference_id`,说明一次运行、多次尝试和回调标识之间的关系。
3. 沿 `UNKNOWN`、重复 usage、迟到 usage 和人工审批四种情况,指出费用记录怎样变化,以及每次变化所属的事务。
## 测试阅读入口
以下文件用于追踪预期行为;本文编写时只阅读用例,没有执行其中的业务请求。
- [test_tenant_dispatch.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_tenant_dispatch.py)、[test_capacity_quotas.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_capacity_quotas.py):租户调度与预留额度。
- [test_token_budget.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_token_budget.py)、[test_interrupted_model_budget.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_interrupted_model_budget.py):预算、未知用量与中断处理。
- [test_money_budget.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_money_budget.py)、[test_billing_statements.py](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_billing_statements.py):人民币费用、账单与双人审批。