- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro) - CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/ - 新增 /spec-close、/pre-change 两个工作流命令 - DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表) - BD_Manual → BD_manual 命名统一(48 个文件) - 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数) - 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表) - 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档) - docs/database/README.md 索引更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
实施计划:P14 — AI 模块改造 — DashScope 迁移 + 调度器完善
概述
按依赖关系从底层到顶层逐步实施:环境变量/配置 → DashScope 客户端 → 防护层(熔断/限流/预算) → 运行日志 → 调度器 → API 路由 → 服务适配 → ETL 触发 → 数据库迁移 → 收尾。每个任务构建在前序任务之上,确保无孤立代码。
任务
-
1. 环境变量与配置基础
-
1.1 重写
apps/backend/app/ai/config.py,实现AIConfigdataclass- 定义所有
DASHSCOPE_*环境变量字段和INTERNAL_API_TOKEN - 实现
from_env()类方法,缺失必需变量时立即抛出异常 - 删除所有
BAILIAN_*引用 - 需求: 2.1, 2.2, 2.3, 2.5
- 定义所有
-
1.2 编写属性测试:环境变量校验完整性
- Property 2: 环境变量校验完整性
- 对任意必需变量子集缺失,
from_env()应抛出异常,不返回含空字符串的配置 - 验证: 需求 2.5
-
1.3 更新
.env、.env.template、pyproject.toml.env/.env.template:BAILIAN_*→DASHSCOPE_*,新增DASHSCOPE_WORKSPACE_ID、INTERNAL_API_TOKENpyproject.toml:移除openai依赖,新增dashscope依赖- 需求: 2.4, 1.7
-
-
2. DashScopeClient 核心客户端
-
2.1 创建
apps/backend/app/ai/dashscope_client.py,实现DashScopeClient类call_app()— App2~8 单轮调用,asyncio.to_thread()包装,返回(parsed_json, tokens_used, new_session_id)call_app_stream()— App1 流式调用,线程消费同步迭代器 +asyncio.Queue桥接 async generator_call_with_retry()— 指数退避重试(1s→2s→4s),HTTP 4xx 不重试,5xx/超时/连接错误重试- 非合法 JSON 响应纯重试(最大 3 次),不做本地修复
- 需求: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6
-
2.2 定义异常层级
DashScopeError基类及子类:DashScopeApiError、DashScopeAuthError、DashScopeTimeoutError、DashScopeJsonParseError、CircuitOpenError、RateLimitExceededError、BudgetExceededError- 需求: 1.5, 5.6, 6.3, 7.3
-
2.3 编写属性测试:重试策略正确性
- Property 1: 重试策略正确性
- 4xx 立即抛出不重试,5xx/超时/连接错误最多重试 3 次,非法 JSON 触发重试
- 验证: 需求 1.5, 1.6
-
2.4 编写属性测试:Application API 响应解析
- Property 20: Application API 响应解析
- 合法 JSON 正确解析为 dict,非法 JSON 触发重试
- 验证: 需求 4.4
-
-
3. 检查点 — 确保所有测试通过
- 确保所有测试通过,ask the user if questions arise.
-
4. 防护层:熔断器、限流器、Token 预算
-
4.1 创建
apps/backend/app/ai/circuit_breaker.py,实现CircuitBreaker类- 按
app_id独立计数,_BreakerState内部状态 check()/record_success()/record_failure()方法- 状态机:CLOSED → OPEN(连续 5 次失败)→ HALF_OPEN(60 秒后)→ CLOSED/OPEN
- 需求: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6
- 按
-
4.2 编写属性测试:熔断器 app_id 隔离 + 状态机转换
- Property 5: 熔断器 app_id 隔离
- Property 6: 熔断器状态机转换
- 不同 app_id 互不影响;状态转换符合 CLOSED→OPEN→HALF_OPEN→CLOSED/OPEN 规则
- 验证: 需求 5.1, 5.2, 5.3, 5.4, 5.5
-
4.3 创建
apps/backend/app/ai/rate_limiter.py,实现RateLimiter类- 滑动窗口内存计数器
check_user_rate()— App1 每用户每分钟 10 次check_store_rate()— App2~8 每门店每小时 100 次- 需求: 6.1, 6.2, 6.3, 6.4
-
4.4 编写属性测试:限流器窗口控制
- Property 7: 限流器窗口控制
- 窗口内未超阈值允许,超阈值拒绝;窗口外历史不影响当前判断
- 验证: 需求 6.1, 6.2
-
4.5 创建
apps/backend/app/ai/budget_tracker.py,实现BudgetTracker类check_budget()从ai_run_logs聚合日/月 token 消耗- 日预算 100,000 / 月预算 2,000,000
- 返回
BudgetStatus(allowed, daily_used, monthly_used, reason) - 需求: 7.1, 7.2, 7.3, 7.4, 7.5
-
4.6 编写属性测试:Token 预算检查正确性
- Property 8: Token 预算检查正确性
- 日聚合 = 当日 success 记录 tokens_used 之和;超限时 allowed=false
- 验证: 需求 7.1, 7.3
-
-
5. AI 运行日志服务
-
5.1 创建
apps/backend/app/ai/run_log_service.py,实现AIRunLogService类create_log()— 创建 pending 记录update_running()/update_success()/update_failed()/update_timeout()— 状态转换get_daily_token_usage()/get_monthly_token_usage()— 聚合查询request_prompt截断为前 2000 字符- 需求: 16.1, 16.2, 16.3, 16.4, 16.5, 16.6
-
5.2 编写属性测试:AI 运行日志状态机 + Prompt 截断
- Property 18: AI 运行日志状态机
- Property 19: Prompt 截断不变量
- 状态转换正确(pending→running→success/failed/timeout);prompt ≤ 2000 字符
- 验证: 需求 16.1, 16.2, 16.3, 16.4, 16.5, 16.6
-
-
6. 检查点 — 确保所有测试通过
- 确保所有测试通过,ask the user if questions arise.
-
7. Dispatcher 调度器重写
-
7.1 重写
apps/backend/app/ai/dispatcher.py,实现AIDispatcher类- 移除所有
asyncio.run()和asyncio.new_event_loop()调用 - 所有入口改为
async def,用asyncio.create_task()发起后台任务 - 超时用
asyncio.wait_for() - 集成 CircuitBreaker、RateLimiter、BudgetTracker
_run_step()— 单步执行:熔断检查→限流检查→预算检查→调用→记录日志- 需求: 8.1, 8.2, 8.3, 8.4
- 移除所有
-
7.2 实现事件触发链编排
_handle_consumption()— 消费事件:App3→App8→App7(无助教)/ +App4→App5(有助教)_handle_note()— 备注事件:App6→App8_handle_task_assigned()— 任务分配:App4→App5_handle_dws_completed()— DWS 完成:App2 预生成(8 个时间维度)- 调用链某步失败时记录错误,后续步骤使用已有缓存继续
- 需求: 9.1, 9.2, 9.3, 9.4, 9.5, 11.1, 11.2, 11.3, 11.4
-
7.3 实现幂等去重逻辑
_check_dedup()— 按(event_type, member_id, site_id, date)去重is_forced=true时跳过去重检查- 写入
ai_trigger_jobs记录 - 需求: 12.1, 12.2
-
7.4 编写属性测试:事件类型到调用链映射
- Property 9: 事件类型到调用链映射
- 各事件类型映射到正确的 App 调用链
- 验证: 需求 9.1, 9.2, 9.3, 11.1
-
7.5 编写属性测试:调用链容错不中断
- Property 10: 调用链容错不中断
- 任意步骤失败后后续步骤仍继续执行
- 验证: 需求 9.4
-
7.6 编写属性测试:事件去重与强制执行
- Property 12: 事件去重与强制执行
- 重复自动事件跳过;
is_forced=true正常执行 - 验证: 需求 12.1, 12.2
-
-
8. 检查点 — 确保所有测试通过 ✅ 76 passed
- 确保所有测试通过,ask the user if questions arise.
-
9. API 路由与端点
-
9.1 创建
apps/backend/app/routers/internal_ai.py,实现内部触发 APIPOST /api/internal/ai/trigger端点TriggerRequestPydantic 模型(event_type, connector_type, site_id, member_id, payload)verify_internal_token()依赖注入,校验Authorization: Internal-Token {token}- 写入
ai_trigger_jobs后立即返回{trigger_job_id, status: "pending"},后台异步执行 - 在 FastAPI app 中注册路由
- 需求: 10.1, 10.2, 10.3, 10.4, 10.5
-
9.2 编写属性测试:内部 API 认证
- Property 11: 内部 API 认证
- token 缺失/不匹配返回 401;匹配时正常处理
- 验证: 需求 10.2, 10.3
-
9.3 适配
apps/backend/app/routers/xcx_chat.pySSE 端点- 替换
_get_bailian_client()→_get_dashscope_client() - 调用
client.call_app_stream(app_id, prompt, session_id, biz_params) - 构建
prompt+biz_params(User_ID, Role, Nickname) - 保持 SSE 事件格式:
event: message/event: done/event: error - 流结束后记录
ai_run_logs - 需求: 15.1, 15.2, 15.3, 15.4, 3.3
- 替换
-
9.4 编写属性测试:SSE 事件流格式
- Property 17: SSE 事件流格式
- 零或多个
event: message,最终以恰好一个done或error结束 - 验证: 需求 15.2
-
-
10. 服务层适配
-
10.1 适配
apps/backend/app/services/ai/chat_service.py— session_id 双轨逻辑- 新对话生成 session_id(格式
conv_{conversation_id}_{created_timestamp}) - 每条消息同时写入本地
ai_messages - session_id 过期时从本地加载最近 20 条历史重建
- 保持对话复用规则:task 无时限、customer/coach 3 天、general 新建
- 需求: 3.1, 3.2, 3.4, 3.5, 3.6
- 新对话生成 session_id(格式
-
10.2 编写属性测试:session_id 格式 + 对话复用规则
- Property 3: session_id 格式不变量
- Property 4: 对话复用规则
- session_id 匹配
^conv_\d+_\d+$;复用规则按入口类型正确判断 - 验证: 需求 3.1, 3.6
-
10.3 适配
apps/backend/app/services/ai/cache_service.py— 缓存状态与过期策略- 新增
status字段处理(valid/expired/invalidated/generating) - 写入前设
generating,完成后设valid - 查询仅返回
status='valid'且未过期的记录 - 按 App 类型设置过期时间(App2 当日 23:59:59、App3~5/7/8 七天、App6 三十天)
- App2~8 每 App 保留最新 20,000 条,超限清理最旧记录
- 需求: 13.1, 13.2, 13.3, 13.4, 13.5
- 新增
-
10.4 编写属性测试:缓存过期策略 + 查询过滤 + 保留上限
- Property 14: 缓存过期策略正确性
- Property 15: 缓存查询过滤
- Property 16: 缓存保留上限
- 过期时间匹配策略;查询仅返回 valid 且未过期;每 App ≤ 20,000 条
- 验证: 需求 13.1, 13.4, 13.5
-
10.5 实现 App8 幂等写入
member_retention_clue- 事务中 DELETE + INSERT,同一 member 同一天只执行一次
- 事务失败自动回滚,记录错误到
ai_trigger_jobs.error_message - 需求: 12.3, 12.4
-
10.6 编写属性测试:App8 幂等写入
- Property 13: App8 幂等写入
- 多次执行后同一 member 同一天记录数恒为 1
- 验证: 需求 12.3
-
-
11. 检查点 — 确保所有测试通过 ✅ 106 passed
- 确保所有测试通过,ask the user if questions arise.
-
12. ETL 触发集成
- 12.1 修改
apps/etl/connectors/feiqiu/tasks/相关 DWS 任务- DWS 任务完成后通过
requests发送POST /api/internal/ai/trigger - 携带
Authorization: Internal-Token {INTERNAL_API_TOKEN}Header - 事件类型:
dws_completed(App2 预生成)/consumption(消费事件链) - 新增
utils/ai_trigger.py工具函数 +BaseDwsTask.AI_TRIGGER_EVENT类属性 FinanceDailyTask→dws_completed,MemberConsumptionTask→consumption- 需求: 10.6
- DWS 任务完成后通过
- 12.1 修改
-
13. 数据库迁移
- 13.1 编写迁移脚本
db/zqyy_app/migrations/2026-03-22__p14_ai_module.sql- CREATE TABLE
biz.ai_run_logs(含 3 个索引) - CREATE TABLE
biz.ai_trigger_jobs(含 3 个索引,含去重部分索引) - ALTER TABLE
biz.ai_conversationsADD COLUMNsession_id - ALTER TABLE
biz.ai_cacheADD COLUMNstatus+ CHECK 约束 - 编写对应回滚脚本(逆序 DROP/ALTER)+ 验证 SQL(7 条)
- 需求: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7
- CREATE TABLE
- 13.1 编写迁移脚本
-
14. 收尾:文档与集成验证
-
14.1 更新 BD 手册
- 更新
docs/database/BD_Manual_ai_tables.md:新增ai_run_logs、ai_trigger_jobs表结构 - 更新
ai_cache.status和ai_conversations.session_id字段说明 - 需求: 14.1, 14.2, 14.3, 14.4
- 更新
-
14.2 文档同步
- 更新
docs/prd/ai-app-prompts.md:环境变量映射 BAILIAN_* → DASHSCOPE_* - 更新
apps/backend/README.md:AI 模块架构说明 - 更新
docs/DOCUMENTATION-MAP.md:新增文档条目 - 需求: 2.4
- 更新
-
-
15. 最终检查点 — 确保所有测试通过 ✅ 505 passed(P14 全部 15 个测试文件通过,80 个预存失败均非 P14 相关)
- 确保所有测试通过,ask the user if questions arise.
说明
- 标记
*的子任务为可选(属性测试),可跳过以加速 MVP - 每个任务关联了具体的需求编号,确保可追溯
- 属性测试任务标注了对应的 Property 编号和验证的需求条款
- 检查点确保增量验证,避免问题累积
- 实现语言:Python(与设计文档一致)