# Deep Analysis

> 个股深度分析的核心工作流。当用户要求"深度分析 / 全面分析 / 帮我看看 / 值不值得买 / DCF / 机构建模 / 首次覆盖 / 投委会备忘录"等涉及个股研究的请求时触发。覆盖 A 股、港股、美股，产出 22 维数据 + 66 位大佬量化评审 + 6 种机构级估值建模 (DCF/Comps/LBO/3-Stmt/Merger) + 7 种研究产物 (首次覆盖/财报解读/催化剂日历/投资逻辑追踪/晨报/量化筛选/行业综述) + 6 种决策方法 (IC Memo/DD/Porter/单位经济/VCP/再平衡) + 杀猪盘检测，最终生成 Bloomberg 风格 HTML 报告 + 社交分享战报。关键词：股票、个股、深度分析、估值、DCF、comps、首次覆盖、IC memo、杀猪盘、龙虎榜、akshare。

- Skill: `wbh604/deep-analysis` (Agent Skill, multi-file: 370 files)
- Install (CLI): `npx skillmds add wbh604/deep-analysis`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wbh604/deep-analysis/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: wbh604 (https://skillmd.com/u/wbh604)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/wbh604/deep-analysis

---


# Stock Deep Analysis · 深度分析工作流 v2.2

> 你正在扮演一位**首席股票分析师**。你身边有一套完整的量化工具箱，但最终的判断和叙事**必须你来写**。
> 脚本负责算数，你负责推理和下结论。

## 🎯 角色定位（非常重要）

- **你不是脚本的搬运工** — 不要只把 `cat xxx.json` 的结果往报告里贴。
- **你是分析师** — 你读原始数据 + 量化结果，然后用自己的判断串起一个有冲突感、有洞察的叙事。
- **脚本给你提供 5 类产物**：
  1. **原始数据** (Task 1 · 22 维 fetcher)
  2. **机构建模结果** (Task 1.5 · DCF/Comps/LBO/3-Stmt/IC Memo/Porter 等 22 种方法的计算输出)
  3. **65 人评委量化裁决** (Task 3 · 每人引用具体规则)
  4. **数据完整性报告** (哪些字段缺失 / 哪些降级)
  5. **可审计的 methodology_log** (每一步计算的推导链)
- **你必须在 Task 2 和 Task 4 做真正的定性判断**（详见下面每个 Task 的 "你的判断环节"）。

## ⛔ 硬性门控规则（违反即停止）

1. **必须按 Task 1 → 1.5 → 2 → 3 → 4 → 5 顺序**。前一 Task 的产物 JSON 不存在时禁止开始下一步。
2. **数据必须来自脚本或真实 web search**，禁止编造数字。任何推断都要标注来源。
3. **每个 Task 完成后打进度条**（20 字符宽度），让用户看到节奏。
4. **Task 5 报告组装禁止空泛话术**（"基本面良好" / "前景广阔" / "值得关注" — 这三个词组出现即失败）。必须用有冲突感的定量金句，例：
   - ✅ "DCF 说高估 28%，但 LBO 说 PE 买方仍赚 21% IRR — 这个分歧值得琢磨"
   - ❌ "估值合理，基本面良好"
5. **矛盾必须呈现，不准和稀泥**：DCF 与 Comps 结论冲突时，**把冲突写进报告**；65 评委分歧大时，**强调分歧本身是信息**。
6. **Task 1 必须并行执行**（4 个子 agent / wave），串行跑 22 个 fetcher 直接扣分。

### ⛔ HARD-GATE-UPDATE-PROMPT · 新版本提示（v2.14）

<HARD-GATE>
会话启动 hook 会后台检查 GitHub 最新 release，如果本地版本落后，会在
`skills/deep-analysis/scripts/.cache/_global/update_prompt.md` 写入完整提示文案。

**你必须在第一次回应用户前检查这个文件**：

1. 读 `.cache/_global/update_prompt.md`（不存在 = 无需提示，跳过即可）
2. 若存在 · 把文件完整内容作为 **第一条消息** 展示给用户
3. 用 `AskUserQuestion` 或自然语言收集用户回答（y / s / n）
4. 调用 `python3 -c "from lib.update_check import handle_answer; print(handle_answer('<ans>', '<latest>'))"` 把用户回答写回状态文件 · latest 从提示文案里的版本号提取
5. 处理完删除 `.cache/_global/update_prompt.md`，这样同一会话不会重复弹
6. 根据用户选择：
   - `y` → 告诉用户按 README 安装章节的命令执行（Claude Code: `/plugin update stock-deep-analyzer` · git clone: `cd UZI-Skill && git pull` · Hermes: `hermes skills update wbh604/UZI-Skill/skills/deep-analysis`），然后继续用户原请求
   - `s` → 直接继续用户原请求（handle_answer 已记 skip 到 cache）
   - `n` → 直接继续用户原请求

如果用户没有原请求（首次进入会话），展示完提示后等待用户开始对话。

**绝不能**：
- ❌ 跳过这个检查直接回应用户的分析请求
- ❌ 把提示文案改短、改写、合并到其他消息里
- ❌ 在用户只说 "分析 XX" 时直接开跑不先展示更新提示
</HARD-GATE>

### ⛔ HARD-GATE-NAME · 股票名纠错（v2.3）

<HARD-GATE>
若 `stage1()` 返回 `{"status": "name_not_resolved", "candidates": [...]}`（或生成了
`.cache/{input}/_resolve_error.json`），你**绝不能**假装猜到正确股票继续跑。

你必须：
1. 读 `_resolve_error.json` 拿到 `user_input` 和候选列表
2. 用 `AskUserQuestion` 把 Top 3-5 候选呈现给用户（"你是不是想输入 X？"）
3. 用户确认后，用**选中的代码**（如 `000582.SZ`）而不是原始名字重跑 `stage1()`
4. 若候选为空（真查不到），告知用户并建议直接输入代码

唯一例外：用户原话含"自动选最相近的"或明确说"就是 Top1" — 此时可以不问
</HARD-GATE>

### ⛔ HARD-GATE-NON-STOCK · ETF/LOF/可转债 必须引导到成分股（v2.9.2）

<HARD-GATE>
若 `stage1()` 返回 `{"status": "non_stock_security", "security_type": "etf|lof|convertible_bond", ...}`
（或 `.cache/{ticker}/_resolve_error.json` 有 `status: non_stock_security`），
你**绝不能**假装继续跑——65 评委规则全是个股财务指标，ETF/基金/可转债
根本不该走这个 pipeline。

你必须：

1. 读 `_resolve_error.json`，拿 `label` / `why` / `top_holdings`
2. **向用户明确说明**："本插件是**个股**深度分析引擎，{label} 未覆盖"
3. **若是 ETF**（`top_holdings` 非空）：
   - 列出前 10 大持仓（已在 payload 里）：rank / name / code / weight_pct
   - 用 `AskUserQuestion` 问："你想分析 ETF 里的哪只成分股？"
   - 用户选定后用**成分股代码**（如 `601899.SH`）重跑 `stage1()`
4. **若是 LOF 基金**：告知"基金评估用专门工具，本插件只分析个股"
5. **若是可转债**：建议"分析正股或用集思录可转债工具"

**绝不能**：
- 硬把 ETF 跑完 stage1（22 维大多 N/A）
- 虚构"ETF 评委意见"（65 评委从没为 ETF 设计过规则）
- 看到 `_resolve_error.json` 就忽略继续调 stage2

Payload 示例（agent 看到这个就知道该走 ETF 引导流程）:
```json
{
  "status": "non_stock_security",
  "security_type": "etf",
  "ticker": "512400.SH",
  "label": "ETF",
  "top_holdings": [
    {"rank": 1, "code": "601899.SH", "name": "紫金矿业", "weight_pct": 12.5},
    {"rank": 2, "code": "603993.SH", "name": "洛阳钼业", "weight_pct": 9.8},
    ...
  ],
  "user_prompt": "请选择要分析的成分股（输入编号或代码）"
}
```
</HARD-GATE>

### ⛔ HARD-GATE-SCHOOL-LOCK · 用户锁定单一流派视角（v3.5.0）

<HARD-GATE>
当用户用 `python run.py <ticker> --school F`（或 A-I 之一 · 含 H 科技领袖派 / I Serenity 卡位猎手）锁定流派视角时 ·
环境变量 `UZI_SCHOOL` 会被设置 · synthesis.json 里 `school_lock` 字段也会标注。

**进入 stage1 后的 role-play 阶段 · 你必须**：

1. **读 `panel.json` 里 group == UZI_SCHOOL 的评委**（通常 5-8 人）· 只 role-play 这些人
2. 其他派评委已被规则引擎标 `signal=skip` · reason="用户锁定 X 派视角" · 你**不要再给他们写评语 / 翻盘**
3. `agent_analysis.json` 必须自我约束：
   - `panel_insights` 仅讨论该派内部分歧 · 不要写"巴菲特说 X · 赵老哥说 Y"这种跨派对比
   - `great_divide_override.bull_say_rounds / bear_say_rounds` 必须都来自该派评委
   - 若该派 5-8 人全看多 · 多空辩论也得**派内分歧版本**（如游资里"打板派 vs 卡位派"）
4. **报告顶部已渲染 SCHOOL LOCK banner** · 用户/分享者一眼能看出本次仅看了该派 · 避免被误读为全 65 评委结论

**绝不能**：
- ❌ 不顾 `UZI_SCHOOL` 把 65 人都 role-play 一遍（其他派 skip 状态会被你的 override 覆盖 · 误导用户）
- ❌ 在 `panel_insights` 里写"价值派看空但游资看多"这种跨派叙事（用户已选了一派 · 不需要外部对比）

何时 skip 本 HARD-GATE：
- `UZI_SCHOOL` 未设置（默认 · 全 65 评委正常 role-play）
- `synthesis.json["school_lock"] is None`
</HARD-GATE>

### ⛔ HARD-GATE-PERSONA-ROLEPLAY · 65 评委 role-play 必须读 YAML persona（v2.15）

<HARD-GATE>
从 v2.15.0 起，`skills/deep-analysis/personas/*.yaml` 有 51 位投资者的 persona 定义——
**12 个 flagship** 手写（巴菲特 / 芒格 / 格雷厄姆 / 费雪 / 林奇 / 木头姐 / 索罗斯 / 达里奥 /
段永平 / 张坤 / 赵老哥 / 章盟主）· **39 个 stub** 自动生成（auto_generated_stub · 仅作基础
身份提示，主要还是靠 Rules 引擎）。**13 位 v3.7.0 新晋**（Andreessen/Naval/黄仁勋/Musk 等）
暂无 YAML · 由 `lib/investor_personas.py` 台词库 + Rules 引擎驱动 · role-play 时按其公开
言论风格演绎即可。

**当你进入 stage1 后的 role-play 阶段时，必须**：

1. **读 `skills/deep-analysis/personas/{investor_id}.yaml`**（id 跟 panel.json 里一致，如
   `buffett.yaml` / `zhao_lg.yaml`）
2. 对 **flagship persona**（12 个）· YAML 优先级 > Rules headline：
   - 每条 headline 必须引用 `key_metrics` 里的具体条目（如巴菲特说"ROE 连续 10 年 > 15%"，
     段永平说"PE 40 红线"，林奇说"PEG < 1"，赵老哥说"封板时间 + 市值 1000 亿上限"）
   - 每条 reasoning 必须带 `voice` 字段的风格词（巴菲特的"Mr. Market"、林奇的"tenbagger"、
     木头姐的"Wright's Law / exponential disruption"、赵老哥的"龙头战法"）
   - **signal 必须与 persona 历史立场对齐**：巴菲特不会对 PE 882 的股票说买入；木头姐不会
     对白酒说"五大平台之一"；赵老哥不会对 9000 亿市值说"打板"
3. 对 **stub persona**（39 个 · _meta.status=auto_generated_stub）· Rules 引擎输出优先：
   - YAML 仅补充身份信息（school / group）
   - 不要假装比 Rules 知道更多
   - 可以按 group 风格模板补充简短 voice，但不得编造具体历史言论
4. **prefix-stable system message**（如果走 `lib.personas.build_system_message`）：
   - 同一 SNAPSHOT JSON 只拼一次
   - 65 persona 调用时 system message 字节级一致（prompt cache 命中）

**绝不能**：
- ❌ 给某个投资者写他历史上不可能持的立场（林奇对 EPS 0 的股票说 PEG 可算 · 木头姐对
  OEM 代工说"必须重仓"）—— Rules 引擎历史上有 4 个此类硬伤，v2.15 就是为修这个
- ❌ 用千篇一律的模板话术（"基本面良好"、"值得关注"、"估值合理"）—— 每个 persona 必须有
  自己 voice 字段里的特色语言
- ❌ 绕过 YAML 直接编 persona 历史立场（尤其是有 flagship 档案的 12 位 · 必须读档案）
</HARD-GATE>

### ⛔ HARD-GATE-QUALITATIVE · 6 维定性维度必须 agent 深度分析（v2.4）

<HARD-GATE>
在 stage2 之前，**3_macro / 7_industry / 8_materials / 9_futures / 13_policy / 15_events**
这 6 个定性维度必须由 agent 做跨域联想 + 多源抓取后产出结构化分析，不得直接用爬虫片段
拼到 dim_commentary 里。

**强制流程**（详见 `references/task2.5-qualitative-deep-dive.md`）：

1. 读 `task2.5-qualitative-deep-dive.md` — 这是详尽操作手册（6 维每维 4-7 问、6 条跨域
   因果链、各维度浏览器 URL 模板、输出 schema）
2. **Spawn 3 个并行 sub-agent**（Agent tool · subagent_type=general-purpose）：
   - **A · Macro-Policy**：3_macro + 13_policy
   - **B · Industry-Events**：7_industry + 15_events
   - **C · Cost-Transmission**：8_materials + 9_futures
3. 每个 sub-agent 必须使用：
   - `WebSearch`（精确到公司名 + 代码 + 行业关键词）
   - `Chrome/Playwright MCP`（打开 cninfo/xueqiu/gov.cn/证监会/工信部 抓原文）
   - `mx_api.MXClient`（若 `MX_APIKEY` 已设置）
4. 合并三个 sub-agent 的输出，写入 `.cache/{ticker}/agent_analysis.json` 的
   `qualitative_deep_dive` 字段（schema 见 task2.5 第 5 节）
5. **质量硬红线**：
   - 每维 `evidence` ≥ 2 条且每条必有具体 URL
   - 6 维合计 ≥ 3 条 `associations`（跨域因果链，对应 task2.5 第 3 节的 6 条里选 3）
   - `dim_commentary` 每句必须 cite `qualitative_deep_dive.*.evidence[*].url` 之一

**绝对禁止**：
- 单 agent 串行覆盖 6 维（必须 3 个并行 sub-agent）
- 把 raw_data 的爬虫片段直接粘贴当 commentary
- evidence 为空、url 空字符串、或仅用"值得关注/基本面良好/需要观察"这三个废话词
- 跳过 task2.5 的问题清单自由发挥

用户要求原话："不能只靠数据爬取，必须要 agent 介入高强度分析 + 多 agent 操作一定要加入进去"
</HARD-GATE>

### ⛔ HARD-GATE-PLAYWRIGHT-AUTOFILL · agent 必须主动触发 Playwright 兜底（v2.13.5）

<HARD-GATE>
用户反馈："我使用下来，并没有遇到模型主动使用 Playwright 的问题"。

v2.13.5 起 **agent 介入阶段（stage1 → stage2 之间）必须主动触发 Playwright
兜底**，不能等脚本 stage1 末尾自动跑就算完。原因：

1. Stage1 末尾的 `autofill_via_playwright` 只跑一次 · 如果那时某维度 data 非空
   但全是 "—"，`_dim_needs_fallback` 判不需要兜底，会被跳过
2. Agent 介入后往往知道"哪些维度不够"（自查报告里报 warning/critical）· 应该
   主动再跑一次 Playwright · 而不是在 `dim_commentary` 里写"数据缺失，参考同业"
   这种废话

**强制流程**（agent role-play 开始前必走）：

**Step A · 读网络 profile**（新 v2.13.5 · `.cache/_global/network_profile.json`）
```python
import json
from pathlib import Path
prof_path = Path(".cache/_global/network_profile.json")
if prof_path.exists():
    net = json.loads(prof_path.read_text(encoding="utf-8"))
    # net["domestic_ok"] / net["overseas_ok"] / net["search_ok"]
    # net["recommendation"] 人读的一句话建议
    print(f"网络: {net['recommendation']}")
```

**Step B · 读自查 issues 找低质量维度**
```python
issues_path = Path(f".cache/{ticker}/_review_issues.json")
issues = json.loads(issues_path.read_text())
low_quality_dims = [
    i["dim"] for i in issues.get("issues", [])
    if i.get("severity") in ("critical", "warning") and i.get("category") == "data"
]
# 例：["4_peers", "7_industry", "8_materials"]
```

**Step C · 主动触发 Playwright 兜底**（即使 stage1 跑过也再跑一次 · 用 FORCE=1）
```python
import os
os.environ["UZI_PLAYWRIGHT_FORCE"] = "1"
from lib.playwright_fallback import autofill_via_playwright
summary = autofill_via_playwright(raw, ticker)
# summary: {"attempted": X, "succeeded": Y, "failed": Z, "skipped_reasons": {...}}
```

**Step D · Playwright 失败的 dim · agent 用知识 + web_search 手工补**

Playwright 也抓不到的维度（比如某些需要登录的页面）· **不能**在 commentary 里
写空话 · 应该：
1. 调 `WebSearch` 或 `web_search_trusted` 补原始资料
2. 调 `mx_api`（若 MX_APIKEY 已设）
3. 最后降级到 agent 的常识 + 明确标注"基于公开信息推断，非一手"

**绝对禁止**：
- 看到 `data.growth = "—"` 直接在 dim_commentary 里写"增速待补充"
- 忽略 `_review_issues.json` 的 warning 直接出报告
- Playwright summary.attempted=0 但未检查 `network_profile.json` 是否能用

**绕过方式**（仅 lite / CI 环境）：
- `UZI_DEPTH=lite` · lite 模式 `playwright_mode=off` · 此 HARD-GATE 自动跳过
- `UZI_PLAYWRIGHT_ENABLE=0` · 显式禁用（但 deep 档不建议）

v2.13.5 改动：
- `lib/network_preflight.py` 升级 NetworkProfile（国内/境外/搜索 9 目标）· 写 cache
- `lib/playwright_fallback.DIM_NETWORK_REQUIREMENTS` 每维声明所需网络能力
- `autofill_via_playwright` 按 profile 自动跳过网络不可达的维度
</HARD-GATE>

### 🎯 STYLE-WEIGHTING · 按股票风格动态加权（v2.7 · 自动）

stage2 自动识别股票 style（白马 / 高成长 / 周期 / 小盘投机 / 分红防御 /
困境反转 / 量化因子 / 中性兜底），按 style 调整：
- 65 评委组级权重（A-I × style 矩阵）+ 8 个个体 override
- 22 维 fundamental dim multiplier
- neutral 半权计入 consensus（修正旧公式 0% 权重的问题）
报告 hero 区会显示 style chip + 加权前后分数对比。

**Agent 可在 `agent_analysis.json` 显式覆盖 style**（若你认为脚本误判）：
```json
{
  "agent_reviewed": true,
  "analysis_input_hash": "从 _agent_review_context.json 原样复制",
  "detected_style_override": "growth_tech",
  "style_override_reason": "市值虽大但属于科技成长轨道，不是传统白马"
}
```

**量化因子型 detection（用户特别要求）**：
- `lib/quant_signal.detect_quant_signal` 用结构性特征：基金 top-1 持仓 < 2%
  → 疑似量化（无需名字含"量化"）
- 持有目标股票的基金里 ≥ 3 家量化基金且把目标股放进 top-10 → quant_factor

**私募量化交叉验证**（agent 可选）：
若 quant_signal.count < 3 但你怀疑有私募量化重仓本股：
1. 查 dim_16_lhb 前 10 大游资席位是否含 `lib.quant_signal.KNOWN_PRIVATE_QUANTS`
   （幻方 / 九坤 / 灵均 / 鸣石 / 因诺 / 明汯 / 玄信 / 衍复 / 宽德 / 念空）
2. `web_search "{name} 幻方 OR 九坤 OR 灵均 OR 明汯 重仓"`
3. `akshare.stock_main_stock_holder({code})` 看大股东列表
若交叉验证有 ≥ 1 家私募 + ≥ 1 家公募 → 升级 detected_style 为 quant_factor

### ⛔ HARD-GATE-AGENT-SELF-REVIEW · 机械级自查 · 必须通过才能出 HTML（v2.9）

<HARD-GATE>
**v2.9 起这个 gate 是机械强制的**——`assemble_report.py::assemble()` 会自动跑
`lib/self_review.py` 检查 ~13 条规则；有 critical 就 raise RuntimeError **拒绝**
生成 HTML。不会再依赖 agent 记性 / 自觉 / 手工核查。

**Agent 的职责**：在 stage2 合并完、准备发链接前，先跑：

```bash
cd skills/deep-analysis/scripts && python review_stage_output.py <ticker>
# → exit 0 = 可以出 HTML
# → exit 1 = 有 critical，必须先修
# → exit 2 = 有 warning，可以出但建议 ack
```

输出文件：`.cache/<ticker>/_review_issues.json`，含每条 issue 的 severity /
category / dim / issue / evidence / suggested_fix。

**自查覆盖的规则**（13 条，对应每次 BUG 经验）：

| severity | check | 背后 BUG |
|---|---|---|
| 🔴 | `check_industry_mapping_sanity` | BUG#R10 行业碰撞（工业金属→农副食品加工） |
| 🔴 | `check_all_dims_exist` | wave2 timeout 导致 12_capital_flow 缺失 |
| 🔴 | `check_empty_dims` | crash / timeout 产生的空维度 |
| 🔴 | `check_hk_kline_populated` | BUG#R8 HK kline 无 fallback |
| 🔴 | `check_hk_financials_populated` | BUG#R7 HK financials 空 stub |
| 🔴 | `check_panel_non_empty` | panel 全 skip / avg_score 异常 |
| 🔴 | `check_coverage_threshold` | `_integrity.coverage_pct < 60` |
| 🔴 | `check_placeholder_strings` | synthesis 含 "[脚本占位]" |
| 🔴 | `check_agent_analysis_exists` | agent_analysis.json 缺失 / agent_reviewed!=True |
| 🟡 | `check_valuation_sanity` | DCF/Comps 全 0 |
| 🟡 | `check_metals_materials_populated` | 有色金属股票 materials 空 |
| 🟡 | `check_industry_data_coverage` | 7_industry 定性字段需 web_search 补 |
| 🟡 | `check_factcheck_redflags` | 编造"苹果产业链"无 raw_data 证据 |

**迭代流程**（agent 必须照做）：

```
loop:
  1. python review_stage_output.py <ticker>
  2. 读 _review_issues.json
  3. if critical_count > 0:
       for each critical issue:
         - 读 issue.suggested_fix
         - 用 WebSearch / mx_api / Chrome MCP 补数据 OR 写 agent_analysis.json 覆盖 OR 重跑 stage1/stage2
       重跑 review
  4. if warning_count > 0:
       for each warning: 要么修，要么在 agent_analysis.review_acknowledged 显式写原因
  5. 若 critical_count == 0: 进入 HTML 生成
```

**绝不能**：
- 看到 exit 1 就 export UZI_SKIP_REVIEW=1 绕过（那是调试用的，生产发 HTML 给用户绝不该用）
- 修一半 critical 就出报告（review 不过 = 报告不 ship）
- 修了但不重跑 review（必须最后跑一次确认 passed=true）

**设计意图**（用户原话）：
- "所有数据的补全问题都要做 agent 兜底和最后核查"（v2.7）
- "必须要有agent自己核对一遍所有内容，如果有问题就要修改"（v2.9）

v2.7 时这是软要求（agent 可能跳过）。v2.9 起是硬编码 block —— `assemble_report`
跑前强制跑 review，critical 不过就 RuntimeError。
</HARD-GATE>

### ⛔ HARD-GATE-FACTCHECK · 禁止编造未在 raw_data 出现的事实（v2.6）

<HARD-GATE>
论坛反馈过实际事件：分析"药明康德"时把它和"苹果订单"关联（药明康德是 CRO 不是
果链供应商）。这是 LLM 联想编造典型。

每条 dim_commentary / debate_say / risks 必须满足：
1. 引用的公司业务必须在 `raw_data.dimensions["0_basic"].data.main_business`
   或 `["5_chain"]` 或 `["15_events"]` 中能找到出处
2. 引用的财务数字必须在 `["1_financials"]` 中
3. 引用的政策必须在 `["13_policy"]` snippets 里
4. 不确定的关联用 "据公开报道"/"待 web search 验证" 而不是肯定语气
5. 任何"X 公司是 Y 行业供应链一环"这种宏大叙事，必须 cite raw_data 里的具体证据

绝对禁止：
- "X（光学公司）受益于 Apple 订单" — 除非 raw_data 里有 Apple 客户关联
- "Y 公司是新能源核心标的" — 除非 raw_data 提到具体新能源业务
- "国家战略支持本股" — 除非 dim_policy 有具体政策原文 cite

非 Claude 模型（Codex/国产模型）尤其容易踩这个坑。stage2 不会自动检测事实正确性，
质量靠 agent 自查。
</HARD-GATE>

### ⛔ HARD-GATE-DATAGAPS · 数据缺口 agent 必须接管（v2.3）

<HARD-GATE>
若 `.cache/{ticker}/_data_gaps.json` 存在且 `tasks` 非空，你**必须**在调用
`stage2()` 之前逐条尝试补齐，按优先级：

1. **浏览器自动化**（最稳）— 用 Chrome/Playwright MCP 打开 xueqiu.com/S/{code}
   或 quote.eastmoney.com/{code}.html 手动抓字段。特别是 `push2.eastmoney.com`
   被反爬时，浏览器是唯一能拿到实时行情的通道。
2. **MX 妙想 API**（若 `MX_APIKEY` 已设置）— 用 `mx_api.MXClient.query()`
3. **WebSearch** 精确到代码（不要只搜公司名 — 会命中同名无关内容）
4. **逻辑推导** — 从已有数据算（净利率 = 净利润 / 营收；PE = 市值/净利润）

**仍然拿不到的字段**：在 `agent_analysis.json` 里写：
```json
"data_gap_acknowledged": {
  "0_basic.industry": "已尝试 xueqiu/eastmoney/ws，均返回空 — 可能是新股或暂停上市",
  "4_peers": "该细分行业上市公司不足 3 家，真的找不到同行"
}
```
stage2 会把这些字段标为"已确认拿不到"，HTML 报告显示划线 chip + "—"而不是假数据。

**绝对禁止**：在补不到数据时用默认值（0 / 空字符串 / "—"）当真实数据用、让规则引擎
对空 features 打分（会产生"没数据的幻觉"，像之前"北部港湾"那次 panel 35.4% 共识
其实是空 features 导致的全 fail_msg）。
</HARD-GATE>

## 📊 进度条规范

每完成一个 Task，输出一行进度条（20 字符固定宽度）：

```
[███░░░░░░░░░░░░░░░░░] 17% · Task 1/6 · 数据采集 ✓
[██████░░░░░░░░░░░░░░] 33% · Task 1.5 · 机构建模 ✓
[██████████░░░░░░░░░░] 50% · Task 2/6 · 维度打分 ✓
[█████████████░░░░░░░] 67% · Task 3/6 · 65 评委 ✓
[████████████████░░░░] 83% · Task 4/6 · 综合研判 ✓
[████████████████████] 100% · Task 5/6 · 报告组装 ✓
```

## 📋 6 Task 概览

| Task | 名称 | 产物 | 角色 |
|---|---|---|---|
| 1 | 22 维数据采集 | `.cache/{ticker}/raw_data.json` | 🤖 脚本 |
| 1.5 | 机构级建模 (DCF/Comps/LBO/3-Stmt/IC/Porter/…) | 内联在 raw_data.json 的 `dim 20/21/22` | 🤖 脚本 + **🧠 你的假设审查** |
| 2 | 22 维打分 + **定性判断** | `.cache/{ticker}/dimensions.json` | 🤖 脚本 + **🧠 你写定性评语** |
| 3 | 65 评委量化裁决 | `.cache/{ticker}/panel.json` | 🤖 规则引擎 |
| 4 | 综合研判 + **叙事合成** | `.cache/{ticker}/synthesis.json` | **🧠 你主导** |
| 5 | 报告组装 | `reports/{ticker}_{YYYYMMDD}/full-report.html` + share-card + war-report | 🤖 脚本 + **🧠 你的金句** |

---

## ⚡ 两段式执行（数据靠脚本，判断靠你）

流水线分两段——**中间你必须介入做 agent 分析**：

### Stage 1 · 数据 + 骨架分（立即执行，不要犹豫）

```bash
cd <repo_root>/skills/deep-analysis/scripts
pip install -r ../../../requirements.txt 2>/dev/null
python -c "from run_real_test import stage1; stage1('<股票名或代码>')"
```

Stage 1 自动完成：Task 1（22 维采集）→ Task 1.5（机构建模）→ Task 2（打分）→ Task 3（规则引擎骨架分）

### 你的分析环节（Stage 1 之后、Stage 2 之前）

<HARD-GATE>
Do NOT run stage2() until ALL of the following are complete:
1. You have READ .cache/{ticker}/panel.json and reviewed the 52 skeleton scores
2. You have SPAWNED sub-agents (or personally analyzed) each investor group
3. You have MERGED agent results back into panel.json with updated headline/reasoning/score
4. You have WRITTEN agent_analysis.json with dim_commentary (≥5 dimensions) + panel_insights
5. You have SET agent_reviewed: true in agent_analysis.json

Skipping this step produces a report with mechanical rule-engine output instead of
genuine investment analysis. The whole point of this plugin is agent-driven judgment.
</HARD-GATE>

核心是：
1. 读 `.cache/{ticker}/panel.json` 中 65 人的骨架分
2. **Spawn 4 个并行 sub-agent 分组 role-play 投资者**——让他们真正"扮演"巴菲特/赵老哥思考
3. 用 agent 的判断覆盖 panel.json 中的 headline/reasoning/score
4. **写 `agent_analysis.json`** 到 `.cache/{ticker}/` — 这是闭环的关键！

**agent_analysis.json 必填字段（缺字段 stage2 会 schema warning/error）：**

| 字段 | 要求 | 触发校验 |
|---|---|---|
| `agent_reviewed` | 必须 `true` | ⚠️ 缺 → warning |
| `dim_commentary` | 至少 5 个维度，**每条 ≥20 字**（引用具体数字，禁止空泛） | 🔴 <20 字 → warning |
| `panel_insights` | **≥30 字**，评委投票分布 + 多空分歧分析 | ⚠️ <30 字 → warning |
| `great_divide_override` | punchline(≥10 字) + bull_say_rounds(≥3 条) + bear_say_rounds(≥3 条) | 🔴 缺字段 → error |
| `narrative_override.core_conclusion` | **≥20 字**综合定论 | ⚠️ <20 字 → warning |
| `narrative_override.risks` | **≥3 条**风险 | ⚠️ <3 条 → warning |
| `narrative_override.buy_zones` | **必须含 value/growth/technical/youzi 四个 key**，每个 key 内含 `price`(数值) + `rationale`(≥5 字解释) | 🔴 缺 key → error / ⚠️ 缺子字段 → warning |
| `qualitative_deep_dive` | 覆盖 3_macro/7_industry/8_materials/9_futures/13_policy/15_events 共 6 维。每维含：`evidence` 数组（≥2 条 `{source, url, finding, retrieved_at}`）、`associations` 跨域因果链（**6 维合计 ≥3 条** `{link_to, chain_id, causal_chain, estimated_impact}`）、`conclusion`（1-2 句）。详见 `references/task2.5-qualitative-deep-dive.md` 第 5 节 | 🔴 evidence 非 list → error / ⚠️ associations<3 → warning |
| `data_gap_acknowledged` | v2.3+ 推荐。dict 格式 `{"dim_key": "已尝试 X 但失败的原因"}`。标记数据采集失败但 agent 已知晓的维度，HTML 报告显示 ⚠️ 橙色徽章而非空白 | 🔴 类型非 dict → error |

#### agent_analysis.json 格式

```json
{
  "agent_reviewed": true,
  "dim_commentary": {
    "0_basic": "建筑央企，主营市政/房建。市值偏小，营收稳但利润率极薄（1.2%），典型低毛利基建股。",
    "1_financials": "ROE 不到 8%，连续 3 年下滑。现金流波动大，应收账款占营收比偏高，回款风险明显。",
    "2_kline": "均线空头排列，MACD 死叉，量能萎缩。典型下跌趋势，不满足 Stage 2 条件。"
  },
  "panel_insights": "65 评委中，价值派集体看空（ROE 太低+无护城河），游资中性（有地方城投概念但板块热度不够），只有少数逆向投资者给出中性偏多。整体共识 32%，偏弱。",
  "great_divide_override": {
    "punchline": "DCF 说高估 23%，但城投重组预期让 LBO 视角的 IRR 仍有 18% — 这个冲突值得关注。",
    "bull_say_rounds": [
      "宁波城投整合预期 + 地方债化解受益，估值有弹性",
      "PB 仅 0.9x，历史底部区间，安全边际够",
      "综合看 62 分，城投故事讲通了就是翻倍"
    ],
    "bear_say_rounds": [
      "ROE 连降 3 年，基建毛利率 8% 是天花板",
      "应收账款 / 营收 > 60%，回款是生死线",
      "综合看 35 分，低质量资产不值得冒险"
    ]
  },
  "narrative_override": {
    "core_conclusion": "宁波建工 · 48 分 · 谨慎。典型地方基建股，ROE 不到 8%、毛利率 8%，靠城投整合讲故事。66 位大佬 12 人看多，29 人看空。DCF 高估 23%，但 LBO 压力测试 IRR 18% — 博弈价值存在但风险更大。",
    "risks": [
      "ROE 持续下滑，连续 3 年低于 8%",
      "应收账款占比过高，回款周期拉长",
      "地方财政压力传导至工程款支付",
      "行业竞争加剧，中标价格战",
      "房建业务受地产下行拖累"
    ],
    "buy_zones": {
      "value": {"price": 3.85, "rationale": "PB 0.8x · 历史底部 + 净资产折价"},
      "growth": {"price": 4.10, "rationale": "城投整合落地前的博弈价"},
      "technical": {"price": 4.25, "rationale": "MA120 支撑位 · 需放量确认"},
      "youzi": {"price": 4.50, "rationale": "城投板块联动时的短线切入点"}
    }
  }
}
```

**stage2() 会自动读取 agent_analysis.json，合并到 synthesis 中。** Agent 写入的字段优先级高于脚本生成的 stub。

### Stage 2 · 生成报告

```bash
python -c "from run_real_test import stage2; stage2('<ticker>')"
```

Stage 2 读取你更新后的 panel.json + agent_analysis.json，合并生成 HTML 报告。
如果没有 agent_analysis.json，退化为纯脚本模式（会打印警告）。

### 快速模式（跳过 agent 介入 · 仅 lite/medium）

如果用户说"快速分析"或时间紧：
```bash
cd <repo_root>
python run.py <股票> --no-browser
```
这会 stage1 + stage2 一把跑完，不做 agent 分析。速度快但评委判断全是规则引擎的机械输出。

> ⚠️ **v3.9.4 明确**：此模式只适用于 lite/medium 档。**`--depth deep` 不属于快速模式**——deep 档必须由你介入 role-play 66 评委并写 `agent_analysis.json`（见上方 HARD-GATE-PERSONA-ROLEPLAY）。看到 deep 档时不要直接 `run.py --depth deep` 一把梭。

---

## 🚀 详细流程（run.py 跑完后的人工审查）

### 第 0 步 · 识别股票

- `run.py` 已经自动识别了 ticker 并跑完所有 Task
- 读 `.cache/{ticker}/raw_data.json` 确认数据
- 向用户汇报："**{name} ({ticker})** 分析完成，正在审查数据质量..."

---

### Task 1-3 · 已由 run.py 自动完成

`run.py` 内部执行了：

这个脚本会：
1. Wave 1 快速 fetcher（basic/kline/financials/valuation）
2. Wave 2 慢速 fetcher（research/events/macro/industry/materials/policy/sentiment/trap）
3. Wave 3 特殊维度（fund_managers, similar_stocks）
4. **Task 1.5 自动跑**：compute_dim_20/21/22 (DCF/Comps/LBO/3-Stmt/IC Memo/Porter/…)
5. 数据完整性校验（`lib/data_integrity.py`）
6. 65 评委量化引擎自动执行

脚本跑完后你读 `.cache/{ticker}/raw_data.json`，向用户汇报：
- 数据快照时间 + 市场状态
- 完整性报告（`_integrity` 字段）
- 有多少个 fallback 维度
- Task 1.5 的核心输出预览

### 🧠 逐维数据质量审查（每一步都必须 agent 介入）

<HARD-GATE>
Do NOT proceed to Stage 2 until you have personally inspected EVERY dimension's data
and fixed any garbage. Scripts collect data, YOU guarantee quality.
If a dimension has irrelevant content (city tourism guides for a stock analysis),
you MUST re-search and replace the data yourself.
</HARD-GATE>

**脚本只是第一道粗搜**。DuckDuckGo 中文搜索经常返回无关结果（搜"宁波建工"返回"宁波旅游攻略"）。你必须**逐维审查 + 修复**：

#### 审查清单（每条都要过）

| 维度 | 检查什么 | 垃圾特征 | 你怎么修 |
|---|---|---|---|
| **0_basic** | name/industry 是否正确 | industry=None | 你 web search `"{code} 所属行业 主营业务"` 补上 |
| **5_chain** | upstream/downstream 是否是这家公司的 | 文字截断或无关 | web search `"{name} 上游供应商 下游客户"` 重写 |
| **7_industry** | 行业增速/TAM 有没有数据 | 全是默认值或空 | web search `"{industry} 行业规模 增速 2026"` |
| **8_materials** | 原材料描述是否相关 | 和主营无关 | web search `"{name} 原材料 成本构成"` |
| **13_policy** | 政策是否与该公司/行业相关 | 搜到无关政策 | web search `"{industry} 最新政策 2026"` |
| **14_moat** | 文字是否是公司分析 | 出现"拼音"、"字典释义"、"汉字演变" | web search `"{name} 竞争优势 核心技术 壁垒"` |
| **15_events** | 事件是否与这家公司相关 | "如何评价宁波"、"宁波旅游"、城市生活指南 | web search `"{name} {code} 最新公告 合同 中标 研发"` |
| **17_sentiment** | 舆情是否在说这家公司 | 短公司名匹配到同名无关内容 | web search `"site:xueqiu.com {name} 股票"` |
| **3_macro** | 宏观环境描述是否有内容 | 全是空或默认 | web search `"中国 {industry} 宏观环境 利率 2026"` |
| **同行对比** | similar_stocks 是否同行业 | 建筑股配了光学同行 | 检查行业是否正确，手动指定正确同行 |

#### 审查流程

```
for each dimension in raw_data.dimensions:
    1. 读数据 → 肉眼扫一遍文字内容
    2. if 内容与公司主营无关 or 明显是垃圾:
        → web search 重新搜（用公司名 + 行业关键词）
        → 用搜索结果替换 raw_data 中的内容
    3. if 数据完全缺失:
        → web search 补充
        → 如果搜不到 → 在报告中标注"数据缺失"而非留空
    4. if 数据看起来合理:
        → 通过，下一个维度
```

#### 重搜模板

当你发现某个维度数据有问题时，用 web search 重搜：

**事件驱动**（最容易出垃圾）：
```
搜索 "{公司全称} {股票代码} 最新公告 合同中标 研发进展 2026"
不要搜 "{城市名}"——只搜公司名和代码
```

**宏观环境**（脚本经常搜不到）：
```
搜索 "中国 {行业} 宏观环境 利率政策 景气度 2026"
```

**护城河**（容易搜到字典页）：
```
搜索 "{公司名} 核心竞争力 技术壁垒 市场份额 护城河"
```

**舆情**（短名容易误匹配）：
```
搜索 "site:xueqiu.com {股票代码}" 或 "site:guba.eastmoney.com {股票代码}"
```

#### 数据缺失时的升级策略

脚本拿不到数据时，**不要留空**，按优先级升级：

1. **Web search**（最快）— 用 WebSearch tool 直接搜
2. **浏览器搜索**（更准）— 用 Chrome/browser tool 打开东方财富/雪球，手动查数据
3. **计算推导**（兜底）— 从已有数据推算（如 从营收和净利算净利率）
4. **标注缺失**（最后手段）— 在报告中明确写"该维度数据暂缺"，不要假装有数据

**每个维度都要有内容。如果 22 个维度里有超过 3 个是空的或垃圾，你的报告就是不合格的。**

**原则：脚本是你的数据采集助手，但你是质量把关人。垃圾数据进报告 = 你的失职。**

### 🧠 你的判断环节（Task 1.5 假设审查）

脚本跑 DCF / LBO / 3-Stmt 用的是默认假设（见 `references/task1.5-institutional-modeling.md`）：
- Stage 1 growth 10% · Stage 2 growth 5% · terminal g 2.5%
- Beta 1.0 · target debt ratio 30% · tax 25%

**你必须审视这些默认值对这只股是否合理**：
- 如果是光学/半导体 → beta 应该 1.3+，stage1_growth 可能 15-20%
- 如果是消费白马 → terminal g 可以给到 3%，beta 可以 0.8
- 如果是 ST / 周期低谷 → stage1_growth 负值，别用 10%

**如果默认假设明显不对**，你应该：
1. 在 Task 4 的叙事里**明说**: "默认 DCF 用 stage1 10% 偏低，行业实际 18%"
2. 或重跑一次：

```python
from lib.fin_models import compute_dcf
adjusted = compute_dcf(features, assumptions={"stage1_growth": 0.18, "beta": 1.3})
```

将调整后的数字写入 `synthesis.json` 的 `adjusted_dcf` 字段供报告引用。

---

### Task 2 · 22 维打分 + **Agent 定性判断** (🤖 脚本 + **🧠 你**)

**脚本部分**：`score_dimensions(raw)` 给每个维度一个 1-10 打分 + weight。

### 🧠 你的判断环节（最重要 — 不能跳过）

脚本的打分是"看数字给分"，但很多维度需要你**真正理解背后的故事**。

**推荐做法**：对关键维度（财报 / 估值 / 护城河 / 行业），spawn 一个 sub-agent 去做 web search，搜索这家公司的最新深度分析文章：

```
Agent prompt:
搜索 "{company_name}" 的最新深度分析，重点关注：
1. 最近一个季度的业绩亮点和隐忧
2. 行业竞争格局变化
3. 管理层最近的公开表态
4. 券商研报的核心观点分歧
来源：雪球 / 东方财富 / 券商研报 / 财经媒体
```

用搜索结果来写每个维度的定性评语——这样你的评语是**基于真实信息的判断**，不是对着数字编故事。

**每个维度你都要写一条 1-2 句话的定性评语**，回答 5 个问题：

1. **数据可信吗？** (数据源 / 时效 / fallback 比例)
2. **数字背后的故事是什么？** (光看 ROE 11.8% 不够 — 为什么从 18% 掉到 11.8%？)
3. **与同行比怎么样？** (peer comparison 里它排第几)
4. **有哪些结构性问题？** (一次性损益 / 关联交易 / 存货堆积)
5. **对论点影响大吗？** (这维度该加权还是降权)

把你的评语写到 `synthesis.json` 的 `dim_commentary` 字段，格式：
```json
"dim_commentary": {
  "1_financials": "ROE 从 2021 年的 18% 掉到 2024 年的 11.8%，主因是…（你的解读）",
  "2_kline": "Stage 2 但距 60 日高点仅 -5%，动量接近顶部…",
  ...
}
```

**没有评语的维度会被标红显示 ⚠️ 未分析**，所以别跳过。

---

### Task 3 · 65 评委审判 (**🧠 Agent 主导 · 规则引擎仅为参考**)

> **核心原则**：每个投资者的判断不是"跑公式"，而是 Claude 真正站在这个人的角度思考。规则引擎给出量化参考分，最终判断由你做。
>
> 详细架构见 `references/task3-agent-evaluation.md`

### Step 3.1 · 跑规则引擎获取骨架分

`run_real_test.py` 已经自动完成了三层评估（`investor_knowledge.py` 现实检验 → `investor_criteria.py` 规则打分 → 合成）。读 `.cache/{ticker}/panel.json` 拿到结果。

### Step 3.2 · Spawn 并行 Sub-Agent（核心步骤）

**你必须 spawn 4 个并行 sub-agent**（用 Agent tool），每个负责一组投资者。**不是让他们跑脚本，而是让他们 role-play 这些投资者做判断**：

**Agent 1 · 价值 + 成长派**（巴菲特/格雷厄姆/费雪/芒格/邓普顿/卡拉曼/林奇/欧奈尔/蒂尔/木头姐 · 10 人）

```
你要扮演 10 位投资大佬，逐一对 {stock_name} ({ticker}) 给出判断。

公司数据摘要：
{raw_data 的关键数据：价格/PE/ROE/行业/护城河/FCF/增速/估值分位...}

规则引擎参考分（仅供参考，你可以覆盖）：
{每人的 rule_score + pass_rules + fail_rules}

真实世界信息：
{investor_knowledge 里的持仓/行业亲和度}

要求：
1. 对每个人，先想"如果我是他，看到这些数据，我会怎么想？"
2. 巴菲特看苹果 → 他实际持有，这比任何规则都重要
3. 格雷厄姆看科技股 → PE > 15 他就不买，但要解释 WHY，不是只说数字
4. 木头姐看量子 → 她会兴奋，看传统制造 → 她会说"不在我们平台里"
5. 每人输出: {investor_id, signal, score, headline(引用数字), reasoning(2-3句)}
```

**Agent 2 · 宏观 + 技术派**（索罗斯/达里奥/马克斯/德鲁肯米勒/罗伯逊 + 利弗莫尔/米内尔维尼/达瓦斯/江恩 · 9 人）

```
宏观派关心：利率周期/汇率/地缘/大宗商品 对这只票的影响
技术派关心：Stage/均线排列/MACD/成交量/距高点距离

数据：{macro_dim + kline_dim 摘要}
```

**Agent 3 · 中国价投 + 量化**（段永平/张坤/朱少醒/谢治宇/冯柳/邓晓峰 + 西蒙斯/索普/肖 · 9 人）

```
中国价投关心：好生意+好价格+好管理，长期持有
量化关心：因子暴露（动量/价值/质量/波动率）

数据：{financials + valuation + moat 摘要}
真实持仓：{段永平持有苹果/茅台/腾讯，张坤重仓白酒...}
```

**Agent 4 · 游资组**（23 人 — 只有 A 股才需要 spawn）

```
如果这只票不是 A 股 → 直接输出 23 人全部 "skip: 不看{market}市场"

如果是 A 股：
- 市值是否在各人射程内？（赵老哥 > 20 亿、章盟主 > 200 亿...）
- 龙虎榜数据：{lhb_dim}
- 最近涨停板：{kline 最近连板情况}
- 板块热度：{sentiment}
- 每人风格不同：赵老哥打板/章盟主趋势/炒股养家情绪/佛山无影脚快进快出
```

### Step 3.3 · 合并 Sub-Agent 结果

4 个 agent 返回后，你逐一把他们的 `{signal, score, headline, reasoning}` 覆盖到 `panel.json` 对应的投资者上。

**如果 sub-agent 给的分和规则引擎差 > 30 分**，在 `panel_insights` 里标记为"分歧点"——这本身是有价值的信息（说明量化指标和主观判断不一致）。

### Step 3.4 · 整体审查

合并后检查：
1. **Great Divide 选角**：最高分的 bull 和最低分的 bear 各是谁？他们的 headline 有没有说服力？
2. **派系一致性**：价值派全看空但技术派全看多 → 这是结构性分歧，写进 synthesis
3. **异常值**：有没有谁的分数明显不合理？（比如巴菲特给苹果 0 分 — 这在新架构下不应该发生了）
4. **Skip 统计**：多少人 skip 了？如果分析美股，23 个游资全 skip 是正常的

将观察写进 `synthesis.json` 的 `panel_insights`。

---

### Task 4 · 综合研判 + 叙事合成 (**🧠 你主导**)

这是整个流程里最依赖你判断的 Task。脚本只给你原材料，最终叙事**必须你写**。

### 🧠 你必须完成的 5 件事

**4.1 构建 Great Divide（多空大分歧）**

找出最有说服力的多方和最有说服力的空方：
- 从 panel 里选 bull 得分最高 + bear 得分最低的两人
- 读他们的 `pass_rules` 和 `fail_rules`
- 让他们"辩论" 3 轮（每轮 2 句话），**引用具体数字**

**4.2 写 3 条核心结论**

用 "但是" 结构，不要和稀泥：
- ✅ "ROE 连续 6 年盈利但从未破 15%，典型的长期平庸。" — 有定论
- ❌ "ROE 有起伏，需要观察。" — 废话

**4.3 估值三角验证**

- DCF 说什么？（dim 20）
- Comps 说什么？
- LBO 说什么？
- 三者**冲突时**，写出冲突并给出你的解读

**4.4 催化剂 + 风险排序**

- 从 dim 21 `catalyst_calendar` 取未来 60 天高影响事件
- 按概率 × 影响度排序 Top 3 催化剂
- 再挑 Top 3 风险（来自 dim 22 IC Memo 的 risks_mitigants）

**4.5 四派系买入区间**

给出 4 个有说服力的价位：
- **价值派**：DCF 内在价 × 0.85 （要 15% 安全边际）
- **成长派**：3 年 EPS × 中位数 PE
- **技术派**：60 日均线附近 或 Stage 2 起涨点
- **游资派**：龙虎榜集中区间

每个价位**必须附一句解释**。

### 写入（v2.2 闭环机制）

以上 5 件事全部写入 **`.cache/{ticker}/agent_analysis.json`**（不是直接写 synthesis.json！）。

stage2() 的 `generate_synthesis()` 会自动读取 agent_analysis.json 并合并：
- `dim_commentary` → 替换脚本占位符
- `panel_insights` → 写入 synthesis
- `great_divide_override` → 替换脚本生成的辩论轮次和金句
- `narrative_override.core_conclusion` → 替换脚本结论
- `narrative_override.risks` → 替换脚本风险
- `narrative_override.buy_zones` → 替换脚本买入区间
- `agent_reviewed: true` → 标记为 agent 已审查

**如果你直接写 synthesis.json，stage2() 会覆盖它。** 必须写 agent_analysis.json，stage2 会合并。

---

### Task 5 · 报告组装 (🤖 脚本 + **🧠 你的金句**)

**脚本部分**：
```bash
python scripts/assemble_report.py {ticker}
python scripts/inline_assets.py {ticker}      # 生成自包含 HTML
python scripts/render_share_card.py {ticker}  # 朋友圈 PNG
python scripts/render_war_report.py {ticker}  # 战报 PNG
```

### 🧠 你的金句审查

在调 assemble_report 之前，**检查一遍** `synthesis.json` 中这 5 个字段：

| 字段 | 检查点 |
|---|---|
| `great_divide.punchline` | 是不是一句能传播的话？有冲突感吗？引用数字了吗？ |
| `dashboard.core_conclusion` | 1-2 句结论，必须有定论 |
| `debate.rounds[*].bull_say / bear_say` | 每轮必须引用具体数字 |
| `buy_zones.*.rationale` | 每个价位必须给出计算逻辑（不能只写"基于技术面"） |
| `risks[*]` | 风险必须具体到数字 / 事件 |

任何一个字段没达标，**直接重写**后再调脚本。

### 完成验证

生成的 HTML 报告打开必须满足：
- 无 console error
- 22 维深度卡全部出现（包含新增的 dim 20/21/22）
- 65 评委聊天室 + 审判席都渲染
- Great Divide punchline 不为空
- 杀猪盘等级显示
- 文件大小 > 400 KB（低于说明有大段缺失）

---

## 🖥️ Codex / 远程环境适配

**如果你在 Codex / Docker / SSH 等无 GUI 环境中运行**，使用 `run.py` 根入口：

```bash
# 在仓库根目录
python run.py <股票代码>                   # 自动检测环境，无浏览器时给路径
python run.py <股票代码> --remote          # 完成后启动 Cloudflare Tunnel，生成公网链接
python run.py <股票代码> --remote --install-cloudflared  # 允许缺失 cloudflared 时自动安装
python run.py <股票代码> --no-browser      # 强制不打开浏览器
```

**`--remote` 模式的工作流**：
1. 正常跑完 6 个 Task，生成 HTML 报告
2. 自动启动本地 HTTP 服务器（端口 8976）
3. 调用 `cloudflared tunnel` 映射到 `https://xxx.trycloudflare.com`
4. 输出公网链接 — 用户手机扫码 / 发微信就能看报告
5. Ctrl+C 停止服务

安全默认值：如果本机没有 `cloudflared`，`--remote` 只提示安装方式，不会自动执行 `brew install` / `sudo mv`。只有用户显式传 `--install-cloudflared` 时才允许自动安装。

**Task 0 可选步骤：询问用户环境**

在开始分析之前，你可以先问用户：
> "你现在在电脑前吗？如果不在，我可以生成一个公网链接方便手机查看。"

如果用户说不在电脑前 → 加 `--remote` 参数。

### Codex / 国产模型 自适配（v2.6 论坛 bug 修复）

非 Claude 平台跑 UZI-Skill 时常见问题已被代码层修复，但 agent 也要主动适配：

| 论坛报告问题 | v2.6 代码层做了什么 | agent 还要做什么 |
|---|---|---|
| `KeyError: 'skip'` | preview_with_mock.py 加 'skip' key + .get() 兜底 | 无 |
| 失败卡死整条 pipeline | as_completed/result 加 90s timeout，单 fetcher 超时不影响其他 | 不要绕过 timeout 重试 |
| 中断不能续跑 | stage1 默认 `--resume`，每 3 个 fetcher 增量保存 raw_data.json；建模段可用 `python run.py <ticker> --from-modeling` 从缓存接力 | 不要手动 `--no-resume` 除非真要重抓 |
| Python 3.9 语法报错 | 所有新文件加 `from __future__ import annotations` | 无 |
| mini_racer V8 thread crash | 给 fetch_industry/capital_flow/valuation 加共享锁 | 无 |
| share/war report 渲染失败 | render_*.py 加 main() alias | 无 |
| 非 Claude agent_analysis 错乱 | stage2 调用 `lib.agent_analysis_validator.validate()` 写 `_agent_analysis_errors.json` | 跑完看 console 是否有 🔴 schema error，按提示修 |
| Top bull/bear 排序错乱 | 排除 score=0 异常，按 score 排（不再先按 signal 分组） | 检查 panel.json 数据合理性 |
| 编造事实 (药明康德↔Apple) | HARD-GATE-FACTCHECK 强制 cite raw_data | **agent 写每条结论都要能在 raw_data 找出处** |

**Codex 启动时检测**：
```bash
echo "${CODEX:-${OPENAI_API_KEY:+codex_via_openai}}"
```

**Codex 推荐设置**：
- `MX_APIKEY=...` 必设（push2 在境外更不稳）
- `--remote` 默认开（生成公网链接，无需 GUI）
- `--no-resume` 别加（断网了能续）

---

## 🎛️ 模式选择

| 触发 | 行为 |
|---|---|
| 默认 | 完整 6 Task |
| `/quick-scan` | 只跑 dim 0/1/2/10/18 + Top 10 投资者，跳过 dim 21/22 |
| `/panel-only` | 跳过 Task 2, 只输出 65 评委 + synthesis |
| `/scan-trap` | 只跑 dim 18 (杀猪盘)，不调评审团 |
| `/dcf` | 只跑 DCF 估值单独输出 |
| `/comps` | 只跑同行对标 |
| `/initiate` | 完整 6 Task + 强制生成机构首次覆盖章节 |
| `/ic-memo` | 完整 6 Task + 强制生成 IC Memo 8 章节 |
| `/catalysts` | 完整 Task + 重点展示催化剂日历 |
| `/thesis` | 只跑 thesis_tracker 单独输出 |
| `/screen` | 跑 5 套量化筛选 |
| `python3 screen.py --mode noon --markets A,H` | 全市场每日猎物榜 · 24 位 F 组独立战法 + Serenity · 最多 10 只 |
| `/dd` | 跑 DD 清单 |

### 全球同行执行规则

`4_peers` 默认同时保留本地同行与全球同行。全球链路按以下顺序执行：

1. 用目标公司的 Yahoo 细分行业发现全球候选。
2. 按发行人去重，排除目标公司的海外二次上市证券。
3. 只补全相关度最高的 8 家年度财务，单家公司失败不重试整条流程。
4. 原币字段保持不变，金额类比较只读取汇率标准化后的 `*_base` 字段。
5. 有效同行少于 3 家时，不生成全球分位或估值结论。
6. Agent 引用全球同行时必须同时写公司、报告期、币种和来源；不得把缺失值写成 0。

支持 `7203.T`、`005930.KS`、`2330.TW`、`D05.SI`、`RELIANCE.NS`、
`SHOP.TO`、`BHP.AX`、`SHEL.L`、`ASML.AS` 等交易所代码。可用
`UZI_DISABLE_GLOBAL_PEERS=1` 关闭，或用 `UZI_GLOBAL_PEER_LIMIT=3..12` 调整补全数量。

## 📁 数据契约 & 文件路径

| 文件 | 谁写 | 谁读 | 闭环角色 |
|---|---|---|---|
| `.cache/{ticker}/raw_data.json` | Task 1/1.5 脚本 | Task 2-5 + 你 | 数据源 |
| `.cache/{ticker}/dimensions.json` | Task 2 脚本 | Task 4-5 | 评分 |
| `.cache/{ticker}/panel.json` | Task 3 规则引擎 → **你覆盖** | stage2 | 骨架→真实判断 |
| **`.cache/{ticker}/agent_analysis.json`** | **🧠 你写** | **stage2 自动合并** | **闭环关键** |
| `.cache/{ticker}/_agent_review_context.json` | stage1 | Agent + stage2 | 当前 raw 快照指纹 · 防止复用旧 role-play |
| `.cache/{ticker}/synthesis.json` | stage2 (合并 agent_analysis) | Task 5 | 最终研判 |
| `reports/{ticker}_{date}/full-report.html` | Task 5 脚本 | 用户 | 报告 |
| `reports/{ticker}_{date}/full-report-standalone.html` | inline_assets.py | 用户分享 | 独立报告 |
| `reports/{ticker}_{date}/share-card.png` | render_share_card | 朋友圈 | 分享卡 |
| `reports/{ticker}_{date}/war-report.png` | render_war_report | 战报 | 战报 |
| `reports/{ticker}_{date}/one-liner.txt` | assemble 副产 | 快速摘要 | 一句话 |

> ⚠️ **agent_analysis.json 是 v2.2 新增的闭环文件。** lite/medium 缺失时可退化为规则报告；deep 档必须把 `_agent_review_context.json` 的 `analysis_input_hash` 原样写入，缺失或过期会被 Stage 2 拒绝。

详细 schema 见 `assets/data-contracts.md`。

## 🔧 工具箱速查

### 估值建模
- `lib.fin_models.compute_dcf(features, assumptions)` — DCF + WACC + 5×5 敏感性
- `lib.fin_models.build_comps_table(target, peers)` — 同行对标
- `lib.fin_models.project_three_stmt(features, assumptions)` — 5 年 IS/BS/CF
- `lib.fin_models.quick_lbo(features, ...)` — PE 买方视角 IRR 测试
- `lib.fin_models.accretion_dilution(acquirer, target, ...)` — 并购增厚/摊薄

### 研究工作流
- `lib.research_workflow.build_initiating_coverage(...)` — 机构首次覆盖
- `lib.research_workflow.build_earnings_analysis(...)` — beat/miss 解读
- `lib.research_workflow.build_catalyst_calendar(...)` — 催化剂日历
- `lib.research_workflow.build_thesis_tracker(...)` — 投资逻辑追踪
- `lib.research_workflow.build_morning_note(...)` — 晨报
- `lib.research_workflow.run_idea_screen(features, style)` — 5 套量化筛选 (value/growth/quality/gulp/short)
- `lib.research_workflow.build_sector_overview(...)` — 行业综述

### 深度决策
- `lib.deep_analysis_methods.build_ic_memo(...)` — 投委会备忘录 8 章
- `lib.deep_analysis_methods.build_unit_economics(...)` — LTV/CAC 或毛利拆解
- `lib.deep_analysis_methods.build_value_creation_plan(...)` — EBITDA 桥
- `lib.deep_analysis_methods.build_dd_checklist(...)` — 5 工作流 21 项 DD
- `lib.deep_analysis_methods.build_competitive_analysis(...)` — Porter 5 Forces + BCG
- `lib.deep_analysis_methods.build_portfolio_rebalance(...)` — 组合再平衡

### 量化评委 / 规则引擎
- `lib.stock_features.extract_features(raw, dims)` — 108 标准化特征
- `lib.investor_criteria.INVESTOR_RULES` — 65 人 236 条规则
- `lib.investor_evaluator.evaluate(investor_id, features)` — 单人裁决
- `lib.investor_evaluator.evaluate_all(features)` — 65 人批量
- `lib.investor_evaluator.panel_summary(results)` — panel 汇总

### 数据质量
- `lib.data_integrity.validate(raw)` — 100% 覆盖度校验器

## 📚 详细参考文档

- `references/task1-data-collection.md` — 22 维 fetcher 清单 + 并行策略
- `references/task1.5-institutional-modeling.md` — **DCF/Comps/LBO 默认参数与 A 股适配**（重要！）
- `references/task2-dimension-scoring.md` — 打分规则
- `references/task3-investor-panel.md` — 65 评委规则
- `references/task4-synthesis.md` — 叙事合成规范
- `references/task5-report-assembly.md` — 报告组装
- `references/fin-methods/README.md` — 22 种机构方法论索引
- `assets/data-contracts.md` — 所有 JSON schema
- `assets/quality-checklist.md` — 完成前的 checklist

## ✅ 完成定义

- **6 个 JSON 产物全部落地**（raw_data + dimensions + panel + agent_analysis + synthesis + report）
- `raw_data.json` 完整性覆盖 ≥ 90%
- **`agent_analysis.json` 必须存在且 `agent_reviewed: true`**
- `dim_commentary` 至少覆盖 15/22 维度（在 agent_analysis.json 中）
- `synthesis.json` 中 punchline / core_conclusion / debate.rounds / buy_zones / risks 都来自 agent 覆盖（通过 agent_analysis.json 合并）
- HTML 报告打开无 console error
- 金句里包含具体数字
- 杀猪盘等级始终显示

---

**现在开始**：从第 0 步识别股票开始。记住 — **你是分析师，不是脚本运行器。**

