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