# 审计记录:后端 DashScope tokens_used 提取修复 **日期**:2026-04-30 **会话**:处理接管台账 `A1-02`,修复 DashScope `usage.models` 嵌套结构下 `tokens_used=0` 的预算追踪问题 **影响范围**:`apps/backend/app/ai/dashscope_client.py`、`apps/backend/tests/tests/unit/test_dashscope_client_usage.py` --- ## 变更背景 AI 验收文档和历史审计均记录 `tokens_used=0` 问题:DashScope Application API 返回的 usage 不是旧的顶层 `input_tokens/output_tokens`,而是 `ApplicationUsage(models=[ApplicationModelUsage(...)])`。如果无法正确提取 token 计数,会影响: - `biz.ai_run_logs.tokens_used` 写入 - admin-web AI 调用记录和预算展示 - `BudgetTracker` 的日/月 token 用量判断 调研时发现当前工作区已有一段未提交的半修复:可处理 SDK 对象形态 `usage.models`,但普通 dict 形态 `{"models": [...]}` 仍会漏算为 0。 --- ## 变更摘要 ### `apps/backend/app/ai/dashscope_client.py` - 新增 `_field_value()`,统一读取 dict、DashScope `DictMixin`、普通对象字段。 - 新增 `_safe_int()`,对 token 字段做安全整数转换,异常值按 0 处理。 - 新增 `_extract_tokens_used()`,按以下优先级提取 token: - `usage.models[*].input_tokens/output_tokens` - `usage.total_tokens` - `usage.input_tokens/output_tokens` - `DashScopeClient.call_app()` 改为调用 `_extract_tokens_used(response.usage)`,避免分支逻辑散落在主流程中。 ### `apps/backend/tests/tests/unit/test_dashscope_client_usage.py` - 新增 5 个单元测试,覆盖: - SDK `ApplicationUsage(models=[...])` - 普通 dict `{"models": [...]}` - 顶层 dict `input_tokens/output_tokens` - 对象 `total_tokens` - usage 缺失时返回 0 --- ## TDD 记录 先新增测试并运行 RED: ```powershell cd apps/backend C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/unit/test_dashscope_client_usage.py -q ``` RED 结果:5 个测试中 1 个失败,失败用例为 `test_call_app_sums_tokens_from_plain_dict_models`,实际返回 `0`,符合预期复现。 修复后再次运行同一测试: ```powershell C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/unit/test_dashscope_client_usage.py -q ``` GREEN 结果:5/5 通过。 --- ## 验证 已执行: ```powershell cd apps/backend C:\Project\NeoZQYY\.venv\Scripts\python.exe -m compileall app/ai/dashscope_client.py C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/unit/test_dashscope_client_usage.py tests/tests/unit/test_xcx_chat_ai_fallback.py::TestAIFallback::test_ai_success_returns_real_reply -q C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/integration/test_ai_full_chain.py::test_note_chain -q C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/integration/test_ai_full_chain.py::test_failure_logging -q C:\Project\NeoZQYY\.venv\Scripts\python.exe -m pytest tests/tests/test_ai_prompts_smoke.py::test_dispatcher_registers_5_handlers -q ``` 结果: - `compileall`:通过。 - token 提取与对话成功路径:6/6 通过。 - AI note chain:通过。 - failure logging:通过。 - dispatcher handler 注册:通过。 补充验证: - `tests/tests/test_ai_dispatcher.py` 全文件运行 124 秒超时。 - 单独运行 `TestProperty10ChainOrder::test_note_event` 时,失败原因为 Hypothesis `DeadlineExceeded`:单例耗时约 3.6s,超过默认 200ms;不是断言失败,也不是本次 token 提取逻辑失败。该测试债未在本次修复中处理。 --- ## 风险与影响 | 风险 | 结论 | |------|------| | 预算追踪 | 新成功调用可从 `usage.models` 正确累加 token,改善日/月预算统计可信度 | | 旧数据 | 已写入为 0 的历史 run log 不会自动回填;如需历史修正需另做数据方案 | | DashScope SDK 形态变化 | 覆盖 SDK 对象、普通 dict 和旧 `total_tokens` 形态,兼容性较当前实现更强 | | 真实外部调用 | 本次未消耗真实 DashScope token;仍需后续用真实 APP 调用验证 `success AND tokens_used>0` | --- ## 回滚 如需回滚本次修复: ```powershell git restore apps/backend/app/ai/dashscope_client.py Remove-Item -LiteralPath apps/backend/tests/tests/unit/test_dashscope_client_usage.py ``` 回滚后普通 dict `models` 形态会重新漏算为 0;若回到 HEAD 基线,SDK `ApplicationUsage.models` 形态也会重新漏算。