Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a070cc4600 | |||
| a41e2c28d4 | |||
| ee027396a0 | |||
| df0b3fa267 |
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# Local editors and note tools
|
||||||
|
.obsidian/
|
||||||
|
.agents/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Local environment and secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.template
|
||||||
|
|
||||||
|
# Python bytecode and caches
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# Django/runtime generated files
|
||||||
|
db.sqlite3
|
||||||
|
*.sqlite3
|
||||||
|
media/
|
||||||
|
staticfiles/
|
||||||
|
|
||||||
|
# Test, lint, and coverage caches
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Logs and temporary files
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
@@ -1 +1,137 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
本文件是给 AI 和开发者看的后端协作导航。开发任何后端功能前,先按本文件判断要读哪些 spec、contract、plan 和 checklist。
|
||||||
|
|
||||||
|
## 项目一句话
|
||||||
|
|
||||||
|
第一版要做一个公司内部 AI 协作最小闭环:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板一句话
|
||||||
|
-> AI 草稿
|
||||||
|
-> 人工确认
|
||||||
|
-> 创建事项或定时提醒
|
||||||
|
-> 飞书通知
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 页面看到结果和失败记录
|
||||||
|
```
|
||||||
|
|
||||||
|
这不是完整任务系统、完整 AI 工作台、完整看板,也不是全自动 Agent。
|
||||||
|
|
||||||
|
## 当前技术栈
|
||||||
|
|
||||||
|
| 层级 | 约定 |
|
||||||
|
| ----- | ------------------------------------------- |
|
||||||
|
| 后端 | Python Django + Django REST Framework |
|
||||||
|
| ORM | Django ORM + Django Migrations |
|
||||||
|
| 数据库 | PostgreSQL 18.x,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL |
|
||||||
|
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,用于老板秘书会话、对话消息、BotContext 快照、模型请求/响应和 Agent 决策快照;AI 记忆表不能替代正式业务表 |
|
||||||
|
| Agent 编排 | 第一版不引入 LangChain / LangGraph,先用 Django service 层实现轻量 `AiSecretaryAgent` |
|
||||||
|
| 定时任务 | Django management command + 独立 scheduler 进程 |
|
||||||
|
| 后台管理 | Django Admin |
|
||||||
|
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
|
||||||
|
| 飞书 | 飞书身份登录、机器人私聊、个人消息、交互卡片和回调 |
|
||||||
|
| 部署 | Docker Compose + Nginx + Gunicorn |
|
||||||
|
| | |
|
||||||
|
## 渐进式阅读规则
|
||||||
|
|
||||||
|
普通任务只读:
|
||||||
|
|
||||||
|
1. `AGENTS.md`
|
||||||
|
2. 当前相关 `docs/specs/*.md`
|
||||||
|
3. 当前 active plan
|
||||||
|
|
||||||
|
涉及接口时再读:
|
||||||
|
|
||||||
|
1. `docs/contracts/API接口约定.md`
|
||||||
|
|
||||||
|
涉及数据库或字段时再读:
|
||||||
|
|
||||||
|
1. `docs/contracts/数据对象约定.md`
|
||||||
|
|
||||||
|
涉及老板 AI 秘书、Agent、意图识别、上下文和草稿生成时先读:
|
||||||
|
|
||||||
|
1. `docs/specs/01_老板AI秘书与AI草稿.md`
|
||||||
|
2. `docs/contracts/数据对象约定.md`
|
||||||
|
3. `docs/contracts/API接口约定.md`
|
||||||
|
|
||||||
|
涉及 AI 对话记忆、上下文恢复、多账号会话或 PostgreSQL `jsonb` 上下文表时再读:
|
||||||
|
|
||||||
|
1. `docs/specs/01_老板AI秘书与AI草稿.md`
|
||||||
|
2. `docs/contracts/数据对象约定.md`
|
||||||
|
|
||||||
|
只处理任务、提醒、通知等业务状态时,以 PostgreSQL 正式业务表为准,不要把 AI 记忆表当业务事实源。
|
||||||
|
|
||||||
|
涉及状态流转时再读:
|
||||||
|
|
||||||
|
1. `docs/contracts/状态流转约定.md`
|
||||||
|
|
||||||
|
涉及权限、安全、日志脱敏、飞书验签、幂等或敏感信息时再读:
|
||||||
|
|
||||||
|
1. `docs/contracts/安全权限日志约定.md`
|
||||||
|
|
||||||
|
涉及飞书时再读:
|
||||||
|
|
||||||
|
1. `docs/specs/03_飞书通知与反馈.md`
|
||||||
|
2. `docs/contracts/API接口约定.md`
|
||||||
|
3. `docs/contracts/安全权限日志约定.md`
|
||||||
|
|
||||||
|
涉及 Review 或提交前检查时再读:
|
||||||
|
|
||||||
|
1. `docs/checklists/AI生成代码检查清单.md`
|
||||||
|
2. `docs/checklists/后端Review清单.md`
|
||||||
|
3. 本次新增或修改的 tests
|
||||||
|
|
||||||
|
不要每次全量读取所有 docs,避免上下文过大、重点变散。
|
||||||
|
|
||||||
|
## 事实源优先级
|
||||||
|
|
||||||
|
1. `docs/contracts/` 是字段、接口、状态、错误码、安全权限日志和全局约定的唯一事实源。
|
||||||
|
2. `docs/specs/` 说明业务边界和模块目标,不复制完整字段表。
|
||||||
|
3. `docs/plans/active/` 是当前施工依据,不用于偷偷改变业务边界。
|
||||||
|
4. `docs/checklists/` 和 tests 是 Review 闸门;tests 是真实约束。
|
||||||
|
|
||||||
|
如果 spec 和 contract 冲突,以 contract 为准,并提出文档修正建议。
|
||||||
|
|
||||||
|
PostgreSQL 是唯一事实源,保存用户、人员映射、AI 草稿、任务、提醒、通知、反馈、失败记录、操作日志、AI 对话消息和 BotContext 快照。
|
||||||
|
|
||||||
|
AI 对话记忆也保存在 PostgreSQL 中,第一阶段使用 `secretary_conversations`、`secretary_messages`、`bot_contexts` 等表;`bot_context_snapshot`、`extracted_facts`、模型请求/响应摘要等结构化弹性字段使用 `jsonb`。
|
||||||
|
|
||||||
|
PostgreSQL 中的 AI 记忆表只能辅助 AI 理解、追问、恢复会话和生成草稿,不能直接作为创建任务、提醒或通知的最终依据。
|
||||||
|
|
||||||
|
## 生成代码禁止事项
|
||||||
|
|
||||||
|
1. 不允许 AI 未经人工确认直接创建事项、提醒或发送通知。
|
||||||
|
2. 不允许普通员工给别人创建事项或提醒。
|
||||||
|
3. 不允许绕过后端权限校验,只靠前端隐藏按钮。
|
||||||
|
4. 不允许自动操作交易系统、公司历史数据库、后台管理系统或其他外部业务系统。
|
||||||
|
5. 不允许把 API Key、App Secret、飞书 token、OAuth code/state、回调验签密钥、一次性操作 token、完整手机号、完整邮箱写进代码或普通日志。
|
||||||
|
6. 不允许把老板解释中的游戏化表达写进事项标题、接收人、时间、反馈要求等结构化字段。
|
||||||
|
7. 不允许把第一版明确不做的功能混入主线。
|
||||||
|
8. 不允许只写 AI 记忆表就认为任务、提醒或通知已经创建成功。
|
||||||
|
9. 不允许 AI 根据 PostgreSQL AI 记忆表绕过正式业务表中的权限、草稿确认和业务状态校验。
|
||||||
|
10. 不允许把老板 AI 秘书写成普通聊天接口;必须经过 `AiSecretaryAgent` 的意图识别、上下文读取、JSON 校验、草稿/追问/降级流程。
|
||||||
|
|
||||||
|
## AI 写代码推荐流程
|
||||||
|
|
||||||
|
```text
|
||||||
|
人确定任务
|
||||||
|
-> AI 读 AGENTS.md
|
||||||
|
-> AI 读对应 spec
|
||||||
|
-> AI 检查 docs/plans/active/ 是否已有当前 plan
|
||||||
|
-> 没有 plan:读必要 contracts,生成 plan 给人确认
|
||||||
|
-> 已有 plan:只按当前 plan 执行
|
||||||
|
-> AI 读 checklists
|
||||||
|
-> AI 确认本次要新增或修改哪些 tests
|
||||||
|
-> AI 按 plan 写代码
|
||||||
|
-> AI 自测
|
||||||
|
-> 泽源跑测试
|
||||||
|
-> 乔大卫 / 田宇 Review 关键代码
|
||||||
|
-> 王一多验收闭环
|
||||||
|
```
|
||||||
|
|
||||||
|
active plan 是当前施工依据。除非发现 plan 明确不符合 spec/contracts,否则不要重新生成新 plan。
|
||||||
|
|
||||||
|
## 第一版不做
|
||||||
|
|
||||||
|
AI 工作台、技能市场、文件上传、多模态处理、会议纪要、日报、文档摘要、复杂反馈看板、复杂 BI、成本驾驶舱、完整工作流、完整项目管理、多级子任务、甘特图、复杂审批流、复杂组织架构、cron 表达式、日历系统、自动操作交易系统、自动操作历史数据库、自动读取员工本地文件、手机端网页完整适配、把飞书机器人开放为程经理或普通员工通用派活入口。
|
||||||
|
|||||||
+138
@@ -1 +1,139 @@
|
|||||||
|
# ARCHITECTURE.md
|
||||||
|
|
||||||
|
本文是后端架构入口,说明 Django 项目内部模块边界和调用规则。具体业务需求看 `docs/specs/`,字段、接口、状态、安全权限和日志边界以 `docs/contracts/` 为准。
|
||||||
|
|
||||||
|
## 架构原则
|
||||||
|
|
||||||
|
1. 第一版采用 Django 模块化单体,不拆微服务。
|
||||||
|
2. app 负责边界和数据归属,复杂业务逻辑放 service 层。
|
||||||
|
3. view / serializer 只做请求响应、参数校验和呈现,不承载复杂流程。
|
||||||
|
4. 所有关键动作必须写操作日志或失败记录。
|
||||||
|
5. 飞书回调、AI 输出、定时触发必须幂等。
|
||||||
|
6. 权限必须在接口层和 service 层双重校验。
|
||||||
|
7. 敏感信息脱敏、飞书验签和访问边界以 `docs/contracts/安全权限日志约定.md` 为准。
|
||||||
|
|
||||||
|
## 运行时与依赖版本
|
||||||
|
|
||||||
|
> 第一版新项目采用仍在官方扩展支持期内的 Django 5.2 LTS。Docker / 部署环境固定 Python 3.12.13;本地开发允许 Python 3.11 或 3.12,但提交前必须在部署基线 Python 3.12.13 上跑完整测试。
|
||||||
|
|
||||||
|
| 类别 | 版本约定 |
|
||||||
|
| --------------- | -------------------------------------------------------------------------------- |
|
||||||
|
| Python | 部署 `3.12.13`;本地开发 `>=3.11,<3.13` |
|
||||||
|
| Web 框架 | `Django==5.2.15` |
|
||||||
|
| API 框架 | `djangorestframework==3.17.1` |
|
||||||
|
| ORM / Migration | Django ORM + Django Migrations,跟随 `Django==5.2.15` |
|
||||||
|
| 数据库 | PostgreSQL `18.x`,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL |
|
||||||
|
| PostgreSQL 驱动 | `psycopg[binary]`,具体 patch 版本以 `requirements.txt` 锁定为准 |
|
||||||
|
| 定时任务 | `APScheduler==3.11.2` + `django-apscheduler==0.7.0`,通过独立 management command 进程运行 |
|
||||||
|
| AI 接入 | `dashscope==1.25.23`;百炼管理类 OpenAPI 可选 `alibabacloud-bailian20231229==2.13.1` |
|
||||||
|
| 飞书接入 | `lark-oapi==1.6.8` |
|
||||||
|
| 登录态 / 加解密 | `PyJWT==2.13.0` + `cryptography==49.0.0` |
|
||||||
|
| HTTP / 配置 | `requests==2.34.2`、`httpx==0.28.1`、`python-dotenv==1.2.2` |
|
||||||
|
| WSGI | `gunicorn==26.0.0` |
|
||||||
|
| Docker 基础镜像 | `python:3.12.13-slim-bookworm`、`postgres:18`、`nginx:stable-alpine` |
|
||||||
|
|
||||||
|
Python 依赖基线:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Django==5.2.15
|
||||||
|
djangorestframework==3.17.1
|
||||||
|
psycopg[binary]
|
||||||
|
asgiref==3.11.1
|
||||||
|
sqlparse==0.5.5
|
||||||
|
tzdata==2026.2
|
||||||
|
python-dotenv==1.2.2
|
||||||
|
requests==2.34.2
|
||||||
|
httpx==0.28.1
|
||||||
|
APScheduler==3.11.2
|
||||||
|
django-apscheduler==0.7.0
|
||||||
|
dashscope==1.25.23
|
||||||
|
alibabacloud-bailian20231229==2.13.1
|
||||||
|
lark-oapi==1.6.8
|
||||||
|
PyJWT==2.13.0
|
||||||
|
cryptography==49.0.0
|
||||||
|
gunicorn==26.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 推荐 Django app
|
||||||
|
|
||||||
|
| app | 职责 |
|
||||||
|
| --- | --- |
|
||||||
|
| `accounts` | 自定义用户、角色、登录态、飞书身份绑定 |
|
||||||
|
| `people` | 人员映射、称呼匹配、第一批试用人员 |
|
||||||
|
| `drafts` | `AiSecretaryAgent`、AI 草稿、补充/重说、BotContext、对话消息和草稿状态流转 |
|
||||||
|
| `tasks` | 事项创建、程经理确认、事项状态流转 |
|
||||||
|
| `reminders` | 定时提醒、固定周期、触发记录 |
|
||||||
|
| `notifications` | 飞书通知、通知补发、通知状态 |
|
||||||
|
| `feedbacks` | 飞书卡片反馈、平台内反馈 |
|
||||||
|
| `feishu` | 飞书登录、机器人事件、卡片回调、验签 |
|
||||||
|
| `prompts` | 背景摘要、老板风格提示词、业务规则版本 |
|
||||||
|
| `audit` | 失败记录、操作日志、模型调用日志 |
|
||||||
|
|
||||||
|
## 分层约定
|
||||||
|
|
||||||
|
| 层 | 放什么 | 不放什么 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `models` | 字段、索引、基础枚举、简单 model 方法 | 跨模块流程、外部 API 调用 |
|
||||||
|
| `serializers` | 入参出参结构、字段级校验、展示转换 | 状态机、权限决策、飞书调用 |
|
||||||
|
| `views` | DRF ViewSet/APIView、权限入口、调用 service | 复杂业务分支、事务主体 |
|
||||||
|
| `services` | 业务流程、状态流转、事务、幂等、日志 | HTTP 请求细节 |
|
||||||
|
| `clients` | 阿里百炼、飞书 API 等外部调用封装 | 业务状态修改 |
|
||||||
|
| `tasks/commands` | scheduler、补发、批处理命令 | Web 请求上下文依赖 |
|
||||||
|
|
||||||
|
## 核心调用方向
|
||||||
|
|
||||||
|
```text
|
||||||
|
views
|
||||||
|
-> serializers
|
||||||
|
-> services
|
||||||
|
-> models / clients
|
||||||
|
-> audit logs / failure records
|
||||||
|
```
|
||||||
|
|
||||||
|
允许:
|
||||||
|
|
||||||
|
1. `drafts` 通过 service 转换为 `tasks` 或 `reminders`。
|
||||||
|
2. `tasks` / `reminders` 通过 service 创建 `notifications`。
|
||||||
|
3. `notifications` 通过 `feishu` client 发送消息。
|
||||||
|
4. `feishu` 回调通过 service 更新 `drafts`、`feedbacks`、`tasks`、`reminders`。
|
||||||
|
5. 所有模块写入 `audit`。
|
||||||
|
6. `drafts` 中的 AI 对话记忆只辅助理解和草稿修订,不替代 `tasks`、`reminders`、`notifications` 等正式业务表。
|
||||||
|
|
||||||
|
避免:
|
||||||
|
|
||||||
|
1. app 之间直接互相改 model 状态。
|
||||||
|
2. 在 view 里直接调用飞书或模型 API。
|
||||||
|
3. 在 serializer 里做状态流转。
|
||||||
|
4. 在 scheduler 中绕过 service 直接改业务表。
|
||||||
|
|
||||||
|
## 必须写日志的动作
|
||||||
|
|
||||||
|
1. 飞书登录成功或失败。
|
||||||
|
2. 机器人收到老板消息。
|
||||||
|
3. 非老板私聊机器人。
|
||||||
|
4. AI 生成草稿、解析失败。
|
||||||
|
5. 人工确认、修改、取消草稿。
|
||||||
|
6. 老板点击补充/重说。
|
||||||
|
7. 程经理确认复杂事项。
|
||||||
|
8. 创建事项、创建提醒。
|
||||||
|
9. 发送通知、补发通知、通知失败。
|
||||||
|
10. 接收反馈,尤其是“有问题”反馈。
|
||||||
|
11. 定时提醒触发成功或失败。
|
||||||
|
12. 失败记录处理。
|
||||||
|
13. 背景摘要或老板风格提示词版本启用。
|
||||||
|
|
||||||
|
## 后台任务
|
||||||
|
|
||||||
|
定时提醒扫描使用独立进程,不放在 Gunicorn worker 内。生产和测试环境必须保证同一环境同一应用库同时只有一个 scheduler 进程在运行;如果未来多实例部署,需要先引入外部锁、leader election 或数据库级幂等锁。建议命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py run_reminder_scheduler
|
||||||
|
```
|
||||||
|
|
||||||
|
调度器每次触发前必须检查幂等键:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reminder_id + trigger_time + receiver_id + notification_channel
|
||||||
|
```
|
||||||
|
|
||||||
|
同一个提醒、同一个触发时间、同一个接收人,只允许生成一条有效通知。
|
||||||
|
|||||||
@@ -1 +1,86 @@
|
|||||||
|
# 公司内部 AI 协作 MVP 后端
|
||||||
|
|
||||||
|
本仓库用于实现第一版公司内部 AI 协作最小闭环:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板一句话
|
||||||
|
-> AI 草稿
|
||||||
|
-> 人工确认
|
||||||
|
-> 创建事项或定时提醒
|
||||||
|
-> 飞书通知
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 页面看到结果和失败记录
|
||||||
|
```
|
||||||
|
|
||||||
|
第一版不是完整任务系统、AI 工作台、复杂看板或全自动 Agent。所有关键动作都必须经过人工确认,AI 只生成可确认草稿。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 层级 | 约定 |
|
||||||
|
| ----- | ------------------------------------------- |
|
||||||
|
| 后端 | Python Django + Django REST Framework |
|
||||||
|
| ORM | Django ORM + Django Migrations |
|
||||||
|
| 数据库 | PostgreSQL 18.x,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL |
|
||||||
|
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,保存老板秘书会话、消息、BotContext 和模型调用快照 |
|
||||||
|
| 定时任务 | Django management command + 独立 scheduler 进程 |
|
||||||
|
| 后台管理 | Django Admin |
|
||||||
|
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
|
||||||
|
| 飞书 | 飞书身份登录、机器人私聊、个人消息、交互卡片和回调 |
|
||||||
|
| 部署 | Docker Compose + Nginx + Gunicorn |
|
||||||
|
|
||||||
|
## 当前施工基线
|
||||||
|
|
||||||
|
1. 后端框架使用 `Django==5.2.15` + `djangorestframework==3.17.1`。
|
||||||
|
2. Docker / 部署环境固定 Python `3.12.13`;本地开发允许 Python 3.11 或 3.12,提交前必须在 Python 3.12.13 上跑完整测试。
|
||||||
|
3. 后端开发先采用 mock-first:单元测试和最小闭环测试不得访问真实百炼、真实飞书或公网回调。
|
||||||
|
4. 定时提醒 scheduler 独立于 Gunicorn worker,同一环境同一应用库同时只能运行一个 scheduler 进程。
|
||||||
|
5. 老板 AI 秘书统一走 `POST /api/secretary/handle-message`,AI 只生成草稿、追问或回复,不直接创建任务、提醒或通知。
|
||||||
|
|
||||||
|
## 文档入口
|
||||||
|
|
||||||
|
开发前先读:
|
||||||
|
|
||||||
|
1. `AGENTS.md`
|
||||||
|
2. 当前相关 `docs/specs/*.md`
|
||||||
|
3. `docs/plans/active/01_老板一句话闭环实施计划.md`
|
||||||
|
|
||||||
|
涉及接口、字段、状态时,再读:
|
||||||
|
|
||||||
|
1. `docs/contracts/API接口约定.md`
|
||||||
|
2. `docs/contracts/数据对象约定.md`
|
||||||
|
3. `docs/contracts/状态流转约定.md`
|
||||||
|
4. `docs/contracts/安全权限日志约定.md`
|
||||||
|
|
||||||
|
提交或 Review 前读:
|
||||||
|
|
||||||
|
1. `docs/checklists/AI生成代码检查清单.md`
|
||||||
|
2. `docs/checklists/后端Review清单.md`
|
||||||
|
3. 本次新增或修改的 tests
|
||||||
|
|
||||||
|
## 本地启动
|
||||||
|
|
||||||
|
当前仓库还处在后端文档和施工计划阶段,Django 项目骨架尚未创建。代码落地后,本节应补充:
|
||||||
|
|
||||||
|
1. Python 虚拟环境创建方式。
|
||||||
|
2. 依赖安装命令。
|
||||||
|
3. `.env` 示例和环境变量说明。
|
||||||
|
4. 数据库迁移命令。
|
||||||
|
5. 本地启动命令。
|
||||||
|
6. 测试命令。
|
||||||
|
|
||||||
|
## 环境变量边界
|
||||||
|
|
||||||
|
这些值必须放在环境变量或服务器安全配置中,不能提交到仓库:
|
||||||
|
|
||||||
|
1. 阿里百炼 API Key。
|
||||||
|
2. 飞书 App ID。
|
||||||
|
3. 飞书 App Secret。
|
||||||
|
4. 飞书回调验签密钥。
|
||||||
|
5. 数据库账号密码。
|
||||||
|
6. OAuth code/state 和一次性操作 token 明文。
|
||||||
|
|
||||||
|
普通日志中不得打印 API Key、App Secret、飞书 token、OAuth code/state、一次性操作 token、回调验签密钥、完整手机号或完整邮箱。
|
||||||
|
|
||||||
|
## 第一版不做
|
||||||
|
|
||||||
|
第一版不做 AI 工作台、技能市场、文件上传、多模态处理、会议纪要、日报、文档摘要、复杂反馈看板、复杂 BI、成本驾驶舱、完整工作流、完整项目管理、多级子任务、甘特图、复杂审批流、复杂组织架构、cron 表达式、日历系统、自动操作交易系统、自动操作历史数据库、自动读取员工本地文件、手机端网页完整适配,也不把飞书机器人开放为程经理或普通员工通用派活入口。
|
||||||
|
|||||||
@@ -1 +1,89 @@
|
|||||||
|
# 项目总览
|
||||||
|
|
||||||
|
## 第一版目标
|
||||||
|
|
||||||
|
第一版要跑通一个公司内部 AI 协作最小闭环:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板一句话
|
||||||
|
-> AI 草稿
|
||||||
|
-> 人工确认
|
||||||
|
-> 创建事项或定时提醒
|
||||||
|
-> 飞书通知
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 页面看到结果和失败记录
|
||||||
|
```
|
||||||
|
|
||||||
|
AI 只负责整理、建议和生成草稿,不直接创建事项、不直接创建提醒、不直接通知别人。所有关键动作都必须经过人工确认。
|
||||||
|
|
||||||
|
## 角色
|
||||||
|
|
||||||
|
| 角色 | 第一版能力 |
|
||||||
|
| --- | --- |
|
||||||
|
| 老板 | 通过飞书机器人或平台输入一句话、确认草稿、创建事项或提醒、查看自己相关记录 |
|
||||||
|
| 程经理 | 接收复杂事项待确认提醒,在平台确认、修改和分发复杂事项 |
|
||||||
|
| 普通员工 | 接收事项和提醒,反馈已收到、处理中、已完成或有问题,创建自己的提醒 |
|
||||||
|
| 管理员 / AI 团队 | 维护人员映射、提示词版本、失败记录和必要日志 |
|
||||||
|
|
||||||
|
## 核心模块
|
||||||
|
|
||||||
|
1. 老板 AI 秘书:一句话入口、`AiSecretaryAgent`、意图分类、AI 草稿、补充/重说、普通问答兜底、对话记忆和 BotContext。
|
||||||
|
2. 事项任务:承接确认后的事项、通知、反馈和状态查看。
|
||||||
|
3. 定时提醒:承接一次性和固定周期提醒,到点通知并记录结果。
|
||||||
|
4. 飞书集成:身份登录、老板机器人私聊、个人消息、交互卡片和回调。
|
||||||
|
5. 权限日志失败记录:角色权限、操作日志、失败记录、敏感信息边界。
|
||||||
|
6. 安全权限日志约定:飞书验签、幂等、日志脱敏、访问范围和 tests 真实约束。
|
||||||
|
|
||||||
|
## 第一版闭环
|
||||||
|
|
||||||
|
老板一句话到事项:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板在飞书机器人私聊或平台输入
|
||||||
|
-> AI 生成事项草稿
|
||||||
|
-> 老板确认 / 补充/重说 / 取消
|
||||||
|
-> 简单事项直接创建,复杂事项转程经理确认
|
||||||
|
-> 飞书通知接收人
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 发起人或程经理查看结果
|
||||||
|
```
|
||||||
|
|
||||||
|
老板一句话到提醒:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板输入未来提醒
|
||||||
|
-> AI 生成提醒草稿
|
||||||
|
-> 老板确认 / 补充/重说 / 取消
|
||||||
|
-> 创建提醒
|
||||||
|
-> 到点飞书通知
|
||||||
|
-> 按需反馈
|
||||||
|
-> 查看通知结果和失败原因
|
||||||
|
```
|
||||||
|
|
||||||
|
异常复盘:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AI 解析失败 / 通知失败 / 回调失败 / 定时触发失败
|
||||||
|
-> 写入失败记录
|
||||||
|
-> AI 团队处理
|
||||||
|
-> 必要时补发、取消、重新处理或同步程经理
|
||||||
|
-> 处理结果留痕
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 层级 | 约定 |
|
||||||
|
| --- | --- |
|
||||||
|
| 后端 | Python Django + Django REST Framework |
|
||||||
|
| ORM | Django ORM + Django Migrations |
|
||||||
|
| 数据库 | PostgreSQL 18.x,作为唯一数据源 |
|
||||||
|
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,保存老板秘书会话、消息、BotContext 和模型调用快照 |
|
||||||
|
| 定时任务 | Django management command + 独立 scheduler 进程 |
|
||||||
|
| 后台管理 | Django Admin |
|
||||||
|
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
|
||||||
|
| 飞书 | 飞书身份登录、机器人私聊、个人消息、交互卡片和回调 |
|
||||||
|
| 部署 | Docker Compose + Nginx + Gunicorn |
|
||||||
|
|
||||||
|
## 第一版不做
|
||||||
|
|
||||||
|
不做完整任务系统、完整 AI 工作台、技能市场、文件上传、多模态处理、会议纪要、日报、文档摘要、复杂反馈看板、复杂 BI、成本驾驶舱、完整工作流、完整项目管理、多级子任务、甘特图、复杂审批流、复杂组织架构、cron 表达式、日历系统、自动操作交易系统、自动操作历史数据库、自动读取员工本地文件、手机端网页完整适配,也不把飞书机器人开放为程经理或普通员工通用派活入口。
|
||||||
|
|||||||
@@ -1 +1,56 @@
|
|||||||
|
# 当前任务
|
||||||
|
|
||||||
|
更新时间:2026-06-22
|
||||||
|
|
||||||
|
## 当前迭代目标
|
||||||
|
|
||||||
|
按本地 `docs/specs/01_老板AI秘书与AI草稿.md` 的老板 AI 秘书方案,统一压缩包内后端协作文档、contract、active plan 和 checklist,让后续 AI 与开发者可以按同一套口径写 Django 后端代码。
|
||||||
|
|
||||||
|
本轮不直接实现业务代码,因为当前仓库还没有 Django 项目骨架。文档重点是先统一 AI 秘书入口、PostgreSQL 数据源、AI 对话记忆、BotContext、稳定 JSON 输出、任务/提醒/通知边界和失败记录。
|
||||||
|
|
||||||
|
可行性审查后,active plan 已补充 mock-first、Django 5.2 LTS、PostgreSQL 18.x、Task 1 可执行顺序、自定义用户模型首次迁移前设置、AI 对话记忆、周期提醒状态和 scheduler 单实例约束。
|
||||||
|
|
||||||
|
## 本周任务
|
||||||
|
|
||||||
|
| 事项 | 负责人 | 状态 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 补齐 `README.md` 项目说明 | 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/00_项目总览.md` | 王一多 / 泽源协作 | 进行中 |
|
||||||
|
| 补齐 `docs/specs/01_老板AI秘书与AI草稿.md` | 王一多 / 泽源 / 焕然 | 进行中 |
|
||||||
|
| 补齐 `docs/specs/02_事项任务.md` | 乔大卫 / 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/specs/03_飞书通知与反馈.md` | 田宇 | 进行中 |
|
||||||
|
| 补齐 `docs/specs/04_定时提醒.md` | 田宇 / 乔大卫 | 进行中 |
|
||||||
|
| 补齐 `docs/specs/05_权限日志失败记录.md` | 乔大卫 | 进行中 |
|
||||||
|
| 补齐 `docs/contracts/API接口约定.md` | 田宇 / 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/contracts/数据对象约定.md` | 乔大卫 / 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/contracts/状态流转约定.md` | 乔大卫 | 进行中 |
|
||||||
|
| 新增 `docs/contracts/安全权限日志约定.md` | 乔大卫 / 田宇 / 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/checklists/AI生成代码检查清单.md` | 泽源 | 进行中 |
|
||||||
|
| 补齐 `docs/checklists/后端Review清单.md` | 乔大卫 / 田宇 | 进行中 |
|
||||||
|
| 预留并维护 `tests/` 真实约束 | 泽园 / 焕然 | 进行中 |
|
||||||
|
| 补齐第一份 active plan | 泽源 / 焕然 | 进行中 |
|
||||||
|
|
||||||
|
## 当前卡点
|
||||||
|
|
||||||
|
1. Django 项目骨架尚未创建。
|
||||||
|
2. 飞书正式应用配置、回调地址、个人消息权限和第一批试用人员映射还需要联调确认。
|
||||||
|
3. 阿里百炼 API Key、飞书 App Secret 等密钥不能写入仓库,需要后续通过环境变量配置。
|
||||||
|
4. 第一批试用人员姓名、常见称呼、手机号或邮箱映射需要后续维护。
|
||||||
|
5. PostgreSQL 连接、迁移和 `jsonb` 字段使用方式需要在项目骨架创建后验证。
|
||||||
|
6. 本地当前 Python 可能不是部署基线;提交前需要在 Python 3.12.13 环境跑完整测试。
|
||||||
|
7. 当前仅预留 tests 目录,Django 骨架创建后需要把权限、状态、飞书幂等、调度幂等、日志脱敏和 AI 人工确认边界落成真实测试。
|
||||||
|
|
||||||
|
## 待确认问题
|
||||||
|
|
||||||
|
1. 第一批试用人员名单和角色白名单。
|
||||||
|
2. 老板飞书身份映射方式:手机号、邮箱或手工 open_id。
|
||||||
|
3. 演示阶段是否先使用平台账号登录兜底,还是直接接飞书扫码登录。
|
||||||
|
4. 飞书个人消息和交互卡片权限是否已开通。
|
||||||
|
5. 演示环境域名和 HTTPS 回调地址。
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
1. 完成本轮文档补齐。
|
||||||
|
2. 由泽源按修订后的 active plan 创建 Django 项目骨架、基础 app、PostgreSQL 模型和迁移。
|
||||||
|
3. 先用 mock 百炼和 mock 飞书跑通后端最小闭环测试。
|
||||||
|
4. 优先跑通 AI 草稿、人工确认、事项创建、飞书通知和反馈记录这条最小链路。
|
||||||
|
|||||||
@@ -1 +1,109 @@
|
|||||||
|
# AI 生成代码检查清单
|
||||||
|
|
||||||
|
AI 生成或修改后端代码前后都要检查本清单。checklists 是 Review 闸门,不是写完后随手看一眼。
|
||||||
|
|
||||||
|
## 1. 文档读取
|
||||||
|
|
||||||
|
- [ ] 已读 `AGENTS.md`。
|
||||||
|
- [ ] 已读当前相关 `docs/specs/*.md`。
|
||||||
|
- [ ] 已读当前 active plan。
|
||||||
|
- [ ] 涉及接口时已读 `docs/contracts/API接口约定.md`。
|
||||||
|
- [ ] 涉及字段或数据库时已读 `docs/contracts/数据对象约定.md`。
|
||||||
|
- [ ] 涉及老板 AI 秘书、Agent、BotContext 或对话记忆时已读 `docs/specs/01_老板AI秘书与AI草稿.md`。
|
||||||
|
- [ ] 涉及状态流转时已读 `docs/contracts/状态流转约定.md`。
|
||||||
|
- [ ] 涉及飞书时已读 `docs/specs/03_飞书通知与反馈.md`。
|
||||||
|
- [ ] 涉及权限、安全、日志脱敏、飞书验签或幂等时已读 `docs/contracts/安全权限日志约定.md`。
|
||||||
|
|
||||||
|
## 2. 范围控制
|
||||||
|
|
||||||
|
- [ ] 只实现 active plan 要求的范围。
|
||||||
|
- [ ] 没有混入第一版明确不做的功能。
|
||||||
|
- [ ] 没有把 AI 工作台、技能市场、复杂看板、复杂 BI、cron、自定义日历等能力带入主线。
|
||||||
|
- [ ] 没有为了方便临时绕过权限、状态或日志要求。
|
||||||
|
|
||||||
|
## 3. 权限
|
||||||
|
|
||||||
|
- [ ] 普通员工不能给别人创建事项。
|
||||||
|
- [ ] 普通员工不能给别人创建提醒。
|
||||||
|
- [ ] 接收人之外的人不能反馈事项或提醒。
|
||||||
|
- [ ] 飞书机器人私聊入口第一版只允许老板使用。
|
||||||
|
- [ ] 程经理复杂事项确认走平台,不通过机器人对话处理。
|
||||||
|
- [ ] 权限在后端校验,不只靠前端隐藏按钮。
|
||||||
|
|
||||||
|
## 4. AI 草稿
|
||||||
|
|
||||||
|
- [ ] AI 只生成草稿,不直接创建事项、提醒或通知。
|
||||||
|
- [ ] 老板消息统一走 `POST /api/secretary/handle-message`。
|
||||||
|
- [ ] `source + message_id` 幂等,不重复调用模型、不重复创建草稿、不重复写 `secretary_messages`。
|
||||||
|
- [ ] AI 输出 JSON 已做结构校验。
|
||||||
|
- [ ] `qa/realtime_qa/note/need_more_info/unknown/unsupported` 不创建事项、提醒或通知。
|
||||||
|
- [ ] 实时问答第一版只做暂不支持提示,不做交易判断。
|
||||||
|
- [ ] `need_more_info` 最多 3 个问题、最多 3 轮追问。
|
||||||
|
- [ ] PostgreSQL AI 对话记忆只辅助理解和草稿修订,不替代任务、提醒、通知等正式业务表。
|
||||||
|
- [ ] 结构化字段没有写入游戏化表达。
|
||||||
|
- [ ] 模型调用记录了背景摘要、老板风格和业务规则版本。
|
||||||
|
- [ ] AI 解析失败会写 `model_call_logs` 和 `failure_records`。
|
||||||
|
|
||||||
|
## 5. 状态
|
||||||
|
|
||||||
|
- [ ] 草稿、事项、提醒、通知、反馈、失败记录的状态枚举来自 `状态流转约定.md`。
|
||||||
|
- [ ] 非法状态跳转返回 `state_conflict`。
|
||||||
|
- [ ] 已取消对象不能继续转换、通知或反馈。
|
||||||
|
- [ ] `converted` 草稿不会重复转换。
|
||||||
|
- [ ] 通知失败不会被标记为已通知。
|
||||||
|
- [ ] 有问题反馈必须填写原因。
|
||||||
|
|
||||||
|
## 6. 飞书
|
||||||
|
|
||||||
|
- [ ] 飞书回调必须验签。
|
||||||
|
- [ ] 飞书事件使用 `event_id` 或 `idempotency_key` 幂等。
|
||||||
|
- [ ] 卡片重复点击不会重复创建事项、提醒或反馈。
|
||||||
|
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知只记录事件,不写业务对象。
|
||||||
|
- [ ] 飞书通知只发送摘要和链接。
|
||||||
|
- [ ] 非老板机器人访问不会调用 AI。
|
||||||
|
- [ ] 回调失败会写 `feishu_events` 和 `failure_records`。
|
||||||
|
|
||||||
|
## 7. 定时提醒
|
||||||
|
|
||||||
|
- [ ] scheduler 独立于 Gunicorn worker 运行。
|
||||||
|
- [ ] 同一环境同一应用库同时只有一个 scheduler 进程运行。
|
||||||
|
- [ ] 只有 `active` 提醒会触发。
|
||||||
|
- [ ] `paused` 和 `cancelled` 不会触发。
|
||||||
|
- [ ] 同一提醒、同一触发时间、同一接收人、同一渠道只生成一条有效通知。
|
||||||
|
- [ ] 触发失败会写失败记录。
|
||||||
|
|
||||||
|
## 8. 事务和幂等
|
||||||
|
|
||||||
|
- [ ] 创建事项或提醒、创建通知、写日志在事务边界内处理。
|
||||||
|
- [ ] 外部调用失败不会留下半截错误状态。
|
||||||
|
- [ ] 补发保留原失败记录和新发送结果。
|
||||||
|
- [ ] 重复请求不会重复执行业务动作。
|
||||||
|
|
||||||
|
## 9. 日志和敏感信息
|
||||||
|
|
||||||
|
- [ ] 关键动作写 `operation_logs`。
|
||||||
|
- [ ] 失败写 `failure_records`。
|
||||||
|
- [ ] 普通日志不包含 API Key、App Secret、飞书 token。
|
||||||
|
- [ ] 普通日志不包含完整手机号或完整邮箱。
|
||||||
|
- [ ] 原始错误摘要不包含密钥。
|
||||||
|
- [ ] OAuth code/state、回调验签密钥、一次性操作 token 明文不进入日志或业务表。
|
||||||
|
- [ ] `model_call_logs` 原始请求/响应和 `feishu_events.raw_payload` 访问范围受限。
|
||||||
|
|
||||||
|
## 10. 测试
|
||||||
|
|
||||||
|
- [ ] 权限测试覆盖普通员工不能给别人创建事项或提醒。
|
||||||
|
- [ ] 状态测试覆盖非法跳转。
|
||||||
|
- [ ] 飞书测试覆盖验签失败、幂等重复事件和非接收人反馈。
|
||||||
|
- [ ] 定时测试覆盖重复触发防护。
|
||||||
|
- [ ] 事务测试覆盖通知失败不留下半截数据。
|
||||||
|
- [ ] AI 测试覆盖解析失败、实时问答和人工确认边界。
|
||||||
|
- [ ] 每个 plan 完成前已说明新增或修改了哪些 tests、覆盖哪些 spec / contract 约定、如何运行。
|
||||||
|
|
||||||
|
## 11. 文档同步
|
||||||
|
|
||||||
|
- [ ] 改接口同步 `docs/contracts/API接口约定.md`。
|
||||||
|
- [ ] 改字段同步 `docs/contracts/数据对象约定.md`。
|
||||||
|
- [ ] 改状态同步 `docs/contracts/状态流转约定.md`。
|
||||||
|
- [ ] 改权限、安全、日志脱敏、飞书验签或幂等同步 `docs/contracts/安全权限日志约定.md`。
|
||||||
|
- [ ] 改飞书逻辑同步 `docs/specs/03_飞书通知与反馈.md`。
|
||||||
|
- [ ] 改 AI 规则同步 `docs/specs/01_老板AI秘书与AI草稿.md`。
|
||||||
|
|||||||
@@ -1 +1,89 @@
|
|||||||
|
# 后端 Review 清单
|
||||||
|
|
||||||
|
后端 Review 优先找风险、权限漏洞、状态错误、事务问题、缺测试和敏感信息泄漏。
|
||||||
|
|
||||||
|
## 1. 权限
|
||||||
|
|
||||||
|
- [ ] DRF permission class 覆盖入口权限。
|
||||||
|
- [ ] service 层覆盖业务权限。
|
||||||
|
- [ ] 普通员工不能给别人创建事项或提醒。
|
||||||
|
- [ ] 反馈人必须是事项或提醒接收人。
|
||||||
|
- [ ] 飞书身份没有直接绕过平台角色权限。
|
||||||
|
- [ ] 非老板机器人私聊不会触发 AI。
|
||||||
|
- [ ] 失败记录仅管理员 / AI 团队和必要管理角色可见。
|
||||||
|
- [ ] 权限、安全、日志脱敏、验签和幂等符合 `docs/contracts/安全权限日志约定.md`。
|
||||||
|
|
||||||
|
## 2. 状态流转
|
||||||
|
|
||||||
|
- [ ] 状态枚举与 `docs/contracts/状态流转约定.md` 一致。
|
||||||
|
- [ ] 状态变化集中在 service 层。
|
||||||
|
- [ ] 非法状态跳转返回明确错误。
|
||||||
|
- [ ] 已取消、已转换、已完成等终态不会被重复处理。
|
||||||
|
- [ ] 有问题反馈必须留下原因。
|
||||||
|
|
||||||
|
## 3. 事务和一致性
|
||||||
|
|
||||||
|
- [ ] 创建事项 / 提醒 / 通知 / 日志的事务边界清晰。
|
||||||
|
- [ ] 外部 API 失败不会造成业务状态误判。
|
||||||
|
- [ ] 通知失败不会标记已通知。
|
||||||
|
- [ ] 补发不会覆盖历史失败记录。
|
||||||
|
- [ ] scheduler 不绕过 service 直接改业务表。
|
||||||
|
|
||||||
|
## 4. 飞书
|
||||||
|
|
||||||
|
- [ ] 回调已验签。
|
||||||
|
- [ ] `event_id` 或 `idempotency_key` 幂等。
|
||||||
|
- [ ] 卡片重复点击幂等。
|
||||||
|
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知不会继续写业务对象。
|
||||||
|
- [ ] 通知内容只包含摘要和链接。
|
||||||
|
- [ ] 飞书 token、App Secret 不进入日志。
|
||||||
|
- [ ] 找不到通知、草稿、事项、提醒时有失败记录。
|
||||||
|
|
||||||
|
## 5. AI
|
||||||
|
|
||||||
|
- [ ] AI 输出只进入草稿。
|
||||||
|
- [ ] 老板消息统一走 `POST /api/secretary/handle-message`。
|
||||||
|
- [ ] `source + message_id` 幂等覆盖模型调用、草稿创建和 `secretary_messages`。
|
||||||
|
- [ ] 模型 JSON 有 validator。
|
||||||
|
- [ ] `qa` / `realtime_qa` / `note` / `need_more_info` / `unknown` / `unsupported` 不创建事项或提醒。
|
||||||
|
- [ ] 实时问答不做交易判断。
|
||||||
|
- [ ] PromptContext 版本被记录。
|
||||||
|
- [ ] PostgreSQL AI 对话记忆只辅助理解和草稿修订,不替代正式业务表。
|
||||||
|
- [ ] PostgreSQL AI 记忆读写失败会写 `memory_store_failed`,不继续依赖内存确认草稿。
|
||||||
|
- [ ] 游戏化表达没有进入结构化字段。
|
||||||
|
|
||||||
|
## 6. 定时提醒
|
||||||
|
|
||||||
|
- [ ] 只支持一次性、每天、每周、每月。
|
||||||
|
- [ ] 不支持 cron 和复杂日历。
|
||||||
|
- [ ] 同一环境同一应用库同时只有一个 scheduler 进程。
|
||||||
|
- [ ] `paused`、`cancelled` 不触发。
|
||||||
|
- [ ] 触发幂等键覆盖提醒、时间、接收人和渠道。
|
||||||
|
- [ ] 触发失败可复盘。
|
||||||
|
|
||||||
|
## 7. 日志和失败
|
||||||
|
|
||||||
|
- [ ] 关键动作写 `operation_logs`。
|
||||||
|
- [ ] 失败写 `failure_records`。
|
||||||
|
- [ ] 失败记录包含类型、关联对象、原因、处理状态。
|
||||||
|
- [ ] 失败处理必须有处理结果。
|
||||||
|
- [ ] 日志包含操作人和渠道。
|
||||||
|
- [ ] OAuth code/state、回调验签密钥、一次性操作 token、完整手机号和完整邮箱没有进入普通日志。
|
||||||
|
- [ ] 模型原始请求/响应和飞书原始 payload 的访问范围受限。
|
||||||
|
|
||||||
|
## 8. 测试
|
||||||
|
|
||||||
|
- [ ] 单元测试覆盖权限、状态、AI 输出校验、飞书回调、提醒幂等。
|
||||||
|
- [ ] 失败路径有测试。
|
||||||
|
- [ ] 测试没有依赖真实飞书或真实百炼网络调用。
|
||||||
|
- [ ] 测试 fixture 不包含真实手机号、邮箱、token 或密钥。
|
||||||
|
- [ ] 每个 plan 完成前说明了新增或修改的 tests、覆盖的 spec / contract 约定和运行方式。
|
||||||
|
|
||||||
|
## 9. 文档同步
|
||||||
|
|
||||||
|
- [ ] 改接口同步 `docs/contracts/API接口约定.md`。
|
||||||
|
- [ ] 改字段同步 `docs/contracts/数据对象约定.md`。
|
||||||
|
- [ ] 改状态同步 `docs/contracts/状态流转约定.md`。
|
||||||
|
- [ ] 改权限、安全、日志脱敏、飞书验签或幂等同步 `docs/contracts/安全权限日志约定.md`。
|
||||||
|
- [ ] 改飞书逻辑同步 `docs/specs/03_飞书通知与反馈.md`。
|
||||||
|
- [ ] 改 AI 规则同步 `docs/specs/01_老板AI秘书与AI草稿.md`。
|
||||||
|
|||||||
@@ -1 +1,80 @@
|
|||||||
|
# 联调验收清单
|
||||||
|
|
||||||
|
联调验收看闭环是否真实可用,不看页面数量。
|
||||||
|
|
||||||
|
## 1. 身份和入口
|
||||||
|
|
||||||
|
- [ ] 用户可以通过飞书扫码登录 / 身份登录进入平台。
|
||||||
|
- [ ] 飞书登录可以匹配到平台用户和角色。
|
||||||
|
- [ ] 未匹配用户或停用用户不能进入平台,并有失败记录。
|
||||||
|
- [ ] 老板可以通过飞书机器人私聊提交一句话。
|
||||||
|
- [ ] 老板消息统一进入 `POST /api/secretary/handle-message`。
|
||||||
|
- [ ] 非老板私聊机器人不会触发 AI,并有未授权访问记录。
|
||||||
|
|
||||||
|
## 2. AI 草稿
|
||||||
|
|
||||||
|
- [ ] AI 可以把老板一句话解析为结构化草稿。
|
||||||
|
- [ ] AI 草稿可以展示原始输入、类型、内容、接收人候选、时间、反馈要求和缺失字段。
|
||||||
|
- [ ] 草稿必须人工确认后才创建事项或提醒。
|
||||||
|
- [ ] 普通聊天、普通记录、实时问答兜底、`note` 和 `unknown` 不会创建事项、提醒或通知。
|
||||||
|
- [ ] 老板可以确认、补充/重说、取消草稿。
|
||||||
|
- [ ] 补充/重说 30 分钟上下文可以生效并过期。
|
||||||
|
- [ ] 补充/重说后旧确认卡片失效,新草稿能追溯 `parent_draft_id`。
|
||||||
|
- [ ] PostgreSQL 中可以查看 `secretary_messages` 和当前 `BotContext`。
|
||||||
|
- [ ] 实时问答不会创建事项、提醒或通知。
|
||||||
|
|
||||||
|
## 3. 事项闭环
|
||||||
|
|
||||||
|
- [ ] 简单事项可以从草稿生成。
|
||||||
|
- [ ] 复杂事项在老板确认后创建 `pending_manager_confirm` 事项壳。
|
||||||
|
- [ ] 程经理收到飞书提醒后进入平台确认。
|
||||||
|
- [ ] 程经理补齐事项壳后才通知最终接收人。
|
||||||
|
- [ ] 事项可以飞书通知接收人。
|
||||||
|
- [ ] 接收人可以反馈已收到、处理中、已完成、有问题。
|
||||||
|
- [ ] 有问题反馈必须留下原因。
|
||||||
|
- [ ] 发起人或程经理可以看到事项结果。
|
||||||
|
|
||||||
|
## 4. 定时提醒闭环
|
||||||
|
|
||||||
|
- [ ] 手动创建一次性提醒。
|
||||||
|
- [ ] 从草稿创建提醒。
|
||||||
|
- [ ] 支持每天、每周、每月固定周期提醒。
|
||||||
|
- [ ] 到点触发飞书通知。
|
||||||
|
- [ ] 自己提醒自己默认不需要反馈。
|
||||||
|
- [ ] 老板 / 程经理给别人设置的提醒默认需要反馈。
|
||||||
|
- [ ] 暂停、恢复、取消按状态约定生效。
|
||||||
|
- [ ] 同一提醒同一触发时间同一接收人同一渠道不会重复触发同一通知。
|
||||||
|
- [ ] 演示 / 联调环境同一应用库只启动一个 scheduler 进程。
|
||||||
|
|
||||||
|
## 5. 飞书通知和回调
|
||||||
|
|
||||||
|
- [ ] 飞书个人消息可以发送。
|
||||||
|
- [ ] 飞书交互卡片可以发送。
|
||||||
|
- [ ] 卡片按钮回调可以被平台接收。
|
||||||
|
- [ ] 回调验签失败不会执行业务动作。
|
||||||
|
- [ ] 重复点击卡片不会重复创建反馈或转换草稿。
|
||||||
|
- [ ] 没有稳定 `event_id` 的回调使用 `idempotency_key` 幂等。
|
||||||
|
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知只记录事件,不写业务对象。
|
||||||
|
- [ ] 飞书手机端平台反馈页可以提交“有问题”原因,并拒绝过期或失效链接。
|
||||||
|
- [ ] 通知成功和失败都有记录。
|
||||||
|
|
||||||
|
## 6. 失败复盘
|
||||||
|
|
||||||
|
- [ ] AI 解析失败有记录。
|
||||||
|
- [ ] PostgreSQL AI 记忆读写失败有记录。
|
||||||
|
- [ ] 人员映射缺失有记录。
|
||||||
|
- [ ] 飞书登录失败有记录。
|
||||||
|
- [ ] 飞书通知失败有记录。
|
||||||
|
- [ ] 回调失败有记录。
|
||||||
|
- [ ] 定时提醒触发失败有记录。
|
||||||
|
- [ ] 管理员 / AI 团队可以处理失败并留下处理结果。
|
||||||
|
|
||||||
|
## 7. 安全边界
|
||||||
|
|
||||||
|
- [ ] 飞书通知只发摘要和链接。
|
||||||
|
- [ ] 普通日志不含 API Key、App Secret、飞书 token。
|
||||||
|
- [ ] 普通日志不含完整手机号和完整邮箱。
|
||||||
|
- [ ] 普通日志不含 OAuth code/state、回调验签密钥和一次性操作 token 明文。
|
||||||
|
- [ ] 模型原始请求/响应和飞书原始 payload 只对管理员 / AI 团队开放排查。
|
||||||
|
- [ ] 不自动操作交易系统、历史数据库、后台管理系统或其他外部业务系统。
|
||||||
|
- [ ] 第一版明确不做的功能没有混入主线。
|
||||||
|
|||||||
@@ -1 +1,219 @@
|
|||||||
|
# API 接口约定
|
||||||
|
|
||||||
|
本文件是后端接口路径、请求响应格式、错误码和分页格式的唯一事实源。spec 只描述业务边界,不重复完整接口返回。
|
||||||
|
|
||||||
|
## 1. 通用规则
|
||||||
|
|
||||||
|
1. 所有业务接口使用 `/api` 前缀。
|
||||||
|
2. 请求和响应使用 JSON。
|
||||||
|
3. 时间字段使用 ISO 8601 字符串,并统一保存为服务器时区感知时间。
|
||||||
|
4. 列表接口默认分页。
|
||||||
|
5. 动作类接口使用 `POST /api/{resources}/{id}/{action}`。
|
||||||
|
6. 飞书回调接口必须单独记录原始 payload。
|
||||||
|
7. 后端必须做权限校验,不能只靠前端隐藏入口。
|
||||||
|
8. 涉及权限、安全、日志脱敏、飞书验签和幂等时,以 `docs/contracts/安全权限日志约定.md` 为准。
|
||||||
|
|
||||||
|
## 2. 统一响应格式
|
||||||
|
|
||||||
|
成功:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {},
|
||||||
|
"error": null,
|
||||||
|
"request_id": "req_20260622_xxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"data": null,
|
||||||
|
"error": {
|
||||||
|
"code": "permission_denied",
|
||||||
|
"message": "当前用户无权执行该操作",
|
||||||
|
"detail": {}
|
||||||
|
},
|
||||||
|
"request_id": "req_20260622_xxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
分页:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"items": [],
|
||||||
|
"page": 1,
|
||||||
|
"page_size": 20,
|
||||||
|
"total": 0
|
||||||
|
},
|
||||||
|
"error": null,
|
||||||
|
"request_id": "req_20260622_xxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 通用错误码
|
||||||
|
|
||||||
|
| code | HTTP | 含义 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `validation_error` | 400 | 请求字段校验失败 |
|
||||||
|
| `unauthenticated` | 401 | 未登录或登录态失效 |
|
||||||
|
| `permission_denied` | 403 | 当前用户无权操作 |
|
||||||
|
| `not_found` | 404 | 对象不存在或不可见 |
|
||||||
|
| `state_conflict` | 409 | 当前状态不允许该操作 |
|
||||||
|
| `idempotency_conflict` | 409 | 幂等键冲突或重复请求 |
|
||||||
|
| `card_expired` | 409 | 飞书卡片、通知或一次性操作链接已失效 |
|
||||||
|
| `ai_parse_failed` | 422 | AI 输出无法解析或不符合结构 |
|
||||||
|
| `ai_model_failed` | 502 | 百炼或模型网关失败,重试后仍不可用 |
|
||||||
|
| `missing_person_mapping` | 422 | 缺少人员映射 |
|
||||||
|
| `memory_store_failed` | 500 | AI 对话记忆、消息或 BotContext 保存/读取失败 |
|
||||||
|
| `draft_convert_failed` | 500 | 草稿确认后转换任务或提醒失败 |
|
||||||
|
| `feishu_signature_invalid` | 401 | 飞书回调验签失败 |
|
||||||
|
| `feishu_api_failed` | 502 | 飞书接口调用失败 |
|
||||||
|
| `reminder_trigger_failed` | 500 | 定时提醒触发失败 |
|
||||||
|
| `internal_error` | 500 | 未分类服务端错误 |
|
||||||
|
|
||||||
|
## 4. 用户与人员映射
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/users/me` | 获取当前用户 | 已登录 |
|
||||||
|
| GET | `/api/users` | 用户列表 | 管理员 / AI 团队 |
|
||||||
|
| GET | `/api/auth/feishu/login` | 发起飞书扫码登录 / 身份登录 | 公开 |
|
||||||
|
| GET | `/api/auth/feishu/callback` | 飞书登录回调 | 公开回调 |
|
||||||
|
| POST | `/api/auth/logout` | 退出登录 | 已登录 |
|
||||||
|
| GET | `/api/person-mappings` | 人员映射列表 | 管理员 / AI 团队 |
|
||||||
|
| POST | `/api/person-mappings` | 新增人员映射 | 管理员 / AI 团队 |
|
||||||
|
| PATCH | `/api/person-mappings/{id}` | 修改人员映射 | 管理员 / AI 团队 |
|
||||||
|
| POST | `/api/person-mappings/{id}/resolve-feishu` | 根据手机号或邮箱解析飞书身份 | 管理员 / AI 团队 |
|
||||||
|
|
||||||
|
## 5. Prompt 上下文
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/prompt-contexts` | 查看提示词上下文配置 | 管理员 / AI 团队 |
|
||||||
|
| PATCH | `/api/prompt-contexts/{id}` | 修改提示词上下文配置 | 管理员 / AI 团队 |
|
||||||
|
| POST | `/api/prompt-contexts/{id}/activate` | 启用某个提示词上下文版本 | 管理员 / AI 团队 |
|
||||||
|
|
||||||
|
## 6. AI 草稿
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/api/secretary/handle-message` | 统一老板消息入口,返回草稿、追问、普通回复、上下文回顾或错误降级 | 老板;调试入口需管理员 token |
|
||||||
|
| GET | `/api/ai-drafts` | 草稿列表 | 相关用户、程经理、管理员 |
|
||||||
|
| GET | `/api/ai-drafts/{id}` | 草稿详情 | 相关用户、程经理、管理员 |
|
||||||
|
| PATCH | `/api/ai-drafts/{id}` | 修改草稿字段 | 草稿发起人、待确认程经理 |
|
||||||
|
| POST | `/api/ai-drafts/{id}/confirm` | 确认草稿 | 草稿发起人、待确认程经理 |
|
||||||
|
| POST | `/api/ai-drafts/{id}/cancel` | 取消草稿 | 草稿发起人 |
|
||||||
|
| POST | `/api/ai-drafts/{id}/convert` | 转换为事项或提醒 | 草稿发起人、待确认程经理 |
|
||||||
|
| POST | `/api/ai-drafts/{id}/follow-up` | 根据补充/重说重新生成草稿 | 草稿发起人 |
|
||||||
|
|
||||||
|
`POST /api/secretary/handle-message` 请求示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"source": "feishu",
|
||||||
|
"message_id": "msg_xxx",
|
||||||
|
"sender": {
|
||||||
|
"open_id": "ou_xxx",
|
||||||
|
"name": "老板",
|
||||||
|
"role": "boss"
|
||||||
|
},
|
||||||
|
"text": "明天上午提醒东东给我订会议室",
|
||||||
|
"context": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应 `data` 建议包含:
|
||||||
|
|
||||||
|
- `intent`
|
||||||
|
- `reply_type`
|
||||||
|
- `answer`
|
||||||
|
- `draft`
|
||||||
|
- `questions`
|
||||||
|
- `context_summary`
|
||||||
|
- `failure`
|
||||||
|
|
||||||
|
`reply_type` 建议取值:
|
||||||
|
|
||||||
|
- `draft_preview`
|
||||||
|
- `follow_up_question`
|
||||||
|
- `plain_answer`
|
||||||
|
- `context_summary`
|
||||||
|
- `unsupported`
|
||||||
|
- `error`
|
||||||
|
|
||||||
|
`note` / `unknown` / `qa` / `realtime_qa` / `need_more_info` / `unsupported` 不得创建事项、提醒或通知;如需留痕,只返回回复、追问、上下文摘要或失败信息。
|
||||||
|
|
||||||
|
兼容接口 `/api/ai-drafts/parse`、`/api/demo/boss-message` 可以保留,但内部必须转发到 `/api/secretary/handle-message`。
|
||||||
|
|
||||||
|
## 7. 事项
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/tasks` | 事项列表 | 相关用户、程经理、管理员 |
|
||||||
|
| POST | `/api/tasks` | 手动创建事项 | 老板、程经理 |
|
||||||
|
| GET | `/api/tasks/{id}` | 事项详情 | 相关用户、程经理、管理员 |
|
||||||
|
| PATCH | `/api/tasks/{id}` | 修改事项 | 发起人、程经理 |
|
||||||
|
| POST | `/api/tasks/{id}/manager-confirm` | 程经理确认复杂事项 | 指定程经理 |
|
||||||
|
| POST | `/api/tasks/{id}/cancel` | 取消事项 | 发起人、程经理 |
|
||||||
|
| POST | `/api/tasks/{id}/notify` | 发送或补发通知 | 发起人、程经理、管理员 |
|
||||||
|
| POST | `/api/tasks/{id}/feedback` | 平台内反馈 | 接收人 |
|
||||||
|
|
||||||
|
## 8. 定时提醒
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/reminders` | 提醒列表 | 相关用户、程经理、管理员 |
|
||||||
|
| POST | `/api/reminders` | 创建提醒 | 已登录;给别人创建需老板或程经理 |
|
||||||
|
| GET | `/api/reminders/{id}` | 提醒详情 | 相关用户、程经理、管理员 |
|
||||||
|
| PATCH | `/api/reminders/{id}` | 修改提醒 | 创建人、发起人、管理员 |
|
||||||
|
| POST | `/api/reminders/{id}/pause` | 暂停提醒 | 创建人、发起人、管理员 |
|
||||||
|
| POST | `/api/reminders/{id}/resume` | 恢复提醒 | 创建人、发起人、管理员 |
|
||||||
|
| POST | `/api/reminders/{id}/cancel` | 取消提醒 | 创建人、发起人、管理员 |
|
||||||
|
| POST | `/api/reminders/{id}/notify` | 手动补发提醒通知 | 创建人、发起人、管理员 |
|
||||||
|
| POST | `/api/reminders/{id}/feedback` | 平台内反馈 | 接收人 |
|
||||||
|
|
||||||
|
## 9. 通知、反馈和失败
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/notifications` | 通知记录列表 | 相关用户、程经理、管理员 |
|
||||||
|
| GET | `/api/notifications/{id}` | 通知详情 | 相关用户、程经理、管理员 |
|
||||||
|
| POST | `/api/notifications/{id}/retry` | 通知补发 | 发起人、程经理、管理员 |
|
||||||
|
| GET | `/api/feedbacks` | 反馈列表 | 相关用户、程经理、管理员 |
|
||||||
|
| GET | `/api/failure-records` | 失败记录列表 | 管理员 / AI 团队;必要时程经理 |
|
||||||
|
| GET | `/api/failure-records/{id}` | 失败详情 | 管理员 / AI 团队;必要时程经理 |
|
||||||
|
| PATCH | `/api/failure-records/{id}` | 更新处理状态 | 管理员 / AI 团队 |
|
||||||
|
| POST | `/api/failure-records/{id}/resolve` | 填写处理结果并标记已处理 | 管理员 / AI 团队 |
|
||||||
|
|
||||||
|
## 10. 飞书回调
|
||||||
|
|
||||||
|
| 方法 | 路径 | 作用 | 权限 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/api/feishu/events` | 飞书事件回调,包含机器人消息事件 | 飞书验签 |
|
||||||
|
| POST | `/api/feishu/bot/messages` | 可选:单独承接机器人私聊消息 | 飞书验签 |
|
||||||
|
| POST | `/api/feishu/card-callback` | 飞书卡片回调 | 飞书验签 |
|
||||||
|
|
||||||
|
飞书回调要求:
|
||||||
|
|
||||||
|
1. 必须验签。
|
||||||
|
2. 必须记录原始 payload 到 `feishu_events`。
|
||||||
|
3. 必须使用 `event_id` 幂等;没有稳定 `event_id` 时必须使用 `idempotency_key`。
|
||||||
|
4. 验签失败不得执行业务动作。
|
||||||
|
5. 草稿确认类卡片必须校验 `ai_drafts.active_card_notification_id`。
|
||||||
|
6. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录 `feishu_events`,不写业务对象。
|
||||||
|
7. 回调处理失败必须写入 `failure_records`。
|
||||||
|
|
||||||
|
## 11. 敏感信息返回边界
|
||||||
|
|
||||||
|
1. API 不返回 API Key、App Secret、飞书 token。
|
||||||
|
2. 手机号和邮箱默认脱敏返回,除非管理员维护页确有需要。
|
||||||
|
3. 飞书通知只发送摘要和链接,不发送敏感全文。
|
||||||
|
4. 模型调用日志对外接口默认不返回完整 prompt 和原始大段上下文。
|
||||||
|
5. 飞书原始 payload、模型原始请求/响应、OAuth code/state、一次性操作 token 和回调验签密钥的访问边界以 `安全权限日志约定.md` 为准。
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# 安全权限日志约定
|
||||||
|
|
||||||
|
本文件是第一版安全、权限、日志脱敏、回调验签、幂等和访问边界的唯一事实源。涉及这些规则时,代码、接口、测试和 Review 结论应以本文件为准。
|
||||||
|
|
||||||
|
本文件不展开完整 RBAC、安全合规体系或审计平台,只卡住最小闭环里 AI 和后端最容易写错的硬规则。
|
||||||
|
|
||||||
|
## 1. 核心底线
|
||||||
|
|
||||||
|
1. AI 只能生成草稿、追问或回复,不能绕过人工确认直接创建事项、创建提醒或发送通知。
|
||||||
|
2. 业务权限以平台 `users.role`、启用状态和必要白名单为准;飞书身份只用于登录认证、身份匹配和操作人追溯。
|
||||||
|
3. 权限必须在 DRF permission class 和 service 层同时校验,不能只靠前端隐藏按钮或飞书卡片按钮。
|
||||||
|
4. 飞书登录回调、事件回调和卡片回调必须验签;验签失败不得执行业务动作。
|
||||||
|
5. 通知、飞书事件、卡片点击、草稿转换和提醒调度必须幂等。
|
||||||
|
6. 密钥、token、完整手机号、完整邮箱、一次性操作 token 和回调验签密钥不得进入普通日志。
|
||||||
|
7. 权限失败、验签失败、通知失败、回调失败、AI 解析失败、模型失败、未授权机器人访问和调度失败必须可复盘。
|
||||||
|
|
||||||
|
## 2. 身份边界
|
||||||
|
|
||||||
|
| 身份来源 | 可用于 | 不可用于 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 平台用户 `users` | 登录态、角色、权限、数据范围 | 自然语言称呼解析 |
|
||||||
|
| `person_mappings` | 称呼、别名、飞书身份、第一批试用人员映射 | 替代平台登录和角色权限 |
|
||||||
|
| 飞书 open_id / user_id / union_id | 登录认证、消息接收人、回调操作人追溯 | 直接绕过平台权限 |
|
||||||
|
| AI 对话记忆表 | 上下文恢复、追问、草稿修订辅助 | 替代正式业务表、权限判断和人工确认 |
|
||||||
|
|
||||||
|
同一个真实人员如果可以登录平台,应在 `person_mappings.user_id` 绑定对应 `users.id`。飞书身份同步失败时写入 `failure_records`,不得静默降级成“默认有权限”。
|
||||||
|
|
||||||
|
## 3. 角色权限
|
||||||
|
|
||||||
|
| 动作 | 老板 | 程经理 | 普通员工 | 管理员 / AI 团队 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 飞书登录 / 平台登录 | 可用 | 可用 | 可用 | 可用 |
|
||||||
|
| 飞书机器人私聊 AI 秘书 | 可用 | 不开放 | 不开放 | 仅测试或排查 |
|
||||||
|
| 确认、取消、补充自己的 AI 草稿 | 可用 | 仅待自己确认的复杂事项 | 不开放 | 仅排查,不代替业务确认 |
|
||||||
|
| 创建事项 | 可用 | 可用 | 不开放给别人 | 可测试或维护 |
|
||||||
|
| 创建自己的提醒 | 可用 | 可用 | 可用 | 可用 |
|
||||||
|
| 给别人创建提醒 | 可用 | 可用 | 不开放 | 可测试或维护 |
|
||||||
|
| 反馈事项或提醒 | 仅自己相关 | 仅自己相关 | 仅自己作为接收人 | 可测试,不代替业务反馈 |
|
||||||
|
| 查看失败记录原始信息 | 必要业务摘要 | 必要业务摘要 | 不开放 | 可用 |
|
||||||
|
| 维护人员映射和提示词版本 | 不开放 | 不开放 | 不开放 | 可用 |
|
||||||
|
|
||||||
|
普通员工给别人创建事项或提醒、非接收人反馈、非老板使用机器人入口,都必须拒绝并记录。
|
||||||
|
|
||||||
|
## 4. 人工确认边界
|
||||||
|
|
||||||
|
1. `task` / `reminder` AI 输出必须先生成可确认草稿。
|
||||||
|
2. 草稿确认前必须校验发起人、草稿状态、缺失字段、接收人映射和当前有效卡片。
|
||||||
|
3. `note` / `unknown` / `qa` / `realtime_qa` / `need_more_info` / `unsupported` 不得进入事项、提醒或通知闭环。
|
||||||
|
4. 复杂事项在老板确认后创建 `tasks.status=pending_manager_confirm` 的事项壳,由程经理补齐接收人、内容并触发通知。
|
||||||
|
5. 草稿、任务、提醒、通知的状态流转必须走 service 层;非法跳转返回 `state_conflict`。
|
||||||
|
|
||||||
|
## 5. 飞书安全和幂等
|
||||||
|
|
||||||
|
1. 飞书回调必须先验签,再解析和执行业务动作。
|
||||||
|
2. `feishu_events.event_id` 应尽量唯一;没有稳定 `event_id` 时,用 `operator_open_id + action_value + notification_id + target_id` 生成 `idempotency_key` 并唯一约束。
|
||||||
|
3. 同一飞书事件只处理一次;重复事件只返回既有处理结果,不重复创建草稿、事项、提醒、反馈或通知。
|
||||||
|
4. 草稿确认类卡片必须匹配 `ai_drafts.active_card_notification_id`。
|
||||||
|
5. 旧卡片、失效卡片、已取消草稿、已替代草稿、已过期草稿和已转换草稿的回调只记录 `feishu_events`,返回“该卡片已失效,请以最新卡片或平台详情为准”,不得写业务对象。
|
||||||
|
6. 接收人反馈类卡片重复点击必须幂等;是否覆盖最新反馈状态由 feedback service 明确处理,但每次有效点击都应可追溯。
|
||||||
|
7. 程经理待确认提醒在飞书里只跳转平台,不通过机器人对话分发事项。
|
||||||
|
|
||||||
|
## 6. 通知和操作 token
|
||||||
|
|
||||||
|
1. `notifications.idempotency_key` 必须按 `purpose` 生成并设置唯一约束。
|
||||||
|
2. 草稿确认卡片、程经理确认提醒、事项通知、提醒触发通知和补发通知应使用不同 `purpose`。
|
||||||
|
3. 平台反馈页或一次性操作链接只能保存 token hash,例如 `action_token_hash`,不得保存明文 token。
|
||||||
|
4. 卡片或操作链接过期时写 `expires_at`,被新卡片替代或业务取消时写 `invalidated_at`。
|
||||||
|
5. `expires_at` 或 `invalidated_at` 已生效后,回调不得继续确认、转换、通知或反馈,只能记录事件和提示失效。
|
||||||
|
|
||||||
|
## 7. 日志脱敏
|
||||||
|
|
||||||
|
普通应用日志、`operation_logs.summary`、`operation_logs.metadata`、`failure_records.raw_error` 和列表页摘要不得包含:
|
||||||
|
|
||||||
|
1. 阿里百炼 API Key。
|
||||||
|
2. 飞书 App Secret、tenant access token、user access token。
|
||||||
|
3. 飞书回调验签密钥。
|
||||||
|
4. OAuth code 和 OAuth state 的完整值。
|
||||||
|
5. 平台反馈页一次性操作 token 明文。
|
||||||
|
6. 完整手机号和完整邮箱。
|
||||||
|
7. 数据库账号密码和连接串密码。
|
||||||
|
8. 飞书原始 payload 中的敏感字段。
|
||||||
|
9. 模型 prompt 中的敏感全文或过长背景库全文。
|
||||||
|
|
||||||
|
手机号和邮箱在普通日志中只能脱敏展示。密钥、token、OAuth code/state、一次性操作 token 原文只能存在请求处理内存或安全配置中,不得落普通业务表。
|
||||||
|
|
||||||
|
## 8. 访问范围
|
||||||
|
|
||||||
|
| 信息 | 普通员工 | 老板 | 程经理 | 管理员 / AI 团队 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 自己相关事项、提醒、反馈 | 可看摘要 | 可看相关记录 | 可看管理范围 | 可排查 |
|
||||||
|
| 失败记录列表 | 不可见 | 必要摘要 | 必要摘要 | 可见 |
|
||||||
|
| `model_call_logs` 原始请求和响应 | 不可见 | 不可见 | 不可见 | 可排查 |
|
||||||
|
| `feishu_events.raw_payload` | 不可见 | 不可见 | 不可见 | 可排查 |
|
||||||
|
| 完整手机号、邮箱 | 默认不可见 | 默认脱敏 | 默认脱敏 | 维护页按需可见 |
|
||||||
|
| 操作 token 明文 | 不可见 | 不可见 | 不可见 | 不可见 |
|
||||||
|
|
||||||
|
模型日志和飞书原始 payload 可以为排查保存必要结构,但必须通过接口权限和 Admin 权限限制访问,普通列表页默认只展示摘要。
|
||||||
|
|
||||||
|
## 9. 必须写失败记录的情况
|
||||||
|
|
||||||
|
1. AI 输出无法解析或不符合 schema。
|
||||||
|
2. 百炼或模型网关重试后仍失败。
|
||||||
|
3. PostgreSQL AI 记忆、消息或 BotContext 保存/读取失败。
|
||||||
|
4. 草稿确认后转换事项或提醒失败。
|
||||||
|
5. 缺少人员映射且阻断确认。
|
||||||
|
6. 普通员工越权创建事项或提醒。
|
||||||
|
7. 非接收人提交反馈。
|
||||||
|
8. 飞书登录失败、验签失败、事件格式异常或回调处理失败。
|
||||||
|
9. 飞书通知发送失败或补发失败。
|
||||||
|
10. 定时提醒触发失败或重复触发被跳过需要留痕。
|
||||||
|
11. 有问题反馈缺少原因。
|
||||||
|
|
||||||
|
如果 AI 解析失败时无法创建最小 `parse_failed` 草稿,`failure_records.target_type` 可以使用 `model_call_log` 并关联对应模型调用日志。
|
||||||
|
|
||||||
|
## 10. 必须测试的真实约束
|
||||||
|
|
||||||
|
每个 plan 完成前,必须说明新增或修改了哪些测试,以及这些测试覆盖了哪些 spec / contract 约定。第一版至少用测试卡住:
|
||||||
|
|
||||||
|
1. 权限:普通员工不能给别人创建事项或提醒。
|
||||||
|
2. 权限:非接收人不能反馈事项或提醒。
|
||||||
|
3. AI:AI 输出只能生成草稿,不能绕过人工确认直接执行。
|
||||||
|
4. AI:`note` / `unknown` 不创建事项、提醒或通知。
|
||||||
|
5. 状态:草稿、事项、提醒不能非法跳转。
|
||||||
|
6. 飞书:回调必须验签,重复事件和重复点击必须幂等。
|
||||||
|
7. 飞书:旧卡片或失效卡片只记录事件,不写业务对象。
|
||||||
|
8. 定时:同一个提醒、同一触发时间、同一接收人、同一渠道不能重复触发通知。
|
||||||
|
9. 事务:通知失败时不能留下被误判为已通知的业务状态。
|
||||||
|
10. 日志:密钥、token、完整手机号、完整邮箱和一次性操作 token 不进普通日志。
|
||||||
|
|
||||||
|
## 11. 第一版不做
|
||||||
|
|
||||||
|
1. 不做复杂组织架构和多级 RBAC。
|
||||||
|
2. 不做独立审计平台。
|
||||||
|
3. 不做复杂安全合规体系。
|
||||||
|
4. 不让 AI 自动操作交易系统、公司历史数据库、后台管理系统或其他外部业务系统。
|
||||||
|
5. 不把飞书机器人开放为程经理或普通员工通用派活入口。
|
||||||
@@ -1 +1,392 @@
|
|||||||
|
# 数据对象约定
|
||||||
|
|
||||||
|
本文件是核心表、字段、外键关系和状态字段的唯一事实源。Django model、serializer、接口返回和测试用例中的字段应与本文件一致。
|
||||||
|
|
||||||
|
## 1. 通用字段
|
||||||
|
|
||||||
|
多数业务表使用:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 主键,建议 bigint 或 uuid |
|
||||||
|
| `created_at` | 创建时间 |
|
||||||
|
| `updated_at` | 更新时间 |
|
||||||
|
| `created_by` | 创建人,可为空时必须说明来源 |
|
||||||
|
| `updated_by` | 最后修改人 |
|
||||||
|
| `deleted_at` | 软删除时间,可选 |
|
||||||
|
| `remark` | 备注,可选 |
|
||||||
|
|
||||||
|
第一版建议使用软删除,避免误删过程记录。
|
||||||
|
|
||||||
|
## 2. users
|
||||||
|
|
||||||
|
平台登录、角色判断和数据范围控制。Django 项目初始化时建议使用自定义用户模型。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 用户 ID |
|
||||||
|
| `username` | 登录名或内部唯一标识 |
|
||||||
|
| `name` | 显示姓名 |
|
||||||
|
| `role` | `boss`、`manager`、`employee`、`admin` |
|
||||||
|
| `phone` | 手机号,可选,普通日志需脱敏 |
|
||||||
|
| `email` | 邮箱,可选,普通日志需脱敏 |
|
||||||
|
| `feishu_open_id` | 飞书 open_id,可选 |
|
||||||
|
| `feishu_user_id` | 飞书 user_id,可选 |
|
||||||
|
| `feishu_union_id` | 飞书 union_id,可选 |
|
||||||
|
| `auth_provider` | `local`、`feishu` |
|
||||||
|
| `last_login_channel` | `platform`、`feishu_scan`、`feishu_in_app` |
|
||||||
|
| `status` | `active`、`disabled` |
|
||||||
|
| `last_login_at` | 最后登录时间 |
|
||||||
|
|
||||||
|
## 3. person_mappings
|
||||||
|
|
||||||
|
把老板输入中的姓名、称呼、部门或角色映射到平台用户和飞书身份。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 映射 ID |
|
||||||
|
| `user_id` | 关联平台用户,可选 |
|
||||||
|
| `display_name` | 标准姓名 |
|
||||||
|
| `aliases` | JSON 数组,例如 `["东东", "CJN"]` |
|
||||||
|
| `department` | 部门 |
|
||||||
|
| `business_role` | 业务角色,例如行政、程经理、下单员 |
|
||||||
|
| `manager_user_id` | 第一批试用管理范围归属,可选 |
|
||||||
|
| `phone` | 手机号,可选 |
|
||||||
|
| `email` | 邮箱,可选 |
|
||||||
|
| `feishu_open_id` | 飞书 open_id |
|
||||||
|
| `feishu_user_id` | 飞书 user_id,可选 |
|
||||||
|
| `feishu_union_id` | 飞书 union_id,可选 |
|
||||||
|
| `mapping_status` | `pending`、`resolved`、`failed` |
|
||||||
|
| `is_trial_user` | 是否第一批试用人员 |
|
||||||
|
| `note` | 备注 |
|
||||||
|
|
||||||
|
口径:
|
||||||
|
|
||||||
|
1. `users` 是登录、角色和权限判断的权威对象。
|
||||||
|
2. `person_mappings` 是自然语言称呼、手机号 / 邮箱、飞书身份和第一批试用人员映射的权威对象。
|
||||||
|
3. 同一个真实人员如果可以登录平台,应在 `person_mappings.user_id` 绑定对应 `users.id`。
|
||||||
|
4. 程经理第一批试用管理范围先用 `manager_user_id` 或等价白名单表达,不引入完整组织树。
|
||||||
|
|
||||||
|
## 4. feishu_auth_sessions
|
||||||
|
|
||||||
|
记录飞书登录过程和平台用户绑定结果。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 会话 ID |
|
||||||
|
| `state` | OAuth state 或登录状态标识 |
|
||||||
|
| `feishu_open_id` | 飞书 open_id |
|
||||||
|
| `feishu_user_id` | 飞书 user_id |
|
||||||
|
| `feishu_union_id` | 飞书 union_id,可选 |
|
||||||
|
| `matched_user_id` | 匹配到的平台用户 |
|
||||||
|
| `login_channel` | `feishu_scan`、`feishu_in_app` |
|
||||||
|
| `status` | `pending`、`success`、`failed` |
|
||||||
|
| `failure_reason` | 失败原因 |
|
||||||
|
| `completed_at` | 完成时间 |
|
||||||
|
|
||||||
|
## 5. secretary_conversations
|
||||||
|
|
||||||
|
老板 AI 秘书会话。第一阶段只有一个老板秘书入口,可先固定 `conversation_id=boss_secretary_default`,但表结构要能支持后续多会话。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 会话 ID |
|
||||||
|
| `conversation_id` | 业务会话唯一标识 |
|
||||||
|
| `boss_id` | 关联老板用户 |
|
||||||
|
| `source` | `feishu`、`web`、`debug` |
|
||||||
|
| `status` | `active`、`closed` |
|
||||||
|
| `last_message_at` | 最近消息时间 |
|
||||||
|
|
||||||
|
## 6. secretary_messages
|
||||||
|
|
||||||
|
老板输入、AI 回复、AI 追问、普通记录和上下文回顾的统一消息记录。该表只追加,不修改,用于对话回放、上下文回顾和问题复盘。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 消息 ID |
|
||||||
|
| `conversation_id` | 关联 `secretary_conversations.conversation_id` |
|
||||||
|
| `boss_id` | 关联老板用户 |
|
||||||
|
| `source` | `feishu`、`web`、`debug` |
|
||||||
|
| `message_id` | 外部消息 ID 或内部生成 ID,和 `source` 组成幂等键 |
|
||||||
|
| `role` | `boss`、`assistant`、`system` |
|
||||||
|
| `text` | 原始输入或 AI 回复 |
|
||||||
|
| `answer` | AI 给老板的回复,可为空 |
|
||||||
|
| `intent_type` | `task`、`reminder`、`qa`、`realtime_qa`、`note`、`need_more_info`、`unknown`、`unsupported`、`context_summary` |
|
||||||
|
| `draft_id` | 关联 AI 草稿,可为空 |
|
||||||
|
| `raw_payload` | 原始请求摘要,避免保存密钥 |
|
||||||
|
| `bot_context_snapshot` | JSONB,消息发生时的上下文快照 |
|
||||||
|
| `created_at` | 消息时间 |
|
||||||
|
|
||||||
|
## 7. bot_contexts
|
||||||
|
|
||||||
|
记录老板机器人私聊中的补充/重说上下文。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 上下文 ID |
|
||||||
|
| `conversation_id` | 当前老板秘书会话 ID |
|
||||||
|
| `boss_id` | PostgreSQL 中的老板用户 ID |
|
||||||
|
| `status` | `empty`、`awaiting_more_info`、`awaiting_confirm`、`awaiting_follow_up`、`expired`、`cleared` |
|
||||||
|
| `pending_draft_id` | 当前待补充或待确认的草稿 ID |
|
||||||
|
| `pending_draft_type` | `task`、`reminder`、`none` |
|
||||||
|
| `last_intent` | 上一条有效意图 |
|
||||||
|
| `expires_at` | 上下文有效期,进入待补充或待确认时重置为 30 分钟后 |
|
||||||
|
| `follow_up_count` | 当前草稿累计追问轮次 |
|
||||||
|
| `last_message_id` | 最近一次参与上下文判断的消息 ID |
|
||||||
|
| `extracted_facts` | JSONB,从多轮对话提取的候选事实,只用于草稿修订 |
|
||||||
|
| `updated_at` | 上下文最近更新时间 |
|
||||||
|
|
||||||
|
`awaiting_follow_up` 专用于老板点击飞书确认卡片上的“补充/重说”后,等待下一条消息修订上一版草稿;该状态必须带 `pending_draft_id` 和 `expires_at`。
|
||||||
|
|
||||||
|
## 8. prompt_contexts
|
||||||
|
|
||||||
|
维护 AI 秘书调用模型时加载的上下文版本。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 上下文 ID |
|
||||||
|
| `context_type` | `company_background_summary`、`boss_communication_style`、`ai_secretary_rules`、`role_and_alias_rules` |
|
||||||
|
| `version` | 版本号 |
|
||||||
|
| `title` | 标题 |
|
||||||
|
| `content` | 实际使用的摘要或规则文本 |
|
||||||
|
| `status` | `draft`、`active`、`archived` |
|
||||||
|
| `activated_at` | 启用时间 |
|
||||||
|
| `created_by` | 创建人 |
|
||||||
|
|
||||||
|
模型调用日志只保存本次使用的摘要、版本号和必要规则,避免反复保存完整背景库全文。
|
||||||
|
|
||||||
|
## 9. ai_drafts
|
||||||
|
|
||||||
|
保存 AI 从一句话整理出的可确认草稿。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 草稿 ID |
|
||||||
|
| `source` | `platform`、`feishu_bot` |
|
||||||
|
| `source_message_id` | 飞书或外部入口消息 ID,可选 |
|
||||||
|
| `created_by` | 发起人 |
|
||||||
|
| `parent_draft_id` | 补充/重说生成新草稿时,关联上一版草稿 |
|
||||||
|
| `raw_input` | 原始输入 |
|
||||||
|
| `intent` | `task`、`reminder`、`qa`、`realtime_qa`、`note`、`need_more_info`、`unknown`、`unsupported` |
|
||||||
|
| `draft_type` | `task`、`reminder`、`none` |
|
||||||
|
| `should_create_draft` | `task/reminder` 为 true;其他意图必须为 false |
|
||||||
|
| `status` | 见状态流转约定 |
|
||||||
|
| `title` | 草稿标题 |
|
||||||
|
| `content` | 事项或提醒内容 |
|
||||||
|
| `receiver_candidates` | JSON,接收人候选和置信度 |
|
||||||
|
| `receiver_text` | 未映射成功时保留老板原始称呼 |
|
||||||
|
| `selected_receiver_id` | 人工确认后的接收人,可选 |
|
||||||
|
| `scheduled_at` | 提醒时间,可为空;任务可使用 `schedule_text` 表达期望时间 |
|
||||||
|
| `schedule_text` | 老板原始时间表达 |
|
||||||
|
| `recurrence_type` | `none`、`daily`、`weekly`、`monthly` |
|
||||||
|
| `requires_feedback` | 是否需要反馈 |
|
||||||
|
| `route_type` | `none`、`direct_after_boss_confirm`、`manager_confirm_required` |
|
||||||
|
| `need_manager_confirm` | 是否需要程经理确认 |
|
||||||
|
| `missing_fields` | JSON 数组 |
|
||||||
|
| `questions` | JSON 数组,最多 3 个;确认草稿一般为空 |
|
||||||
|
| `answer` | 给老板看的回复摘要;不得写“已通知、已创建、已发送”等执行语义 |
|
||||||
|
| `active_card_notification_id` | 当前有效确认卡片通知,可选 |
|
||||||
|
| `superseded_by_draft_id` | 被补充/重说的新草稿替代时,指向新草稿 |
|
||||||
|
| `model_call_log_id` | 关联模型调用日志 |
|
||||||
|
| `confirmed_by` | 确认人 |
|
||||||
|
| `confirmed_at` | 确认时间 |
|
||||||
|
| `cancelled_reason` | 取消原因 |
|
||||||
|
|
||||||
|
草稿生命周期口径:
|
||||||
|
|
||||||
|
1. 每个草稿同一时间只能有一张有效确认卡片,对应 `active_card_notification_id`。
|
||||||
|
2. 老板点击“补充/重说”后,原草稿进入 `awaiting_follow_up`,原确认卡片应失效。
|
||||||
|
3. 30 分钟内收到补充消息后,系统生成新草稿,`parent_draft_id` 指向原草稿;原草稿进入 `superseded`,`superseded_by_draft_id` 指向新草稿。
|
||||||
|
4. 超过 30 分钟仍未收到补充消息时,`bot_contexts` 标记为 `expired`,原草稿进入 `expired`;老板下一条消息按新输入处理。
|
||||||
|
5. `note` / `unknown` 不进入事项、提醒或通知闭环;如需留痕,只能保存回复、消息、模型调用日志或最小草稿记录。
|
||||||
|
|
||||||
|
## 10. model_call_logs
|
||||||
|
|
||||||
|
记录模型输入输出、模型名、耗时和结果。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 日志 ID |
|
||||||
|
| `provider` | `bailian` |
|
||||||
|
| `model_name` | 模型名 |
|
||||||
|
| `prompt_version` | 提示词版本 |
|
||||||
|
| `background_context_version` | 公司背景摘要版本 |
|
||||||
|
| `boss_style_version` | 老板沟通风格版本 |
|
||||||
|
| `ai_secretary_rules_version` | AI 秘书业务规则版本 |
|
||||||
|
| `context_snapshot` | 本次使用的上下文版本和摘要 |
|
||||||
|
| `input_text` | 用户原始输入 |
|
||||||
|
| `request_payload` | 请求摘要,避免保存密钥 |
|
||||||
|
| `response_payload` | 模型原始响应结构 |
|
||||||
|
| `parsed_result` | 后端解析后的 `AiSecretaryResponse` JSON |
|
||||||
|
| `status` | `success`、`failed` |
|
||||||
|
| `error_message` | 失败原因 |
|
||||||
|
| `latency_ms` | 耗时 |
|
||||||
|
| `created_by` | 调用发起人 |
|
||||||
|
|
||||||
|
## 11. tasks
|
||||||
|
|
||||||
|
需要接收人处理并反馈的事情。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 事项 ID |
|
||||||
|
| `source_type` | `ai_draft`、`manual` |
|
||||||
|
| `source_draft_id` | 来源草稿,可选 |
|
||||||
|
| `title` | 标题 |
|
||||||
|
| `content` | 事项内容 |
|
||||||
|
| `initiator_id` | 发起人 |
|
||||||
|
| `receiver_id` | 接收人;复杂事项在程经理确认前可为空 |
|
||||||
|
| `manager_id` | 复杂事项确认人,可选 |
|
||||||
|
| `status` | 见状态流转约定 |
|
||||||
|
| `visible_feedback_status` | `received`、`in_progress`、`completed`、`problem` |
|
||||||
|
| `requires_feedback` | 是否需要反馈 |
|
||||||
|
| `due_at` | 截止或期望反馈时间,可选 |
|
||||||
|
| `confirmed_at` | 确认时间 |
|
||||||
|
| `notified_at` | 通知时间 |
|
||||||
|
| `completed_at` | 完成时间 |
|
||||||
|
| `problem_reason` | 有问题原因,可选 |
|
||||||
|
|
||||||
|
复杂事项必须在老板确认后创建 `status=pending_manager_confirm` 的事项壳,不得一直停留在草稿中等待程经理补齐。
|
||||||
|
|
||||||
|
## 12. reminders
|
||||||
|
|
||||||
|
未来某个时间触发的提醒。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 提醒 ID |
|
||||||
|
| `source_type` | `ai_draft`、`manual` |
|
||||||
|
| `source_draft_id` | 来源草稿,可选 |
|
||||||
|
| `related_task_id` | 关联事项,可选 |
|
||||||
|
| `title` | 标题 |
|
||||||
|
| `content` | 提醒内容 |
|
||||||
|
| `initiator_id` | 发起人 |
|
||||||
|
| `receiver_id` | 接收人 |
|
||||||
|
| `status` | 见状态流转约定 |
|
||||||
|
| `scheduled_at` | 首次提醒时间 |
|
||||||
|
| `next_trigger_at` | 下一次触发时间 |
|
||||||
|
| `recurrence_type` | `none`、`daily`、`weekly`、`monthly` |
|
||||||
|
| `requires_feedback` | 是否需要反馈 |
|
||||||
|
| `last_triggered_at` | 最近触发时间 |
|
||||||
|
| `cancelled_at` | 取消时间 |
|
||||||
|
|
||||||
|
## 13. notifications
|
||||||
|
|
||||||
|
飞书或平台内通知发送记录。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 通知 ID |
|
||||||
|
| `target_type` | `ai_draft`、`task`、`reminder`、`failure_record` |
|
||||||
|
| `target_id` | 关联对象 ID |
|
||||||
|
| `purpose` | `draft_confirm`、`manager_confirm`、`task_notify`、`reminder_trigger` |
|
||||||
|
| `receiver_id` | 接收人 |
|
||||||
|
| `channel` | `feishu_personal`、`feishu_group`、`platform` |
|
||||||
|
| `message_type` | `text`、`card` |
|
||||||
|
| `title` | 通知标题 |
|
||||||
|
| `summary` | 通知摘要 |
|
||||||
|
| `link_url` | 平台详情链接 |
|
||||||
|
| `card_id` | 飞书卡片 ID,可选 |
|
||||||
|
| `status` | `pending`、`sending`、`sent`、`failed`、`retrying`、`cancelled`、`expired` |
|
||||||
|
| `trigger_time` | 提醒触发时间;非提醒触发通知可为空 |
|
||||||
|
| `idempotency_key` | 幂等键,按通知目的生成并唯一约束 |
|
||||||
|
| `action_token_hash` | 平台反馈页一次性操作 token 的 hash,可选 |
|
||||||
|
| `feishu_message_id` | 飞书消息 ID |
|
||||||
|
| `sent_at` | 发送时间 |
|
||||||
|
| `expires_at` | 卡片或通知操作过期时间,可选 |
|
||||||
|
| `invalidated_at` | 被新卡片替代或业务取消时的失效时间,可选 |
|
||||||
|
| `failure_reason` | 失败原因 |
|
||||||
|
| `retry_count` | 重试次数 |
|
||||||
|
| `last_retry_at` | 最近重试时间 |
|
||||||
|
|
||||||
|
提醒触发类通知的幂等键格式建议:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reminder:{reminder_id}:{trigger_time}:{receiver_id}:{channel}
|
||||||
|
```
|
||||||
|
|
||||||
|
任务通知、草稿确认卡片和程经理待确认提醒也必须生成各自的幂等键,避免重复点击、重复补发或回调重放造成多条有效通知。
|
||||||
|
|
||||||
|
## 14. feedbacks
|
||||||
|
|
||||||
|
接收人的反馈状态和问题原因。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 反馈 ID |
|
||||||
|
| `target_type` | `task`、`reminder` |
|
||||||
|
| `target_id` | 关联对象 ID |
|
||||||
|
| `feedback_by` | 反馈人 |
|
||||||
|
| `status` | `received`、`in_progress`、`completed`、`problem` |
|
||||||
|
| `problem_reason` | 有问题原因,`problem` 时必填 |
|
||||||
|
| `source` | `feishu_card`、`platform` |
|
||||||
|
| `notification_id` | 关联通知 |
|
||||||
|
| `created_at` | 反馈时间 |
|
||||||
|
|
||||||
|
## 15. failure_records
|
||||||
|
|
||||||
|
AI、通知、回调、调度等失败原因。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 失败记录 ID |
|
||||||
|
| `failure_type` | 见状态流转约定中的失败类型 |
|
||||||
|
| `target_type` | 关联对象类型 |
|
||||||
|
| `target_id` | 关联对象 ID |
|
||||||
|
| `status` | `pending`、`processing`、`resolved`、`cancelled` |
|
||||||
|
| `reason` | 失败原因 |
|
||||||
|
| `raw_error` | 原始错误摘要,不含密钥 |
|
||||||
|
| `need_manager_sync` | 是否需要同步程经理 |
|
||||||
|
| `handled_by` | 处理人 |
|
||||||
|
| `handled_at` | 处理时间 |
|
||||||
|
| `handle_result` | 处理结果 |
|
||||||
|
|
||||||
|
`target_type` 可使用 `ai_draft`、`task`、`reminder`、`notification`、`feishu_event`、`model_call_log` 等实际对象类型。AI 解析失败时,如果无法创建最小 `parse_failed` 草稿,可关联 `model_call_log`。
|
||||||
|
|
||||||
|
## 16. operation_logs
|
||||||
|
|
||||||
|
人工确认、修改、取消、补发等过程留痕。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 日志 ID |
|
||||||
|
| `actor_id` | 操作人 |
|
||||||
|
| `action` | 动作 |
|
||||||
|
| `target_type` | 对象类型 |
|
||||||
|
| `target_id` | 对象 ID |
|
||||||
|
| `channel` | `platform`、`feishu_bot`、`feishu_card`、`scheduler`、`admin` |
|
||||||
|
| `result` | `success`、`failed` |
|
||||||
|
| `summary` | 操作摘要 |
|
||||||
|
| `metadata` | JSON,避免敏感明文 |
|
||||||
|
| `created_at` | 操作时间 |
|
||||||
|
|
||||||
|
## 17. feishu_events
|
||||||
|
|
||||||
|
飞书回调原始事件和处理结果。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `id` | 事件 ID |
|
||||||
|
| `event_id` | 飞书事件 ID |
|
||||||
|
| `event_type` | 事件类型 |
|
||||||
|
| `operator_open_id` | 操作人 open_id |
|
||||||
|
| `action_value` | 按钮值 |
|
||||||
|
| `idempotency_key` | 事件处理幂等键,可选 |
|
||||||
|
| `target_type` | `ai_draft`、`task`、`reminder`、`auth`、`bot` |
|
||||||
|
| `target_id` | 关联业务对象 |
|
||||||
|
| `notification_id` | 关联通知 |
|
||||||
|
| `raw_payload` | 原始事件 |
|
||||||
|
| `process_status` | `pending`、`processed`、`failed`、`ignored` |
|
||||||
|
| `process_result` | 处理结果 |
|
||||||
|
| `created_at` | 接收时间 |
|
||||||
|
|
||||||
|
`event_id` 应尽量设置唯一约束;如果飞书某类回调没有稳定 `event_id`,则用 `operator_open_id + action_value + notification_id + target_id` 生成 `idempotency_key` 并唯一约束。重复事件只返回已处理结果,不重复创建草稿、事项、提醒、反馈或通知。
|
||||||
|
|
||||||
|
## 18. 敏感字段规则
|
||||||
|
|
||||||
|
1. `phone`、`email` 可入库,但普通日志和普通列表接口默认脱敏。
|
||||||
|
2. API Key、App Secret、飞书 token、飞书回调验签密钥、OAuth code/state、一次性操作 token 明文不得入库到业务表或普通日志。
|
||||||
|
3. `raw_error`、`metadata`、`request_payload`、`response_payload` 必须避免密钥和完整个人联系方式。
|
||||||
|
4. 飞书通知内容只保存和发送摘要,不展开敏感全文。
|
||||||
|
5. `model_call_logs` 原始请求/响应和 `feishu_events.raw_payload` 仅供管理员 / AI 团队排查,普通员工不可见,老板和程经理只在必要业务范围内查看摘要。
|
||||||
|
6. 平台反馈页一次性操作 token 只能保存 hash,不能保存明文。
|
||||||
|
|||||||
@@ -1 +1,321 @@
|
|||||||
|
# 状态流转约定
|
||||||
|
|
||||||
|
本文件是状态枚举、允许流转、触发人和日志要求的唯一事实源。代码枚举、接口返回和测试用例应与本文件一致。
|
||||||
|
|
||||||
|
## 1. 通用原则
|
||||||
|
|
||||||
|
1. 状态统一使用英文枚举。
|
||||||
|
2. 状态变化必须通过 service 层完成。
|
||||||
|
3. 关键状态变化必须写 `operation_logs`。
|
||||||
|
4. 非法状态跳转必须返回 `state_conflict`。
|
||||||
|
5. 通知、回调、调度相关状态变化必须幂等。
|
||||||
|
|
||||||
|
## 2. AI 草稿状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending_confirmation` | 草稿已整理完成,等待老板确认、修改、取消或补充/重说 |
|
||||||
|
| `awaiting_follow_up` | 老板已点击补充/重说,等待 30 分钟内下一条补充消息 |
|
||||||
|
| `confirmed` | 已人工确认,等待转换为事项或提醒 |
|
||||||
|
| `converted` | 已转换为事项或提醒;复杂事项已创建 `pending_manager_confirm` 事项壳也视为已转换 |
|
||||||
|
| `cancelled` | 已取消 |
|
||||||
|
| `answered` | 已直接回复或留痕,不进入事项、提醒或通知闭环 |
|
||||||
|
| `superseded` | 已被补充/重说生成的新草稿替代 |
|
||||||
|
| `expired` | 补充/重说等待超时,旧确认卡片不可继续使用 |
|
||||||
|
| `parse_failed` | AI 解析或草稿处理失败,仅用于调试追踪 |
|
||||||
|
|
||||||
|
允许流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending_confirmation -> confirmed
|
||||||
|
pending_confirmation -> awaiting_follow_up
|
||||||
|
pending_confirmation -> cancelled
|
||||||
|
pending_confirmation -> parse_failed
|
||||||
|
awaiting_follow_up -> superseded
|
||||||
|
awaiting_follow_up -> expired
|
||||||
|
awaiting_follow_up -> cancelled
|
||||||
|
confirmed -> converted
|
||||||
|
confirmed -> parse_failed
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. `parse_failed`、`cancelled`、`converted`、`answered`、`superseded`、`expired` 为终态。
|
||||||
|
2. 未进入 `confirmed` 的草稿不得转换。
|
||||||
|
3. `note`、`unknown`、`qa`、`realtime_qa`、`need_more_info`、`unsupported` 不进入事项、提醒或通知闭环;如需留痕,只能使用 `answered` 或 `parse_failed`。
|
||||||
|
4. 老板点击补充/重说后,原草稿进入 `awaiting_follow_up`,并使原确认卡片失效。
|
||||||
|
5. 30 分钟内收到补充消息后,新草稿 `parent_draft_id` 指向原草稿,原草稿进入 `superseded`。
|
||||||
|
6. 超过 30 分钟后,原草稿进入 `expired`,老板下一条消息按新输入处理。
|
||||||
|
7. 飞书回调确认、取消、补充/重说前,必须校验回调来自 `active_card_notification_id` 对应的当前有效卡片。
|
||||||
|
8. 复杂事项不使用草稿状态等待程经理确认;老板确认后应创建 `tasks.status=pending_manager_confirm` 的事项壳。
|
||||||
|
9. `answered` 和 `parse_failed` 可以作为最小留痕草稿的初始终态,不参与确认和转换。
|
||||||
|
|
||||||
|
## 3. 事项状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending_manager_confirm` | 等待程经理确认 |
|
||||||
|
| `pending_notify` | 待通知 |
|
||||||
|
| `notified` | 已通知 |
|
||||||
|
| `notify_failed` | 通知失败 |
|
||||||
|
| `feedback_received` | 已收到反馈 |
|
||||||
|
| `completed` | 已完成 |
|
||||||
|
| `problem` | 有问题 |
|
||||||
|
| `cancelled` | 已取消 |
|
||||||
|
|
||||||
|
允许流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending_manager_confirm -> pending_notify
|
||||||
|
pending_manager_confirm -> cancelled
|
||||||
|
pending_notify -> notified
|
||||||
|
pending_notify -> notify_failed
|
||||||
|
notify_failed -> pending_notify
|
||||||
|
notified -> feedback_received
|
||||||
|
notified -> completed
|
||||||
|
notified -> problem
|
||||||
|
feedback_received -> completed
|
||||||
|
feedback_received -> problem
|
||||||
|
problem -> pending_notify
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. `cancelled` 为终态。
|
||||||
|
2. 未通知事项不得接收飞书反馈。
|
||||||
|
3. `problem` 必须有问题原因。
|
||||||
|
4. 通知失败不得标记为 `notified`。
|
||||||
|
|
||||||
|
## 4. 事项用户可见反馈状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `received` | 已收到 |
|
||||||
|
| `in_progress` | 处理中 |
|
||||||
|
| `completed` | 已完成 |
|
||||||
|
| `problem` | 有问题 |
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. `problem` 必须填写 `problem_reason`。
|
||||||
|
2. 反馈人必须是事项接收人。
|
||||||
|
3. `received` 和 `in_progress` 反馈写入 `feedbacks.status`,并将 `tasks.status` 置为 `feedback_received`、`tasks.visible_feedback_status` 置为对应值。
|
||||||
|
4. `completed` 反馈将 `tasks.status` 和 `tasks.visible_feedback_status` 都置为 `completed`。
|
||||||
|
5. `problem` 反馈将 `tasks.status` 和 `tasks.visible_feedback_status` 都置为 `problem`,并写入 `problem_reason`。
|
||||||
|
|
||||||
|
## 5. 提醒状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `active` | 生效中 |
|
||||||
|
| `paused` | 已暂停 |
|
||||||
|
| `triggered` | 一次性提醒已触发 |
|
||||||
|
| `trigger_failed` | 触发失败 |
|
||||||
|
| `cancelled` | 已取消 |
|
||||||
|
| `expired` | 已过期 |
|
||||||
|
|
||||||
|
允许流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
active -> paused
|
||||||
|
paused -> active
|
||||||
|
active -> triggered
|
||||||
|
active -> trigger_failed
|
||||||
|
trigger_failed -> active
|
||||||
|
active -> cancelled
|
||||||
|
paused -> cancelled
|
||||||
|
trigger_failed -> cancelled
|
||||||
|
triggered -> expired
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. 只有 `active` 可以被 scheduler 触发。
|
||||||
|
2. `cancelled` 为终态。
|
||||||
|
3. `paused` 不触发通知。
|
||||||
|
4. `trigger_failed` 必须写失败记录。
|
||||||
|
5. 一次性提醒成功触发后,状态从 `active` 变为 `triggered`,并可在后续清理或展示时变为 `expired`。
|
||||||
|
6. `daily`、`weekly`、`monthly` 周期提醒成功触发后状态保持 `active`,只更新 `last_triggered_at` 和下一次 `next_trigger_at`。
|
||||||
|
7. 提醒反馈不改变提醒调度状态;反馈结果保存在 `feedbacks`,提醒是否继续触发只由 `status`、`repeat_rule` 和 `next_trigger_at` 决定。
|
||||||
|
|
||||||
|
## 6. 重复规则
|
||||||
|
|
||||||
|
| 值 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `none` | 一次性 |
|
||||||
|
| `daily` | 每天 |
|
||||||
|
| `weekly` | 每周 |
|
||||||
|
| `monthly` | 每月 |
|
||||||
|
|
||||||
|
第一版不支持 cron 表达式和自定义复杂周期。
|
||||||
|
|
||||||
|
## 7. 通知状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending` | 待发送 |
|
||||||
|
| `sending` | 发送中 |
|
||||||
|
| `sent` | 已发送 |
|
||||||
|
| `failed` | 发送失败 |
|
||||||
|
| `retrying` | 补发中 |
|
||||||
|
| `cancelled` | 已取消 |
|
||||||
|
| `expired` | 卡片或通知操作已过期 |
|
||||||
|
|
||||||
|
允许流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending -> sending
|
||||||
|
pending -> sent
|
||||||
|
pending -> failed
|
||||||
|
sending -> sent
|
||||||
|
sending -> failed
|
||||||
|
failed -> retrying
|
||||||
|
retrying -> sent
|
||||||
|
retrying -> failed
|
||||||
|
pending -> cancelled
|
||||||
|
failed -> cancelled
|
||||||
|
pending -> expired
|
||||||
|
sent -> expired
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. `sent` 不得重复发送同一通知。
|
||||||
|
2. 补发必须保留原失败记录和新发送结果。
|
||||||
|
3. 飞书通知必须使用幂等键。
|
||||||
|
4. `expired`、`cancelled` 通知不得继续处理卡片确认、反馈或补充/重说。
|
||||||
|
5. 被新卡片替代或业务取消时,必须写 `invalidated_at`。
|
||||||
|
|
||||||
|
通知幂等键建议:
|
||||||
|
|
||||||
|
```text
|
||||||
|
target_type + target_id + receiver_id + channel + trigger_time
|
||||||
|
```
|
||||||
|
|
||||||
|
提醒触发幂等键建议:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reminder_id + trigger_time + receiver_id + notification_channel
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 反馈状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `received` | 已收到 |
|
||||||
|
| `in_progress` | 处理中 |
|
||||||
|
| `completed` | 已完成 |
|
||||||
|
| `problem` | 有问题 |
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. 反馈目标只能是 `task` 或 `reminder`。
|
||||||
|
2. 反馈人必须是接收人。
|
||||||
|
3. `problem` 必须填写一句原因。
|
||||||
|
4. 重复点击卡片不得重复创建相同反馈。
|
||||||
|
|
||||||
|
## 9. 失败记录状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending` | 待处理 |
|
||||||
|
| `processing` | 处理中 |
|
||||||
|
| `resolved` | 已处理 |
|
||||||
|
| `cancelled` | 已取消 |
|
||||||
|
|
||||||
|
允许流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending -> processing
|
||||||
|
pending -> resolved
|
||||||
|
pending -> cancelled
|
||||||
|
processing -> resolved
|
||||||
|
processing -> cancelled
|
||||||
|
```
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. `resolved` 必须填写处理结果。
|
||||||
|
2. 状态变化必须写操作日志。
|
||||||
|
|
||||||
|
## 10. 失败类型
|
||||||
|
|
||||||
|
| 类型 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `ai_parse_failed` | AI 输出解析失败 |
|
||||||
|
| `ai_model_failed` | 百炼或模型网关超时、网络失败、服务异常,重试后仍失败 |
|
||||||
|
| `bot_message_failed` | 入口消息结构缺失、无法解析文本或 sender 信息 |
|
||||||
|
| `missing_person_mapping` | 缺少人员映射 |
|
||||||
|
| `memory_store_failed` | PostgreSQL AI 记忆表、上下文或快照保存/读取失败 |
|
||||||
|
| `draft_convert_failed` | 老板或程经理确认后,转换任务/提醒失败 |
|
||||||
|
| `feishu_auth_failed` | 飞书登录失败 |
|
||||||
|
| `feishu_send_failed` | 飞书通知失败 |
|
||||||
|
| `feishu_callback_failed` | 飞书回调处理失败 |
|
||||||
|
| `feishu_signature_invalid` | 飞书验签失败 |
|
||||||
|
| `reminder_trigger_failed` | 定时提醒触发失败 |
|
||||||
|
| `bot_unauthorized` | 非老板使用机器人入口 |
|
||||||
|
| `follow_up_expired` | 补充/重说上下文过期 |
|
||||||
|
| `user_feedback_problem` | 用户反馈有问题 |
|
||||||
|
| `permission_error` | 权限拒绝 |
|
||||||
|
| `system_error` | 未归类系统异常 |
|
||||||
|
|
||||||
|
## 11. 飞书事件处理状态
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending` | 已接收待处理 |
|
||||||
|
| `processed` | 已处理 |
|
||||||
|
| `failed` | 处理失败 |
|
||||||
|
| `ignored` | 已忽略 |
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
1. 同一 `event_id` 只处理一次。
|
||||||
|
2. 如果飞书某类回调没有稳定 `event_id`,必须使用 `idempotency_key` 做事件幂等。
|
||||||
|
3. 验签失败不得执行业务动作。
|
||||||
|
4. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录事件并标记 `ignored`,不得写业务对象。
|
||||||
|
5. 处理失败必须写失败记录。
|
||||||
|
|
||||||
|
## 12. 用户和配置状态
|
||||||
|
|
||||||
|
用户状态:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `active` | 启用 |
|
||||||
|
| `disabled` | 停用 |
|
||||||
|
|
||||||
|
人员映射状态:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending` | 待解析 |
|
||||||
|
| `resolved` | 已解析 |
|
||||||
|
| `failed` | 解析失败 |
|
||||||
|
|
||||||
|
Prompt 上下文状态:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `draft` | 草稿 |
|
||||||
|
| `active` | 启用 |
|
||||||
|
| `archived` | 已归档 |
|
||||||
|
|
||||||
|
飞书登录会话状态:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `pending` | 登录处理中 |
|
||||||
|
| `success` | 登录成功 |
|
||||||
|
| `failed` | 登录失败 |
|
||||||
|
|
||||||
|
机器人上下文状态:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
| --- | --- |
|
||||||
|
| `empty` | 当前没有可补充或可确认的草稿 |
|
||||||
|
| `awaiting_more_info` | AI 已追问,等待老板补充 |
|
||||||
|
| `awaiting_confirm` | 草稿已整理完成,等待老板确认、修改、取消或转经理 |
|
||||||
|
| `awaiting_follow_up` | 老板点击补充/重说后,等待下一条消息修订上一版草稿 |
|
||||||
|
| `expired` | 已过期 |
|
||||||
|
| `cleared` | 草稿已确认、取消或转换完成,上下文已清空 |
|
||||||
|
|||||||
@@ -1 +1,602 @@
|
|||||||
|
# 老板一句话闭环 Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 建立第一版后端最小闭环:老板一句话生成 AI 草稿,人工确认后创建事项或提醒,通过飞书通知接收人并记录反馈和失败。
|
||||||
|
|
||||||
|
**Architecture:** 使用 Django 模块化单体。DRF view 负责请求入口,serializer 负责入参出参校验,service 负责权限、状态流转、事务和幂等,client 负责阿里百炼和飞书外部调用,audit 负责操作日志和失败记录。
|
||||||
|
|
||||||
|
**Tech Stack:** 部署 Python 3.12.13、本地开发 Python 3.11/3.12、Django 5.2 LTS、Django REST Framework、Django ORM、PostgreSQL 18.x、Django management command scheduler、阿里百炼 API、飞书开放平台。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 施工前必须阅读
|
||||||
|
|
||||||
|
1. `AGENTS.md`
|
||||||
|
2. `ARCHITECTURE.md`
|
||||||
|
3. `docs/specs/01_老板AI秘书与AI草稿.md`
|
||||||
|
4. `docs/specs/02_事项任务.md`
|
||||||
|
5. `docs/specs/03_飞书通知与反馈.md`
|
||||||
|
6. `docs/specs/04_定时提醒.md`
|
||||||
|
7. `docs/specs/05_权限日志失败记录.md`
|
||||||
|
8. `docs/contracts/API接口约定.md`
|
||||||
|
9. `docs/contracts/数据对象约定.md`
|
||||||
|
10. `docs/contracts/状态流转约定.md`
|
||||||
|
11. `docs/contracts/安全权限日志约定.md`
|
||||||
|
12. `docs/checklists/AI生成代码检查清单.md`
|
||||||
|
13. `docs/checklists/后端Review清单.md`
|
||||||
|
|
||||||
|
## 范围边界
|
||||||
|
|
||||||
|
本计划只做第一版最小闭环:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板一句话
|
||||||
|
-> AI 草稿
|
||||||
|
-> 人工确认
|
||||||
|
-> 创建事项或定时提醒
|
||||||
|
-> 飞书通知
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 页面/API/Admin 可看到结果和失败记录
|
||||||
|
```
|
||||||
|
|
||||||
|
本计划不做 AI 工作台、技能市场、文件上传、多模态处理、会议纪要、日报、文档摘要、复杂反馈看板、复杂 BI、成本驾驶舱、完整工作流、完整项目管理、多级子任务、甘特图、复杂审批流、复杂组织架构、cron 表达式、日历系统、自动操作交易系统、自动操作历史数据库、自动读取员工本地文件、手机端网页完整适配,也不把飞书机器人开放为程经理或普通员工通用派活入口。
|
||||||
|
|
||||||
|
## 可执行性门禁
|
||||||
|
|
||||||
|
1. 本计划先跑通 mock-first 后端闭环;单元测试和最小闭环测试不得访问真实百炼、真实飞书或公网回调。
|
||||||
|
2. 真实联调前必须确认飞书正式应用、HTTPS 回调地址、个人消息权限、交互卡片权限、老板 / 程经理 / 第一批试用人员映射,以及百炼 API Key 环境变量。
|
||||||
|
3. Docker / 部署环境固定 Python 3.12.13;本地开发允许 Python 3.11 或 3.12,但提交前必须在 Python 3.12.13 环境跑完整测试。
|
||||||
|
4. scheduler 必须作为独立进程运行,同一环境同一应用库同时只能启动一个 scheduler 进程。
|
||||||
|
5. tests 是真实约束;每个任务完成前必须说明新增或修改了哪些测试、覆盖哪些 spec / contract 约定和如何运行。
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
第一轮代码施工建议创建这些目录和文件:
|
||||||
|
|
||||||
|
```text
|
||||||
|
requirements.txt
|
||||||
|
manage.py
|
||||||
|
config/
|
||||||
|
__init__.py
|
||||||
|
settings.py
|
||||||
|
urls.py
|
||||||
|
wsgi.py
|
||||||
|
apps/
|
||||||
|
accounts/
|
||||||
|
people/
|
||||||
|
prompts/
|
||||||
|
audit/
|
||||||
|
drafts/
|
||||||
|
tasks/
|
||||||
|
reminders/
|
||||||
|
notifications/
|
||||||
|
feedbacks/
|
||||||
|
feishu/
|
||||||
|
tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
每个 Django app 采用统一结构:
|
||||||
|
|
||||||
|
```text
|
||||||
|
apps/<app>/
|
||||||
|
__init__.py
|
||||||
|
admin.py
|
||||||
|
apps.py
|
||||||
|
models.py
|
||||||
|
serializers.py
|
||||||
|
services.py
|
||||||
|
urls.py
|
||||||
|
views.py
|
||||||
|
tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
外部 API 封装放在:
|
||||||
|
|
||||||
|
```text
|
||||||
|
apps/drafts/ai_client.py
|
||||||
|
apps/feishu/client.py
|
||||||
|
apps/feishu/signature.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Django 项目骨架和依赖基线
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `requirements.txt`
|
||||||
|
- Create: `manage.py`
|
||||||
|
- Create: `config/__init__.py`
|
||||||
|
- Create: `config/settings.py`
|
||||||
|
- Create: `config/urls.py`
|
||||||
|
- Create: `config/wsgi.py`
|
||||||
|
- Create: `apps/__init__.py`
|
||||||
|
- Create: `apps/accounts/__init__.py`
|
||||||
|
- Create: `apps/accounts/apps.py`
|
||||||
|
- Create: `apps/people/__init__.py`
|
||||||
|
- Create: `apps/people/apps.py`
|
||||||
|
- Create: `apps/prompts/__init__.py`
|
||||||
|
- Create: `apps/prompts/apps.py`
|
||||||
|
- Create: `apps/audit/__init__.py`
|
||||||
|
- Create: `apps/audit/apps.py`
|
||||||
|
- Create: `apps/drafts/__init__.py`
|
||||||
|
- Create: `apps/drafts/apps.py`
|
||||||
|
- Create: `apps/tasks/__init__.py`
|
||||||
|
- Create: `apps/tasks/apps.py`
|
||||||
|
- Create: `apps/reminders/__init__.py`
|
||||||
|
- Create: `apps/reminders/apps.py`
|
||||||
|
- Create: `apps/notifications/__init__.py`
|
||||||
|
- Create: `apps/notifications/apps.py`
|
||||||
|
- Create: `apps/feedbacks/__init__.py`
|
||||||
|
- Create: `apps/feedbacks/apps.py`
|
||||||
|
- Create: `apps/feishu/__init__.py`
|
||||||
|
- Create: `apps/feishu/apps.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写依赖文件**
|
||||||
|
|
||||||
|
`requirements.txt` 第一版固定包含:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Django==5.2.15
|
||||||
|
djangorestframework==3.17.1
|
||||||
|
psycopg[binary]
|
||||||
|
asgiref==3.11.1
|
||||||
|
sqlparse==0.5.5
|
||||||
|
tzdata==2026.2
|
||||||
|
python-dotenv==1.2.2
|
||||||
|
requests==2.34.2
|
||||||
|
httpx==0.28.1
|
||||||
|
APScheduler==3.11.2
|
||||||
|
django-apscheduler==0.7.0
|
||||||
|
dashscope==1.25.23
|
||||||
|
alibabacloud-bailian20231229==2.13.1
|
||||||
|
lark-oapi==1.6.8
|
||||||
|
PyJWT==2.13.0
|
||||||
|
cryptography==49.0.0
|
||||||
|
gunicorn==26.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 Django 项目骨架和 app 空壳**
|
||||||
|
|
||||||
|
使用 `config` 作为项目配置包,`apps` 作为业务 app 根目录。Task 1 必须先创建所有业务 app 的 `__init__.py` 和 `apps.py`,保证后续加入 `INSTALLED_APPS` 时可导入。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 配置 settings**
|
||||||
|
|
||||||
|
`config/settings.py` 必须包含:
|
||||||
|
|
||||||
|
1. `INSTALLED_APPS` 加入 Django contrib app、DRF,以及已创建且可导入的业务 app 空壳。
|
||||||
|
2. Task 1 暂不设置 `AUTH_USER_MODEL`;Task 2 实现 `apps.accounts.models.User` 后、首次 `makemigrations` / `migrate` 前再设置 `AUTH_USER_MODEL = "accounts.User"`。
|
||||||
|
3. 数据库配置读取环境变量。
|
||||||
|
4. 飞书和百炼密钥只读取环境变量。
|
||||||
|
5. 日志配置避免输出密钥。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行 Django 检查**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py check
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `System check identified no issues`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: 账号、人员映射、Prompt、AI 记忆和审计基础模型
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `config/settings.py`
|
||||||
|
- Create: `apps/accounts/models.py`
|
||||||
|
- Create: `apps/people/models.py`
|
||||||
|
- Create: `apps/prompts/models.py`
|
||||||
|
- Create: `apps/audit/models.py`
|
||||||
|
- Create: `apps/drafts/models.py`
|
||||||
|
- Create: `apps/accounts/admin.py`
|
||||||
|
- Create: `apps/people/admin.py`
|
||||||
|
- Create: `apps/prompts/admin.py`
|
||||||
|
- Create: `apps/audit/admin.py`
|
||||||
|
- Test: `apps/accounts/tests/test_permissions.py`
|
||||||
|
- Test: `apps/audit/tests/test_sensitive_logging.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 实现自定义 User**
|
||||||
|
|
||||||
|
字段必须覆盖 `docs/contracts/数据对象约定.md` 中的 `users`。实现后立刻在 `config/settings.py` 设置 `AUTH_USER_MODEL = "accounts.User"`,且必须发生在第一次迁移之前。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现 PersonMapping**
|
||||||
|
|
||||||
|
字段必须覆盖 `person_mappings`,并支持别名 JSON、`user_id` 和 `manager_user_id`。`users` 负责登录和权限,`person_mappings` 负责称呼、飞书身份和第一批试用人员映射。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现 PromptContext**
|
||||||
|
|
||||||
|
支持 `company_background_summary`、`boss_communication_style`、`ai_secretary_rules`、`role_and_alias_rules` 四类上下文,只允许同类一个 `active` 版本。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现 AI 对话记忆基础表**
|
||||||
|
|
||||||
|
实现 `secretary_conversations`、`secretary_messages`、`bot_contexts`。`secretary_messages` 只追加,不修改;`bot_contexts` 保存当前待确认草稿、30 分钟过期时间、`follow_up_count` 和 `extracted_facts`。JSON 弹性字段使用 PostgreSQL `jsonb`,不得用内存隐藏保存可确认状态。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 实现 FailureRecord、OperationLog、ModelCallLog**
|
||||||
|
|
||||||
|
失败类型和状态必须来自 `docs/contracts/状态流转约定.md`。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 写权限、AI 记忆和脱敏测试**
|
||||||
|
|
||||||
|
测试必须覆盖:
|
||||||
|
|
||||||
|
1. 普通员工不能访问失败记录列表。
|
||||||
|
2. 普通日志工具不会输出完整手机号。
|
||||||
|
3. 普通日志工具不会输出完整邮箱。
|
||||||
|
4. 日志元数据不会保存 API Key、App Secret 或飞书 token。
|
||||||
|
5. `secretary_messages` 幂等键 `source + message_id` 不重复写入。
|
||||||
|
6. PostgreSQL AI 记忆读写失败时返回 `memory_store_failed`,不继续依赖内存确认草稿。
|
||||||
|
|
||||||
|
- [ ] **Step 7: 生成并运行迁移**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py makemigrations
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py test apps.accounts apps.audit apps.drafts
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: migrations 成功,权限、审计和 AI 记忆测试通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: AiSecretaryAgent、AI 输出校验和统一消息入口
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `apps/drafts/models.py`
|
||||||
|
- Create: `apps/drafts/serializers.py`
|
||||||
|
- Create: `apps/drafts/ai_client.py`
|
||||||
|
- Create: `apps/drafts/services.py`
|
||||||
|
- Create: `apps/drafts/views.py`
|
||||||
|
- Create: `apps/drafts/urls.py`
|
||||||
|
- Test: `apps/drafts/tests/test_ai_output_validator.py`
|
||||||
|
- Test: `apps/drafts/tests/test_parse_service.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 实现 AiDraft 模型和 AiSecretaryResponse 结构**
|
||||||
|
|
||||||
|
字段必须覆盖 `ai_drafts`,状态必须来自 `状态流转约定.md`。`qa/realtime_qa/note/need_more_info/unknown/unsupported/context_summary` 不进入事项、提醒或通知闭环,只写 `secretary_messages`、`model_call_logs`、必要 `operation_logs`,或按 contract 写最小留痕草稿。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现 AI 输出 validator**
|
||||||
|
|
||||||
|
validator 必须校验:
|
||||||
|
|
||||||
|
1. JSON 可解析。
|
||||||
|
2. `intent` 只能是 `task/reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported`。
|
||||||
|
3. `task` / `reminder` 才允许 `should_create_draft = true`,`draft_type` 必须对应为 `task` 或 `reminder`。
|
||||||
|
4. `qa/realtime_qa/note/need_more_info/unknown/unsupported` 必须 `should_create_draft=false`、`draft_type=none`。
|
||||||
|
5. `need_more_info` 时 `questions` 至少 1 个、最多 3 个;其他 intent 时 `questions` 为空数组。
|
||||||
|
6. `task` 时 `route_type` 只能是 `direct_after_boss_confirm` 或 `manager_confirm_required`,其他 intent 为 `none`。
|
||||||
|
7. `answer` 不得出现“已通知、已创建、已发送”等执行语义。
|
||||||
|
8. 结构化字段不得包含明显游戏化表达,也不得编造老板没说过的业务结论。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现可 mock 的 ai_client**
|
||||||
|
|
||||||
|
`ai_client` 只封装阿里百炼调用,不修改业务表。测试中必须使用 mock,不访问真实网络。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现 AiSecretaryAgent service**
|
||||||
|
|
||||||
|
service 流程:
|
||||||
|
|
||||||
|
```text
|
||||||
|
记录原始消息
|
||||||
|
-> 按 source + message_id 做幂等检查
|
||||||
|
-> 校验老板角色
|
||||||
|
-> 读取 BotContext
|
||||||
|
-> 判断 follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command
|
||||||
|
-> 组装 PromptContext
|
||||||
|
-> 调用 ai_client
|
||||||
|
-> 写 model_call_logs
|
||||||
|
-> 校验 AiSecretaryResponse JSON
|
||||||
|
-> 生成草稿 / 追问 / 直接回复
|
||||||
|
-> 写 secretary_messages、operation_logs 和必要 failure_records
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 实现 API**
|
||||||
|
|
||||||
|
实现:
|
||||||
|
|
||||||
|
1. `POST /api/secretary/handle-message`
|
||||||
|
2. `GET /api/ai-drafts`
|
||||||
|
3. `GET /api/ai-drafts/{id}`
|
||||||
|
4. `PATCH /api/ai-drafts/{id}`
|
||||||
|
5. `POST /api/ai-drafts/{id}/cancel`
|
||||||
|
6. `POST /api/ai-drafts/{id}/follow-up`
|
||||||
|
|
||||||
|
兼容接口 `/api/ai-drafts/parse`、`/api/demo/boss-message` 可以保留,但内部必须转发到 `/api/secretary/handle-message`。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 跑测试**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py test apps.drafts
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: AI 输出校验、解析失败、实时问答、补充/重说测试全部通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: 事项、提醒、通知和反馈模型与服务
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/tasks/models.py`
|
||||||
|
- Create: `apps/tasks/services.py`
|
||||||
|
- Create: `apps/tasks/views.py`
|
||||||
|
- Create: `apps/reminders/models.py`
|
||||||
|
- Create: `apps/reminders/services.py`
|
||||||
|
- Create: `apps/reminders/views.py`
|
||||||
|
- Create: `apps/notifications/models.py`
|
||||||
|
- Create: `apps/notifications/services.py`
|
||||||
|
- Create: `apps/feedbacks/models.py`
|
||||||
|
- Create: `apps/feedbacks/services.py`
|
||||||
|
- Test: `apps/tasks/tests/test_task_permissions.py`
|
||||||
|
- Test: `apps/reminders/tests/test_reminder_permissions.py`
|
||||||
|
- Test: `apps/feedbacks/tests/test_feedback_rules.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 实现模型**
|
||||||
|
|
||||||
|
字段必须覆盖 `tasks`、`reminders`、`notifications`、`feedbacks`。`notifications` 必须包含 `target_type=ai_draft`、`purpose`、`trigger_time`、`idempotency_key`、`action_token_hash`、`expires_at`、`invalidated_at` 等字段。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现事项 service**
|
||||||
|
|
||||||
|
必须支持:
|
||||||
|
|
||||||
|
1. 老板和程经理创建事项。
|
||||||
|
2. 普通员工不能给别人创建事项。
|
||||||
|
3. 程经理确认复杂事项。
|
||||||
|
4. 通知失败保留失败状态。
|
||||||
|
5. 有问题反馈必须填写原因。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现提醒 service**
|
||||||
|
|
||||||
|
必须支持:
|
||||||
|
|
||||||
|
1. 所有人创建自己的提醒。
|
||||||
|
2. 老板和程经理给别人创建提醒。
|
||||||
|
3. 普通员工不能给别人创建提醒。
|
||||||
|
4. 一次性、每天、每周、每月重复规则。
|
||||||
|
5. 暂停、恢复、取消。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现通知和反馈 service**
|
||||||
|
|
||||||
|
必须支持:
|
||||||
|
|
||||||
|
1. 生成飞书通知记录。
|
||||||
|
2. 使用幂等键避免重复通知。
|
||||||
|
3. 接收人反馈。
|
||||||
|
4. 非接收人反馈返回 `permission_denied`,并写 `FailureRecord(permission_error)`。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 实现 API**
|
||||||
|
|
||||||
|
按 `docs/contracts/API接口约定.md` 实现 tasks、reminders、notifications、feedbacks 相关接口。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 跑测试**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py test apps.tasks apps.reminders apps.notifications apps.feedbacks
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 权限、状态、反馈原因和通知失败测试通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: 飞书登录、机器人事件和卡片回调
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/feishu/models.py`
|
||||||
|
- Create: `apps/feishu/client.py`
|
||||||
|
- Create: `apps/feishu/signature.py`
|
||||||
|
- Create: `apps/feishu/services.py`
|
||||||
|
- Create: `apps/feishu/views.py`
|
||||||
|
- Create: `apps/feishu/urls.py`
|
||||||
|
- Test: `apps/feishu/tests/test_signature.py`
|
||||||
|
- Test: `apps/feishu/tests/test_event_idempotency.py`
|
||||||
|
- Test: `apps/feishu/tests/test_bot_access.py`
|
||||||
|
- Test: `apps/feishu/tests/test_card_callback.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 实现 FeishuEvent 和 FeishuAuthSession**
|
||||||
|
|
||||||
|
字段必须覆盖 `feishu_events` 和 `feishu_auth_sessions`。
|
||||||
|
|
||||||
|
本任务所有测试必须 mock 飞书 SDK / HTTP client,不依赖真实飞书应用、公网回调或个人消息权限。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现验签**
|
||||||
|
|
||||||
|
验签失败返回 `feishu_signature_invalid`,不得执行业务动作。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现事件幂等**
|
||||||
|
|
||||||
|
同一 `event_id` 重放时只返回已有处理结果,不重复创建草稿、事项、提醒或反馈。没有稳定 `event_id` 的卡片回调必须使用 `idempotency_key`。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现老板机器人入口**
|
||||||
|
|
||||||
|
非老板私聊机器人时:
|
||||||
|
|
||||||
|
```text
|
||||||
|
不调用 AI
|
||||||
|
不生成草稿
|
||||||
|
回复当前入口仅对老板开放
|
||||||
|
写 bot_unauthorized 失败记录
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 实现卡片回调**
|
||||||
|
|
||||||
|
支持:
|
||||||
|
|
||||||
|
1. 老板确认草稿。
|
||||||
|
2. 老板补充/重说。
|
||||||
|
3. 老板取消草稿。
|
||||||
|
4. 接收人反馈事项或提醒。
|
||||||
|
5. 程经理待确认提醒只跳转平台。
|
||||||
|
6. 旧卡片、失效卡片、已替代草稿和已过期通知只记录 `feishu_events`,不写业务对象。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 跑测试**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py test apps.feishu
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 验签、幂等、非老板访问、卡片反馈测试通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: 草稿转换、事项通知和提醒调度闭环
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `apps/drafts/services.py`
|
||||||
|
- Modify: `apps/tasks/services.py`
|
||||||
|
- Modify: `apps/reminders/services.py`
|
||||||
|
- Modify: `apps/notifications/services.py`
|
||||||
|
- Create: `apps/reminders/management/commands/run_reminder_scheduler.py`
|
||||||
|
- Test: `apps/drafts/tests/test_convert_draft.py`
|
||||||
|
- Test: `apps/reminders/tests/test_scheduler.py`
|
||||||
|
- Test: `apps/notifications/tests/test_notification_retry.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 实现草稿确认和转换**
|
||||||
|
|
||||||
|
`confirmed -> converted` 只允许执行一次。`task` 草稿转换为事项,`reminder` 草稿转换为提醒。转换失败时不回滚老板确认,写 `FailureRecord(draft_convert_failed)`。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现复杂事项转程经理确认**
|
||||||
|
|
||||||
|
`route_type = manager_confirm_required` 时,老板确认后草稿进入 `confirmed`,转换时先创建 `tasks.status=pending_manager_confirm` 的事项壳,并生成给程经理的飞书提醒;事项壳创建成功后草稿进入 `converted`。程经理确认事项壳后补齐接收人和内容,再进入通知流程。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现 scheduler command**
|
||||||
|
|
||||||
|
命令名:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py run_reminder_scheduler
|
||||||
|
```
|
||||||
|
|
||||||
|
scheduler 只扫描 `active` 且 `next_trigger_at <= now` 的提醒。一次性提醒成功触发后走 `active -> triggered`;每天、每周、每月提醒成功触发后保持 `active`,只更新 `last_triggered_at` 和下一次 `next_trigger_at`。
|
||||||
|
|
||||||
|
同一环境同一应用库只允许一个 scheduler 进程运行;测试中要用幂等键验证重复扫描不会重复创建通知。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现提醒触发幂等**
|
||||||
|
|
||||||
|
幂等键:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reminder_id + trigger_time + receiver_id + notification_channel
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 实现通知补发**
|
||||||
|
|
||||||
|
补发必须保留原失败记录,并写新的通知发送结果。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 跑测试**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py test apps.drafts apps.reminders apps.notifications
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 草稿不重复转换、提醒不重复触发、通知补发测试通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 7: Admin、路由汇总、最小验收脚本
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `config/urls.py`
|
||||||
|
- Modify: `apps/*/admin.py`
|
||||||
|
- Create: `tests/test_minimal_closure.py`
|
||||||
|
- Modify: `README.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 汇总 API 路由**
|
||||||
|
|
||||||
|
`config/urls.py` 挂载:
|
||||||
|
|
||||||
|
1. `/api/secretary/`
|
||||||
|
2. `/api/ai-drafts/`
|
||||||
|
3. `/api/tasks/`
|
||||||
|
4. `/api/reminders/`
|
||||||
|
5. `/api/notifications/`
|
||||||
|
6. `/api/feedbacks/`
|
||||||
|
7. `/api/failure-records/`
|
||||||
|
8. `/api/feishu/`
|
||||||
|
9. `/api/auth/feishu/`
|
||||||
|
|
||||||
|
- [ ] **Step 2: 注册 Admin**
|
||||||
|
|
||||||
|
Admin 至少可查看和维护:
|
||||||
|
|
||||||
|
1. 用户。
|
||||||
|
2. 人员映射。
|
||||||
|
3. Prompt 上下文。
|
||||||
|
4. AI 草稿。
|
||||||
|
5. 事项。
|
||||||
|
6. 提醒。
|
||||||
|
7. 通知。
|
||||||
|
8. 反馈。
|
||||||
|
9. 失败记录。
|
||||||
|
10. 操作日志。
|
||||||
|
11. 模型调用日志。
|
||||||
|
12. 飞书事件。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 写最小闭环测试**
|
||||||
|
|
||||||
|
测试覆盖:
|
||||||
|
|
||||||
|
```text
|
||||||
|
boss 用户提交一句话
|
||||||
|
-> mock AI 返回 task 草稿
|
||||||
|
-> boss 确认
|
||||||
|
-> 创建事项
|
||||||
|
-> mock 飞书通知成功
|
||||||
|
-> 接收人反馈 completed
|
||||||
|
-> 事项状态 completed
|
||||||
|
-> 有操作日志和通知记录
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 更新 README 本地启动**
|
||||||
|
|
||||||
|
补充:
|
||||||
|
|
||||||
|
1. 安装依赖。
|
||||||
|
2. 环境变量。
|
||||||
|
3. 迁移。
|
||||||
|
4. 创建测试用户。
|
||||||
|
5. 启动 Web。
|
||||||
|
6. 启动 scheduler。
|
||||||
|
7. 运行测试。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 跑完整测试**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py test
|
||||||
|
python manage.py check
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 全部测试通过,Django check 无错误。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 完成标准
|
||||||
|
|
||||||
|
1. 所有模型字段与 `docs/contracts/数据对象约定.md` 一致。
|
||||||
|
2. 所有状态与 `docs/contracts/状态流转约定.md` 一致。
|
||||||
|
3. 所有接口路径与 `docs/contracts/API接口约定.md` 一致。
|
||||||
|
4. AI 只生成草稿,人工确认后才创建事项或提醒。
|
||||||
|
5. 普通员工不能给别人创建事项或提醒。
|
||||||
|
6. 飞书回调验签和幂等有测试。
|
||||||
|
7. 定时提醒重复触发防护有测试。
|
||||||
|
8. 通知失败、AI 解析失败、回调失败有失败记录。
|
||||||
|
9. 普通日志不包含密钥、token、完整手机号或完整邮箱。
|
||||||
|
10. OAuth code/state、回调验签密钥、一次性操作 token 明文不进入普通日志或业务表。
|
||||||
|
11. 每个任务完成前说明新增或修改了哪些 tests、覆盖哪些 spec / contract 约定和如何运行。
|
||||||
|
12. `python manage.py test` 和 `python manage.py check` 通过。
|
||||||
|
|
||||||
|
## 执行备注
|
||||||
|
|
||||||
|
如果执行中发现本计划与 spec 或 contracts 冲突,以 contracts 为准,并先提出计划变更建议;不要偷偷修改业务边界。
|
||||||
|
|||||||
@@ -1 +1,9 @@
|
|||||||
|
# 飞书通知回调实施计划归档占位
|
||||||
|
|
||||||
|
当前没有已完成的飞书通知回调实施计划。
|
||||||
|
|
||||||
|
后续当飞书通知、交互卡片和回调能力完成并通过联调验收后,再从 `docs/plans/active/` 移入本目录。当前施工依据只看:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/plans/active/01_老板一句话闭环实施计划.md
|
||||||
|
```
|
||||||
|
|||||||
@@ -1 +1,930 @@
|
|||||||
|
# 01_老板 AI 秘书与 AI 草稿
|
||||||
|
|
||||||
|
## 1. 模块目标
|
||||||
|
|
||||||
|
本模块负责把老板在飞书私聊或平台入口输入的一句话,交给 `AiSecretaryAgent` 理解、归类、补上下文,并生成可确认的 AI 草稿或直接回复。
|
||||||
|
|
||||||
|
这个模块是老板秘书的大脑,不直接创建任务、提醒或发送通知。所有执行动作都必须走人工确认后的任务、提醒和通知模块。
|
||||||
|
|
||||||
|
说明:整体方案中的“事项、需求、安排”在第一版统一归入 `task`。如果后续单独拆出 requirement 类型,需要先更新本 spec 和数据对象约定。
|
||||||
|
|
||||||
|
## 2. 第一版做什么
|
||||||
|
|
||||||
|
第一版核心定位:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板一句话
|
||||||
|
-> AiSecretaryAgent 判断意图和上下文
|
||||||
|
-> 意图归类为 task / reminder / qa / realtime_qa / note / need_more_info / unknown / unsupported
|
||||||
|
-> 稳定 AiSecretaryResponse JSON
|
||||||
|
-> 生成 AI 草稿 / 追问 / 直接回复
|
||||||
|
-> 老板确认、补充、取消或转程经理
|
||||||
|
```
|
||||||
|
|
||||||
|
- 支持老板通过飞书私聊或平台入口输入文本。
|
||||||
|
- 统一走 `POST /api/secretary/handle-message`。
|
||||||
|
- 判断 `task/reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported`。
|
||||||
|
- 生成稳定的 `AiSecretaryResponse` JSON。
|
||||||
|
- 对任务和提醒只生成可确认草稿,不直接执行。
|
||||||
|
- 对普通聊天、实时问答兜底、普通记录直接回复或记录。
|
||||||
|
- 支持 30 分钟内补充、重说、确认、取消和转程经理。
|
||||||
|
- 支持低置信度追问和最多 3 轮追问收敛。
|
||||||
|
- 使用 PostgreSQL 持久化老板秘书当前会话记忆、消息记录和 `BotContext`。
|
||||||
|
- 记录模型调用、提示词版本、操作日志和失败记录。
|
||||||
|
|
||||||
|
## 3. 第一版不做
|
||||||
|
|
||||||
|
- 不做员工通用 AI 工作台。
|
||||||
|
- 不让程经理或普通员工通过机器人派活。
|
||||||
|
- 不让 AI 自动创建任务、提醒、通知或外部系统操作。
|
||||||
|
- 不做复杂多轮通用聊天记忆、跨账号记忆或长期画像;第一阶段只持久化老板秘书当前会话所需的对话记忆和草稿上下文。
|
||||||
|
- 不把公司背景库全文每次塞进模型,只使用 `PromptContext` 中的压缩摘要和版本。
|
||||||
|
- 不做实时行情、交易、数据库、后台系统自动查询和自动判断。
|
||||||
|
- 不把普通记录做成独立知识库、向量库或长期记忆,只保留当前演示闭环需要的消息记录。
|
||||||
|
|
||||||
|
## 4. 核心流程
|
||||||
|
|
||||||
|
### 4.1 统一入口
|
||||||
|
|
||||||
|
所有老板消息统一走:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/secretary/handle-message
|
||||||
|
```
|
||||||
|
|
||||||
|
飞书回调、`/api/demo/boss-message`、未来平台入口都转发到该入口。
|
||||||
|
|
||||||
|
处理顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
记录原始消息
|
||||||
|
-> 按 source + message_id 做幂等检查
|
||||||
|
-> 校验发送人角色
|
||||||
|
-> 非老板直接固定回复并记录
|
||||||
|
-> 读取 BotContext,外部 context 只作为参考
|
||||||
|
-> 判断本次消息是 follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command
|
||||||
|
-> 组装 PromptContext
|
||||||
|
-> 调用模型
|
||||||
|
-> 校验 AiSecretaryResponse
|
||||||
|
-> 写 ModelCallLog
|
||||||
|
-> 生成草稿 / 追问 / 直接回复
|
||||||
|
-> 写 OperationLog 和必要 FailureRecord
|
||||||
|
```
|
||||||
|
|
||||||
|
幂等规则:
|
||||||
|
|
||||||
|
- 同一个 `source + message_id` 重复进入时,不重复调用模型、不重复创建草稿、不重复写业务对象。
|
||||||
|
- 幂等命中时,不重复追加 PostgreSQL `secretary_messages`,不重复写 `SecretaryMessage`,只返回既有处理结果。
|
||||||
|
- 飞书或外部入口的原始回调可以写 `FeishuEventLog` 并标记为 duplicate,但不能污染老板对话记忆。
|
||||||
|
- 如果上一轮已经处理成功,直接返回上一轮处理结果或可重放的 `reply_type/answer/draft_id`。
|
||||||
|
- 如果上一轮处理失败,允许按失败类型进入人工重试或后台重试,但必须继续复用同一个幂等键。
|
||||||
|
|
||||||
|
### 4.2 上下文规则
|
||||||
|
|
||||||
|
`BotContext / Conversation` 记录老板当前可补充的草稿。
|
||||||
|
|
||||||
|
`BotContext.status` 建议取值:
|
||||||
|
|
||||||
|
```text
|
||||||
|
empty
|
||||||
|
awaiting_more_info
|
||||||
|
awaiting_confirm
|
||||||
|
expired
|
||||||
|
cleared
|
||||||
|
```
|
||||||
|
|
||||||
|
状态含义:
|
||||||
|
|
||||||
|
- `empty`:当前没有可补充或可确认的草稿。
|
||||||
|
- `awaiting_more_info`:AI 已追问,等待老板补充。
|
||||||
|
- `awaiting_confirm`:草稿已整理完成,等待老板确认、修改、取消或转经理。
|
||||||
|
- `expired`:超过 30 分钟,旧上下文只可用于回顾,不再默认参与修改。
|
||||||
|
- `cleared`:草稿已确认、取消或转换完成,当前上下文已清空。
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- 每次进入 `awaiting_more_info` 或 `awaiting_confirm` 状态时重置 `expires_at = now + 30 分钟`。
|
||||||
|
- 点击或说出“补充/重说”后,30 分钟内下一条消息优先尝试作为上一条草稿的修正。
|
||||||
|
- 30 分钟内不能无条件吞掉所有消息,必须先判断为 `follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command`。
|
||||||
|
- 老板说“换成东东”“改到明天”“刚才那个不要了”,优先修正当前草稿。
|
||||||
|
- 老板说“另外再安排一个”“新建一个”“再帮我记一个”,创建新草稿。
|
||||||
|
- 老板说“确认”“就这样”“发给他”,确认当前草稿。
|
||||||
|
- 老板说“算了”“取消”“不要了”,取消当前草稿并清空上下文。
|
||||||
|
- 老板问“刚才我说了什么”“总结一下”,做上下文回顾,不修改草稿。
|
||||||
|
- 超过 30 分钟,上下文标记 `expired`,下一条默认按新输入处理;如果老板明显仍在说上一条草稿,可先提示“上一条草稿已过期,我按新请求处理”。
|
||||||
|
- 老板确认、取消或草稿转换为任务/提醒后立即清空上下文,不受 30 分钟窗口约束。
|
||||||
|
- 30 分钟窗口只绑定未确认草稿;草稿进入 `confirmed / converted / cancelled / superseded / expired` 后,后续“改一下刚才那个”不能再修改原草稿,只能作为已发布任务/提醒变更请求或第一阶段能力外请求处理。
|
||||||
|
- 多次 `follow_up` 必须合并到同一个 `pending_draft`,直到老板确认、取消、过期或明确新建,不得每次补充都新建草稿。
|
||||||
|
- `BotContext` 只提供上下文候选,不强制绑定消息。当本地规则或模型无法判断是修改当前草稿、新建请求、普通聊天还是取消当前草稿时,必须走 `need_more_info` 追问,不允许默认修改当前草稿。
|
||||||
|
- 老板可以通过“另外、重新来、换个事、先别管这个、取消、重新安排”等自然语言随时跳出当前上下文。
|
||||||
|
|
||||||
|
`command` 不是模型最终 `intent`,而是进入模型前后的消息预分类,用于处理“确认、取消、转经理、补充/重说、上下文回顾”等对当前 `BotContext` 的操作。命中明确 command 时,后端优先处理当前上下文;需要模型理解时,再把 command 结果作为上下文提示传给模型。
|
||||||
|
|
||||||
|
命中确认、取消、补充、转经理等 command,但当前没有待处理草稿时,不抛异常、不创建新草稿,按 `qa`/`plain_answer` 回复“目前没有待确认的草稿”。
|
||||||
|
|
||||||
|
自然语言指令最小关键词集合:
|
||||||
|
|
||||||
|
| 类型 | 关键词 |
|
||||||
|
| --- | --- |
|
||||||
|
| 新建信号 | 另外、新建、再来一个、再安排、再记一个 |
|
||||||
|
| 跳出信号 | 重新来、换个事、先别管这个、说另一个、重新安排 |
|
||||||
|
| 补充信号 | 改成、改为、换成、改到、补充、加上 |
|
||||||
|
| 确认信号 | 确认、就这样、发给他、可以了 |
|
||||||
|
| 取消信号 | 取消、算了、不要了、作废、刚才那个不要 |
|
||||||
|
|
||||||
|
关键词只是第一层判断,最终仍要结合 BotContext、当前草稿和模型结构化结果确认。
|
||||||
|
|
||||||
|
### 4.3 追问收敛
|
||||||
|
|
||||||
|
- 缺少关键字段时先追问。
|
||||||
|
- 每次最多 3 个问题。
|
||||||
|
- 累计最多 3 轮追问。
|
||||||
|
- 老板说“就这样”“差不多”“先这样”时,停止追问,生成带 `missing_fields` 的草稿。
|
||||||
|
- 3 轮仍补不全,也生成带 `missing_fields` 的草稿,不继续拉扯。
|
||||||
|
- 每次追问都必须保存一条 `SecretaryMessage`,`intent_type=need_more_info`,内容为本次给老板的问题。
|
||||||
|
- 追问轮次写入 `BotContext.follow_up_count`,并记录 `OperationLog(action=ask_follow_up)`。
|
||||||
|
- `follow_up_count` 在以下情况重置为 0:草稿被确认、取消、过期、进入 `pending_confirmation`,或生成带 `missing_fields` 的终态草稿后收到下一条新消息。
|
||||||
|
|
||||||
|
## 5. 数据对象
|
||||||
|
|
||||||
|
本模块主要涉及:
|
||||||
|
|
||||||
|
- `AiSecretaryResponse`
|
||||||
|
- `BotContext / Conversation`
|
||||||
|
- `SecretaryDraft / ai_drafts`
|
||||||
|
- `SecretaryMessage / note`
|
||||||
|
- `PromptContext`
|
||||||
|
- `ModelCallLog`
|
||||||
|
- `OperationLog`
|
||||||
|
- `FailureRecord`
|
||||||
|
- `FeishuEventLog`
|
||||||
|
|
||||||
|
字段事实源以 `docs/contracts/数据对象约定.md` 为准。本 spec 只强调模块规则。
|
||||||
|
|
||||||
|
`BotContext` 归本模块维护;`05_权限日志失败记录.md` 只消费它做调试展示、过期记录和操作审计。
|
||||||
|
|
||||||
|
`BotContext` 第一阶段建议字段:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `conversation_id` | 当前老板秘书会话 ID,第一阶段可固定为 `boss_secretary_default` |
|
||||||
|
| `boss_id` | PostgreSQL 中的老板用户 ID |
|
||||||
|
| `status` | `empty/awaiting_more_info/awaiting_confirm/expired/cleared` |
|
||||||
|
| `pending_draft_id` | 当前待补充或待确认的 PostgreSQL 草稿 ID |
|
||||||
|
| `pending_draft_type` | `task/reminder/none` |
|
||||||
|
| `last_intent` | 上一条有效意图 |
|
||||||
|
| `expires_at` | 当前上下文过期时间,进入待补充或待确认时重置为 30 分钟后 |
|
||||||
|
| `follow_up_count` | 当前草稿累计追问轮次 |
|
||||||
|
| `last_message_id` | 最近一次参与上下文判断的消息 ID |
|
||||||
|
| `extracted_facts` | 从多轮对话提取的候选事实,只用于草稿修订,不作为业务事实源 |
|
||||||
|
| `updated_at` | 上下文最近更新时间 |
|
||||||
|
|
||||||
|
`SecretaryMessage` 是老板消息、AI 追问、普通记录和上下文回顾的统一保存对象。`note` 不新建独立业务表,第一版复用 `SecretaryMessage`,至少保存 `message_id/source/sender_id/requester_open_id/requester_name/text/answer/intent_type/raw_payload/created_at`,并在回顾时按时间倒序读取默认最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿摘要。`note` 类型可以没有 `answer`,或 `answer=""`,回顾时主要展示原始 `text`。
|
||||||
|
|
||||||
|
`PromptContext` 至少包含:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `company_background_summary` | 公司背景库压缩摘要,不塞全文 |
|
||||||
|
| `boss_communication_style` | 老板大白话风格和回复口吻规则 |
|
||||||
|
| `ai_secretary_rules` | 本 spec 中的第一阶段业务规则摘要 |
|
||||||
|
| `role_and_alias_rules` | 角色、人员称呼和别名规则摘要 |
|
||||||
|
| `current_bot_context` | 当前 `BotContext` 摘要 |
|
||||||
|
| `pending_draft_summary` | 当前待确认草稿摘要,没有则为空 |
|
||||||
|
| `recent_messages_summary` | 最近对话摘要,用于判断补充、新建、普通聊天 |
|
||||||
|
| `prompt_version` | 提示词版本号,必须写入 `ModelCallLog` |
|
||||||
|
|
||||||
|
第一阶段不再依赖纯内存保存对话上下文,也不再拆多套数据库。PostgreSQL 是唯一数据源:角色、人员映射、草稿确认、任务、提醒、通知、反馈、失败记录、操作日志,以及老板秘书当前会话的 AI 对话记忆、消息记录和 `BotContext` 快照都落 PostgreSQL。
|
||||||
|
|
||||||
|
### 5.1 第一阶段对话记忆持久化(PostgreSQL 最小版)
|
||||||
|
|
||||||
|
第一阶段只做一个老板秘书入口的对话记忆持久化,不做多账号、多租户、跨老板记忆合并、长期画像、embedding 召回或复杂记忆策略。
|
||||||
|
|
||||||
|
PostgreSQL 最小保存三类 AI 记忆数据:
|
||||||
|
|
||||||
|
- `secretary_conversations`:当前老板秘书会话,可先使用固定 `conversation_id=boss_secretary_default`,并关联 PostgreSQL 中的 `boss_id`。
|
||||||
|
- `secretary_messages`:老板输入、AI 回复、追问、上下文回顾、普通记录等对话消息。
|
||||||
|
- `bot_contexts`:当前待确认草稿、`pending_draft_id`、上一条意图、30 分钟过期时间、`follow_up_count`、是否等待补充等上下文状态。
|
||||||
|
|
||||||
|
读写规则:
|
||||||
|
|
||||||
|
- `secretary_messages` 只追加,不修改,用于对话回放、上下文回顾和问题复盘。
|
||||||
|
- `bot_contexts` 表示当前最新上下文,可覆盖更新。
|
||||||
|
- `secretary_messages.bot_context_snapshot` 使用 `jsonb` 保存消息发生时的快照,不代表当前最新状态。
|
||||||
|
- `bot_contexts.extracted_facts` 使用 `jsonb` 保存从多轮对话里提取出的结构化事实,例如 `receiver_text/task_content/schedule_text/requires_feedback`;这些事实只能用于草稿修订,不能替代正式业务表。
|
||||||
|
- 对 `conversation_id + created_at`、`source + message_id`、`bot_contexts.conversation_id` 建索引;`jsonb` 字段第一阶段只做读取展示和调试,不作为复杂查询主路径。
|
||||||
|
|
||||||
|
最小字段建议:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"conversation_id": "boss_secretary_default",
|
||||||
|
"boss_id": "postgres_user_id",
|
||||||
|
"source": "feishu|web|debug",
|
||||||
|
"message_id": "外部消息ID或内部生成ID",
|
||||||
|
"role": "boss|assistant|system",
|
||||||
|
"text": "原始输入或AI回复",
|
||||||
|
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unknown|unsupported|context_summary",
|
||||||
|
"draft_id": "postgres_ai_draft_id",
|
||||||
|
"bot_context_snapshot": {},
|
||||||
|
"created_at": "2026-06-22T10:00:00+08:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
边界规则:
|
||||||
|
|
||||||
|
- PostgreSQL 是唯一事实源;AI 对话记忆表也在 PostgreSQL 内,但不能替代正式业务表。
|
||||||
|
- `secretary_messages`、`bot_contexts` 中的 `draft_id/task_id/reminder_id/notification_id` 只能引用正式业务表记录,不能替代对应业务状态。
|
||||||
|
- 创建草稿、确认草稿、创建任务、创建提醒、发送通知、员工反馈,必须以 PostgreSQL 事务和正式业务表状态为准。
|
||||||
|
- 服务重启后必须能从 PostgreSQL 恢复最近有效 `BotContext`,避免 30 分钟内的补充/确认丢失。
|
||||||
|
- 如果 PostgreSQL 的 AI 记忆表或上下文读写失败,不能继续依赖内存悄悄处理确认类命令;必须降级回复“暂时没处理好,请稍后再试”,并写入 `FailureRecord(memory_store_failed)`。如果 PostgreSQL 整体不可用导致 `FailureRecord` 也无法写入,至少写应用日志/告警,恢复后补偿记录。
|
||||||
|
- 后续如果要做多账号、多会话、长期记忆或向量召回,优先在 PostgreSQL 中扩展 schema、`jsonb`、全文索引或 pgvector;只有 PostgreSQL 明确无法满足时,再另行评估外部存储。
|
||||||
|
|
||||||
|
### 5.2 AiSecretaryResponse
|
||||||
|
|
||||||
|
模型必须返回稳定 JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unknown|unsupported",
|
||||||
|
"draft_type": "task|reminder|none",
|
||||||
|
"should_create_draft": true,
|
||||||
|
"route_type": "none|direct_after_boss_confirm|manager_confirm_required",
|
||||||
|
"answer": "给老板看的自然语言回复",
|
||||||
|
"draft": {
|
||||||
|
"title": "草稿标题",
|
||||||
|
"content": "整理后内容",
|
||||||
|
"receiver_candidates": ["东东"],
|
||||||
|
"receiver_text": "东东",
|
||||||
|
"scheduled_at": "2026-06-23T09:00:00+08:00",
|
||||||
|
"schedule_text": "明天上午 9 点",
|
||||||
|
"recurrence_type": "none|daily|weekly|monthly",
|
||||||
|
"requires_feedback": true,
|
||||||
|
"need_manager_confirm": false,
|
||||||
|
"missing_fields": [
|
||||||
|
{
|
||||||
|
"field": "receiver",
|
||||||
|
"reason": "missing_person_mapping",
|
||||||
|
"raw_value": "小王",
|
||||||
|
"message": "无法识别接收人"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"questions": ["最多3个追问"],
|
||||||
|
"reason": "简要说明判断原因"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
校验规则:
|
||||||
|
|
||||||
|
- `task/reminder` 必须 `should_create_draft=true`,`draft_type` 必须对应为 `task` 或 `reminder`。
|
||||||
|
- `qa/realtime_qa/note/need_more_info/unknown/unsupported` 必须 `should_create_draft=false`,`draft_type=none`。
|
||||||
|
- `task` 时 `route_type` 必填,只能是 `direct_after_boss_confirm` 或 `manager_confirm_required`。
|
||||||
|
- `reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported` 时 `route_type=none`。
|
||||||
|
- `route_type=manager_confirm_required` 时,`draft.need_manager_confirm=true`;老板主动说“给程经理看看”“让经理确认”时,也必须置为 true。
|
||||||
|
- `route_type=direct_after_boss_confirm` 时,`draft.need_manager_confirm=false`。
|
||||||
|
- `task` 必须有事项内容;时间不是必填项。
|
||||||
|
- `reminder` 必须有提醒内容和可解析时间;缺时间必须追问或进入 `missing_fields`。
|
||||||
|
- `missing_fields` 必须是对象数组;`field` 和 `reason` 必填,`raw_value` 和 `message` 可选。常用 `reason` 包括 `missing_person_mapping`、`time_not_parsed`、`receiver_missing`、`content_missing`、`scheduled_at_missing`。
|
||||||
|
- `questions` 最多 3 个;`intent=need_more_info` 时至少 1 个、最多 3 个,其他 intent 必须为空数组。
|
||||||
|
- `reason` 必填,用于调试和复盘;写入 `ModelCallLog.parsed_result`,不返回给老板,但可通过调试接口查看。
|
||||||
|
- `answer` 不能出现“已通知”“已创建”“已发送”“已经安排”等执行语义;出现时视为校验失败或强制改写为“我整理成草稿,等你确认”。
|
||||||
|
- `answer` 是给老板看的口语回复,`draft.content` 是正式草稿正文;二者可以表达方式不同,但不能语义矛盾。
|
||||||
|
- `title/content/receiver/schedule` 不能编造老板没说过的具体业务结论,只能整理、压缩和明确表达。
|
||||||
|
- `receiver_candidates` 无法解析到 open_id 时,不直接失败;保留 `receiver_text`,把 `missing_person_mapping` 写入 `missing_fields`,草稿仍可生成,但确认前必须由后端、调试页或人员映射补齐。
|
||||||
|
- 草稿确认前必须检查 `missing_fields`;阻塞字段未补齐时不允许正常确认,回复老板补齐关键信息。调试页如果强制确认,必须写对应 `FailureRecord` 和操作日志。
|
||||||
|
- 非法 JSON 不允许进入任务、提醒、通知流程。
|
||||||
|
|
||||||
|
确认阻塞规则:
|
||||||
|
|
||||||
|
| draft_type | 阻止确认的 missing_fields.reason | 不阻止确认 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| task | `content_missing`、`receiver_missing`、`missing_person_mapping` | `time_not_parsed`、`scheduled_at_missing` |
|
||||||
|
| reminder | `content_missing`、`scheduled_at_missing`、`time_not_parsed`、`receiver_missing`、`missing_person_mapping` | 无 |
|
||||||
|
|
||||||
|
如果模型无法判断本次输入应归为 `follow_up / new_request / qa / cancel`,必须返回 `need_more_info` 并追问,例如“你是想修改刚才那条草稿,还是新建一条?”。
|
||||||
|
|
||||||
|
### 5.3 意图区分
|
||||||
|
|
||||||
|
| intent | 处理 |
|
||||||
|
| --- | --- |
|
||||||
|
| task | 生成任务草稿,等待老板确认 |
|
||||||
|
| reminder | 生成提醒草稿,等待老板确认 |
|
||||||
|
| qa | 直接回答,不生成草稿、不通知 |
|
||||||
|
| realtime_qa | 固定提示需要实时数据查询,第一版暂不作为正式能力 |
|
||||||
|
| note | 只保存消息记录,不生成草稿、不通知,可用于“刚才说了什么”回顾 |
|
||||||
|
| need_more_info | 信息不足但属于可处理范围,主动追问 |
|
||||||
|
| unknown | 暂时无法判断意图,只允许澄清或留痕 |
|
||||||
|
| unsupported | 超出第一版能力,解释为什么不能做,不追问执行字段 |
|
||||||
|
|
||||||
|
优先级和兜底规则:
|
||||||
|
|
||||||
|
- 天气、新闻、股价、行情、今日走势、实时政策等需要实时数据的输入,固定归为 `realtime_qa`。
|
||||||
|
- `realtime_qa.answer` 固定使用模板:“这个需要实时数据查询,第一版暂不作为正式能力;我不会把它生成任务或提醒。”
|
||||||
|
- 超出第一版能力但不属于实时数据类的输入,归为 `unsupported`。
|
||||||
|
- `unsupported.answer` 应说明当前入口只处理老板事项草稿、提醒草稿、普通问答和普通记录,不追问执行字段。
|
||||||
|
- `unknown.answer` 只能澄清或提示补充,不得创建事项、提醒或通知。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
- “安排一下” -> `need_more_info`,追问安排谁、做什么。
|
||||||
|
- “帮我分析今天的股票走势” -> `realtime_qa`,提示第一版不做正式实时数据查询或投资判断。
|
||||||
|
- “帮我写一份完整年报” -> `unsupported`,说明超出第一版老板秘书入口能力。
|
||||||
|
- “记一下,今天先别催东东” -> `note`,只保存记录。
|
||||||
|
|
||||||
|
### 5.4 草稿状态
|
||||||
|
|
||||||
|
`ai_drafts.status` 只表示已经落 PostgreSQL 草稿表的草稿状态。非执行意图(`qa/realtime_qa/note/unknown/unsupported`)和纯追问结果(`need_more_info`)不得进入事项、提醒或通知闭环;如需留痕,只保存 `SecretaryMessage`、`BotContext`、`model_call_logs` 或最小草稿记录。
|
||||||
|
|
||||||
|
`ai_drafts.status` 以 `docs/contracts/状态流转约定.md` 为准,核心状态:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending_confirmation
|
||||||
|
awaiting_follow_up
|
||||||
|
confirmed
|
||||||
|
converted
|
||||||
|
cancelled
|
||||||
|
answered
|
||||||
|
superseded
|
||||||
|
expired
|
||||||
|
parse_failed
|
||||||
|
```
|
||||||
|
|
||||||
|
对老板可见时可以简化为:待确认、等待补充、已确认、已取消、已失效、已完成、已失败。
|
||||||
|
|
||||||
|
状态含义:
|
||||||
|
|
||||||
|
- `pending_confirmation`:草稿可确认,等待老板确认、补充/重说或取消。
|
||||||
|
- `awaiting_follow_up`:老板点击补充/重说后,等待 30 分钟内下一条消息。
|
||||||
|
- `confirmed`:确认链路已完成,可以交给任务或提醒模块转换。
|
||||||
|
- `converted`:草稿已经成功转换为任务或提醒;复杂事项已创建 `pending_manager_confirm` 事项壳也视为转换完成。
|
||||||
|
- `cancelled`:老板取消草稿。
|
||||||
|
- `answered`:已直接回复或留痕,不转换为任务或提醒。
|
||||||
|
- `superseded`:已被补充/重说生成的新草稿替代。
|
||||||
|
- `expired`:补充/重说等待超时,旧确认卡片不可继续使用。
|
||||||
|
- `parse_failed`:草稿或 AI 解析过程失败,仅用于调试追踪。
|
||||||
|
|
||||||
|
非草稿处理状态:
|
||||||
|
|
||||||
|
- `need_more_info`:本轮只追问老板,不创建 `ai_drafts`;追问内容保存为 `SecretaryMessage(intent_type=need_more_info)`,上下文状态为 `BotContext.status=awaiting_more_info`。
|
||||||
|
- `answered`:`qa/realtime_qa/note/context_summary/unknown/unsupported` 的处理结果,不转换为任务或提醒,不影响当前有效 `BotContext`。
|
||||||
|
- 追问 3 轮仍补不全、但系统决定生成带 `missing_fields` 的草稿时,模型输出必须从 `need_more_info` 转为 `task` 或 `reminder`,创建 `pending_confirmation` 草稿,并由确认阻塞规则控制能否确认。
|
||||||
|
|
||||||
|
`DRAFTING` 不作为第一版 `ai_drafts.status` 持久化枚举。实现中如需表达“AI 已决定生成草稿但还在校验/解析”的过程态,只能作为方法内的内存标记或局部变量;落库状态必须来自 `状态流转约定.md`。
|
||||||
|
|
||||||
|
草稿确认卡片生命周期:
|
||||||
|
|
||||||
|
- 每个草稿同一时间只能有一张有效确认卡片,对应 `active_card_notification_id`。
|
||||||
|
- 老板点击“补充/重说”后,原草稿进入 `awaiting_follow_up`,原确认卡片应失效。
|
||||||
|
- 30 分钟内收到补充消息后,新草稿 `parent_draft_id` 指向原草稿,原草稿进入 `superseded`。
|
||||||
|
- 超过 30 分钟仍未收到补充消息时,原草稿进入 `expired`,老板下一条消息按新输入处理。
|
||||||
|
- 飞书回调处理确认、取消、补充/重说前,必须校验回调来自当前有效卡片;旧卡片只记录事件,不写业务对象。
|
||||||
|
|
||||||
|
### 5.5 Prompt 硬规则
|
||||||
|
|
||||||
|
- AI 只整理草稿,不直接执行动作。
|
||||||
|
- AI 不得在回复里声称“已创建、已通知、已发送、已安排完成”。
|
||||||
|
- AI 不得编造老板没有给出的业务事实、责任结论、交易判断或研究结论。
|
||||||
|
- 结构化字段必须正式清晰,不能出现游戏化表达。
|
||||||
|
- 实时问答、行情、股价、新闻、天气等输入,第一版默认不作为正式能力,走 `realtime_qa` 兜底。
|
||||||
|
- note 只作为消息记录,不升级成任务、提醒或通知。
|
||||||
|
|
||||||
|
模型调用失败策略:
|
||||||
|
|
||||||
|
- 百炼或模型网关超时、网络失败、服务异常时,最多自动重试 1 次。
|
||||||
|
- 重试仍失败时,不创建草稿、不继续修改 `BotContext`,返回 `reply_type=error`。
|
||||||
|
- 失败回复固定为:“暂时没处理好,请稍后再试。”
|
||||||
|
- 写 `ModelCallLog`,并写 `FailureRecord(ai_model_failed)`。
|
||||||
|
- 模型返回非法 JSON、schema 校验失败或字段不匹配,走 `ai_parse_failed`,不使用 `ai_model_failed`。
|
||||||
|
|
||||||
|
## 6. 接口需求
|
||||||
|
|
||||||
|
### 6.1 统一消息入口
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/secretary/handle-message
|
||||||
|
```
|
||||||
|
|
||||||
|
输入包括:
|
||||||
|
|
||||||
|
- `source`
|
||||||
|
- `message_id`
|
||||||
|
- `sender.open_id`
|
||||||
|
- `sender.name`
|
||||||
|
- `sender.role`
|
||||||
|
- `text`
|
||||||
|
- `context.has_pending_draft`
|
||||||
|
- `context.pending_draft_id`
|
||||||
|
|
||||||
|
`context` 是外部调用方可选提示。后端必须以本地 `BotContext` 为准:
|
||||||
|
|
||||||
|
- 调用方没传 context 时,后端自己查询 BotContext。
|
||||||
|
- 调用方传了 context 但和 BotContext 不一致时,以 BotContext 为准。
|
||||||
|
- 外部 context 不能绕过草稿归属、过期时间和权限校验。
|
||||||
|
|
||||||
|
返回包括:
|
||||||
|
|
||||||
|
- `intent`
|
||||||
|
- `reply_type`
|
||||||
|
- `answer`
|
||||||
|
- `draft`
|
||||||
|
- `questions`
|
||||||
|
- `context_summary`
|
||||||
|
- `failure`
|
||||||
|
|
||||||
|
`failure` 只在不可恢复错误或降级时返回。结构建议:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "ai_parse_failed|ai_model_failed|bot_message_failed|follow_up_expired|bot_unauthorized|missing_person_mapping|memory_store_failed|draft_convert_failed|permission_error|system_error",
|
||||||
|
"message": "给前端或调试页看的失败说明",
|
||||||
|
"record_id": "failure record id,可为空"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
当发生 `ai_parse_failed`、`ai_model_failed`、`memory_store_failed`、`system_error` 等不可恢复错误时,`reply_type=error`,`answer` 使用固定兜底话术:“暂时没处理好,请稍后再试。”,并写 `FailureRecord`。
|
||||||
|
|
||||||
|
`reply_type` 建议取值:
|
||||||
|
|
||||||
|
- `draft_preview`:返回草稿预览,等待老板确认。
|
||||||
|
- `follow_up_question`:返回追问问题。
|
||||||
|
- `plain_answer`:普通问答回复。
|
||||||
|
- `context_summary`:上下文回顾回复。
|
||||||
|
- `unsupported`:能力外兜底回复。
|
||||||
|
- `error`:降级或失败回复。
|
||||||
|
|
||||||
|
当老板问“刚才我说了什么”“总结一下”时,返回 `reply_type=context_summary`,并返回 `context_summary` 字段,默认包含最近 10 条 `messages`、当前待确认草稿、最近 3 条已确认/已转换草稿摘要;该回复不创建草稿、不覆盖当前待确认草稿。
|
||||||
|
|
||||||
|
### 6.2 兼容接口
|
||||||
|
|
||||||
|
现有 Demo 接口保留,但内部转发:
|
||||||
|
|
||||||
|
- `/api/demo/boss-message`
|
||||||
|
- `/api/feishu/callback`
|
||||||
|
- `/api/demo/drafts/{id}/confirm`
|
||||||
|
- `/api/demo/drafts/{id}/cancel`
|
||||||
|
|
||||||
|
`/api/feishu/callback` 的验签、去重、鉴权和原始事件记录归 `03_飞书通知与反馈.md`、`05_权限日志失败记录.md` 负责;本模块只消费解析后的老板消息或草稿操作命令。
|
||||||
|
|
||||||
|
## 7. 权限规则
|
||||||
|
|
||||||
|
- 飞书机器人私聊入口第一版只允许老板使用。
|
||||||
|
- 非老板消息不调用 AI,不生成草稿,不创建业务对象。
|
||||||
|
- 草稿确认、取消、补充和转经理必须是草稿发起老板本人。
|
||||||
|
- 程经理不能通过机器人对话派活,只通过 Web 或通知跳转确认复杂任务。
|
||||||
|
- AI 不得绕过人工确认。
|
||||||
|
|
||||||
|
## 8. 状态流转
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板输入
|
||||||
|
-> AI 解析成功
|
||||||
|
-> draft_preview / follow_up_question / plain_answer
|
||||||
|
-> 老板补充
|
||||||
|
-> 新草稿或修订草稿
|
||||||
|
-> 老板确认
|
||||||
|
-> direct_after_boss_confirm: confirmed -> TaskAdapter / ReminderService 转换 -> converted
|
||||||
|
-> manager_confirm_required: confirmed -> 创建 pending_manager_confirm 事项壳 -> converted -> 程经理确认后通知接收人
|
||||||
|
```
|
||||||
|
|
||||||
|
草稿确认后的边界:
|
||||||
|
|
||||||
|
- `direct_after_boss_confirm`:老板确认成功后,草稿进入 `confirmed`,转换成功后进入 `converted`。
|
||||||
|
- `manager_confirm_required`:老板确认成功后,草稿进入 `confirmed`;转换时先创建 `tasks.status=pending_manager_confirm` 的事项壳,并给程经理发送待确认提醒;事项壳创建成功后草稿进入 `converted`。
|
||||||
|
- 程经理确认复杂事项时,补齐接收人、内容和通知信息,使事项进入 `pending_notify`,再通知接收人。
|
||||||
|
- 任务/提醒转换失败时,不回滚老板确认;草稿保持 `confirmed`,写 `FailureRecord(draft_convert_failed)`,支持后台重试或人工处理。
|
||||||
|
- 老板确认后要清空老板侧 `BotContext`;后续经理确认不再依赖老板 30 分钟上下文。
|
||||||
|
- `confirmed / converted / cancelled / superseded / expired` 后,01 不再直接修改原草稿;老板后续说“把刚才发出去那个改一下”,应识别为任务/提醒变更请求,第一阶段未接入变更能力时回复暂不支持或引导到任务模块。
|
||||||
|
|
||||||
|
直接回复流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
qa / realtime_qa / note / unknown / context_summary
|
||||||
|
-> answered 处理结果
|
||||||
|
-> 不进入事项、提醒或通知闭环,不清空当前有效 BotContext
|
||||||
|
```
|
||||||
|
|
||||||
|
`context_summary` 也必须保存为一条 `SecretaryMessage(intent_type=context_summary)`,便于调试页回看“老板什么时候要求回顾、系统回了什么”。
|
||||||
|
|
||||||
|
失败流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
模型调用失败
|
||||||
|
-> 重试 1 次
|
||||||
|
-> 仍失败则不创建可确认草稿
|
||||||
|
-> FailureRecord(ai_model_failed)
|
||||||
|
|
||||||
|
JSON 非法 / schema 校验失败
|
||||||
|
-> 不创建可确认草稿
|
||||||
|
-> 可选创建 parse_failed 草稿仅用于调试;如果无法创建最小草稿,failure_records.target_type 使用 model_call_log
|
||||||
|
-> FailureRecord(ai_parse_failed)
|
||||||
|
```
|
||||||
|
|
||||||
|
`parse_failed` 草稿不能确认、不能转换为任务/提醒、不能发送通知,只能用于调试追踪。
|
||||||
|
|
||||||
|
取消流转:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending_confirmation
|
||||||
|
-> cancelled
|
||||||
|
-> 清空 BotContext
|
||||||
|
|
||||||
|
awaiting_more_info 且还没有 ai_drafts
|
||||||
|
-> 清空 BotContext
|
||||||
|
-> 记录 OperationLog(cancel_context)
|
||||||
|
```
|
||||||
|
|
||||||
|
复杂事项壳 `pending_manager_confirm` 阶段如需取消或退回,归程经理确认模块或任务模块处理;01 只负责展示草稿来源和记录操作,不再把它当作可补充草稿处理。
|
||||||
|
|
||||||
|
## 9. 失败和日志
|
||||||
|
|
||||||
|
必须记录:
|
||||||
|
|
||||||
|
- 老板原始输入。
|
||||||
|
- BotContext 命中或过期。
|
||||||
|
- PromptContext 使用版本。
|
||||||
|
- 模型请求、响应、耗时和解析结果。
|
||||||
|
- `AiSecretaryResponse.reason`,写入 `ModelCallLog.parsed_result`,只用于调试和复盘。
|
||||||
|
- AI JSON 校验失败。
|
||||||
|
- 草稿创建、修订、确认、取消、转经理。
|
||||||
|
- AI 追问消息,保存为 `SecretaryMessage(intent_type=need_more_info)`。
|
||||||
|
- 追问轮次变化,保存到 `BotContext.follow_up_count`。
|
||||||
|
- 追问动作,记录 `OperationLog(action=ask_follow_up)`。
|
||||||
|
- 非老板访问。
|
||||||
|
- note 消息保存。
|
||||||
|
- context_summary 上下文回顾,保存为 `SecretaryMessage(intent_type=context_summary)`。
|
||||||
|
- receiver_candidates 解析失败和人工补映射。
|
||||||
|
|
||||||
|
`failure.type` 和 `FailureRecord.failure_type` 使用同一组枚举:
|
||||||
|
|
||||||
|
| failure_type | 触发条件 |
|
||||||
|
| --- | --- |
|
||||||
|
| `ai_parse_failed` | 模型返回非法 JSON、schema 校验失败、字段组合不符合 `AiSecretaryResponse` |
|
||||||
|
| `ai_model_failed` | 百炼或模型网关超时、网络失败、服务异常,重试 1 次后仍失败 |
|
||||||
|
| `bot_message_failed` | 入口消息结构缺失、无法解析文本或 sender 信息,导致无法进入正常处理 |
|
||||||
|
| `follow_up_expired` | 存在旧 `BotContext` 但已超过 30 分钟,老板仍尝试修改上一条草稿 |
|
||||||
|
| `bot_unauthorized` | 非老板访问老板秘书入口 |
|
||||||
|
| `missing_person_mapping` | 接收人候选无法映射真实人员;生成草稿时可记录低严重度,确认被阻止或调试页强制确认时必须记录 |
|
||||||
|
| `memory_store_failed` | PostgreSQL AI 记忆表、上下文或快照保存/读取失败 |
|
||||||
|
| `draft_convert_failed` | 老板或程经理确认后,转换任务/提醒失败 |
|
||||||
|
| `permission_error` | 非草稿发起老板尝试确认、取消、补充或转程经理 |
|
||||||
|
| `system_error` | 未归类系统异常 |
|
||||||
|
|
||||||
|
## 10. 给 AI 的测试样例
|
||||||
|
|
||||||
|
### 10.1 任务草稿
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
让东东跟进一下合同
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 生成 `task` 草稿。
|
||||||
|
- 不因为没有时间反复追问。
|
||||||
|
- 接收人候选包含东东。
|
||||||
|
- `should_create_draft=true`。
|
||||||
|
|
||||||
|
### 10.2 提醒草稿
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
明天上午 9 点提醒东东准备咖啡
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 生成 `reminder` 草稿。
|
||||||
|
- `scheduled_at` 使用 `Asia/Shanghai`。
|
||||||
|
- recurrence 为 `none`。
|
||||||
|
|
||||||
|
### 10.3 补充上一条
|
||||||
|
|
||||||
|
前置:存在一条待确认 `task` 草稿,内容为“让东东跟进合同”,没有明确 `due_at` 或 `schedule_text`;该草稿不是提醒草稿。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
改成明天下午
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `follow_up`。
|
||||||
|
- 修订上一条草稿,不新建任务。
|
||||||
|
- 更新该任务草稿的 `due_at` 或 `schedule_text`,不修改 `reminder.scheduled_at`。
|
||||||
|
|
||||||
|
### 10.4 新建例外
|
||||||
|
|
||||||
|
前置:存在待确认草稿。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
另外再安排一个,让行政订会议室
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `new_request`。
|
||||||
|
- 新建草稿。
|
||||||
|
|
||||||
|
### 10.5 取消
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
算了,刚才那个不要了
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 取消当前草稿。
|
||||||
|
- 清空上下文。
|
||||||
|
|
||||||
|
### 10.6 非老板访问
|
||||||
|
|
||||||
|
输入:普通员工私聊机器人。
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 固定回复“当前入口是老板秘书,如需处理事项请进入平台。”
|
||||||
|
- 不调用 AI。
|
||||||
|
- 写失败和操作日志。
|
||||||
|
|
||||||
|
### 10.7 追问信息不足
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
安排一下
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `need_more_info`。
|
||||||
|
- 不生成任务或提醒。
|
||||||
|
- 追问“安排谁、做什么”等关键问题。
|
||||||
|
|
||||||
|
### 10.8 实时问答兜底
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我分析今天的股票走势
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `realtime_qa`。
|
||||||
|
- 不生成草稿。
|
||||||
|
- 回复固定模板:“这个需要实时数据查询,第一版暂不作为正式能力;我不会把它生成任务或提醒。”
|
||||||
|
|
||||||
|
### 10.9 普通记录
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
记一下,今天先别催东东
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `note`。
|
||||||
|
- 只保存消息记录。
|
||||||
|
- 不创建草稿、不通知任何人。
|
||||||
|
|
||||||
|
### 10.10 接收人解析失败
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
让小王处理一下合同
|
||||||
|
```
|
||||||
|
|
||||||
|
前置:`PersonResolver` 无法识别“小王”。
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 草稿可以生成。
|
||||||
|
- `receiver_text=小王`。
|
||||||
|
- `missing_fields` 包含 `{ "field": "receiver", "reason": "missing_person_mapping", "raw_value": "小王" }`。
|
||||||
|
- 正常确认被阻止,回复老板需要先补齐人员映射。
|
||||||
|
|
||||||
|
### 10.11 能力外请求
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
帮我写一份完整年报
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `unsupported`。
|
||||||
|
- 不生成草稿。
|
||||||
|
- 不追问执行人、时间等任务字段。
|
||||||
|
- 说明当前入口只处理老板事项草稿、提醒草稿、普通问答和普通记录。
|
||||||
|
|
||||||
|
### 10.12 上下文回顾
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
我刚才说了什么
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 返回 `reply_type=context_summary`。
|
||||||
|
- 返回 `context_summary`,默认摘要最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿。
|
||||||
|
- 不创建新草稿,不覆盖当前待确认草稿。
|
||||||
|
- 保存 `SecretaryMessage(intent_type=context_summary)`。
|
||||||
|
|
||||||
|
### 10.13 无草稿 command
|
||||||
|
|
||||||
|
前置:当前没有待确认草稿。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
确认
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 命中 command,但不抛异常。
|
||||||
|
- 不创建草稿。
|
||||||
|
- 返回 `reply_type=plain_answer`。
|
||||||
|
- 回复“目前没有待确认的草稿”。
|
||||||
|
|
||||||
|
### 10.14 错误降级
|
||||||
|
|
||||||
|
前置:模型返回非法 JSON。
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 不进入任务、提醒、通知流程。
|
||||||
|
- 写 `ModelCallLog` 和 `FailureRecord(ai_parse_failed)`。
|
||||||
|
- 返回 `reply_type=error`。
|
||||||
|
- `answer=暂时没处理好,请稍后再试。`
|
||||||
|
- `failure.type=ai_parse_failed`。
|
||||||
|
- 如落 `parse_failed` 草稿,仅用于调试,不能确认或转换。
|
||||||
|
|
||||||
|
### 10.15 低置信度追问
|
||||||
|
|
||||||
|
前置:存在一条待确认任务草稿。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
这个换一下
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 如果无法判断是修改接收人、时间、措辞,还是切换到新事项,不默认修改当前草稿。
|
||||||
|
- 返回 `intent=need_more_info`。
|
||||||
|
- 追问“你是想修改刚才那条草稿,还是新建一条?具体想换什么?”。
|
||||||
|
|
||||||
|
### 10.16 已发布后修改
|
||||||
|
|
||||||
|
前置:上一条草稿已经 `converted`,`BotContext` 已清空。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
把刚才那个改成明天下午
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 不修改已转换草稿。
|
||||||
|
- 识别为任务/提醒变更请求或第一阶段能力外请求。
|
||||||
|
- 第一阶段未接入变更能力时,回复“刚才的事项已经发布,第一阶段暂不支持直接修改已发布任务;我可以帮你重新整理一条补充说明。”。
|
||||||
|
|
||||||
|
### 10.17 消息幂等
|
||||||
|
|
||||||
|
前置:同一个 `source + message_id` 已处理成功并生成草稿。
|
||||||
|
|
||||||
|
输入:同一消息再次进入。
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 不重复调用模型。
|
||||||
|
- 不重复创建草稿。
|
||||||
|
- 不重复追加 PostgreSQL `secretary_messages` 或 `SecretaryMessage`。
|
||||||
|
- 返回上一轮处理结果或已存在草稿引用。
|
||||||
|
|
||||||
|
### 10.18 待确认草稿中的普通聊天
|
||||||
|
|
||||||
|
前置:存在一条待确认任务草稿。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
这个措辞是不是太硬了?
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 判断为 `qa` 或低置信度追问,不默认修改当前草稿。
|
||||||
|
- 不创建新草稿。
|
||||||
|
- 不清空当前有效 `BotContext`。
|
||||||
|
|
||||||
|
### 10.19 30 分钟过期
|
||||||
|
|
||||||
|
前置:存在一条 `awaiting_confirm` 草稿,但 `expires_at` 已过期。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
改成明天下午
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 不直接修改旧草稿。
|
||||||
|
- 写 `FailureRecord(follow_up_expired)` 或 OperationLog 过期记录。
|
||||||
|
- 按新输入处理,或追问老板“上一条草稿已过期,你是想修改刚才那条还是新建一条?”。
|
||||||
|
|
||||||
|
### 10.20 PostgreSQL AI 记忆读写失败
|
||||||
|
|
||||||
|
前置:PostgreSQL 保存 `secretary_messages` 或 `BotContext` 失败。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
确认
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 不继续依赖内存确认草稿。
|
||||||
|
- 不转换任务或提醒。
|
||||||
|
- 返回 `reply_type=error`。
|
||||||
|
- 写 `FailureRecord(memory_store_failed)`。
|
||||||
|
|
||||||
|
### 10.21 程经理确认流转
|
||||||
|
|
||||||
|
前置:草稿 `route_type=manager_confirm_required`,`draft.need_manager_confirm=true`。
|
||||||
|
|
||||||
|
输入:
|
||||||
|
|
||||||
|
```text
|
||||||
|
确认
|
||||||
|
```
|
||||||
|
|
||||||
|
期望:
|
||||||
|
|
||||||
|
- 老板确认后草稿进入 `confirmed`。
|
||||||
|
- 清空老板侧 `BotContext`。
|
||||||
|
- 创建 `tasks.status=pending_manager_confirm` 的事项壳,不直接通知最终接收人。
|
||||||
|
- 程经理确认后,事项壳进入 `pending_notify` 并通知接收人;草稿在事项壳创建成功后进入 `converted`。
|
||||||
|
|
||||||
|
## 11. Review 重点
|
||||||
|
|
||||||
|
- 是否所有入口都走 `POST /api/secretary/handle-message`。
|
||||||
|
- 是否用 `source + message_id` 做幂等,避免重复调用模型和重复创建草稿。
|
||||||
|
- 是否幂等命中时不会重复追加 PostgreSQL `secretary_messages` 或 `SecretaryMessage`。
|
||||||
|
- 是否实现 30 分钟上下文,但没有无脑吞掉所有消息。
|
||||||
|
- 是否 `BotContext.status` 使用明确状态,并且只绑定未确认草稿。
|
||||||
|
- 是否 `BotContext` 字段按本 spec 集中字段表实现。
|
||||||
|
- 是否低置信度时追问老板,而不是默认修改当前草稿。
|
||||||
|
- 是否 `ai_drafts.status` 和非草稿处理状态已经拆清,`need_more_info/answered` 不误落草稿表。
|
||||||
|
- 是否所有 AI 输出都做 JSON 校验。
|
||||||
|
- 是否 `qa/realtime_qa/note/need_more_info/unknown/unsupported` 没有误进入事项、提醒或通知闭环。
|
||||||
|
- 是否普通任务不强制时间。
|
||||||
|
- 是否提醒缺时间会追问。
|
||||||
|
- 是否 AI 只生成草稿,不直接执行。
|
||||||
|
- 是否禁止“已通知、已创建、已发送”等执行语义。
|
||||||
|
- 是否禁止编造老板没说过的业务结论。
|
||||||
|
- 是否 receiver 解析失败时保留草稿并标记 `missing_person_mapping`。
|
||||||
|
- 是否记录 PromptContext 和 ModelCallLog。
|
||||||
|
- 是否 PromptContext 包含公司背景摘要、老板风格、业务规则、角色别名、当前上下文、待确认草稿摘要、最近消息摘要和版本号。
|
||||||
|
- 是否模型超时/百炼失败最多重试 1 次,仍失败写 `FailureRecord(ai_model_failed)`。
|
||||||
|
- 是否非老板入口完全绕开 AI。
|
||||||
|
- 是否为任务、提醒、通知留下 adapter 边界。
|
||||||
|
- 是否明确区分 `realtime_qa` 和 `unsupported`,实时数据类固定走 `realtime_qa`。
|
||||||
|
- 是否把 `command` 作为消息预分类而不是最终 `intent`。
|
||||||
|
- 是否追问消息也保存为 `SecretaryMessage`。
|
||||||
|
- 是否 `missing_person_mapping` 会阻止正常确认。
|
||||||
|
- 是否不同 `draft_type` 的确认阻塞字段已明确。
|
||||||
|
- 是否 `reason` 必填且只进入日志/调试,不直接展示给老板。
|
||||||
|
- 是否 `DRAFTING` 没有作为持久化草稿状态使用。
|
||||||
|
- 是否 `missing_fields` 使用对象数组,并能区分字段和失败原因。
|
||||||
|
- 是否 command 命中但无草稿时返回普通说明,不抛异常。
|
||||||
|
- 是否 `failure` 返回结构只在错误降级时出现。
|
||||||
|
- 是否 `failure.type` 和 `FailureRecord.failure_type` 使用同一枚举,并有明确触发条件。
|
||||||
|
- 是否对话消息、上下文回顾和 `BotContext` 快照已落 PostgreSQL,而不是只放内存。
|
||||||
|
- 是否 `secretary_messages` 只追加、`bot_contexts` 可覆盖、`bot_context_snapshot` 不作为最新状态。
|
||||||
|
- 是否 PostgreSQL 作为唯一事实源,AI 记忆表不替代正式草稿、任务、提醒、通知和反馈表状态。
|
||||||
|
- 是否 PostgreSQL AI 记忆读写异常时会降级并写 `FailureRecord(memory_store_failed)` 或应用日志/告警。
|
||||||
|
- 是否草稿 `confirmed / converted / cancelled / superseded / expired` 后立即清空或失效上下文,后续修改不再直接改原草稿。
|
||||||
|
- 是否 `manager_confirm_required` 走 `confirmed -> 创建 pending_manager_confirm 事项壳 -> converted -> 程经理确认事项壳 -> pending_notify`,不会绕过程经理确认。
|
||||||
|
- 是否转换失败写 `FailureRecord(draft_convert_failed)`,且不回滚老板确认。
|
||||||
|
- 是否上下文回顾默认范围为最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿。
|
||||||
|
- 是否 `/api/feishu/callback` 的验签、去重和鉴权没有写进本模块。
|
||||||
|
|||||||
@@ -1 +1,135 @@
|
|||||||
|
# 02_事项任务
|
||||||
|
|
||||||
|
## 1. 模块目标
|
||||||
|
|
||||||
|
事项任务模块承接老板 AI 秘书草稿、程经理确认和手动创建事项,负责通知接收人、记录反馈、展示状态和沉淀失败原因。第一版只做轻量事项闭环,不做完整项目管理。
|
||||||
|
|
||||||
|
## 2. 第一版做什么
|
||||||
|
|
||||||
|
1. 事项可以来自 AI 草稿 `confirmed` 后转换。
|
||||||
|
2. 复杂事项来自 `route_type=manager_confirm_required`,老板确认后先创建 `pending_manager_confirm` 的事项壳。
|
||||||
|
3. 事项可以由老板或程经理手动创建。
|
||||||
|
4. 普通员工第一版不默认给别人创建事项。
|
||||||
|
5. 事项必须有发起人、接收人、事项内容、反馈要求和状态。
|
||||||
|
6. 事项生成前必须经过人工确认。
|
||||||
|
7. 事项生成后可以创建飞书通知。
|
||||||
|
8. 接收人可以反馈:已收到、处理中、已完成、有问题。
|
||||||
|
9. 有问题反馈必须填写一句原因。
|
||||||
|
10. 发起人、程经理或有权限人员可以查看处理进度。
|
||||||
|
11. 通知失败或无人反馈的事项需要可见,但不单独做看板。
|
||||||
|
|
||||||
|
## 3. 第一版不做
|
||||||
|
|
||||||
|
1. 不做复杂项目管理系统。
|
||||||
|
2. 不做多级子任务。
|
||||||
|
3. 不做复杂审批流。
|
||||||
|
4. 不做绩效排名。
|
||||||
|
5. 不做任务依赖和甘特图。
|
||||||
|
6. 不做项目级看板。
|
||||||
|
7. 不做独立反馈看板。
|
||||||
|
8. 不从 AI 工作台结果生成事项。
|
||||||
|
|
||||||
|
## 4. 核心流程
|
||||||
|
|
||||||
|
简单事项:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AI 草稿或手动输入
|
||||||
|
-> 人工确认
|
||||||
|
-> AI 草稿进入 confirmed
|
||||||
|
-> 创建 tasks
|
||||||
|
-> 创建 notifications
|
||||||
|
-> 飞书通知接收人
|
||||||
|
-> 接收人反馈
|
||||||
|
-> 更新事项状态
|
||||||
|
-> 写入 operation_logs
|
||||||
|
```
|
||||||
|
|
||||||
|
复杂事项:
|
||||||
|
|
||||||
|
```text
|
||||||
|
老板确认转程经理
|
||||||
|
-> AI 草稿进入 confirmed
|
||||||
|
-> 创建 pending_manager_confirm 事项壳,接收人和内容可由程经理补齐
|
||||||
|
-> 给程经理发飞书提醒
|
||||||
|
-> 程经理进入平台确认、修改和分发
|
||||||
|
-> 事项壳进入 pending_notify
|
||||||
|
-> 通知接收人
|
||||||
|
-> 接收反馈
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 数据对象
|
||||||
|
|
||||||
|
本模块主要使用:
|
||||||
|
|
||||||
|
1. `ai_drafts`
|
||||||
|
2. `tasks`
|
||||||
|
3. `notifications`
|
||||||
|
4. `feedbacks`
|
||||||
|
5. `failure_records`
|
||||||
|
6. `operation_logs`
|
||||||
|
|
||||||
|
字段以 `docs/contracts/数据对象约定.md` 为准。
|
||||||
|
|
||||||
|
## 6. 接口需求
|
||||||
|
|
||||||
|
主要接口:
|
||||||
|
|
||||||
|
1. `GET /api/tasks`
|
||||||
|
2. `POST /api/tasks`
|
||||||
|
3. `GET /api/tasks/{id}`
|
||||||
|
4. `PATCH /api/tasks/{id}`
|
||||||
|
5. `POST /api/tasks/{id}/manager-confirm`
|
||||||
|
6. `POST /api/tasks/{id}/cancel`
|
||||||
|
7. `POST /api/tasks/{id}/notify`
|
||||||
|
8. `POST /api/tasks/{id}/feedback`
|
||||||
|
|
||||||
|
接口格式以 `docs/contracts/API接口约定.md` 为准。
|
||||||
|
|
||||||
|
## 7. 权限规则
|
||||||
|
|
||||||
|
1. 老板可以创建事项。
|
||||||
|
2. 程经理可以创建和分发事项。
|
||||||
|
3. 普通员工不能给别人创建事项。
|
||||||
|
4. 接收人只能反馈自己相关事项。
|
||||||
|
5. 程经理只能确认待自己处理的复杂事项。
|
||||||
|
6. 管理员 / AI 团队可以为排查查看必要记录,但不处理业务分歧。
|
||||||
|
|
||||||
|
## 8. 状态流转
|
||||||
|
|
||||||
|
事项状态以 `docs/contracts/状态流转约定.md` 为准。核心约束:
|
||||||
|
|
||||||
|
1. 未确认事项不得通知接收人。
|
||||||
|
2. `pending_manager_confirm` 必须由程经理确认后才能进入通知流程。
|
||||||
|
3. `manager_confirm_required` 不能绕过程经理直接通知最终接收人;必须先创建 `pending_manager_confirm` 事项壳。
|
||||||
|
4. 通知失败进入 `notify_failed`,不得假装已通知。
|
||||||
|
5. 已取消事项不能反馈、通知或补发。
|
||||||
|
6. 有问题反馈会将事项标记为 `problem`,并记录原因。
|
||||||
|
|
||||||
|
## 9. 失败和日志
|
||||||
|
|
||||||
|
必须记录:
|
||||||
|
|
||||||
|
1. 事项创建来源:AI 草稿、手动创建或程经理确认。
|
||||||
|
2. 创建、修改、取消、通知、补发、反馈操作。
|
||||||
|
3. 权限拒绝。
|
||||||
|
4. 通知失败。
|
||||||
|
5. 有问题反馈原因。
|
||||||
|
6. 人员映射缺失或接收人不明确。
|
||||||
|
|
||||||
|
## 10. 给 AI 的测试样例
|
||||||
|
|
||||||
|
1. 普通员工请求给同事创建事项,应返回权限错误。
|
||||||
|
2. 老板确认简单事项后,事项进入待通知或已通知状态。
|
||||||
|
3. 研究类事项应进入程经理确认路径。
|
||||||
|
4. 接收人反馈“有问题”但不填原因,应返回校验错误。
|
||||||
|
5. 通知失败时,事项状态不得变成已通知。
|
||||||
|
6. 已取消事项不得再次通知。
|
||||||
|
|
||||||
|
## 11. Review 重点
|
||||||
|
|
||||||
|
1. 后端是否做了权限校验,而不是只靠前端隐藏按钮。
|
||||||
|
2. 状态流转是否只走允许路径。
|
||||||
|
3. 通知失败是否留下失败记录。
|
||||||
|
4. 有问题反馈是否强制原因。
|
||||||
|
5. 是否避免把复杂项目管理能力混入第一版。
|
||||||
|
|||||||
@@ -1 +1,168 @@
|
|||||||
|
# 03_飞书通知与反馈
|
||||||
|
|
||||||
|
## 1. 模块目标
|
||||||
|
|
||||||
|
飞书模块负责平台登录身份、老板机器人私聊入口、个人消息通知、交互卡片确认和反馈回调。第一版飞书是闭环主入口之一,所有回调必须验签、幂等、可追溯。
|
||||||
|
|
||||||
|
## 2. 第一版做什么
|
||||||
|
|
||||||
|
1. 飞书扫码登录 / 身份登录。
|
||||||
|
2. 根据飞书身份匹配平台用户和角色。
|
||||||
|
3. 接收老板机器人私聊消息。
|
||||||
|
4. 非老板私聊机器人时提示进入平台并记录未授权访问。
|
||||||
|
5. 发送 AI 草稿确认卡片给老板。
|
||||||
|
6. 发送复杂事项待确认提醒给程经理,按钮只跳转平台。
|
||||||
|
7. 发送事项或提醒通知给接收人。
|
||||||
|
8. 接收卡片按钮回调。
|
||||||
|
9. 支持接收人反馈已收到、处理中、已完成、有问题。
|
||||||
|
10. 记录飞书事件、通知发送结果和回调处理结果。
|
||||||
|
11. 旧卡片、失效卡片和重复点击必须幂等处理。
|
||||||
|
12. 平台内“有问题反馈”页需要在飞书手机端最小可用。
|
||||||
|
|
||||||
|
## 3. 第一版不做
|
||||||
|
|
||||||
|
1. 不把机器人开放成程经理或普通员工通用派活入口。
|
||||||
|
2. 不让程经理在机器人里对话分发事项。
|
||||||
|
3. 不在飞书消息里展开敏感全文。
|
||||||
|
4. 不做飞书卡片内复杂字段编辑表单。
|
||||||
|
5. 不绕过平台角色权限。
|
||||||
|
|
||||||
|
## 4. 核心流程
|
||||||
|
|
||||||
|
飞书登录:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户点击飞书登录
|
||||||
|
-> 跳转飞书授权
|
||||||
|
-> 飞书回调 auth callback
|
||||||
|
-> 校验 state 和 code
|
||||||
|
-> 换取飞书身份
|
||||||
|
-> 匹配平台用户
|
||||||
|
-> 写入 feishu_auth_sessions
|
||||||
|
-> 建立平台登录态
|
||||||
|
```
|
||||||
|
|
||||||
|
机器人私聊:
|
||||||
|
|
||||||
|
```text
|
||||||
|
飞书推送消息事件
|
||||||
|
-> 验签
|
||||||
|
-> 记录 feishu_events
|
||||||
|
-> 匹配平台用户
|
||||||
|
-> 校验 boss 角色
|
||||||
|
-> 转发到 POST /api/secretary/handle-message
|
||||||
|
-> 根据 reply_type 发送确认卡片、追问或普通回复
|
||||||
|
```
|
||||||
|
|
||||||
|
卡片回调:
|
||||||
|
|
||||||
|
```text
|
||||||
|
飞书卡片按钮点击
|
||||||
|
-> 验签
|
||||||
|
-> 记录 feishu_events
|
||||||
|
-> 查找 notification / draft / task / reminder
|
||||||
|
-> 校验 event_id 或 idempotency_key
|
||||||
|
-> 校验操作人权限
|
||||||
|
-> 校验通知、草稿和卡片仍有效
|
||||||
|
-> 执行业务动作
|
||||||
|
-> 写 operation_logs
|
||||||
|
-> 返回飞书处理结果
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 数据对象
|
||||||
|
|
||||||
|
本模块主要使用:
|
||||||
|
|
||||||
|
1. `users`
|
||||||
|
2. `person_mappings`
|
||||||
|
3. `feishu_auth_sessions`
|
||||||
|
4. `feishu_events`
|
||||||
|
5. `notifications`
|
||||||
|
6. `feedbacks`
|
||||||
|
7. `bot_contexts`
|
||||||
|
8. `secretary_messages`
|
||||||
|
9. `failure_records`
|
||||||
|
10. `operation_logs`
|
||||||
|
|
||||||
|
字段以 `docs/contracts/数据对象约定.md` 为准;权限、安全、日志脱敏、验签和幂等以 `docs/contracts/安全权限日志约定.md` 为准。
|
||||||
|
|
||||||
|
## 6. 接口需求
|
||||||
|
|
||||||
|
主要接口:
|
||||||
|
|
||||||
|
1. `GET /api/auth/feishu/login`
|
||||||
|
2. `GET /api/auth/feishu/callback`
|
||||||
|
3. `POST /api/feishu/events`
|
||||||
|
4. `POST /api/feishu/bot/messages`
|
||||||
|
5. `POST /api/feishu/card-callback`
|
||||||
|
6. `POST /api/notifications/{id}/retry`
|
||||||
|
|
||||||
|
接口格式以 `docs/contracts/API接口约定.md` 为准。
|
||||||
|
|
||||||
|
## 7. 权限规则
|
||||||
|
|
||||||
|
1. 飞书身份只用于认证和操作人追溯,业务权限仍以平台角色为准。
|
||||||
|
2. 机器人私聊入口第一版只允许老板使用。
|
||||||
|
3. 老板只能确认、取消、补充自己发起的草稿。
|
||||||
|
4. 程经理待办提醒只能跳转平台确认。
|
||||||
|
5. 反馈人必须是事项或提醒接收人。
|
||||||
|
6. 管理员 / AI 团队可以处理系统失败和补发,但不能代替业务反馈。
|
||||||
|
|
||||||
|
## 8. 状态与幂等
|
||||||
|
|
||||||
|
状态以 `docs/contracts/状态流转约定.md` 为准。幂等约束:
|
||||||
|
|
||||||
|
1. 同一个飞书 `event_id` 只处理一次;没有稳定 `event_id` 时必须使用 `idempotency_key`。
|
||||||
|
2. 同一个通知只允许一个有效发送结果。
|
||||||
|
3. 同一个卡片按钮重复点击不得重复创建事项、提醒或反馈。
|
||||||
|
4. 草稿确认类卡片必须匹配 `ai_drafts.active_card_notification_id`。
|
||||||
|
5. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只写入 `feishu_events`,标记 `ignored`,不得写业务对象。
|
||||||
|
6. 同一个机器人消息还必须按 `source + message_id` 在老板秘书入口二次幂等,不重复追加 `secretary_messages`。
|
||||||
|
7. 回调处理失败必须标记 `feishu_events.process_status = failed` 并写失败记录。
|
||||||
|
|
||||||
|
## 9. 失败和日志
|
||||||
|
|
||||||
|
必须记录:
|
||||||
|
|
||||||
|
1. 飞书登录成功或失败。
|
||||||
|
2. 飞书事件原始 payload。
|
||||||
|
3. 机器人消息处理结果。
|
||||||
|
4. 卡片回调处理结果。
|
||||||
|
5. 通知发送成功或失败。
|
||||||
|
6. 验签失败。
|
||||||
|
7. 找不到关联对象。
|
||||||
|
8. 操作人无权操作。
|
||||||
|
9. 有问题反馈缺少原因。
|
||||||
|
|
||||||
|
普通日志不得打印飞书 token、App Secret、OAuth code/state、一次性操作 token、回调验签密钥、完整手机号或完整邮箱。
|
||||||
|
|
||||||
|
## 10. 平台内反馈页
|
||||||
|
|
||||||
|
飞书卡片无法直接完成“有问题”原因填写时,允许跳转平台内反馈页。第一版不做完整手机网页适配,但该页在飞书手机端必须最小可用:
|
||||||
|
|
||||||
|
1. 能通过飞书登录态或一次性反馈 token 识别反馈人。
|
||||||
|
2. 一次性反馈 token 只保存 hash,不保存明文。
|
||||||
|
3. 只展示事项或提醒摘要、反馈状态选择和问题原因输入。
|
||||||
|
4. 操作人不是接收人、链接过期、通知失效或卡片失效时,只提示不可反馈并记录事件或失败,不写业务对象。
|
||||||
|
5. 提交成功后写入 `feedbacks` 并按状态流转更新事项或提醒。
|
||||||
|
|
||||||
|
## 11. 给 AI 的测试样例
|
||||||
|
|
||||||
|
1. 验签失败的回调应返回拒绝并写失败记录。
|
||||||
|
2. 同一 `event_id` 重放应幂等,不重复创建反馈。
|
||||||
|
3. 非接收人点击反馈按钮应返回权限错误。
|
||||||
|
4. 老板点击取消草稿后,草稿不能再转换。
|
||||||
|
5. 非老板私聊机器人不应调用 AI。
|
||||||
|
6. 飞书通知失败时,应写 `failure_records` 并保留通知失败状态。
|
||||||
|
7. 没有稳定 `event_id` 的卡片回调应使用 `idempotency_key` 幂等。
|
||||||
|
8. 旧确认卡片或已失效通知的回调只记录 `feishu_events`,不得创建事项、提醒或反馈。
|
||||||
|
9. 飞书手机端平台反馈页必须能提交“有问题”原因,并拒绝过期 token。
|
||||||
|
|
||||||
|
## 12. Review 重点
|
||||||
|
|
||||||
|
1. 回调是否验签。
|
||||||
|
2. 回调是否使用 `event_id` 或 `idempotency_key` 幂等。
|
||||||
|
3. 飞书身份是否没有直接绕过平台权限。
|
||||||
|
4. 通知内容是否只发摘要和链接。
|
||||||
|
5. 失败是否可复盘。
|
||||||
|
6. 旧卡片、失效卡片是否不会继续写业务对象。
|
||||||
|
|||||||
@@ -1 +1,143 @@
|
|||||||
|
# 04_定时提醒
|
||||||
|
|
||||||
|
## 1. 模块目标
|
||||||
|
|
||||||
|
定时提醒模块负责创建一次性或固定周期提醒,到点生成通知、发送飞书消息、按需接收反馈,并记录触发结果和失败原因。
|
||||||
|
|
||||||
|
## 2. 第一版做什么
|
||||||
|
|
||||||
|
1. 支持一次性提醒。
|
||||||
|
2. 支持每天、每周、每月固定周期提醒。
|
||||||
|
3. 支持提醒来自手动创建。
|
||||||
|
4. 支持提醒来自老板 AI 草稿 `confirmed` 后转换。
|
||||||
|
5. 支持提醒与事项关联。
|
||||||
|
6. 到点后通知接收人。
|
||||||
|
7. 通知成功或失败都要记录。
|
||||||
|
8. 支持查看自己相关提醒状态。
|
||||||
|
9. 创建人、发起人、管理员可以修改、暂停或取消提醒。
|
||||||
|
10. 提醒可以设置是否需要接收人反馈。
|
||||||
|
|
||||||
|
## 3. 第一版不做
|
||||||
|
|
||||||
|
1. 不做 cron 表达式。
|
||||||
|
2. 不做自定义复杂周期规则。
|
||||||
|
3. 不做日历系统。
|
||||||
|
4. 不做复杂自动化流程。
|
||||||
|
5. 不自动操作外部系统。
|
||||||
|
6. 不从 AI 工作台结果生成提醒。
|
||||||
|
|
||||||
|
## 4. 核心流程
|
||||||
|
|
||||||
|
创建提醒:
|
||||||
|
|
||||||
|
```text
|
||||||
|
手动创建或草稿确认
|
||||||
|
-> 后端校验创建权限
|
||||||
|
-> AI 提醒草稿必须有明确 scheduled_at
|
||||||
|
-> 写入 reminders
|
||||||
|
-> 计算 next_trigger_at
|
||||||
|
-> 写 operation_logs
|
||||||
|
```
|
||||||
|
|
||||||
|
触发提醒:
|
||||||
|
|
||||||
|
```text
|
||||||
|
scheduler 扫描 due reminders
|
||||||
|
-> 校验提醒状态
|
||||||
|
-> 用事务锁定当前批次
|
||||||
|
-> 生成幂等键
|
||||||
|
-> 创建 notifications
|
||||||
|
-> 调用飞书发送
|
||||||
|
-> 更新 last_triggered_at 和 next_trigger_at
|
||||||
|
-> 成功或失败都写记录
|
||||||
|
```
|
||||||
|
|
||||||
|
反馈:
|
||||||
|
|
||||||
|
```text
|
||||||
|
接收人点击卡片反馈
|
||||||
|
-> 飞书回调验签
|
||||||
|
-> 校验接收人权限
|
||||||
|
-> 写 feedbacks
|
||||||
|
-> 保留 reminders 调度状态,反馈结果通过 feedbacks 展示
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 数据对象
|
||||||
|
|
||||||
|
本模块主要使用:
|
||||||
|
|
||||||
|
1. `ai_drafts`
|
||||||
|
2. `tasks`
|
||||||
|
3. `reminders`
|
||||||
|
4. `notifications`
|
||||||
|
5. `feedbacks`
|
||||||
|
6. `failure_records`
|
||||||
|
7. `operation_logs`
|
||||||
|
|
||||||
|
字段以 `docs/contracts/数据对象约定.md` 为准。
|
||||||
|
|
||||||
|
## 6. 接口需求
|
||||||
|
|
||||||
|
主要接口:
|
||||||
|
|
||||||
|
1. `GET /api/reminders`
|
||||||
|
2. `POST /api/reminders`
|
||||||
|
3. `GET /api/reminders/{id}`
|
||||||
|
4. `PATCH /api/reminders/{id}`
|
||||||
|
5. `POST /api/reminders/{id}/pause`
|
||||||
|
6. `POST /api/reminders/{id}/resume`
|
||||||
|
7. `POST /api/reminders/{id}/cancel`
|
||||||
|
8. `POST /api/reminders/{id}/notify`
|
||||||
|
9. `POST /api/reminders/{id}/feedback`
|
||||||
|
|
||||||
|
接口格式以 `docs/contracts/API接口约定.md` 为准。
|
||||||
|
|
||||||
|
## 7. 权限规则
|
||||||
|
|
||||||
|
1. 所有人可以创建自己的提醒。
|
||||||
|
2. 老板和程经理可以给别人创建提醒。
|
||||||
|
3. 普通员工不能给同事创建提醒。
|
||||||
|
4. 创建人、发起人、管理员可以修改、暂停或取消提醒。
|
||||||
|
5. 接收人只能反馈自己相关提醒。
|
||||||
|
|
||||||
|
## 8. 状态流转
|
||||||
|
|
||||||
|
提醒状态以 `docs/contracts/状态流转约定.md` 为准。核心约束:
|
||||||
|
|
||||||
|
1. `active` 提醒才允许 scheduler 触发。
|
||||||
|
2. `paused` 不触发,但可以恢复。
|
||||||
|
3. `cancelled` 不触发、不可恢复。
|
||||||
|
4. `trigger_failed` 必须写失败记录。
|
||||||
|
5. 同一个提醒同一触发时间同一接收人同一渠道只能生成一条有效通知。
|
||||||
|
6. 一次性提醒成功触发后进入 `triggered`;周期提醒成功触发后保持 `active` 并计算下一次 `next_trigger_at`。
|
||||||
|
7. 提醒反馈不改变提醒调度状态,反馈结果通过 `feedbacks` 展示。
|
||||||
|
8. AI 提醒草稿缺少明确时间时不得创建 active 提醒,应由 01 模块追问或标记 `missing_fields`。
|
||||||
|
|
||||||
|
## 9. 失败和日志
|
||||||
|
|
||||||
|
必须记录:
|
||||||
|
|
||||||
|
1. 提醒创建、修改、暂停、恢复、取消。
|
||||||
|
2. scheduler 触发成功或失败。
|
||||||
|
3. 飞书通知发送成功或失败。
|
||||||
|
4. 幂等冲突或重复触发跳过。
|
||||||
|
5. 权限拒绝。
|
||||||
|
6. 有问题反馈原因。
|
||||||
|
|
||||||
|
## 10. 给 AI 的测试样例
|
||||||
|
|
||||||
|
1. 普通员工给同事创建提醒,应返回权限错误。
|
||||||
|
2. `paused` 提醒到点不应触发通知。
|
||||||
|
3. 同一提醒同一触发时间重复扫描,不应重复创建通知。
|
||||||
|
4. 每周提醒触发后,应计算下一次提醒时间。
|
||||||
|
5. 通知失败应写失败记录。
|
||||||
|
6. 自己提醒自己默认不需要反馈,老板或程经理给别人提醒默认需要反馈。
|
||||||
|
|
||||||
|
## 11. Review 重点
|
||||||
|
|
||||||
|
1. 是否防止重复触发。
|
||||||
|
2. 是否限制普通员工给别人创建提醒。
|
||||||
|
3. 是否不支持 cron 等第一版不做内容。
|
||||||
|
4. scheduler 是否走 service 层而不是直接改业务表。
|
||||||
|
5. 失败是否可复盘。
|
||||||
|
6. 是否使用事务锁和 `notifications.idempotency_key` 唯一约束防止重复触发。
|
||||||
|
|||||||
@@ -1 +1,147 @@
|
|||||||
|
# 05_权限日志失败记录
|
||||||
|
|
||||||
|
## 1. 模块目标
|
||||||
|
|
||||||
|
权限、日志和失败记录模块负责保证第一版闭环可控、可追溯、可复盘。权限必须在后端校验,关键操作必须写日志,AI、飞书、调度和业务失败必须形成失败记录。
|
||||||
|
|
||||||
|
权限、安全、日志脱敏、飞书验签幂等和访问边界的全局事实源是 `docs/contracts/安全权限日志约定.md`;本 spec 只说明模块目标和业务补充。
|
||||||
|
|
||||||
|
## 2. 第一版做什么
|
||||||
|
|
||||||
|
1. 定义老板、程经理、普通员工、管理员 / AI 团队四类角色。
|
||||||
|
2. 控制事项、提醒、草稿、反馈、失败记录的可见范围和操作范围。
|
||||||
|
3. 记录关键操作日志。
|
||||||
|
4. 记录 AI 解析失败、模型调用失败、AI 记忆读写失败、草稿转换失败、人员映射缺失、飞书通知失败、回调失败、定时触发失败、用户反馈有问题等失败。
|
||||||
|
5. 支持管理员 / AI 团队查看和处理失败记录。
|
||||||
|
6. 对敏感信息做日志边界约束。
|
||||||
|
|
||||||
|
## 3. 第一版不做
|
||||||
|
|
||||||
|
1. 不做复杂组织架构。
|
||||||
|
2. 不做细粒度多级权限。
|
||||||
|
3. 不做复杂审批流。
|
||||||
|
4. 不做独立失败看板或复杂 BI。
|
||||||
|
5. 不把飞书身份直接等同于业务权限。
|
||||||
|
6. 不把密钥、token、完整手机号、完整邮箱写入普通日志。
|
||||||
|
|
||||||
|
## 4. 核心流程
|
||||||
|
|
||||||
|
权限校验:
|
||||||
|
|
||||||
|
```text
|
||||||
|
请求进入 DRF view
|
||||||
|
-> permission class 做入口校验
|
||||||
|
-> serializer 做参数校验
|
||||||
|
-> service 做业务权限和状态校验
|
||||||
|
-> 通过后执行业务动作
|
||||||
|
```
|
||||||
|
|
||||||
|
失败记录:
|
||||||
|
|
||||||
|
```text
|
||||||
|
业务或外部调用失败
|
||||||
|
-> 写 failure_records
|
||||||
|
-> 保留关联对象和失败类型
|
||||||
|
-> 管理员 / AI 团队处理
|
||||||
|
-> 填写处理结果
|
||||||
|
-> 写 operation_logs
|
||||||
|
```
|
||||||
|
|
||||||
|
操作日志:
|
||||||
|
|
||||||
|
```text
|
||||||
|
关键动作发生
|
||||||
|
-> 记录 actor、action、target_type、target_id
|
||||||
|
-> 记录操作渠道和结果摘要
|
||||||
|
-> 避免记录敏感明文
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 数据对象
|
||||||
|
|
||||||
|
本模块主要使用:
|
||||||
|
|
||||||
|
1. `users`
|
||||||
|
2. `failure_records`
|
||||||
|
3. `operation_logs`
|
||||||
|
4. `model_call_logs`
|
||||||
|
5. `feishu_events`
|
||||||
|
6. `secretary_messages`
|
||||||
|
7. `bot_contexts`
|
||||||
|
|
||||||
|
字段以 `docs/contracts/数据对象约定.md` 为准。
|
||||||
|
|
||||||
|
安全和访问边界以 `docs/contracts/安全权限日志约定.md` 为准。
|
||||||
|
|
||||||
|
## 6. 接口需求
|
||||||
|
|
||||||
|
主要接口:
|
||||||
|
|
||||||
|
1. `GET /api/users/me`
|
||||||
|
2. `GET /api/failure-records`
|
||||||
|
3. `GET /api/failure-records/{id}`
|
||||||
|
4. `PATCH /api/failure-records/{id}`
|
||||||
|
5. `POST /api/failure-records/{id}/resolve`
|
||||||
|
6. `GET /api/notifications`
|
||||||
|
7. `GET /api/notifications/{id}`
|
||||||
|
8. `GET /api/feedbacks`
|
||||||
|
|
||||||
|
接口格式以 `docs/contracts/API接口约定.md` 为准。
|
||||||
|
|
||||||
|
## 7. 权限规则
|
||||||
|
|
||||||
|
1. 普通员工只能看自己作为接收人、创建人或反馈人的记录。
|
||||||
|
2. 老板能看自己发起、创建或接收的记录。
|
||||||
|
3. 程经理能看自己发起、负责、待确认,以及第一批试用管理范围内的记录。
|
||||||
|
4. 管理员 / AI 团队能看失败记录、通知记录、人员映射和必要操作日志。
|
||||||
|
5. 普通员工不能给别人创建事项或提醒。
|
||||||
|
6. 反馈人必须是事项或提醒接收人。
|
||||||
|
7. 飞书登录用户必须匹配到启用状态的平台用户后才能进入平台。
|
||||||
|
8. 模型调用日志、飞书原始 payload、失败记录原始错误和一次性操作 token 明文不得对普通员工开放。
|
||||||
|
|
||||||
|
## 8. 状态流转
|
||||||
|
|
||||||
|
失败记录状态以 `docs/contracts/状态流转约定.md` 为准。核心约束:
|
||||||
|
|
||||||
|
1. 新失败进入 `pending`。
|
||||||
|
2. 处理中进入 `processing`。
|
||||||
|
3. 填写处理结果后进入 `resolved`。
|
||||||
|
4. 确认不处理可进入 `cancelled`。
|
||||||
|
5. 失败记录状态变化必须写操作日志。
|
||||||
|
|
||||||
|
## 9. 必须写日志的动作
|
||||||
|
|
||||||
|
1. 飞书登录成功或失败。
|
||||||
|
2. 机器人收到老板消息。
|
||||||
|
3. 非老板私聊机器人。
|
||||||
|
4. AI 生成草稿、解析失败。
|
||||||
|
5. AI 追问、普通记录、上下文回顾和 BotContext 变化。
|
||||||
|
6. 人工确认、修改、取消草稿。
|
||||||
|
7. 老板点击补充/重说。
|
||||||
|
8. 程经理确认复杂事项。
|
||||||
|
9. 创建事项、创建提醒。
|
||||||
|
10. 发送通知、补发通知、通知失败。
|
||||||
|
11. 接收反馈,尤其是有问题反馈。
|
||||||
|
12. 定时提醒触发成功或失败。
|
||||||
|
13. AI 记忆读写失败、草稿转换失败和失败记录处理。
|
||||||
|
14. 背景摘要或老板风格提示词版本启用。
|
||||||
|
|
||||||
|
## 10. 给 AI 的测试样例
|
||||||
|
|
||||||
|
1. 普通员工创建给别人的事项,应被拒绝并写权限失败日志。
|
||||||
|
2. 普通员工创建给别人的提醒,应被拒绝。
|
||||||
|
3. 非接收人提交反馈,应被拒绝。
|
||||||
|
4. 管理员处理失败记录时,必须留下处理结果。
|
||||||
|
5. 日志中不得包含完整手机号、完整邮箱、飞书 token 或密钥。
|
||||||
|
6. 未匹配平台用户的飞书登录应失败并写失败记录。
|
||||||
|
7. PostgreSQL AI 记忆读写失败时,应写 `memory_store_failed`,不得继续依赖内存确认草稿。
|
||||||
|
8. 飞书旧卡片或失效卡片回调只记录事件,不写业务对象。
|
||||||
|
9. 一次性操作 token 只能保存 hash,普通日志不得出现明文。
|
||||||
|
|
||||||
|
## 11. Review 重点
|
||||||
|
|
||||||
|
1. 后端权限是否完整。
|
||||||
|
2. 状态流转是否可追踪。
|
||||||
|
3. 失败记录是否包含关联对象、失败类型和处理结果。
|
||||||
|
4. 日志是否记录操作人和渠道。
|
||||||
|
5. 敏感信息是否被脱敏。
|
||||||
|
6. 访问范围是否符合 `安全权限日志约定.md`。
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# tests
|
||||||
|
|
||||||
|
`tests/` 是真实约束目录。Django 项目骨架落地前,本目录只做占位;后续测试代码按 Django 默认测试或 pytest 约定放入这里或各 app 的 `tests/` 中。
|
||||||
|
|
||||||
|
每个 active plan 完成前必须说明新增或修改了哪些测试、覆盖了哪些 spec / contract 约定、如何运行,以及还有哪些关键风险未覆盖。
|
||||||
Reference in New Issue
Block a user