跳转至

设计会话 · 2026-08-03 · Alfred V1 重新梳理

元信息 | 状态:已归档(决策已落 ADR-0009~0017)| 最后更新:2026-08-03 | 范围:V1 重新设计完整讨论存档 | 关联:VISION.mdDATA_MODEL_V2.md

这份文档的用途:本轮讨论的完整存档 + 决策台账。 关掉对话之后,任何人(包括未来的我)只读这一份就能接上全部上下文。 状态标记:🔴 待决 / 🟡 倾向 / 🟢 已决(附 ADR 编号)

相关文档:VISION.md · DATA_MODEL_V2.md · GLOSSARY.md · adr/


0. 产品愿景(本轮新增的最上位约束)

Alfred 从「辅助求职的工具」逐步演变为「懂我的个人管家」。

这句话不是口号,它是一条架构约束,会直接否决一些看起来更省事的设计:

愿景推论 对 V1 的硬约束
未来要管健康、记账、社交,不只是求职 内核(对话 / 记忆 / 实体 / 事件 / 提醒)必须领域无关;求职只是第一个领域包
「懂我」= 长期累积,不是单次问答 记忆与对话是一等公民,不是日志;原话永久留存,提炼可重算
「管家」= 会主动找我,不是我问它答 提醒引擎(nudge)不是 V3 的锦上添花,是 V1 就要有骨架
一个人用很久 数据可导出、可回滚、可纠错;错一次记忆会污染很久

分期(详见 VISION.md):

  • V1 求职(当前):投递 / 经历 / Coffee Chat / 简历 / 提醒
  • V2 社交 CRM:人脉网络、关系维护、follow-up
  • V3 个人管家:健康、记账、日程
  • V4 主动代理:跨域联动(「你下周三有面试,但那天你约了体检」)

1. 你本轮的原始输入(原样归档,不做压缩)

一、前端设计

产品形态类似微信聊天界面,支持发送语音、文字、图片、文件等。AI 也会向用户反馈文件、图片或链接。

  1. 第一阶段:小程序。
  2. 第二阶段:个人网页。
  3. 基础功能:包含必要的权限申请(如日历、相册等)及交互提醒。
  4. 疑问:小程序中是否能实现定闹钟等系统级操作?如果不行,网页版是否可行,或有无其他替代方案?

二、后端设计(核心)

重点构建针对求职场景的 V1,能记录岗位投递、心得、Coffee Chat 等信息,并保证后续扩展性。

  1. 工作流:(a) 用户输入多模态信息;(b) AI 解析并转化为结构化数据,用户确认后入库;(c) 生成回答时结合数据库历史上下文与聊天记录。
  2. 技术选型:(a) 参考 LangGraph 或 OpenClaw;(b) 调研 OpenAI Agent SDK;(c) 搜索资料完善方案。
  3. 上下文管理:(a) 聊天连续,需定期更新与压缩;(b) 手动刷新 / AI 自动判断主题拆分 / OpenAI Session 模式?

三、数据库核心概念(你的清单)

  1. Conversation — 每一条对话,含发送时间、内容、回复内容
  2. User — 使用系统的用户,数据相互独立
  3. Person — 联系人(面试官、Coffee Chat 对象),需唯一标识符
  4. Organization — 公司、社团、组织
  5. Location — 地点(北京、香港、深圳)
  6. Jobs / Positions — Jobs = 具体招聘实例(公司+岗位+地点);Positions = 职位大类(后端工程师、产品经理、心理咨询师)
  7. Fact / Skills — Fact = 事实陈述(「我会 Excel」「我和某人是好朋友」);Skills = 具体技能
  8. Event — 核心枢纽,连接 Fact / Person / Organization / Jobs;类型含 Application、Coffee Chat、Interview;待定:Event 与 Application 是否拆分
  9. Resume / Resume Selection — Resume 多版本各有 UUID;Resume Selection 关联特定岗位的简历版本,记录所用的 Facts 或 Experience

四、后续扩展

社交 CRM 系统 → 个人管家(健康管理、记账等)


2. 调研结论(你要求的资料检索,均已核实到一手来源)

2.1 小程序能不能「定闹钟」——能,但要换个说法

能力 小程序 结论
调起系统闹钟 App / 写入闹钟 ❌ 无此 API 不可行,别再找了
写入手机系统日历 + 到点提醒 wx.addPhoneCalendar(单次)/ wx.addPhoneRepeatCalendar(重复)
基础库 ≥ 2.15.0,授权 scope.addPhoneCalendar
参数:startTime(必填, unix 秒)、alarm(默认 true)、alarmOffset(提前秒数)、allDayrepeatInterval(day/week/month/year)
这就是「闹钟」的正解。写进系统日历后,即使用户不打开小程序、甚至卸载小程序,手机照样响
主动推送消息 ⚠️ 订阅消息。一次性订阅:用户每授权一次 = 可下发一条,可累积;长期订阅:仅对政务民生 / 医疗 / 交通 / 金融 / 教育行业开放 个人求职助理拿不到长期订阅。必须靠一次性订阅额度经营
相册 / 拍照 / 录音 / 选文件 wx.chooseMedia / wx.getRecorderManager / wx.chooseMessageFile 满足多模态输入
后台定时任务 ❌ 小程序退到后台即冻结 提醒必须由服务端发起,不能靠客户端定时器

网页版(H5 / PWA)反而更弱:iOS Safari 的 Web Push 必须先「添加到主屏幕」,且后台唤醒不可靠;无法写入系统日历。 → 结论:提醒能力上小程序 > 网页版,网页版的价值在大屏编辑(改简历、看看板),不在提醒。

推荐三层提醒架构(详见 §4.1): 系统日历(离线可靠,硬时间点)+ 一次性订阅消息(在线触达,软提醒)+ 应用内提醒中心(真相来源,永不丢)。

2.2 LangGraph vs OpenAI Agents SDK vs 现状自研

维度 OpenAI Agents SDK LangGraph Alfred 现状
思维模型 Agent + 托管循环 显式状态图(节点+边) 声明式 Skill(YAML manifest + Action 原语)
状态持久化 Sessions(SQLite/Redis),无崩溃恢复 Checkpointer(Postgres),原生持久可恢复 ingest_request + ingest_run 表,天然持久
Human-in-the-loop 需自行实现 原生(图中断 → 审批 → 续跑) dry_run → IngestPreview → commit_ingest 已实现
模型无关 偏 OpenAI 是(LLMProvider Protocol + Fake 实现)

关键判断:你的工作流 (a)(b)(c) 是确定性管道 + 一次人工确认,不是自主 Agent 循环。 LangGraph 的核心卖点(durable checkpoint + HITL)在本项目已由 ingest_request / dry_run / commit 等价实现,且更贴合领域语义。 引入任一框架都要重做三条现有铁律:Provider 抽象、Fake-provider 可测、Action 作为唯一落库入口。

→ 详见 §5 与待写的 ADR-00X。

2.3 OpenClaw 的记忆做法

OpenClaw 的记忆是 agent workspace 里的纯 Markdown 文件,文件即真相来源。 优点:可读、可手改、可 git diff。缺点:无法 JOIN、无法按结构化条件查询、多用户下无法隔离。

Alfred 的取舍(上一轮已决):记忆落 Postgres(memory 表 + pgvector + tsvector 混合召回 + 时间衰减), 但保留「可导出为 Markdown」这一开源精神——把可读性做成导出能力,而不是存储格式。


3. 🔴 冲突清单:你的概念 vs 已落地的表

这一节是本轮最重要的内容。以下 4 条是真冲突,不是措辞差异。

C1 · 「Jobs / Positions」与代码里的 position / occupation 语义互为反义

概念 你的说法 代码现状
具体招聘实例(公司+岗位+地点) Jobs position 表(fingerprint = sha256(company_id + normalized_title + location)
职位大类(后端工程师 / 产品经理) Positions occupation

position 这个词在你我口中指的是相反的东西。这会一路污染 API 路径、前端字段、prompt 措辞。 现在改成本 = 一次迁移;上线后改成本 = 迁移 + 前端 + 历史数据 + 所有 prompt。

→ 见 Q1

C2 · 「Fact」是新概念,但已有 4 张表都能装下「我会 Excel」

现有可承载该语句的表:memory(kind=fact/preference/intent/context)、user_skillexperiencenote。 再加一个 fact 就是 5 个地方都可能存同一件事 —— 查询时哪儿都不全,这是数据模型里最难修的一类病。

必须先定一条唯一归属不变量(invariant),再决定要不要建表。

→ 见 Q2

C3 · 「User」= 多租户,但当前是单用户,且唯一约束会全部失效

现状:user_skill.user_id 是一个裸字符串,没有 user 表,没有认证,没有隔离。 一旦真的两个人用:

  • company.slug 唯一 → 必须变 UNIQUE(owner_id, slug)
  • position.fingerprint 唯一 → 同上
  • skill.slug 唯一 → 但技能词表到底该不该跨用户共享?(这是个真问题,不是顺手改)
  • 每一个查询都要加租户过滤,漏一个就是串号级事故

事后补租户列,是数据库改造里最贵的一类。 现在加一列几乎零成本。

→ 见 Q3

C4 · 「Event 是核心枢纽」与现有 application / interview / interaction 分表冲突

现状:event 是通用时间轴表;application(12 态状态机 + 外键到简历版本 + 投递凭证)、 interview(轮次 / 面试官多对多)、interaction(Coffee Chat 泛化后的接触记录)各自是强类型表。

你的方案把 Event 当成存储主体。若照做,application 的状态机、外键、约束全部要塞进 JSONB —— 不能约束、不能索引、不能 JOIN。 但你要的「给我看 7 月发生的所有事」确实需要统一时间轴。

→ 见 Q4

C5 · 「Conversation」= 已实现的 chat_thread + chat_message(术语统一即可,非冲突)

上一轮已落地:chat_thread(分组/归档)+ chat_message(双方全量原话 + sequence + 软丢弃)+ attachment.chat_message_id待决的是上下文管理机制(手动 / 自动分主题 / Session)。→ 见 Q5

C6 · 「Location」当前只是 position.location 字符串(非冲突,是欠债)

「HK」「香港」「Hong Kong」现在是三个不同的值,筛选必然漏。→ 见 Q7

C7 · 「Organization」vs 现有 company(非冲突,是收窄)

company 装不下学校、社团、NGO、政府机构 —— 而 Coffee Chat 的对象、经历的所属组织经常不是公司。 V2 社交 CRM 更需要泛化。倾向:改名 organization + kind 枚举,成本低(一张表 + 引用它的 5 个外键)。

C8 · 「Resume Selection」≈ 已有 document_version_experience + document_version_bullet

结构基本对上。但你多说了一句关键的:「记录其所使用的 Facts 或 Experience」—— 这暗含一个强要求:简历里每一句话都要能溯源。→ 见 Q6


4. 🔴 遗漏清单:你没提但会真出事的环节

你问「还有哪些环节遗漏」。以下按「会不会真的坑到你」排序。

M1 · 时区(会真的让你错过截止日期)

「8/30 截止」是哪个时区的 23:59?你在香港,投美国岗位,公司在加州。 → 全库 timestamptzuser.timezonedeadline 必须带来源时区且在提醒时按用户当地时间换算

M2 · 推送通道账本(小程序订阅额度是稀缺资源)

一次性订阅 = 授权一次发一条。这意味着额度是要「攒」的资产,且发了要记账,否则会出现「提醒发不出去且你不知道」。 → 需要 notification_channel(用户开通了哪些通道)+ notification_delivery(每条 nudge 走了哪个通道、成功与否、额度扣减)。 当前 nudge 表只有生成,没有投递记录。这是 §2.1 三层架构的落库前提。

🟢 本轮决策(见 §8.2)nudge_rule / nudge(提醒规则与生成)本轮建notification_channel / notification_delivery 账本本轮 defer,但 nudge 预留 channel 字段(email/digest/miniprogram)保证可扩展。 推送主通道 = 后端邮件 + 每日聚合一条(绕开小程序订阅额度稀缺问题),不强制在小程序内做推送。

M3 · 撤销与纠错(记忆一旦记错会污染很久)

现在只有单条软删除。真实场景是:「刚才那条你全记错了,撤销。」 → ingest_request 需要整体回滚能力:一次 ingest 产生的所有实体带同一个 origin_request_id,可一键 revert。

M4 · 幂等(弱网重发)

小程序在地铁里发语音,超时重发 → 两条投递记录。 → 客户端生成 idempotency_key,服务端 UNIQUE

M5 · 成本护栏

每条语音 = STT + LLM + embedding。ingest_run 记了 token,但没有闸门。 → per-user 月度预算 + 超限降级(关掉 embedding / 换小模型 / 排队)。

M6 · 隐私与 PII(多用户上线即触发)

你存的是别人的信息:面试官姓名、Coffee Chat 对象的联系方式。小程序上架要隐私协议,且要能导出/删除。 → person 需要 pii_level;需要 export / purge 端点;GLOSSARY 里要定义什么算 PII。

M7 · AI 产出物的反向归属

你说「AI 也会向用户反馈文件、图片或链接」。当前 attachment.chat_message_id 只表达「用户发来的」, 没有「AI 生成的 PDF 简历属于哪条回复」。 → attachment 需要区分 direction: inbound | outbound,或 produced_by_message_id

M8 · 附件生命周期

语音和截图会以每天几十条的速度堆积。 → 冷存储 / 转写后原音频降采样 / 过期归档策略。

M9 · 抽取质量的回归基准(Evals)

改一次 prompt,抽取质量是升是降,现在没有任何客观信号。 → 建 golden set(20~50 条真实输入 + 期望结构化输出),CI 跑准确率。

M10 · 领域无关内核 vs 求职专用表(愿景 §0 的直接推论)

person / event / fact 现在都带着求职语境。V3 要管健康记账时,是「同一个库加表」还是「同一内核 + 领域包」? 这个决定影响现在要不要把 event / fact 做成领域无关的。 现在选错,V3 要重来。

M11 · 「用户确认」的疲劳度

工作流 (b) 说「用户确认后入库」。如果每条语音都弹确认,用完三天就烦了。 → 需要置信度分级:高置信度直接入库(事后可撤),低置信度才拦下确认。这需要 ingest_run.confidence 与阈值配置。


5. 🔴 待决问题台账

# 问题 状态 ADR
Q1 命名体系:job_posting(实例) + occupation(大类),position 退役 🟢 已决 ADR-001
Q2 Fact 归属:强类型/状态表为真相,memory 残余;fact 为通用状态真相 🟢 已决 ADR-002
Q3 多用户:私有表带 user_id,共享实体/词表表不带 🟢 已决 ADR-003
Q4 Event(行为真相) 与 Fact(状态真相) 并列互补,非派生;Application 独立表 🟢 已决 ADR-002
Q5 上下文:常驻单 Session + AI 自动分段 + 人工干预,混合上下文组装 🟢 已决 ADR-005
Q6 简历溯源:软引用 + 渲染强制 footnote + 校验阻断无源(折中) 🟢 已决 ADR-006
Q7 Location:建 location 实体 + location_alias,结构化地理字段 🟢 已决 ADR-007
Q8 Organization 泛化 companyorganization(kind 枚举) 本轮做 🟢 已决 ADR-009
Q9 编排框架:LangGraph(Python) 编排 + Action 执行,不用 OpenAI SDK 🟢 已决(重要转向) ADR-004
Q10 领域无关内核的边界划在哪(M10) 🟢 已决(内核 = event/fact/person/org/location/skill;领域包 = job_posting/application/interview/experience) ADR-002/008
Q11 job_posting 是否归入共享表(多人可投同一 posting) 🟢 已决(是,共享;其 application 私有,见 §6.4) ADR-003

6. 已决模型 · 第一轮拷问结论(Q1–Q4)

2026-08-03 用户拍板。这是 V1 数据模型的四根支柱,后续一切表结构都建立在这之上。

6.1 Q1 · 命名体系(🟢 已决 · ADR-001)

  • occupation = 职位大类(长期稳定、跨公司可比):software engineer / data scientist / psychologist。
  • job_posting = 具体招聘实例(公司 + occupation + 地点 + 唯一 ID / fingerprint)。
  • 现有 position重命名为 job_postingoccupation 表保留。
  • 反义歧义彻底消除:position 一词在本项目退役,不再用于任何 API / 前端 / prompt。
  • 含义固化:occupation 回答「这是哪一行」,job_posting 回答「这家公司这个地点这个岗位的这一次招聘」。

6.2 Q4 · Event 与 Fact 的并列真相模型(🟢 已决 · ADR-002,最核心)

这一条推翻了我之前「Event = 派生时间轴投影」的假设,是本轮最重要的定论。

Event = 行为的真相(Source of Truth for Behavior)

  • 不可变行为日志(append-only, immutable)。记录「发生了什么」,不记录「现在是什么状态」。
  • 用户说了什么、做了什么、AI 识别了什么语义事件,对「发生了什么」而言 Event 就是真相。
  • Events are the immutable source of truth for behavior — what happened, not state.

Fact = 状态的真相(Source of Truth for State)

  • 从 Event + 业务规则物化(materialize)出来的当前状态快照。
  • 对「现在是什么样 / 某时间段是什么样」而言 Fact 就是真相。
  • valid_fromvalid_to,支持纠错(某条 Fact 被改正时,旧值封口、新值开区间)。
  • Facts are the materialized source of truth for states — what is true now or in a certain period, derived from events + business rules, correctable via valid_from→valid_to.

二者并列互补,不是「谁派生谁」:

  • Event 不是 Fact 的索引,Fact 也不是 Event 的投影。两套独立真相,通过 fact.source_event_id 双向可追。
  • Application / Interview / Interaction 各自是强类型表,不是 Event
  • Event 只记录「Application Submitted 这个行为」;该投递的实际数据(岗位、简历版本、凭证、状态机)仍在 application 表。
  • 即:Event 回答「什么时候发生了投递行为」,application 回答「这次投递的细节」。

落到表结构(领域无关,呼应愿景 §0):

event (append-only 行为日志,领域无关)
  id, type ENUM, actor_user_id, occurred_at,
  source_type (conversation|import|api), source_ref_id,
  payload JSONB  -- 指向本次行为创建的实体: {fact_id?, person_id?, application_id?, ...}

fact (物化状态快照,领域无关)
  id, kind ENUM (skill_declaration|relationship|attribute|...),
  valid_from, valid_to,
  subject_ref, object_ref,        -- 指向 person/skill/organization 等
  proficiency (nullable, 技能类 Fact ),
  source_event_id, source_conversation_id,
  owner_user_id                   -- 私有

6.3 Q2 · Fact 的归属(🟢 已决 · ADR-002)

强类型/状态表是真相,memory 是残余。结合 6.2,细化「我会 Excel」的完整链路:

  1. 原话:「我会 Excel」存于 chat_message(永久留存,呼应愿景「原话永久留存」)。
  2. 行为:AI 识别为「用户声明了一个事实」→ 写一条 event(type=FACT_DECLARED, source=conversation, conversation_id, fact_id)。
  3. 状态:物化出一条 fact(kind=skill_declaration, subject=我, object=Excel skill, proficiency=high, valid_from=now)。
  4. proficiency 的量表定义在 fact 里(每个 skill 的 proficiency scale 可不同)。
  5. 强类型投影:为支持 JOIN 式技能匹配,同步 upsert user_skill(user, excel, proficiency=high)。user_skillfact 中「技能声明」类目的查询优化投影,二者通过约定保持同步。
  6. 残余:无法归入强类型的自由内容 → memory(kind=fact/preference/intent/context)。

一致性不变量(invariant):凡落在受控词表(has_skill / worked_at / knows_person)的声明,同时物化到 fact 与对应强类型表;强类型表是查询真相,fact 是带时间轴与溯源的通用状态真相,memory 仅收容残余。

6.4 Q3 · 多用户边界(🟢 已决 · ADR-003)

规则:私有表每张带 user_id / owner_id;共享表不带(它们是跨用户的真实世界实体 / 词表)。

类别 是否带 user_id
共享(跨用户真实实体 / 词表) user(账户)、personorganizationoccupationskillskill_aliaslocation ❌ 不带
私有(「我」的活动与状态) conversationchat_messageeventfactapplicationinterviewexperienceexperience_sourceuser_skilldocument_versiondocument_version_experienceresume_assetnotememory ✅ 带 user_id

待确认(Q11)job_posting 是真实招聘实体,是否归入共享表(多人可投同一 posting)?倾向:是,但其 application 仍私有。等待用户确认后写入 ADR-003。

6.5 对现有 plan 的修正

  • 推翻 plan 中「Event = 派生时间轴投影」的表述 → 改为「Event = 不可变行为真相,与 Fact 并列」。
  • 新增 fact 为领域无关的状态真相表(带 valid_from/valid_to、source_event_id、proficiency)。
  • user_skill 定位从「真相」调整为「fact 中技能声明的查询投影」。
  • 现有 position 表重命名为 job_posting(见 §3 C1)。

7. 已决模型 · 第二轮拷问结论(Q5–Q11)

7.1 Q5 · 上下文管理(🟢 已决 · ADR-005)

管家类 App 的最佳形态:常驻单 Session + AI 自动分段(segment)+ 人工干预

  • thread 永不变:每用户一个常驻 chat_thread,不做严格 Session 隔离(太割裂)。
  • thread 内切成语义 segments:AI 检测主题漂移自动开新 segment;用户可手动重命名 / 合并 / 归档。
  • 上下文组装 = 混合模式:每次请求时,拼装 (相关 segment 的摘要) + (当前 segment 最近 N 轮) + (检索到的相关 fact/event)。 不纯手动(太累),也不无限常驻(token 爆炸)。这是「带分段的时间线」。
  • 落到表
  • chat_thread:每用户一个常驻,无频繁切换。
  • chat_segment(新增):id, thread_id, title, started_at, ended_at(nullable), status(active|archived|merged), summary_md, user_id
  • chat_message.thread_id / segment_id 外键化(现状已有 thread_id)。
  • context-assembly 服务:输入一条新消息 → 召回相关 segment 摘要 + 当前 segment 近 N 轮 + 向量/BM25 召回 fact/event → 组装成 LLM context。压缩/摘要按 segment 粒度进行,不丢历史原话。

7.2 Q6 · 简历溯源(🟢 已决 · ADR-006)

折中方案(非每句硬绑、非完全不溯源):

  1. 结构化 fact 是唯一真相源
  2. 简历 section 软引用 每个 fact(多对多 link 表,不强制每句死绑一个源)。
  3. 渲染时强制 footnote:给用户返回的简历,每处内容附来源标注。
  4. 校验时阻断无源内容:validator 拦截没有任何 source 的 bullet,不通过。
  5. 允许 polishing:按目标岗位要求做润色,但必须基于 fact(不凭空生成能力)。

落到表: - document_version_fact(新增 link):id, document_version_id, source_type(fact|experience), source_id, section, bullet_text。 - 渲染层追加 footnote([src: fact:xxx]);校验层设 require_source=True 闸门。 - 呼应愿景「每个结论要能回答凭什么这么说」。

7.3 Q7 · Location(🟢 已决 · ADR-007)

  • location 实体:id, slug, name, country, city, district, admin_code, ...(结构化字段,可对接地图检索)。
  • location_aliascanonical_location_id, alias_slug, source(agent|manual)
  • 香港 / 香港特区 / Hong Kong / HK → 同一 location 行,别名入 location_alias
  • job_posting.location_id 改为 FK 指向 location(替代原字符串字段)。
  • 与 Q3 一致:location / location_alias共享表(不带 user_id)。

7.4 Q9 · 编排框架(🟢 已决 · ADR-004,重要转向)

采用 LangGraph(Python)作为主编排骨架,自研一层极薄的 Agent Runtime 封装作为接口;不采用 OpenAI Agent SDK

用户理由:需要的是有状态、可中断、可审计、多 Workflow 共存、可绑 PostgreSQL 的「个人 OS」,不是全自动工具调用 Agent。LangGraph 原生提供 Postgres checkpointer(持久可恢复)+ interrupt/resume(解析后等用户确认再落库),契合工作流 (a)(b)(c)。

关键约束(架构师补充,必须写进 ADR-004): - LangGraph 只做编排层:图定义 workflow、拥有 checkpoint 与 HITL 中断;每个 graph 节点在需落库时调用现有 ActionExecutor。 - 执行层复用现有 Action 原语:保持「Action 是唯一落库入口」「dry_run/commit 语义」「Fake-provider 可测」「模型无关」四条铁律不被破坏。即「LangGraph 管状态与中断,Action 管写库」。 - 测试兼容:Agent Runtime 封装须抽象 graph,使测试可用 LangGraph in-memory checkpointer + 现有 SQLite 内存库跑通,不依赖真实 PG。 - 对现有 plan 的修正(必须重做实现计划):推翻 plan「沿用声明式 Skill+Action 作为唯一编排」的表述 → 改为「LangGraph 编排 + Action 执行」双层。tech-stack 与目录结构需调整:新增 alfred/runtime/(LangGraph graph 定义 + checkpointer 绑定 + 薄 Runtime 接口);skills/ 的 YAML manifest 退化为 node/Action 配置或保留为 Action 定义。现有 ingest_request / dry_run / commit_ingest 机制保留并下沉为 LangGraph 节点的执行单元,而非被替换。

7.5 Q10 · 领域无关内核边界(🟢 已决 · ADR-002/008)

结合 Q4 的并列真相模型,明确内核与领域包的边界:

  • 领域无关内核(通用,V3 管家直接复用)event(行为) / fact(状态) / person / organization / occupation / skill / skill_alias / location / chat_thread / chat_segment / chat_message / memory / reminder / nudge / nudge_rule
  • 求职领域包(V1 专属,未来可平行加健康包/记账包)job_posting / application / interview / experience / experience_source / user_skill / document_version / document_version_experience / document_version_fact / resume_asset
  • 原则:内核表不带求职专有字段;新增领域时不改内核,只加领域包表并通过 fact.kind / event.type 的枚举扩展接入。这保证了 V3「同一内核 + 领域包」的演进路径。

7.6 Q11 · job_posting 共享性(🟢 已决 · ADR-003)

job_posting 是真实招聘实体,归入共享表(多人可投同一 posting,不重复建行);但其 application(我的投递)仍私有、带 user_idorganization / occupation / location 同理为共享。


8. 已决模型 · 第三轮拷问结论(Q8 / M2)

8.1 Q8 · Organization 泛化(🟢 已决 · ADR-009)

现有 company 装不下学校/社团/NGO/政府/基金/开源办公室等。本轮泛化:

  • company重命名为 organization,新增 kind 枚举:company / school / club / ngo / government / fund / open_source_office / other
  • 覆盖:港大/中大(school)、红十字会/入境处(ngo/government)、社团(club)等。
  • 引用它的 5 个外键(position→job_posting、experience、person.employer 等)同步改指向 organization
  • 与 Q3 一致:organization共享表(不带 user_id)。

8.2 M2 · 提醒投递账本(🟢 已决 · 部分本轮)

  • 本轮建nudge_rule(规则定义)+ nudge(生成的提醒实例)+ 定时扫描器。与用户手设的 reminder 分表不混
  • 本轮 defernotification_channel / notification_delivery 账本不建,但 nudge 预留 channel 字段(枚举:email / digest / miniprogram)保证未来可扩展。
  • 推送主通道后端邮件 + 每日聚合一条,绕开小程序一次性订阅额度稀缺问题;不强制在小程序内做推送。
  • 理由:提醒规则本身(何时投递、何时 follow-up)是 V1 核心价值,必须本轮有;而额度记账是「接小程序推送时」才需要的工程细节,先留接口即可。