工作台总设计 · 附录 B · 微信数据平台盘点(normalize)

2026-09-14 库库派读 ~/wechat-data/normalize/(docs / etl / serve / views / ops)后的盘点。全程只读;未打开任何 */plain/*.db;未抄任何消息内容、姓名、wxid。
核心一句话:这是一个纯读 / 纯分析平台。源微信库神圣只读,产出库 normalize.db 是 SQLite 单文件,对外只有一个 stdlib http.server 写的只读 API。系统内不存在任何向微信发消息的通路。
A

实体清单

层级(docs/schema-doc.md + etl/run_etl_full.py):Layer 1 = 每次全量 rebuild 被 DROP 重建;Layer 2 = AI 建议层受保护禁 drop;Layer 3 = 运营表整表 carry。命名规范(docs/schema-naming.md):_v1 基表 / _f_v1 过滤视图 / _d_v1 反范式视图 / _safe_v1 投影视图 / _final_v1 裁决视图。

A1 身份与会话骨架(Layer 1)

实体类型关键列来源已知问题
party_v1基表global_party_id(PK, sha256(wxid)[:16])、raw_wxid_hidden(PII)、display_namedisplay_statusdisplay_sourceremark_latestnick_name_latestfirst_seen_tsetl/build_party.py三个 Restricted PII 列禁止进业务视图
sender_account_v1基表(global_party_id,account,shard) PK、sender_id_by_shardmsg_count、per-account remark/nickETL含 PII,隐私规范明令业务视图不得引用;但 workbench_customer_card_v1 的 R6-49 版本 JOIN 了它
message_v1基表PK(account,shard,thread_id,msg_local_id);sort_seq/server_seq/ts_utc/ts_jst_datesender_global_party_idsender_display_name_snapshotsender_role_snapshotdirectionlocal_typestatusmessage_contentcontent_statusmedia_typemedia_pathquoted_message_idrecalled_message_idvoice_text、lineage 四列etl/extract_messages.py + enrich_bronze.pymessage_content/voice_text = Restricted;image_ocr_text 需下轮 rebuild 才回填
msg_normalized_v1_cache物化缓存与 message_v1 同构etl/populate_cache.pyconversation_v1 走 message_v1 不走 cache
thread_v1基表PK(account,shard,thread_id)、thread_type(private/chatroom/openim/self_only)、counterparty_global_party_idowner_global_party_idchat_room_usernamefirst/last_msg_tsmsg_countetl/derive_threads.pycounterparty 语义见 docs/counterparty-canonical-v1.md:"global" 只指 wxid 假名,不是跨号合人;daemon 增量新 private 线程曾永久 NULL(50 条)
thread_membership_v1 / thread_speaker_v1基表当前成员 / 历史发言者etl/build_thread_membership.py
thread_facts_v1派生基表msg_count_7d/30d/90dlast_msg_tslast_customer_msg_tslast_employee_reply_tsdays_since_last_employee_replyhas_unanswered_customer_msgfirst_response_secondssla_compliant + V2 增列 last_customer_msg_contentlast_employee_reply_contentetl/compute_lifecycle.py + compute_sla.py工作台队列的真正心脏;两个 *_content 列由 ALTER 动态加,不在 schema-doc 里
role_assignment_v1基表role_finalrole_suggestedconfidencesource_layerfactors_jsonis_locked/locked_by/locked_atsuperseded_atetl/classify_roles.pyLayer3 vendor 恒 conf=0.85、Layer4 不送审 → audit_queue_v1 无自然入口
audit_queue_v1基表灰区人审队列classify名存实亡(无入口)
avatar_asset_v1基表头像 URL/缓存/状态avatar_daemon3 条毒循环 42+ 天
account_lineage_v1基表accountstatusowner_wxid_canonicalsupersedes/superseded_bylive_since_tsfrozen_at_tsetl/seed_account_lineage.py
remark_avatar_change_log_v1日志只存 old/new 哈希ETLcarry-whole
normalize_run_v1 / normalize_manifest_v1 / dq_result_v1基表run/build 元数据、DQ 结果(7 项检查)etl/dq_runner.py
event_log_v1 / subscriber_v1 / delivery_attempt_v1基表webhook 事件总线:8 种事件含 customer_unanswereddq_alertrole_changeserve/events.py出站 webhook(HMAC),不是微信通路
api_token_v1基表nametoken_hashscope CHECK(read/read_staff/admin)、activelast_used_atemployee_idscope_jsonr6_20scope_json.account_filter 有解析函数但路由层未接线
bronze_message_raw_v1基表decoder 对齐冻结 schemar6_50尚未进生产库

A2 客户业务层

实体类型说明
lx_customer_profile_v1基表customer_tier(VVIP/CIP/VIP/普通)、tier_sourcetrip_start/end_datedestinationhotelagencyparse_status;由 etl/parse_tiers.py 从备注文本解析
lx_customer_lifecycle_v1基表lead_age_daysis_active_leadis_trip_in_progressis_post_tripis_repeat_customerlast_customer_msg_tslast_employee_reply_tsdays_since_last_employee_reply;derived_daemon 每 30 min
customer_v1视图party + role 过滤 customer;列里没有 role_current
customer_global_v1视图同上 + role_current/ts_created_utc/ts_updated_utc
customer_f_v1视图party JOIN role_current WHERE role='customer'
customer_dashboard_d_v1 / lx_customer_dashboard_v1视图party+profile+lifecycle+avatar 反范式;工作台 customer_card 实际读的是 lx_customer_dashboard_v1
customer_api_safe_v1视图6 列安全投影。已悬空(见 G)
workbench_customer_card_v1视图工作台客户卡。已悬空
customer_tier_suggestion_v1 / customer_tier_human_override_v1Layer2 基表复合键 (account_id,customer_wxid,valid_from_ts_utc),双时态
customer_tier_final_v1视图COALESCE(override, suggestion) + final_source in (human/ai)
customer_notes_v1Layer3 基表note_typevisibility(默认 staff_only)、bodyauthor_employee_id无任何 API 写入口
customer_relationships_v1Layer3 基表party_a/party_b/relationship_type/source/confidence
deleted_party_tombstone_v1Layer3 基表serve/delete_customer.py 按需建,不在 R6 migration 清单里——G 项悬空的根因

A3 行程 / 任务 / SLA(Layer 3,全部 carry)

A4 AI 层(Layer 2,六表受保护)

实体说明谁在写
message_reply_suggestion_v1suggestion_idaccountthread_idreply_to_msg_sort_seqsuggested_reply_textsuggested_reply_type(text/template/quick_reply)、confidencereasoningstatus(pending/accepted/rejected/expired)、reviewed_by全仓无任何 INSERT。生产者不存在
ai_artifact_v1artifact_type/source_type/source_id/content_text/content_json/model_version/confidence + 双时态无写入者
ai_action_log_v1action_type/target_type/target_id/action_detail_json/result_status/error_message无写入者
thread_agent_assignment_v1assigned_agent_type(human / ai_auto / ai_assisted / none)、assigned_agent_idassignment_reason、双时态无写入者

A5 治理 / 隐私 / DQ

A6 请求中提到但不存在的实体

customer_intent_v1data_health_v1、除 lx_view_sales_response_v1 外的 role_*_v1employee_*_v1(除 employee_v1 / employee_binding_v1)全仓零命中。设计工作台时不要假设它们存在。

A7 销售队列视图族(关键风险

视图引用处DDL 位置
lx_view_b1_customer_profileserve/queries.py:410全仓无 DDL
lx_view_b3_sales_queueserve/queries.py:450全仓无 DDL
lx_view_s2_followup_slaserve/queries.py:495, 525全仓无 DDL
lx_view_sales_response_v1仅 SQL 文件自身views/lx_view_sales_response_v1.sql不在 R6_MIGRATIONS 清单内,ETL 不自动应用
internal_brand_account_v1file_ocrparity_gate CARRY_EXACT无 DDL,靠 _carry_log_layers() 从 prod sqlite_master 动态复刻
lx_customer_employee_v1account_shift_window_v1lx_view_sales_response_v1.sql:138,179无 DDL

/v1/lx/* 四个端点依赖的三个视图,定义只存在于活库里(或根本不存在),代码仓无法复现。工作台立项前必须先考古的第一件事。

B

API 清单

B1 openapi.yaml 的 12 个端点(全部 GET)

#端点参数响应scope
1GET /v1/health{ok, integrity_check, msg_count, last_msg_ts} + per-account 新鲜度read
2GET /v1/health/deep{ok, integrity_check, foreign_key_violations}read_staff
3GET /v1/accounts账号谱系列表read
4GET /v1/customerstieractive_onlyaccountlimit(50)、offset分页客户列表read
5GET /v1/customers/{id}path客户详情 / 404read
6GET /v1/threads/unansweredmin_days(1)、accountlimit未回线程read
7GET /v1/threads/{account}/{thread_id}/messageslimitbefore_ts按时间正序消息read
8GET /v1/threads/{account}/{thread_id}/workbenchinclude(默认 messages,customer_card,trip,tasks,suggestions,artifacts)、msg_limit复合对象read_staff
9GET /v1/media/avatar/{party_id}pathJPEG 或占位 SVGread_staff
10GET /v1/media/{account}/{thread_id}/{message_id}path媒体文件read_staff
11GET /v1/artifacts/{artifact_id}pathartifact 内容read_staff
12GET /v1/reports/weekly/{week}format=json/md周报read

B2 未记录但代码里真实存在的 6 条(serve/api.py:86-130

GET /v1/openapi.yaml · GET /v1/lx/customer_profile(读 lx_view_b1_customer_profile)· GET /v1/lx/sales_queue(读 lx_view_b3_sales_queue)· GET /v1/lx/unanswered(读 lx_view_s2_followup_sla,按 minutes_waiting DESC)· GET /v1/lx/unanswered_by_employee · DELETE /v1/customers/{id}(admin,12 张表级联删除 + tombstone)。POST /v1/tokens 出现在文档里,但 do_POST 无条件 405——实际不存在。

B3 servers / hosts —— 三个端口互相矛盾

openapi 写 127.0.0.1:8765 + https://workbench.luxingtravel.comapi.py 默认 8765;实际 launchd 是 --port 9000,且未传 --require-auth;round6 文档写 8023。两点要害:生产 API 进程没开鉴权require_auth=False 时整条 do_GET 不校验 token,read_staff 形同虚设,DELETE 例外);只绑 127.0.0.1,无 HTTPS、无对外监听,仓里没有 nginx/反代配置。

B4 写接口盘点

DELETE /v1/customers/{id} 外,全平台零写端点。 没有 notes 写入、task 创建/流转、assignment 变更、suggestion 采纳/驳回、回复发送。鉴权:Bearer token sha256 存 hash,三档 SCOPE_RANK = {read:1, read_staff:2, admin:3};CORS 白名单 localhost:5173 / :3000 / workbench.luxingtravel.com;错误格式统一 {error, code}

C

工作台逻辑

C1 销售队列怎么算(三条并存、互不统一)

(a) thread_facts_v1.has_unanswered_customer_msg——最底层事实位,derived_daemon 每 1800s 刷;/v1/threads/unanswered 走它。 (b) /v1/lx/* 三兄弟——读三个无 DDLlx_view_*,排序 has_unanswered DESC, last_customer_msg_ts DESC / minutes_waiting DESCassigned_employee_gpi + assignee_confidence 说明视图内已做员工归属推断。 (c) lx_view_sales_response_v1(星星 spec + 阿麦实施,粒度 = account × customer_gpi)——没有任何 API 消费它,但业务逻辑最完整:wait_during_working_hours_min(14 天生成器 × account_shift_window_v1 班次窗口,只累计工作时段等待);is_closing_phrase(约 40 项收尾语白名单);is_explicit_closure(8 条流失措辞);follow_up_bucket 七态noiseclosedno_actionurgent(VVIP 10 min / CIP 15 / VIP 15 / 其他 30)→ elegant_24helegant_48hcold_lead(>7d)→ monitoring。 注意这套 tier 阈值(分钟)与 sla_v1 种子(秒)是两套独立数字,工作台要先统一。

C2 客户卡怎么组装(serve/workbench.py

get_workbench()include 拼 6 段:customer_cardlx_customer_dashboard_v1triptrip_v1(confirmed/booked/in_progress 最新 1 条);taskstask_v1(open/in_progress);suggestionsmessage_reply_suggestion_v1 全量倒序(不过滤 status);artifactsai_artifact_v1(valid_to IS NULL);messagesmessage_v1 倒查后正序。 workbench_customer_card_v1 视图(r6_49)输出 tierlifecycle_stageactive_trip_idopen_tasks_counttotal_tripstotal_revenue_cny、常量 leak_guard_marker='⛔CROSS_ACCOUNT_DATA_FORBIDDEN',配套 assert_account_scope.py 防跨号泄漏。但当前没有任何代码读它。

C3 回复建议怎么生成

没有生成。 建表、carry、读取、单测都有,零 INSERT。docs/HANDOFF.md:530:"LLM classifier not implemented";round6 把 "Real LLM integration" 列为 R7+ 候选。表结构为将来预留(model_versionconfidencereasoningreviewed_bystatus 四态)。

C4 workbench.luxingtravel.com 是什么

尚不存在的前端域名占位符。只出现在 openapi servers、CORS 白名单、契约文档。仓内无任何前端代码。round6 R7+ 候选首条:"Web frontend — deferred to AI OS + real dev team per Ling msg 224"。

D

能否回写微信

不能。全平台不存在任何向微信发送消息的机制。 grep send_message|send_reply|自动回复|wechaty|itchat|appium|osascript.*WeChat|模拟点击 仅命中 2 处内部字段回填。文档明文:源库 ?mode=roserve/api.py:234 POST not supported on read API。 唯一与"回复"沾边的红线是 docs/counterparty-canonical-v1.md §2,约束的是上下文组装thread_v1.counterparty_global_party_id 是唯一可进对客回复组装的身份层;跨号合成画像 internal_person_overlay_v1 必须零 FK/零 view 通向 reply 组装链路;reply 组装只许 JOIN ON (account, party_id) 双键。 结论:若工作台要做"一键回复",发送侧需要一个当前完全不存在的新组件,并且必须先过 counterparty 双锁与内阁红线。

E

治理与人员

Lane(2026-07-07 Ling 裁):拓拓 arch 架构裁决 · 阿麦 impl / 生产写唯一执行人(lx01 单写者)· 星星 业务判决 / 回归 / human_review 签名 / lx_view_sales_response_v1 spec 作者 / 内阁 LXCEO · 锐锐 on-call forensic · Ling 创始人 founder hard 10 条 + 最终裁决。 内阁(cabinet)docs/cabinet_briefing_sop.md——每个 OMO 任务以"内阁简报包"开场:founder hard 10 条、客户 tier 锁死 4 档(VVIP/CIP/VIP/普通,无 UHNW)、4 账号身份(dmd/luxingtravel/zhenguo LIVE,lux 历史,no silent merge)、PII(wxid 不得进业务视图)、升级矩阵、trust-but-verify、产物闸。 守门:parity_gate(判据 1-11,prod-ref 必须静态 .bak)· employee parity guard(seed 真源 FINAL103,变更=星星签→拓拓 ratify→阿麦执行)· human_review 人工终审(is_locked=1 活过任何 refresh)· freshness monitor · enforce_display_contract(扫 7 个业务视图 TEXT 列的 wxid_%/@chatroom)。 freeze flagsetl/FREEZE-NO-FULL-REFRESH-BEFORE-D3a.md 存在则拒跑全量;unfreeze-precheck.sh 四件全真才放行。 核心原则:full-diff > enumerate-known | staff/客户身份 = business fact | 红线靠结构守非纪律守 | guard 必 fail-loud | 承重写 = 备份→执行→验证 + owner 双闸。

F

数据规模与账号

账号 4 个:dmdlux(已退役,并入 luxingtravel)、luxingtravelzhenguo;LIVE 三个。硬约束 lux ≠ luxingtravel,禁止静默跨号合并。 体量(2026-07-07):message_v1 3.29M · party_v1 38.7K · thread_v1 48K · customer 35.9K / employee 103 / vendor 180+ · file_ocr 287K · voice 41K+。 ETL:源(plain decode·只读) → bronze → normalize.db → enrich.db → serving;全量 11 stage 原子 swap;R6 migration 26 个 SQL;carry 三件套。 节奏(ops/launchd/ 7 个服务):etl_incremental_daemon 常驻 3s 轮询;derived_daemon 30 min;avatar_daemon 常驻;etl_full 周日 02:00;backup 每日 03:15(异地 LG01JP);logrotate 04:00;dq_weekly 周一 05:00;api KeepAlive。 备份纪律:活库禁止裸 copy,唯一正确 sqlite3 normalize.db ".backup"

G

已知债务与坑

P1 业务面正确性

  1. 悬空视图族(最高优先)customer_api_safe_v1workbench_customer_card_v1 的 WHERE 结尾引用 deleted_party_tombstone_v1,该表不在 migration 清单里,新建/rebuild 的库一查即炸。消费面已绕行(workbench 读 lx_customer_dashboard_v1),所以线上没炸,但两个"官方"视图是死代码。修法 = 重建视图去 tombstone 引用。
    附加坑(本次新发现)/v1/lx/* 依赖的 lx_view_b1/b3/s2 在代码仓完全没有 DDLlx_customer_employee_v1account_shift_window_v1 也无 DDL。一次全量 rebuild 后 /v1/lx/*lx_view_sales_response_v1 会集体消失。
  2. 灰区机制死路:audit_queue_v1 无自然入口。
  3. _merge_ocr serving 联动:image_ocr_text 要下次 rebuild 才回填。

P2 管线健康

  1. avatar_daemon 毒循环(patch 已备候点头)。5. S2b 周期慢 cycle:真凶是 06-23 起 lx01 机器级 I/O 退化(放大器候选 opencode 820% CPU、llama-server 常驻 ~26GB),contact-delete detector HOLD 87 轮、659 待裁删除积压。6. updated_at 命名违规。7. classify 双实现。8. 活库前提型测试污染单元套件。

P3 与 sweep

9–13:daemon 冷扫优化、备份命名、媒体回填 23.88%→80%、附件回填、/tmp 依赖。S1 LOCKED_GPIDS_7 死常量;S2 硬码账号清单 ≥6 处(加第 4 个号漏改任何一处 = 静默不入库零报错);S3 CARRY_EXACT 只查行数。 其他:employee_binding_v1 列漂移;pii_columns_v1 7 条指向不存在的列;log_pii_access() / resolve_account_filter() 零调用;docs/schema-doc.md 已 stale(5-14 生成)。

H

隐私规范要点(V0.1,Accepted 2026-05-12)

核心:raw wxid 永不出现在业务视图;每个人用假名 global_party_id;显示名 remark > nick_name > 兜底"未命名客户(首次添加 YYYY-MM-DD)"。 Restricted PII:party_v1.raw_wxid_hidden/remark_latest/nick_name_latestsender_account_v1.remark_in_account/nick_name_in_accountmessage_v1.message_content/voice_text。 角色矩阵:Business user 只读业务视图;Dashboard app 只读子集;ETL 读写内部表、源库只读;Developer 只读合成数据;DBA 只读生产。 七个业务视图不得含 raw_wxid_hiddenencrypt_usernamesender_id_by_shardchat_room_username外送模型:spec 里没有任何一条授权数据出给外部 LLM;§4.3 业务仪表盘只消费聚合指标不消费原文;今天事实上零外送。→ Ling 9-14 已裁"聊天原文可送 DeepSeek 或其他 agent",规范要同步补一条。 审计:normalize_run_v1audit_queue_v1、变更日志只存哈希、显示契约巡检;R6 补 pii_access_log_v1 + pii_columns_v1 但未接线。合规闸(§8)发版前全自动测试。

给工作台设计的五条结论

  1. 写侧必须新建:notes / tasks / assignment / suggestion 采纳全部只有表没有接口。
  2. 发送侧完全不存在,且受 counterparty 双锁红线约束,属需要 Ling 裁决的新 scope。
  3. 先做视图考古lx_view_b1/b3/s2 无 DDL、两个官方视图悬空——数据底座一半不可复现。
  4. 鉴权是纸面的:生产 API 未开 --require-authpii_access_log_v1 有表无调用。
  5. 两套 SLA 数字要统一sla_v1(秒)vs lx_view_sales_response_v1(分钟)。