Files
AI-Secretary/docs/specs/01_老板AI秘书与AI草稿.md
2026-06-22 17:30:59 +08:00

40 KiB
Raw Permalink Blame History

01_老板 AI 秘书与 AI 草稿

1. 模块目标

本模块负责把老板在飞书私聊或平台入口输入的一句话,交给 AiSecretaryAgent 理解、归类、补上下文,并生成可确认的 AI 草稿或直接回复。

这个模块是老板秘书的大脑,不直接创建任务、提醒或发送通知。所有执行动作都必须走人工确认后的任务、提醒和通知模块。

说明:整体方案中的“事项、需求、安排”在第一版统一归入 task。如果后续单独拆出 requirement 类型,需要先更新本 spec 和数据对象约定。

2. 第一版做什么

第一版核心定位:

老板一句话
-> 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 统一入口

所有老板消息统一走:

POST /api/secretary/handle-message

飞书回调、/api/demo/boss-message、未来平台入口都转发到该入口。

处理顺序:

记录原始消息
-> 按 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 建议取值:

empty
awaiting_more_info
awaiting_confirm
expired
cleared

状态含义:

  • empty:当前没有可补充或可确认的草稿。
  • awaiting_more_infoAI 已追问,等待老板补充。
  • awaiting_confirm:草稿已整理完成,等待老板确认、修改、取消或转经理。
  • expired:超过 30 分钟,旧上下文只可用于回顾,不再默认参与修改。
  • cleared:草稿已确认、取消或转换完成,当前上下文已清空。

规则:

  • 每次进入 awaiting_more_infoawaiting_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 的草稿,不继续拉扯。
  • 每次追问都必须保存一条 SecretaryMessageintent_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_atsource + message_idbot_contexts.conversation_id 建索引;jsonb 字段第一阶段只做读取展示和调试,不作为复杂查询主路径。

最小字段建议:

{
  "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_messagesbot_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

{
  "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=truedraft_type 必须对应为 taskreminder
  • qa/realtime_qa/note/need_more_info/unknown/unsupported 必须 should_create_draft=falsedraft_type=none
  • taskroute_type 必填,只能是 direct_after_boss_confirmmanager_confirm_required
  • reminder/qa/realtime_qa/note/need_more_info/unknown/unsupportedroute_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 必须是对象数组;fieldreason 必填,raw_valuemessage 可选。常用 reason 包括 missing_person_mappingtime_not_parsedreceiver_missingcontent_missingscheduled_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_missingreceiver_missingmissing_person_mapping time_not_parsedscheduled_at_missing
reminder content_missingscheduled_at_missingtime_not_parsedreceiver_missingmissing_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)不得进入事项、提醒或通知闭环;如需留痕,只保存 SecretaryMessageBotContextmodel_call_logs 或最小草稿记录。

ai_drafts.statusdocs/contracts/状态流转约定.md 为准,核心状态:

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
  • answeredqa/realtime_qa/note/context_summary/unknown/unsupported 的处理结果,不转换为任务或提醒,不影响当前有效 BotContext
  • 追问 3 轮仍补不全、但系统决定生成带 missing_fields 的草稿时,模型输出必须从 need_more_info 转为 taskreminder,创建 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 统一消息入口

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 只在不可恢复错误或降级时返回。结构建议:

{
  "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_failedai_model_failedmemory_store_failedsystem_error 等不可恢复错误时,reply_type=erroranswer 使用固定兜底话术:“暂时没处理好,请稍后再试。”,并写 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_飞书通知与反馈.md05_权限日志失败记录.md 负责;本模块只消费解析后的老板消息或草稿操作命令。

7. 权限规则

  • 飞书机器人私聊入口第一版只允许老板使用。
  • 非老板消息不调用 AI,不生成草稿,不创建业务对象。
  • 草稿确认、取消、补充和转经理必须是草稿发起老板本人。
  • 程经理不能通过机器人对话派活,只通过 Web 或通知跳转确认复杂任务。
  • AI 不得绕过人工确认。

8. 状态流转

老板输入
-> 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 不再直接修改原草稿;老板后续说“把刚才发出去那个改一下”,应识别为任务/提醒变更请求,第一阶段未接入变更能力时回复暂不支持或引导到任务模块。

直接回复流转:

qa / realtime_qa / note / unknown / context_summary
-> answered 处理结果
-> 不进入事项、提醒或通知闭环,不清空当前有效 BotContext

context_summary 也必须保存为一条 SecretaryMessage(intent_type=context_summary),便于调试页回看“老板什么时候要求回顾、系统回了什么”。

失败流转:

模型调用失败
-> 重试 1 次
-> 仍失败则不创建可确认草稿
-> FailureRecord(ai_model_failed)

JSON 非法 / schema 校验失败
-> 不创建可确认草稿
-> 可选创建 parse_failed 草稿仅用于调试;如果无法创建最小草稿,failure_records.target_type 使用 model_call_log
-> FailureRecord(ai_parse_failed)

parse_failed 草稿不能确认、不能转换为任务/提醒、不能发送通知,只能用于调试追踪。

取消流转:

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.typeFailureRecord.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 任务草稿

输入:

让东东跟进一下合同

期望:

  • 生成 task 草稿。
  • 不因为没有时间反复追问。
  • 接收人候选包含东东。
  • should_create_draft=true

10.2 提醒草稿

输入:

明天上午 9 点提醒东东准备咖啡

期望:

  • 生成 reminder 草稿。
  • scheduled_at 使用 Asia/Shanghai
  • recurrence 为 none

10.3 补充上一条

前置:存在一条待确认 task 草稿,内容为“让东东跟进合同”,没有明确 due_atschedule_text;该草稿不是提醒草稿。

输入:

改成明天下午

期望:

  • 判断为 follow_up
  • 修订上一条草稿,不新建任务。
  • 更新该任务草稿的 due_atschedule_text,不修改 reminder.scheduled_at

10.4 新建例外

前置:存在待确认草稿。

输入:

另外再安排一个,让行政订会议室

期望:

  • 判断为 new_request
  • 新建草稿。

10.5 取消

输入:

算了,刚才那个不要了

期望:

  • 取消当前草稿。
  • 清空上下文。

10.6 非老板访问

输入:普通员工私聊机器人。

期望:

  • 固定回复“当前入口是老板秘书,如需处理事项请进入平台。”
  • 不调用 AI。
  • 写失败和操作日志。

10.7 追问信息不足

输入:

安排一下

期望:

  • 判断为 need_more_info
  • 不生成任务或提醒。
  • 追问“安排谁、做什么”等关键问题。

10.8 实时问答兜底

输入:

帮我分析今天的股票走势

期望:

  • 判断为 realtime_qa
  • 不生成草稿。
  • 回复固定模板:“这个需要实时数据查询,第一版暂不作为正式能力;我不会把它生成任务或提醒。”

10.9 普通记录

输入:

记一下,今天先别催东东

期望:

  • 判断为 note
  • 只保存消息记录。
  • 不创建草稿、不通知任何人。

10.10 接收人解析失败

输入:

让小王处理一下合同

前置:PersonResolver 无法识别“小王”。

期望:

  • 草稿可以生成。
  • receiver_text=小王
  • missing_fields 包含 { "field": "receiver", "reason": "missing_person_mapping", "raw_value": "小王" }
  • 正常确认被阻止,回复老板需要先补齐人员映射。

10.11 能力外请求

输入:

帮我写一份完整年报

期望:

  • 判断为 unsupported
  • 不生成草稿。
  • 不追问执行人、时间等任务字段。
  • 说明当前入口只处理老板事项草稿、提醒草稿、普通问答和普通记录。

10.12 上下文回顾

输入:

我刚才说了什么

期望:

  • 返回 reply_type=context_summary
  • 返回 context_summary,默认摘要最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿。
  • 不创建新草稿,不覆盖当前待确认草稿。
  • 保存 SecretaryMessage(intent_type=context_summary)

10.13 无草稿 command

前置:当前没有待确认草稿。

输入:

确认

期望:

  • 命中 command,但不抛异常。
  • 不创建草稿。
  • 返回 reply_type=plain_answer
  • 回复“目前没有待确认的草稿”。

10.14 错误降级

前置:模型返回非法 JSON。

期望:

  • 不进入任务、提醒、通知流程。
  • ModelCallLogFailureRecord(ai_parse_failed)
  • 返回 reply_type=error
  • answer=暂时没处理好,请稍后再试。
  • failure.type=ai_parse_failed
  • 如落 parse_failed 草稿,仅用于调试,不能确认或转换。

10.15 低置信度追问

前置:存在一条待确认任务草稿。

输入:

这个换一下

期望:

  • 如果无法判断是修改接收人、时间、措辞,还是切换到新事项,不默认修改当前草稿。
  • 返回 intent=need_more_info
  • 追问“你是想修改刚才那条草稿,还是新建一条?具体想换什么?”。

10.16 已发布后修改

前置:上一条草稿已经 convertedBotContext 已清空。

输入:

把刚才那个改成明天下午

期望:

  • 不修改已转换草稿。
  • 识别为任务/提醒变更请求或第一阶段能力外请求。
  • 第一阶段未接入变更能力时,回复“刚才的事项已经发布,第一阶段暂不支持直接修改已发布任务;我可以帮你重新整理一条补充说明。”。

10.17 消息幂等

前置:同一个 source + message_id 已处理成功并生成草稿。

输入:同一消息再次进入。

期望:

  • 不重复调用模型。
  • 不重复创建草稿。
  • 不重复追加 PostgreSQL secretary_messagesSecretaryMessage
  • 返回上一轮处理结果或已存在草稿引用。

10.18 待确认草稿中的普通聊天

前置:存在一条待确认任务草稿。

输入:

这个措辞是不是太硬了?

期望:

  • 判断为 qa 或低置信度追问,不默认修改当前草稿。
  • 不创建新草稿。
  • 不清空当前有效 BotContext

10.19 30 分钟过期

前置:存在一条 awaiting_confirm 草稿,但 expires_at 已过期。

输入:

改成明天下午

期望:

  • 不直接修改旧草稿。
  • FailureRecord(follow_up_expired) 或 OperationLog 过期记录。
  • 按新输入处理,或追问老板“上一条草稿已过期,你是想修改刚才那条还是新建一条?”。

10.20 PostgreSQL AI 记忆读写失败

前置:PostgreSQL 保存 secretary_messagesBotContext 失败。

输入:

确认

期望:

  • 不继续依赖内存确认草稿。
  • 不转换任务或提醒。
  • 返回 reply_type=error
  • FailureRecord(memory_store_failed)

10.21 程经理确认流转

前置:草稿 route_type=manager_confirm_requireddraft.need_manager_confirm=true

输入:

确认

期望:

  • 老板确认后草稿进入 confirmed
  • 清空老板侧 BotContext
  • 创建 tasks.status=pending_manager_confirm 的事项壳,不直接通知最终接收人。
  • 程经理确认后,事项壳进入 pending_notify 并通知接收人;草稿在事项壳创建成功后进入 converted

11. Review 重点

  • 是否所有入口都走 POST /api/secretary/handle-message
  • 是否用 source + message_id 做幂等,避免重复调用模型和重复创建草稿。
  • 是否幂等命中时不会重复追加 PostgreSQL secretary_messagesSecretaryMessage
  • 是否实现 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_qaunsupported,实时数据类固定走 realtime_qa
  • 是否把 command 作为消息预分类而不是最终 intent
  • 是否追问消息也保存为 SecretaryMessage
  • 是否 missing_person_mapping 会阻止正常确认。
  • 是否不同 draft_type 的确认阻塞字段已明确。
  • 是否 reason 必填且只进入日志/调试,不直接展示给老板。
  • 是否 DRAFTING 没有作为持久化草稿状态使用。
  • 是否 missing_fields 使用对象数组,并能区分字段和失败原因。
  • 是否 command 命中但无草稿时返回普通说明,不抛异常。
  • 是否 failure 返回结构只在错误降级时出现。
  • 是否 failure.typeFailureRecord.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_requiredconfirmed -> 创建 pending_manager_confirm 事项壳 -> converted -> 程经理确认事项壳 -> pending_notify,不会绕过程经理确认。
  • 是否转换失败写 FailureRecord(draft_convert_failed),且不回滚老板确认。
  • 是否上下文回顾默认范围为最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿。
  • 是否 /api/feishu/callback 的验签、去重和鉴权没有写进本模块。