chore: 文档与 IDE 配置整理
- .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>
This commit is contained in:
252
docs/specs/P14-ai-dashscope-migration/tasks.md
Normal file
252
docs/specs/P14-ai-dashscope-migration/tasks.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 实施计划:P14 — AI 模块改造 — DashScope 迁移 + 调度器完善
|
||||
|
||||
## 概述
|
||||
|
||||
按依赖关系从底层到顶层逐步实施:环境变量/配置 → DashScope 客户端 → 防护层(熔断/限流/预算) → 运行日志 → 调度器 → API 路由 → 服务适配 → ETL 触发 → 数据库迁移 → 收尾。每个任务构建在前序任务之上,确保无孤立代码。
|
||||
|
||||
## 任务
|
||||
|
||||
- [x] 1. 环境变量与配置基础
|
||||
- [x] 1.1 重写 `apps/backend/app/ai/config.py`,实现 `AIConfig` dataclass
|
||||
- 定义所有 `DASHSCOPE_*` 环境变量字段和 `INTERNAL_API_TOKEN`
|
||||
- 实现 `from_env()` 类方法,缺失必需变量时立即抛出异常
|
||||
- 删除所有 `BAILIAN_*` 引用
|
||||
- _需求: 2.1, 2.2, 2.3, 2.5_
|
||||
|
||||
- [x] 1.2 编写属性测试:环境变量校验完整性
|
||||
- **Property 2: 环境变量校验完整性**
|
||||
- 对任意必需变量子集缺失,`from_env()` 应抛出异常,不返回含空字符串的配置
|
||||
- **验证: 需求 2.5**
|
||||
|
||||
- [x] 1.3 更新 `.env`、`.env.template`、`pyproject.toml`
|
||||
- `.env` / `.env.template`:`BAILIAN_*` → `DASHSCOPE_*`,新增 `DASHSCOPE_WORKSPACE_ID`、`INTERNAL_API_TOKEN`
|
||||
- `pyproject.toml`:移除 `openai` 依赖,新增 `dashscope` 依赖
|
||||
- _需求: 2.4, 1.7_
|
||||
|
||||
- [x] 2. DashScopeClient 核心客户端
|
||||
- [x] 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_
|
||||
|
||||
- [x] 2.2 定义异常层级
|
||||
- `DashScopeError` 基类及子类:`DashScopeApiError`、`DashScopeAuthError`、`DashScopeTimeoutError`、`DashScopeJsonParseError`、`CircuitOpenError`、`RateLimitExceededError`、`BudgetExceededError`
|
||||
- _需求: 1.5, 5.6, 6.3, 7.3_
|
||||
|
||||
- [x] 2.3 编写属性测试:重试策略正确性
|
||||
- **Property 1: 重试策略正确性**
|
||||
- 4xx 立即抛出不重试,5xx/超时/连接错误最多重试 3 次,非法 JSON 触发重试
|
||||
- **验证: 需求 1.5, 1.6**
|
||||
|
||||
- [x] 2.4 编写属性测试:Application API 响应解析
|
||||
- **Property 20: Application API 响应解析**
|
||||
- 合法 JSON 正确解析为 dict,非法 JSON 触发重试
|
||||
- **验证: 需求 4.4**
|
||||
|
||||
- [x] 3. 检查点 — 确保所有测试通过
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 4. 防护层:熔断器、限流器、Token 预算
|
||||
- [x] 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_
|
||||
|
||||
- [x] 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**
|
||||
|
||||
- [x] 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_
|
||||
|
||||
- [x] 4.4 编写属性测试:限流器窗口控制
|
||||
- **Property 7: 限流器窗口控制**
|
||||
- 窗口内未超阈值允许,超阈值拒绝;窗口外历史不影响当前判断
|
||||
- **验证: 需求 6.1, 6.2**
|
||||
|
||||
- [x] 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_
|
||||
|
||||
- [x] 4.6 编写属性测试:Token 预算检查正确性
|
||||
- **Property 8: Token 预算检查正确性**
|
||||
- 日聚合 = 当日 success 记录 tokens_used 之和;超限时 allowed=false
|
||||
- **验证: 需求 7.1, 7.3**
|
||||
|
||||
- [x] 5. AI 运行日志服务
|
||||
- [x] 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_
|
||||
|
||||
- [x] 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**
|
||||
|
||||
- [x] 6. 检查点 — 确保所有测试通过
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 7. Dispatcher 调度器重写
|
||||
- [x] 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_
|
||||
|
||||
- [x] 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_
|
||||
|
||||
- [x] 7.3 实现幂等去重逻辑
|
||||
- `_check_dedup()` — 按 `(event_type, member_id, site_id, date)` 去重
|
||||
- `is_forced=true` 时跳过去重检查
|
||||
- 写入 `ai_trigger_jobs` 记录
|
||||
- _需求: 12.1, 12.2_
|
||||
|
||||
- [x] 7.4 编写属性测试:事件类型到调用链映射
|
||||
- **Property 9: 事件类型到调用链映射**
|
||||
- 各事件类型映射到正确的 App 调用链
|
||||
- **验证: 需求 9.1, 9.2, 9.3, 11.1**
|
||||
|
||||
- [x] 7.5 编写属性测试:调用链容错不中断
|
||||
- **Property 10: 调用链容错不中断**
|
||||
- 任意步骤失败后后续步骤仍继续执行
|
||||
- **验证: 需求 9.4**
|
||||
|
||||
- [x] 7.6 编写属性测试:事件去重与强制执行
|
||||
- **Property 12: 事件去重与强制执行**
|
||||
- 重复自动事件跳过;`is_forced=true` 正常执行
|
||||
- **验证: 需求 12.1, 12.2**
|
||||
|
||||
- [x] 8. 检查点 — 确保所有测试通过 ✅ 76 passed
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 9. API 路由与端点
|
||||
- [x] 9.1 创建 `apps/backend/app/routers/internal_ai.py`,实现内部触发 API
|
||||
- `POST /api/internal/ai/trigger` 端点
|
||||
- `TriggerRequest` Pydantic 模型(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_
|
||||
|
||||
- [x] 9.2 编写属性测试:内部 API 认证
|
||||
- **Property 11: 内部 API 认证**
|
||||
- token 缺失/不匹配返回 401;匹配时正常处理
|
||||
- **验证: 需求 10.2, 10.3**
|
||||
|
||||
- [x] 9.3 适配 `apps/backend/app/routers/xcx_chat.py` SSE 端点
|
||||
- 替换 `_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_
|
||||
|
||||
- [x] 9.4 编写属性测试:SSE 事件流格式
|
||||
- **Property 17: SSE 事件流格式**
|
||||
- 零或多个 `event: message`,最终以恰好一个 `done` 或 `error` 结束
|
||||
- **验证: 需求 15.2**
|
||||
|
||||
- [x] 10. 服务层适配
|
||||
- [x] 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_
|
||||
|
||||
- [x] 10.2 编写属性测试:session_id 格式 + 对话复用规则
|
||||
- **Property 3: session_id 格式不变量**
|
||||
- **Property 4: 对话复用规则**
|
||||
- session_id 匹配 `^conv_\d+_\d+$`;复用规则按入口类型正确判断
|
||||
- **验证: 需求 3.1, 3.6**
|
||||
|
||||
- [x] 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_
|
||||
|
||||
- [x] 10.4 编写属性测试:缓存过期策略 + 查询过滤 + 保留上限
|
||||
- **Property 14: 缓存过期策略正确性**
|
||||
- **Property 15: 缓存查询过滤**
|
||||
- **Property 16: 缓存保留上限**
|
||||
- 过期时间匹配策略;查询仅返回 valid 且未过期;每 App ≤ 20,000 条
|
||||
- **验证: 需求 13.1, 13.4, 13.5**
|
||||
|
||||
- [x] 10.5 实现 App8 幂等写入 `member_retention_clue`
|
||||
- 事务中 DELETE + INSERT,同一 member 同一天只执行一次
|
||||
- 事务失败自动回滚,记录错误到 `ai_trigger_jobs.error_message`
|
||||
- _需求: 12.3, 12.4_
|
||||
|
||||
- [x] 10.6 编写属性测试:App8 幂等写入
|
||||
- **Property 13: App8 幂等写入**
|
||||
- 多次执行后同一 member 同一天记录数恒为 1
|
||||
- **验证: 需求 12.3**
|
||||
|
||||
- [x] 11. 检查点 — 确保所有测试通过 ✅ 106 passed
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 12. ETL 触发集成
|
||||
- [x] 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_
|
||||
|
||||
- [x] 13. 数据库迁移
|
||||
- [x] 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_conversations` ADD COLUMN `session_id`
|
||||
- ALTER TABLE `biz.ai_cache` ADD COLUMN `status` + CHECK 约束
|
||||
- 编写对应回滚脚本(逆序 DROP/ALTER)+ 验证 SQL(7 条)
|
||||
- _需求: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7_
|
||||
|
||||
- [x] 14. 收尾:文档与集成验证
|
||||
- [x] 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_
|
||||
|
||||
- [x] 14.2 文档同步
|
||||
- 更新 `docs/prd/ai-app-prompts.md`:环境变量映射 BAILIAN_* → DASHSCOPE_*
|
||||
- 更新 `apps/backend/README.md`:AI 模块架构说明
|
||||
- 更新 `docs/DOCUMENTATION-MAP.md`:新增文档条目
|
||||
- _需求: 2.4_
|
||||
|
||||
- [x] 15. 最终检查点 — 确保所有测试通过 ✅ 505 passed(P14 全部 15 个测试文件通过,80 个预存失败均非 P14 相关)
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
## 说明
|
||||
|
||||
- 标记 `*` 的子任务为可选(属性测试),可跳过以加速 MVP
|
||||
- 每个任务关联了具体的需求编号,确保可追溯
|
||||
- 属性测试任务标注了对应的 Property 编号和验证的需求条款
|
||||
- 检查点确保增量验证,避免问题累积
|
||||
- 实现语言:Python(与设计文档一致)
|
||||
Reference in New Issue
Block a user