ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Friend 会议回执统一化(Meeting Receipt Unification)实施规范:从五处判定到单一持久化权威

Friend 会议回执统一化(Meeting Receipt Unification)实施规范:从五处判定到单一持久化权威 Friend 会议回执统一化Meeting Receipt Unification实施规范从五处判定到单一持久化权威【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend本篇技术指南完整解读 Friendomi项目中一份真实的实施规范《Meeting receipt unification — implementation spec》docs/archive/SPEC-meeting-receipt.md。该规范源于一次摘要正确但 Chat 中会议笔记卡片丢失的生产事故核心方案是把是否应生成会议笔记从五个调用点各自重新计算的事件改造成由 finalization-job 记录持久化、由自愈 reconciler 兜底、客户端停止决策的单一权威。读完本文你将掌握会议回执meeting receipt的字段模型、幂等写入事务、十分钟自愈扫描的查询条件、功能开关的部署声明要求以及对应的单元测试与回归验收标准——文中所有结论均可回溯到当前仓库源码。一、事故现场一份正确摘要却少了 Chat 里的会议笔记卡片2026-08-19 发生了一场 28 分 40 秒的 Google Meet 通话conversation06542b24-ddfe-5dfb-810a-4c57ff407b9815:00:52–15:29:32 UTC系统产出了正确的会议摘要但Chat 中没有生成会议笔记已就绪卡片。规范开篇强调以下均为已实测的事实而非推测检测链路正常本地transcription_sessions显示 session 1587 在meeting_started15:00:52关闭session 1588 以conversationRolemeeting运行至 15:29:32 并在meeting_ended关闭策略为local_segments。后端记录完整conversation 记录携带conversation_rolemeeting、conversation_finalization_reasonmeeting_ended、sourcedesktop、statuscompleted。资格判定通过对真实记录运行is_meeting_treatment_eligible返回True其中duration_s1720.0门槛 ≥300sdedup_speech_s1719.8门槛 ≥60s。笔记本身质量良好structured.title/overview/action_items以及完整的 sectioned app 结果均存在。Chat 只收到通知日志行David and User Explore Hardware Startup Collaboration: Ship a device for testingidturn_8304880b…没有conversationLink回执。同日两次更早的会议确实收到了回执turn_cfi_24f6f918…与turn_cfi_11174c89…渲染为Meeting notes ready - title且两者写于同一时刻 03:30:35.690/.691。规范中给出的排除项同样值得记录它们是排查思路的负面清单被排除的假设排除依据晚加入/错过检测边沿MeetingDetector从第一次探测即触发 ON 边沿且轮换确实发生了≥5min / ≥60s 阈值实测完全达标客户端唤醒门控策略为local_segmentswaitForFinalizationProjectionIfNeeded对非.cloudReconcile立即返回 true完成通知的触发证明该路径已执行旧客户端严格块解码ChatContentBlockCodec解码宽松会忽略未知 keyv1 物化抑制作为永久原因客户端优先尝试 v2仅在 404 时回退 v1且 v1 有意让conversationLinkintents 保持 pending 而非消费值得一提的时序细节同日 03:30 收到回执的两场会议分别只有1m24s 和 1m52s——低于 ≥5min 门槛说明pre-gate旧逻辑在 03:30 仍生效而post-gate新逻辑在 15:29 已生效桌面端运行的是稳定版com.omi.computer-macos0.12.1878 月 18 日 12:45 安装早于e5be633fa7#118328 月 18 日 19:25和4b4745b689#118368 月 18 日 22:37。从现有数据无法判定究竟是哪一环丢掉了回执——这正是本规范要解决的缺陷而非调查缺口。二、统一诊断回执是一次性事件却没有被记录规范的诊断极其凝练会议回执meeting receipt本质是一个事件由当时恰好持有状态的组件发射一次而没有任何东西记录它是否真的发生了。具体而言is_meeting_treatment_eligible曾被在五个调用点、针对四个不同的内存快照、在四个不同的生命周期时刻重新计算backend/routers/developer.py:1479backend/routers/developer.py:1676backend/utils/conversations/process_conversation.py:1719backend/utils/conversations/finalizer.py:173backend/utils/task_intelligence/proactive_engine.py:380而没有任何一处持久化判定结果。此前 #11836 正是这个缝隙里出现的一个 bug——某个快照在 finalization 完成前作答返回了默认值false当时只修复了其中一个调用方本次事故是同一类缺陷的第二次爆发。这是典型的多权威multi-authority反模式判定没有唯一出处任何一处时序错位都会造成正确摘要 缺失回执的静默故障。三、修复总览算一次、存下来、所有人读、缺失自愈修复设计遵循四条铁律判定只算一次在 finalization 时计算 verdict连同输入duration/speech一起持久化其余全部改为读取所有消费方读取存储值而不是重新计算未物化的回执成为可自愈条件缺失的 intent 由周期性 reconciler 重新驱动而不是永久沉默客户端停止决策Chat 到达改为后端所有、基于拉取消除版本错配风险。从当前仓库源码看该设计已经落地为四个组件详见下文各节并有配套的测试、部署开关与运维文档。四、组件 1finalization-job 会议回执——持久化的后端权威记录4.1 关键设计决策与最初调研方案的偏离规范明确列出了三处对最初调研设计的偏离这些决策直接决定了实现的形状不新增第二个meeting_receipt集合扩展现有的conversation_finalization_jobs记录它已拥有 lease-fenced租约围栏终端转换与meeting_treatment_eligible字段local_segments路径写入同样的确定性 completed-job 形状。conversation 文档只携带读投影job 记录才是可审计的权威。新增一个窄的 receipt 记录服务因为现有 post-finalization 流程没有同时覆盖 Cloud Tasks 与from-segments两个终端的公共扼流点process_conversation虽被共享但已完成的 replay 可能跳过它所以从两个终端所有者各调用一次该服务。其确定性的 job 身份 create-if-absent 事务使得回执即使跨重试也只是一次逻辑写入。materialized_at语义收敛它表示桌面内核的确认而非 intent 持久化。重新驱动已持久化的 intent 无法让已发布的 v1 客户端渲染conversationLinkv1 故意排除该块且后端不是可见 Chat 的写入者因此单独跟踪intent_persisted_at只对缺失的 intent做对账v2 客户端稍后自行拉取并确认 pending 行。复用五分钟 sweep回执修复运行在现有的 listen-finalization 五分钟扫描中不新增调度器或服务。成本门不再充当权威预摘要的 meeting-context 成本门仅保留窄的 duration/speech 预览因为最终discarded判定此时尚不存在它不再调用最终 treatment-policy 函数最终 job 回执仍是唯一持久化的判定。4.2 回执字段模型无论判定如何都写一次对每个conversation_rolemeeting的桌面对话在 finalization 时恰好写入一次且无论判定结果如何finalization_job_id, conversation_id, uid eligible: bool reason: eligible | too_short | insufficient_speech | rotation | discarded | not_desktop_meeting duration_s, dedup_speech_s # 输入保证判定可审计 intent_id: str | null intent_persisted_at: datetime | null materialized_at: datetime | null created_at, updated_at两条规则是设计核心它是 eligibility 判定的唯一写入者上文五个调用点全部退化为对存储值的读取is_meeting_treatment_eligible保留为纯策略函数但只剩一个调用者。reason必填没有卡片的会议必须能仅凭数据解释原因杜绝无法归因的静默失败。4.3 源码印证当前仓库中判定策略位于 backend/utils/conversations/meeting_treatment.py其常量与 reason 枚举与规范完全一致MIN_MEETING_DURATION_SECONDS 5 * 60、MIN_TRANSCRIBED_SPEECH_SECONDS 60对应 ≥5min / ≥60s 双门槛MeetingTreatmentReason字面量类型恰好是文档列出的六种eligible / too_short / insufficient_speech / rotation / discarded / not_desktop_meetingmeeting_treatment_verdict()返回 NamedTupleMeetingTreatmentVerdict(eligible, reason, duration_s, dedup_speech_s)即判定 输入的完整可审计组合is_meeting_treatment_eligible现在只是对该 verdict 的兼容包装return meeting_treatment_verdict(conversation).eligible值得注意的细节deduplicated_transcribed_speech_seconds()对转录区间做区间并集interval union而非简单求和——因为桌面端会同时经麦克风与系统音频采集远端语音常产生起始时间相同的孪生片段直接求和会重复计数同一段语音。回执的持久化实现在 backend/database/conversation_finalization_jobs.pyMEETING_RECEIPT_SCHEMA_VERSION 1、MEETING_RECEIPT_RECONCILE_AFTER timedelta(minutes10)对应 10 分钟自愈窗口record_meeting_receipt()通过事务调用_record_meeting_receipt_txn实现 create-if-absent 写入receipt 字段meeting_treatment_eligible、meeting_treatment_reason、meeting_duration_s、meeting_dedup_speech_s、meeting_receipt_intent_id、meeting_receipt_intent_persisted_at、meeting_receipt_materialized_at、meeting_receipt_reconcile_after_at等与规范字段一一对应并把 verdict 投影回 conversation 文档mark_meeting_receipt_intent_persisted()在事务内幂等地写入 intent id若已存在同值则返回 truemark_meeting_receipt_materialized()校验meeting_receipt_intent_id匹配后才记录桌面确认时间。上层服务位于 backend/utils/conversations/meeting_receipt.py模块 docstring 直接点明权威边界The finalization job is authoritative. Conversation fields are a read projection for API and post-processing consumers; the Chat intent is an idempotent derived artifact keyed bycapture:{conversation_id}。其中record_finalized_meeting_receipt()仅在statuscompleted、非 deferred、且为桌面会议角色时落账persist_receipt_intent()调用persist_capture_arrival_intent()生成conversationLinkintent 并回写 receipt。终端接线在 backend/utils/conversations/finalizer.py约 230–240 行在标记 durable fanout projection 完成之前调用record_and_persist_finalized_meeting_receipt注释明确解释排序理由——先落 marker 再发 intent关闭了projection 已完成但 notes-ready intent 尚不存在的小窗口同时注释强调 capture receipt、keyframes、arrival intent不属于derived intelligencefree-tier 桌面会议也必须运行它们以唤醒 Chat。五、组件 2reconciler——让缺失回执自愈5.1 扫描条件与幂等语义周期性扫描的候选条件是eligible true AND intent_id IS NULL AND receipt_created_at now - 10min命中后重新驱动persist_capture_arrival_intent再持久化其稳定的 intent id。整个过程通过既有的continuity_key fcapture:{conversation_id}保持幂等——同一 conversation 的重试不会产生重复 intent。规范明确指出这正是整套设计物有所值的部分它一举涵盖三种场景本次事故的失败无论哪一环丢失了回执观察到的repair adapter 只在幂等命中重试时才触发的既有行为——重试从此不再承重追溯修复历史已完成、但后端 intent 写入被漏掉的桌面会议。它同时明确不承诺让 v1-only 客户端渲染conversationLink该客户端按合同无法把该块解码为 Chat 行intent 会保持 pending 等待 v2。5.2 源码印证扫描实现为 backend/database/conversation_finalization_jobs.py 中的get_meeting_receipt_reconcile_candidates()查询条件与规范逐字对应meeting_treatment_eligibleTrue、meeting_receipt_intent_idNone、meeting_receipt_reconcile_after_at cutofflimit 上限 100。get_meeting_receipt_backfill_candidates()则以游标分页公平地扫描历史已完成会议用于回填没有回执的遗留桌面会议。驱动逻辑在 backend/services/conversation_finalization.pyis_meeting_receipt_reconciler_enabled()读取环境变量MEETING_RECEIPT_RECONCILER_ENABLED1/true/yes/on视为开启默认关闭reconcile_meeting_receipts()返回{repaired, backfilled, skipped, error}四类计数先修复缺失 intent 的候选行再执行历史回填扫描。调度挂接在 backend/main.py启动时经_drain_meeting_receipts()跑一次404–411 行并纳入_periodic_listen_finalization_reconcile周期任务447–452 行其默认间隔由LISTEN_FINALIZATION_RECONCILE_INTERVAL_SECONDS控制、默认300 秒——正是规范所说的复用现有五分钟 listen-finalization sweep。相关流水线说明可参见 backend/docs/listen_pusher_pipeline.mdx。六、组件 3客户端停止决策postMeetingCompletionIfReady保留通知notification功能但不再门控 Chat 到达Chat arrival。Chat 到达从此完全由后端所有、基于拉取。这一改动删除了产生 #11836 及其复发的决策缝隙消除了版本错配风险——旧客户端再也无法让一个正确的后端判定不可见顺带关闭了 Track A 工作流中Remove the deadmeetingTreatmentEligibleplumbing涉及uploadLocalSegments、finalizeCloudSession、resolveExhaustedCloudReconciliation作为结果而非额外任务。从当前仓库源码结构看后端已通过projected_meeting_treatment_eligible()见 backend/utils/conversations/meeting_receipt.py改为读取 conversation 上持久化的投影而非重新计算策略印证了客户端/调用方只读不判的方向。七、组件 4本地重复行约束本地存储中每个会议都有一条影子行backendId相同、conversationRoleambient、创建时间更晚如 1588/1591、1566/1574、1579/1581、1592/1595。规范明确该现象并非本次事故原因正常的会议也有它但shouldNotifyMeetingCompletion以conversationRole .meeting为键因此绑定到同一对话的第二行是一个潜在隐患。修复方式为backendId增加唯一性约束meeting-role 行优先。八、明确不在范围检测问题与交付问题不可混淆Tier-2 检测未知持麦进程项目已决定 telemetry-first遥测优先README 中的误报分析仍然有效基于空闲的对话轮换必须经由串行化边界路径不能成为第二个轮换权威。两者都是真实需求、也都在 Track A 表中但它们是检测detection问题而本规范处理的是交付delivery问题——强行混入会重新引入项目反复警告的多权威模式。且这两者在回执行落地后都会更安全回执给出了可衡量的成功信号。九、证明义务自动通过或视为死亡规范以自动或死亡automatic or dead的标准定义验收五条义务在仓库测试中已有对应实现单元策略每个分支的 verdict reason被记录而非重算见meeting_treatment_verdict全分支覆盖单元reconciler 对合格但无 intent的行精确一次重新驱动continuity key 成立——对应 backend/tests/services/test_conversation_finalization.py 中test_meeting_receipt_reconciler_redrives_one_missing_intent单元v1 物化保持conversationLinkpending且 v2 稍后返回并确认它回归端到端重放 2026-08-19 真实形态28m40s、rolemeeting、meeting_ended、local_segments、from-segments 路径→恰好一个conversationLink回执回填两场2026-08-19 形态、缺失回执 intent 的已完成桌面会议被修复——对应同文件中的test_meeting_receipt_backfill_repairs_two_2026_08_19_shaped_rows。十、发布与部署一个开关但必须声明在每一个 co-host 上发布只引入一个功能开关MEETING_RECEIPT_RECONCILER_ENABLED流程为逻辑测试 → 开关 → beta 上 dogfood无基准测试、无 evals、无 shadow gates遵循 2026-08-11 的既定指令。当前仓库中的开关声明位置印证了declare the flag on every co-host of the code path这条硬性要求backend/deploy/runtime_env/_base.yaml约 74–76 行MEETING_RECEIPT_RECONCILER_ENABLED: falsecategory 为rollout并出现在多个服务的 env 块中backend/charts/backend-listen/dev_omi_backend_listen_values.yaml 与 backend/charts/backend-listen/prod_omi_backend_listen_values.yaml 同样声明了该环境变量配套的 backend/scripts/verify_pusher_cohost_env_diff.py 专门校验该变量在 co-host 之间无差异。规范给出了不这样做的具体后果backend-listen和 Cloud Runbackend服务后者为POST /v1/conversations/{id}/reprocess内联运行process_conversation必须同时声明。只声明其一会造成capture 走 v2 回执、regenerate 走 legacy 路径表面上看像非确定性而非配置缺口。此失败模式在项目 README 中被命名为FC-rollout-flag-absent-from-a-cohost-of-its-code-path。十一、交付顺序与流程约束剪切列表按序组件 4本地重复行约束——完全独立可先行组件 3客户端简化——可作为后续跟进。组件 1 2 单独即可修复用户可见症状是不可约核心。流程约束基于公共上游BasedHardware/omi开分支PR 目标为main绝不直接推main在$OMI_WORKTREES下的 linked worktree 中工作使用受管的git包装器CI 门禁存在不对称性make preflight pre-push hook Swift contract job约 42 分钟、最后报告。在 Swift contract job 报告之前不得将 PR 视为绿灯。结语从事件到记录的系统性教训这份规范的价值不在于修补一次具体故障而在于把一类故障根除凡是被多方依赖、又只发射一次的事件都必须有一个持久化的权威记录和一位自愈者。回执行让每一次该有卡片却没有的会议都能仅凭数据归因reason必填让重试不再是正确性的承重墙幂等 reconciler也让检测类改进Tier-2 检测、空闲轮换有了可衡量的成功信号。对任何构建多端同步、含客户端本地状态的系统而言算一次、存下来、所有人读、缺失自愈、声明到每个 co-host这组原则都值得直接复用。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表