# 股票估值端到端流水线 V2 编码方案 V001 创建人员:`dev.developer.ana.cai` 文件职责:冻结股票估值端到端流水线 V2 的需求边界、模块接口、缓存与失败语义、完整报告、全链路耗时和验收方法。 关联事项:`DEV-ANA-STOCK-VALUATION-PIPELINE-V2-20260801-001`。 上游依据:用户在当前会话确认“按照这个思路去做”;`股票价格合理性评估操作手册_v1.0.md` 文件内版本 v1.1;V1 流水线及铖昌科技实际评估耗时复盘。 管理规范/模板:`../../../../common/dev-doc/编码规范.md`;`../../../编码规范.md`;`../../../开发审计规范.md`。 引用文件:`../股票估值流水线说明.md`;`../../../../dev/ana-dev/stock_valuation_pipeline/README.md`;`../../../../outputs/20260730_stock_valuation_guide/股票价格合理性评估操作手册_v1.0.md`。 记录方式:重型编码方案;方案审核通过前不得进入实现。 ## 1. 问题与目标 V1 只自动化“标准快照完成以后”的机械计算,不能解释从用户发出请求到交付报告的完整耗时。铖昌科技评估的 57 分钟主要消耗在人工取数、数据库差异追查、手工扩写完整报告和重复终检,V1 的毫秒级运行时间并未覆盖这些环节。 V2 的目标是把入口提前到“股票代码 + 估值日”,把出口扩展为“证据包 + 标准快照 + 完整 Markdown 报告 + 自动 QA + 全链路耗时”。 目标时限: 1. 冷启动公开网络采集的机械阶段目标不高于 90 秒;网络异常时按明确超时降级,不无限等待。 2. 有效缓存复评的机械阶段目标不高于 5 秒。 3. 首次完整分析的客户端总耗时目标为 12~20 分钟,其中人工只处理利润持续性、场景倍数和结论边界。 4. 已建立公司基线后的复评总耗时目标为 3~8 分钟。 速度目标不能取消原始披露优先、估值日、TTM、归一化利润、机构时效、现金流、三情景、反向估值、交叉验证、复算和风险边界。 ## 2. 范围与非目标 ### 2.1 本期范围 1. 保持 V1 `--input` 入口和结果合同向后兼容。 2. 新增 `--ticker`、`--as-of`、`--cache-dir`、`--judgment`、`--task-start` 和 `--fixture-dir` 端到端入口。 3. 并发采集行情、法定公告索引、结构化财务数据和机构预测。 4. 保存原始响应、来源元数据、内容哈希和请求索引。 5. 建立公司基线与增量水位,只重取估值日以后发生变化的模块。 6. 将采集结果转换为 V1 标准快照;不可自动确认的字段进入缺口清单。 7. 用统一数据和最小人工判断覆盖层生成固定 16 节完整 Markdown 报告。 8. 自动检查公式、日期、来源、Markdown、链接、占位符和报告关键数字一致性。 9. 记录从 `task_start` 到最终产物提交的总耗时及各阶段耗时。 ### 2.2 非目标 1. 不连接付费数据库,不绕过登录、验证码、访问控制或反爬限制。 2. 不承诺任意网页永久可用;适配器失败时输出可定位缺口。 3. 不自动推断未披露客户、型号、订单或敏感军工信息。 4. 不输出交易指令、收益承诺或自动批准的投资结论。 5. 不用单一通用 PE 替代人工行业与周期判断。 6. 不在本期引入 GUI、服务端、常驻数据库或定时任务。 ## 3. 用户入口与状态 ### 3.1 向后兼容入口 ```powershell python -m stock_valuation_pipeline ` --input ` --output-dir ``` V1 行为和产物继续可用。 ### 3.2 端到端入口 ```powershell python -m stock_valuation_pipeline ` --ticker 001270 ` --as-of 2026-08-01 ` --output-dir ` --cache-dir ` --judgment ` --task-start 2026-08-01T16:45:22+08:00 ``` `--judgment` 可省略。省略时仍生成数据就绪报告,但状态必须是 `DATA_READY_NEEDS_JUDGMENT`,不得把机械情景冒充最终研究结论。 ### 3.3 终态 | 状态 | 含义 | 是否允许形成方向性结论 | |---|---|---| | `COMPLETE` | 采集、快照、计算、报告和 QA 完成 | 仅在判断覆盖层齐全且 QA 无阻断时允许研究草案结论 | | `COMPLETE_WITH_GAPS` | 非核心数据或单个适配器缺失 | 视缺口影响决定;报告必须列出缺口 | | `DATA_READY_NEEDS_JUDGMENT` | 机械数据完成,缺少人工情景或结论边界 | 否 | | `BLOCKED_CORE_INPUT` | 股价、股本、核心财务等不能形成可信快照 | 否 | | `FAILED` | 合同错误、输出提交失败或内部异常 | 否 | ## 4. 模块设计 ### 4.1 `cli.py` 职责:解析两类互斥入口,保持 V1 兼容,将端到端请求交给 `workflow.py`。 硬合同: 1. `--input` 与 `--ticker` 互斥。 2. `--ticker` 模式必须给 `--as-of`、`--output-dir` 和 `--cache-dir`。 3. 日期、股票代码和路径错误必须在网络请求前失败。 4. CLI 的 stdout 只输出结构化终态摘要;进度写 stderr 和运行日志。 ### 4.2 `telemetry.py` 职责:记录用户可感知的全链路墙钟时间。 正式字段: | 字段 | 含义 | |---|---| | `run_id` | 本次运行唯一 ID | | `task_start` | 用户任务起点;来自参数或进程起点 | | `process_start` | CLI 启动时间 | | `stage` | `preflight/acquire/build_snapshot/calculate/render/qa/commit` | | `started_at/finished_at` | 带时区时间 | | `elapsed_seconds` | 单阶段墙钟 | | `task_wall_seconds` | 从 `task_start` 到提交完成 | | `process_wall_seconds` | 从 CLI 启动到提交完成 | | `status/error_code` | 阶段状态与错误码 | 产物:`runtime_metrics.json` 和 `runtime.log`。每个阶段开始、完成和失败都要输出进度;单次网络等待不得超过配置上限。 ### 4.3 `http_client.py` 职责:受控 HTTP GET/POST、超时、有限重试、响应大小限制和测试注入。 规则: 1. 只访问 `provider_registry.json` 允许的域名和协议。 2. 默认连接超时 8 秒、总读取超时 20 秒;临时网络错误最多重试 2 次,退避上限 2 秒。 3. 4xx、解析错误、结构变化不盲目重试。 4. 响应先写缓存临时文件,完成哈希后原子提交。 5. `--fixture-dir` 时禁止真实网络,所有请求由固定夹具提供。 ### 4.4 `cache.py` 职责:内容寻址原始缓存、请求索引、公司基线和增量水位。 目录合同: ```text / blobs/.bin requests//.json companies//baseline.json companies//source_index.json ``` 请求索引至少记录:URL 或规范化请求、请求时间、响应时间、HTTP 状态、内容类型、字节数、SHA-256、来源发布日期、估值日、适配器版本和完成标记。 复用不能只看文件存在,必须同时验证请求指纹、响应哈希、适配器版本、as-of 安全和完成标记。价格、最新公告、机构预测按估值日刷新;未重述历史年报按内容哈希复用。 ### 4.5 `providers.py` V2 使用注册式适配器,不把 URL、缩放系数或字段路径散落到主流程。 第一期适配器: | 适配器 | 用途 | 优先级 | 失败处理 | |---|---|---|---| | 法定公告索引 | 定位年报、季报、预告、修正和公司行动原文 | A1 | 核心报告原文缺失则阻断或警告 | | 行情适配器 | as-of 收盘价、股本和平台市值 | B1,价格×股本复核 | 无价格或股本则阻断 | | 结构化财务适配器 | 三张表历史与本期累计数据 | B1,须回链法定报告 | 与法定口径不一致则阻断 | | 机构预测适配器 | 汇总、明细、日期、EPS和利润 | B2 | 允许空,但显式 `no_usable_forecasts` | 机构数据库最多核查两个。若“汇总数量”和“明细数量”不一致,保存原始汇总、可见明细和差异,最多做算术反推并标记 `GAP`,不得无限追查未知机构。 每个适配器返回统一 `ProviderResult`: ```text provider_id, status, fetched_at, as_of_date, records[], sources[], gaps[], warnings[], raw_artifact_hashes[] ``` ### 4.6 `acquisition.py` 职责:用线程池并行执行相互独立的适配器,再串行执行来源优先级、as-of 和停止搜索判断。 并行上限默认 4。单个适配器失败不取消其他适配器;所有异常都转换为有来源、影响和重试建议的 gap。停止搜索条件为:取得法定原文、公司和报告期匹配、无后续修订、数字可勾稽且无数量级异常。 产物:`acquisition_bundle.json`、`source_evidence.json`、`gap_list.json`。 ### 4.7 `snapshot_builder.py` 职责:把统一采集结果映射到 V1 `snapshot.schema.json`,并保留字段级来源。 规则: 1. TTM 必须使用最近完整年度 + 本年累计 - 上年同期累计。 2. 金额在正式快照中统一为元,股本为股,价格为元/股。 3. 估值日以后发布的信息不得进入快照。 4. 法定报告与结构化数据不一致超过容差时,不自动选择“看起来合理”的一个。 5. 归一化调整、现金可支配性和情景倍数不从网页叙事自动猜测。 产物:`valuation_snapshot.json` 和 `snapshot_build_report.json`。 ### 4.8 `judgment_overlay.schema.json` 最小人工判断覆盖层只承载机器无法可靠替代的内容: 1. 业务及利润来源摘要; 2. 归一化调整及理由; 3. 悲观、基准、乐观利润与倍数; 4. 主模型和交叉验证选择; 5. 关键风险、上调和下调触发器; 6. 结论等级、置信度和边界。 覆盖层不能改写价格、股本、法定利润或来源日期。出现冲突时显式失败。其目标是把人工输入缩小到判断,而不是重新填写财务数据。 ### 4.9 `full_report.py` 职责:从标准快照、统一计算结果、证据、缺口、覆盖层和耗时数据一次生成完整 Markdown。 固定章节:结论摘要;市场与资本结构;信源优先级;业务及利润来源;历史盈利;TTM 与归一化;机构预期;利润质量与现金流;模型选择;三情景;反向隐含条件;交叉验证;持有期测试;风险与触发器;复评清单;数据局限和复算摘要。 报告长度不设上限。所有数字表格只读取统一结果对象,不允许在模板中重复计算。无判断覆盖层时,相关段落明确标记 `GAP/DATA_READY_NEEDS_JUDGMENT`,不得出现 TODO、TBD 或伪结论。 ### 4.10 `report_qa.py` 主流程硬 gate: 1. Markdown 围栏成对; 2. 表格列数一致; 3. 无 TODO/TBD/待计算/NaN/Infinity; 4. 本地产物链接存在; 5. 报告关键数字与 `valuation_results.json` 一致; 6. 结论、情景和当前价格位置一致; 7. 不包含交易指令和收益承诺; 8. 核心字段有来源,且来源日期不晚于 as-of。 外部 URL 的在线可达性只做警告,不得因临时网络抖动改写已经完成的正式结果。 ### 4.11 `workflow.py` 职责:唯一端到端协调入口。 ```text preflight -> acquire concurrently -> build snapshot -> run V1 calculation core -> apply judgment overlay -> render full report -> report QA -> atomic commit ``` 所有正式产物先进入本次运行暂存目录,全部硬 gate 通过后才原子提交到输出目录。失败包保留 `failure.json`、运行日志和已取得的原始缓存索引,但不得留下状态为 COMPLETE 的 manifest。 ## 5. 数据优先级和修订规则 1. A1 法定披露原文决定财务报告、预告、股本变化和公司行动事实。 2. B1 结构化金融数据库用于加速取数,但必须回链匹配的 A1 报告。 3. B2 机构数据库只代表市场预期,不能替代公司承诺。 4. 财报、预告和快报存在修正时,旧版本标记 `superseded`,不得作为主输入。 5. 缓存中的历史报告只有在报告期、公司、内容哈希和未重述状态仍匹配时复用。 6. 价格、股本、最新报告状态、机构预测、现金债务和公司行动每次复评都检查增量。 ## 6. 错误码与降级 | 错误码 | 类型 | 处理 | |---|---|---| | `E_INPUT_CONTRACT` | CLI 或 Schema 错误 | fail-fast | | `E_ASOF_VIOLATION` | 混入未来信息 | 阻断 | | `E_CORE_MARKET_DATA` | 股价或股本缺失 | 阻断 | | `E_CORE_FINANCIAL_DATA` | TTM 所需核心财务缺失 | 阻断 | | `E_SOURCE_CONFLICT` | A1 与结构化数据超容差 | 阻断受影响字段 | | `W_PROVIDER_UNAVAILABLE` | 单个非核心来源不可用 | gap 后继续 | | `W_FORECAST_COVERAGE` | 机构预测为空或明细不全 | 警告后继续 | | `W_STALE_FORECAST` | 机构预测早于最新经营信息 | 排除出时效一致预期 | | `W_EXTERNAL_LINK` | 外部链接暂时不可达 | 警告,不改写结果 | 不得用默认 0、空字符串、伪造预测或测试数据掩盖正式输入缺失。 ## 7. 性能与可观察性 1. 相互独立的网络 I/O 使用最多 4 个线程并发。 2. 同一 URL/请求只下载一次,多个消费者读取同一内容哈希。 3. PDF 原文在 V2 只负责证据归档和必要字段复核;批量财务字段优先使用结构化数据,避免每次全文解析。 4. 每个适配器有开始、完成、命中缓存、失败和耗时日志。 5. 每次交付同时报告 `task_wall_seconds` 和 `process_wall_seconds`,不得再用计算引擎毫秒数代替完整任务耗时。 6. 到达网络预算后结束相应适配器并输出 gap,不做无上限搜索。 ## 8. 重跑与恢复 1. 相同 ticker、as-of、适配器版本和请求指纹可复用已校验原始响应。 2. 新估值日从公司基线增量刷新价格、公告和预测,历史年报不重新下载。 3. 适配器版本、请求参数、响应哈希、Schema 或关键规则改变时重建受影响模块。 4. 计算和报告可从 `acquisition_bundle.json` 或 `valuation_snapshot.json` 独立重跑。 5. 缓存损坏只删除逻辑索引并重新取得对应内容,不影响其他内容寻址对象。 6. 输出目录已有完整结果时默认拒绝覆盖;显式 `--force` 仍通过暂存和原子替换实现。 ## 9. 验收方案 ### 9.1 L0 1. 全包 `py_compile`/import 通过。 2. V1 现有 10 项测试不回退。 3. 新 Schema 和注册表可解析。 ### 9.2 L1 模块测试 1. HTTP 超时、4xx、5xx、超大响应和非法内容类型。 2. 缓存命中、哈希篡改、适配器版本变化、as-of 不安全和未完成标记。 3. 行情缩放、股价×股本勾稽、财务累计口径、机构汇总/明细不一致。 4. 覆盖层禁止改写法定字段。 5. Markdown 围栏、表格、占位符、本地链接和数字一致性。 ### 9.3 L2 离线端到端 使用固定网络夹具运行铖昌科技: 1. 单条 CLI 从 ticker/as-of 生成全部产物; 2. 关键数值与 2026-08-01 已完成报告一致; 3. 无判断覆盖层时为 `DATA_READY_NEEDS_JUDGMENT`; 4. 加覆盖层后生成完整研究草案; 5. 夹具模式网络访问为 0; 6. 冷夹具运行低于 5 秒,有效缓存复跑低于 2 秒。 ### 9.4 L3 边界与回归 1. 无机构预测仍可生成带 gap 的报告。 2. 未来财报或研报不能进入历史 as-of。 3. 汇总 4 家、明细 3 家只产生一个明确 gap,不触发无限搜索。 4. 法定公告和结构化利润冲突时阻断正式结论。 5. 中断、输出冲突和提交失败不留下伪 COMPLETE 产物。 6. V1 长城军工回归和 V2 铖昌科技回归同时通过。 ### 9.5 L4 受控真实网络 smoke 1. 用铖昌科技或另一只深市股票执行一次真实采集。 2. 核对原始响应哈希、官方公告链接、价格日期、股本、市值、财务报告期和机构预测日期。 3. 网络机械链路目标不高于 90 秒;如外站异常,验证在预算内降级并形成 gap。 4. 第二次运行只刷新规定的动态字段并证明历史报告缓存复用。 ## 10. 高风险点与控制 1. 网页字段或缩放系数变化:适配器版本化、夹具回归和数量级检查。 2. 当前接口污染历史 as-of:所有记录按发布日期过滤,无法历史化的字段不得回填。 3. 抓取结果冒充法定事实:字段级来源和 A1 回链是硬门禁。 4. 机构预测样本不透明:汇总与明细分账,差异记 gap。 5. 缓存复用错误:请求、代码、Schema、as-of、哈希和完成标记联合校验。 6. 报告数字漂移:所有表格读取同一结果对象,最终 QA 逐项比对。 7. 人工判断被自动化掩盖:无覆盖层不产生方向性最终结论。 8. 复杂度反而拖慢:V2 只实现首期四类适配器和 16 节报告,不引入数据库、GUI或常驻服务。 ## 11. 方案门禁 本方案属于重型实现。状态为 `PENDING_INDEPENDENT_DESIGN_REVIEW`。只有 `dev.reviewer.ana.cai` 在 `ana-doc/案例审计报告.md` 给出方案 `PASS` 后,才进入代码实现;开发者不自审。