akg-factor-bridge/docs/第一批联调验证清单_2026-07-26.md

17 KiB
Raw Blame History

第一批 · 联调验证清单v22026-07-26

本清单取代 第一批验证清单_2026-07-26.md(那份把「基座机器」和「桥机器」混为一谈, 第 1 步的 docker exec -i akg-postgres psql 在桥的服务器上根本不存在这个容器)。

这一批跨 三个工程 / 至少两台机器,所以每一步都标注了「在哪台机器、哪个工程、 哪个容器里执行」,以及判定标准、失败去哪、怎么回滚。不要跨节跳着做—— 节与节之间有依赖顺序。


§0 这一批到底在联调什么

工程 本批是否改动 改了什么 谁消费
astock-kg基座 只改 DB 里的 4 个视图 + 1 个可空列 + 1 个索引,代码零改动 sql/astock_kg_slot_views.sql文件在桥仓库DDL 落在基座库) 只有桥
akg-factor-bridge 代码全批 四路口径 / 事件过滤 / 分块 / 冻结 / probe corr ——
quant_factor_service平台 无改动 桥往 t_factor_akg_* + factor_metadata 写行 平台评价与下游

耦合面只有一个:桥代码 ↔ 基座库里那 4 个视图。其余都是各自内部的事。


§1 拓扑登记(请你填,我不知道,填完发我一份,后面所有命令按它对号入座)

角色 主机 / 用户 容器名 本批需要的权限 已确认可达?
基座 PG ______ akg-postgres DDL(建视图/加列/建索引)
基座 backend/beat 同上 akg-backend / beat 本批不动 ——
factor@factorevaluation:~/project/akg-factor-bridge akg_factor_bridge 读基座 PG / 读 153 / 写平台 MySQL
153 代理(热度) ______ —— 只读
平台因子库 + gp_day_data ______ —— t_factor_akg_*factor_metadata
开发机 baobao@…/Documents/work/project/ —— git 源头 ——

关于 DDL 账号:基座 compose 里 POSTGRES_USER: ${PG_USER:-akg} —— 这个账号就是 库的 owner天然有 DDL 权限。如果桥 .envAKG_PG_USER 就是它§5 直接能跑; 如果你另外建了只读账号给桥用§5 需要临时用 owner 账号。


§2 已代查的前置事实(你不需要再验证,列出来是为了让你知道风险面有多大)

  1. 改这 4 个视图的影响面 = 0grep -rn "v_factor_" backend/ frontend/src 在 astock-kg 里零命中 —— 四视图目前只有桥在消费,替换它们不会波及基座任何功能。
  2. ALTER TABLE transmission_candidates ADD COLUMN 安全。全仓没有 SELECT * 打在这张表上: transmission.list_candidates 用显式列清单、INSERT 也是显式列清单、 factor_coverage_probe 只取 scan_date, quiet。加一个可空列不会撑坏任何列序假设。
  3. 不删任何数据。§5 的 6 条语句 = 4 个 CREATE OR REPLACE VIEW + 1 个 ALTER TABLE ADD COLUMN IF NOT EXISTS + 1 个 CREATE INDEX IF NOT EXISTSclaims / documents 一个字节不动,不需要 replay不需要重抽。
  4. 基座 PG 是 postgres:16.9-alpine 且端口已发布ports: "${PG_PORT:-5432}:5432" 所以桥跨机连得上§5 的一次性 psql 容器用 postgres:16-alpine 客户端版本也对得上。
  5. 基座 beat 时刻表工作日05:30 主数据 → 17:30 行情快照 → 18:00 hotspot → 18:15 传导扫描 → 周一 06:00 池刷新 → 07:00 日报。 ⇒ 桥的 daily build 必须在 18:15 之后跑,否则当日传导表是空的。

§3 版本对齐(开发机 ↔ 桥服务器,两处必须同一个 commit

# 开发机
cd ~/Documents/work/project/akg-factor-bridge && git rev-parse --short HEAD

# 桥服务器
cd ~/project/akg-factor-bridge && git rev-parse --short HEAD

判定:两个短 hash 一致 → 通过。不一致 → 开发机 git push、桥服务器 git pull 然后必须 docker compose up -d --build(代码卷挂载虽然是 .:/app,但依赖变了要重建)。

⚠️ 桥服务器上 .env不进版本库的(本批新加的 .gitignore 已排除它)。 git pull 不会动它。新旋钮全部有默认值,.env 不改也能跑。


§4 基线快照(改动前跑,用于事后证明"抽取成果没白费"

基座机器上:

docker exec -i akg-postgres psql -U akg -d akg <<'SQL'
SELECT 'claims' t, count(*) n, max(ingestion_date)::date latest FROM claims
UNION ALL SELECT 'documents', count(*), max(ingested_at)::date FROM documents
UNION ALL SELECT 'entity_links', count(*), max(created_at)::date FROM entity_links
UNION ALL SELECT 'company_master', count(*), max(synced_at)::date FROM company_master
UNION ALL SELECT 'consensus_daily', count(*), max(asof_date) FROM consensus_daily
UNION ALL SELECT 'mkt_daily', count(*), max(trade_date) FROM mkt_daily
UNION ALL SELECT 'transmission_candidates', count(*), max(scan_date) FROM transmission_candidates
UNION ALL SELECT 'industry_pools', count(*), max(refreshed_at)::date FROM industry_pools;

-- 顺带回答一个对回填边界很关键的问题mkt_daily 是持久快照表、全库无删除)
SELECT kind, min(trade_date), max(trade_date), count(DISTINCT trade_date) AS days
FROM mkt_daily GROUP BY kind ORDER BY kind;
SQL

留档把输出存下来。§8 判收时再跑一次,前 4 行必须逐字不变


§5 应用视图(本批唯一一次对基座库的写操作

按你人在哪台机器,三选一。只需要跑一次,跑在基座库上,跟你从哪台机器发起无关。

5a · 在桥服务器上,用桥自己的连接(推荐;无需 psql 客户端)

⚠️ 需要先完成 §3apply-views 是本批新增的命令,旧代码没有)。

cd ~/project/akg-factor-bridge
docker compose exec -T akg-factor-bridge python run.py apply-views --dry-run   # 先看要跑哪 6 条
docker compose exec -T akg-factor-bridge python run.py apply-views

.env 里是只读账号,会逐条报 permission denied,改用 owner 账号跑这一次:

docker compose exec -T \
  -e AKG_PG_USER=akg -e AKG_PG_PASSWORD=<基座 PG_PASSWORD> \
  akg-factor-bridge python run.py apply-views

必须用 docker compose exec -e。写成 AKG_PG_USER=x docker compose exec … 只设置宿主机上 compose 客户端进程的环境变量,传不进容器

5b · 在桥服务器上,用一次性 psql 容器§3 还没做完时的即时方案)

cd ~/project/akg-factor-bridge
set -a; . ./.env; set +a
docker run --rm -i -e PGPASSWORD="$AKG_PG_PASSWORD" postgres:16-alpine \
  psql -h "$AKG_PG_HOST" -p "${AKG_PG_PORT:-5432}" -U "$AKG_PG_USER" -d "$AKG_PG_DB" \
  -v ON_ERROR_STOP=1 < sql/astock_kg_slot_views.sql

5c · 在基座机器上(需要先把 sql/astock_kg_slot_views.sql 带过去)

docker exec -i akg-postgres psql -U akg -d akg < sql/astock_kg_slot_views.sql

判定标准(三条路一样)

6 条语句全部成功:CREATE VIEW ×4 + ALTER TABLE + CREATE INDEX,无 ERROR。 5a 会逐条打 / 并在末尾汇总,缺权限时能精确定位到是哪一条。)

回滚

# 视图退回 v1原文件在这两处任选其一
#   桥服务器: .review_backup_20260726/sql_astock_kg_slot_views.sql
#   或 git:   git show 42659b9:sql/astock_kg_slot_views.sql > /tmp/v1.sql
docker exec -i akg-postgres psql -U akg -d akg < /tmp/v1.sql
# ALTER 加的列与索引是纯增量、无害,不需要回滚;真要清:
#   ALTER TABLE transmission_candidates DROP COLUMN IF EXISTS mkt_trade_date;
#   DROP INDEX IF EXISTS idx_tc_target;

§6 兼容性矩阵(联调的核心,四个象限现在全都是安全的

n_paths 列在 v2 视图里原名原义保留(我原来改成了 n_paths_raw,那会让旧桥代码 查不到列而崩 —— 已修正)。所以两个工程可以任意顺序升级、任意一侧独立回滚:

视图 v1 视图 v2
桥 v1旧代码 原状态 照常工作(读 n_paths,旧口径)
桥 v2新代码 可跑,但退回旧口径并打 ⚠️ 告警
v_factor_transmission 还是旧版(无 n_sources
目标状态,用 n_sources

你现在处于左下格(桥已 pull 到 v2、视图还是 v1——这是合法的过渡态 不是故障。做完 §5 就进右下格。

判定右下格已达成

docker compose exec akg-factor-bridge python run.py views

看新增的「视图版本」段,前三项必须是

  ✅ v_factor_transmission.n_sources
  ✅ v_factor_transmission.mkt_trade_date
  ✅ v_factor_events.source_type
  ⬜ v_factor_segment_members第五插槽G2   ← 这一项现在就该是 ⬜

§7 桥侧功能验证

7a · 只读(不写任何库,可反复跑)

cd ~/project/akg-factor-bridge
docker compose exec akg-factor-bridge python run.py views
docker compose exec akg-factor-bridge python run.py probe --section corr     # ★ 最重要
docker compose exec akg-factor-bridge python run.py probe --section upside
docker compose exec akg-factor-bridge python run.py probe --section pools
docker compose exec akg-factor-bridge python run.py probe --section price    # 全表聚合1~3 分钟

pools 节直读基座 industry_pools 表(一次性诊断的例外)。若桥用的是只读账号 且没授这张表,此节会 permission denied——不影响其余三节,跳过即可。

corr 节要回答的两件事(这一节决定第三批怎么定,请把整段原样发我):

  • corr(z_H, z_V):若 |值| > 0.5,「还没热」与「便宜」高度共线, 「三项加权」实际是两项§9-8 权重讨论要重开。
  • w_T=0.50 那行的 top-K 命中数:若 ≈ min(K, 传导票数) 实锤「0.5/0.3/0.2 事实上是传导优先而非加权混合」。 同节还会打印两段式与当前公式的 top20 重合度——越接近 20/20 说明改两段式零代价,第三批 3.2 直接选 A。

7b · 写平台因子库(这一步会往 quant_factor_service 的生产表写行

# 注册幂等ON DUPLICATE KEY UPDATE
docker compose exec akg-factor-bridge python run.py register

# 构建:日期选「传导有数据」的那天,且必须在基座 18:15 传导扫描之后
#      run.py views 的「数据历史深度」段会告诉你各源前沿在哪)
docker compose exec akg-factor-bridge python run.py build all --mode daily --date 2026-07-24

写入是幂等的(按日期区间 DELETE 再 INSERT重跑不会产生重复。 回滚DELETE FROM t_factor_akg_* WHERE trade_date = '<日期>'

逐路对照上一次的行数2026-07-23upside 795 / heat 1674 / event 195 / transmission 83

因子 预期变化 变化原因 不符时看哪里
akg_upside 基本持平 只改了分块读取,口径未动 若骤降 → gp_day_data 代码列没对上
akg_heat 持平 未动 ——
akg_event 大概率下降 新加 source_type='announcement' 白名单 + 单文档封顶 见下
akg_transmission 持平或略变 n_pathsn_sources,重复路径不再重复计数 值会变、行数不该变

akg_event 掉到接近 0 的处置:日志会打印 (事件:按 source_type 剔除 N 条 {...分布...})。若剔除后为空,说明基座里公告的 documents.source_type 不是 announcement 这个字符串。查一下真实取值:

docker exec -i akg-postgres psql -U akg -d akg -c \
  "SELECT source_type, count(*) FROM documents GROUP BY 1 ORDER BY 2 DESC;"

然后在桥服务器 .env 里写 EVENT_SOURCE_TYPES=<真实值> 并重跑,不要改代码

7c · 传导告警的正确读法(容易误判,请按这个读

build akg_transmission 会打三类 ⚠️它们的"没出现"含义各不相同

告警 出现 = 没出现 = ?
quiet 存满 12 条 transmission.pyquiet[:12] 截断 真的没撞(环节未动成员本来就 <12
members_total >= 30 撞基座 topic_contextLIMIT cap 真的没撞
mkt 快照日 ≠ scan_date movers 陈旧 ⚠️ 不能判定——mkt_trade_date 要等第二批改了基座才会被写入,本批全是 NULL桥会跳过这项检查。本批它不出现是必然的,不是"通过"。

前两个告警各命中几个环节,请发我——那是硬伤 1 的实测量级,直接决定第二批里 topic_context 的 cap 该提到多少。

7d · 输入冻结

daily build 跑完会自动冻结。检查:

docker compose exec akg-factor-bridge cat /app/data/frozen/2026-07-24/manifest.json
ls -la data/frozen/2026-07-24/

判定manifest.json 存在,rows 里 universe / consensus / price_close / heat / transmission 各有行数heat 可能是 0——它 T+1 到达,属正常), segment_members / segment_edges 为 0第五/六视图 G2 才有,属正常)。

7e · 事件回填冒烟(验证硬伤 3 真的修好了)

docker compose exec akg-factor-bridge python run.py build akg_event \
    --mode history --start 2024-01-01 --end 2024-03-31 --no-freeze

判定:改之前这条命令必然静默返回「无数据(跳过)」(日历取自热度表,最早 2025-03。 现在应该有真实行数(前提是 2024 年的公告已抽取)。 若仍为空,先看日志是不是被 EVENT_SOURCE_TYPES 过滤掉了——两种"空"日志不一样。


§8 判收标准(逐条可独立判定,一条不过不判收

工程正确性

  • §3 两处 git hash 一致
  • §5 六条 DDL 全部成功
  • §6 views 的视图版本段前三项 、第四项
  • §7a 四节 probe 至少 corr / upside / price 三节出数(pools 权限不足可跳)
  • §7b register + build all,四路都有行数
  • §7d manifest.json 生成且各源有行数
  • §7e 2024 年区间不再静默返空

数据无损(回答"抽取成果会不会白费"

  • §4 的基线 SQL 重跑一次,claims / documents / entity_links / company_master 四行的 countlatest 逐字不变

幂等

  • build all --mode daily --date <同一天> 连跑两次,第二次行数与第一次相同, 平台表无重复(主键 (trade_date, stock_code) 本就保证,此处是验证 DELETE 区间正确)

§9 失败对照表

现象 根因 处置
No such container: akg-postgres 你在桥服务器上,基座容器不在这台机器 用 §5a 或 §5b
permission denied for ... (DDL) 桥用的是只读账号 §5a 的 -e 覆盖成 owner 账号
views 里视图版本三项都 §5 没跑成功 回 §5看 5a 的逐条报错
桥打 v_factor_transmission 还是旧版 处于兼容矩阵左下格 正常过渡态,做完 §5 即消失
akg_event 行数为 0 且日志显示剔除全部 source_type 实际值不是 announcement 查真实取值,改 .envEVENT_SOURCE_TYPES
akg_transmission 行数为 0 当日基座传导扫描没产出beat 18:15 未跑 / 被饿) 换一个 --date,或先在基座跑 transmission_scan
corr 节报「无传导台账」 同上 同上
probe --section pools permission denied 只读账号没授 industry_pools 跳过该节,不影响判收
平台 gp_day_data 查不到 / upside 为 0 代码列形态没对上 .envPRICE_CODE_COL(实测应为 symbol

§10 请回传给我的(拿到这些才能定第三批,不用再靠我的模拟)

  1. §1 拓扑登记表(填好)
  2. §4 基线 SQL 的两段输出(mkt_daily 按 kind 的天数 —— 它决定传导理论上能重扫到哪天)
  3. §5 的执行输出6 条语句的结果)
  4. §6 run.py views 全部输出
  5. §7a probe --section corr 全部输出 ← 最重要
  6. §7a probe --section upside 的 q 档位表
  7. §7b build all 的完整日志(尤其 event 的剔除分布、transmission 的两项 ⚠️ 各命中几个环节)
  8. §8 判收清单的勾选情况

§11 本批不做的事(避免混淆)

  • 不改 astock-kg 任何代码topic_contextORDER BYtransmission.scan 去掉 quiet[:12]_latest_mkt 加参数、sync_consensusasof —— 全在第二批
  • 因此 mkt_trade_date 本批全是 NULL见 §7cquiet[:12] 截断依然存在, moved_ratio 依然建立在任意顺序的 ≤30 抽样上。本批只是让这些问题看得见
  • 赛道门槛 C、akg_scoreakg_gate 都在 G2/G3本批没有。

§12 已拍板的决定(第二批据此实现)

  • 节奏:先实机验证第一批,通过后再动基座。
  • 传导新鲜度断言照常落库,只打告警 + 记 mkt_trade_date,不中止扫描。 理由:保覆盖——传导每日只有几十行,公告批量入库期 beat 一旦被饿就整天空白; 改由桥侧按 mkt_trade_date 自行判断是否采信。第二批的 transmission.scan 已按此改写, allow_stale 参数取消。