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:
Neo
2026-04-06 00:02:37 +08:00
parent 8228b3fa37
commit 70324d8542
185 changed files with 13595 additions and 1219 deletions

View 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→4sHTTP 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_OPEN60 秒后)→ 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/timeoutprompt ≤ 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+ 验证 SQL7 条)
- _需求: 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 passedP14 全部 15 个测试文件通过80 个预存失败均非 P14 相关)
- 确保所有测试通过ask the user if questions arise.
## 说明
- 标记 `*` 的子任务为可选(属性测试),可跳过以加速 MVP
- 每个任务关联了具体的需求编号,确保可追溯
- 属性测试任务标注了对应的 Property 编号和验证的需求条款
- 检查点确保增量验证,避免问题累积
- 实现语言Python与设计文档一致