diff --git a/AGENTS.md b/AGENTS.md index a7c2e2cd..8c553a0d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,12 @@ validation tools. Keep compatibility with both Claude Code and Codex users. Never assume the current date from training data. - Financial data must come from at least two independent sources when the skill requires verification. +- Before spawning research roles, prove live-data access by opening and reading + one current primary source. If that probe fails, stop time-sensitive research + and request dated user-provided material; never substitute training knowledge. +- Current price, shares, market cap, per-share metrics, currency, corporate + actions, and ADR ratios must follow the point-in-time contract in + `skills/financial-data.md`. - Use exact arithmetic tools for market cap, valuation, cross-source checks, and scenario analysis: `python3 tools/financial_rigor.py ...` @@ -63,6 +69,8 @@ validation tools. Keep compatibility with both Claude Code and Codex users. - Before finishing a skill/tool change, run the relevant syntax or generation check. For compatibility changes, run: `python3 scripts/sync-codex-skills.py` +- For research-contract or audit-tool changes, also run: + `python3 -m unittest discover -s tests -v` - To verify generated Codex artifacts are current without rewriting files, run: `python3 scripts/sync-codex-skills.py --check` and, when slash prompts are relevant: diff --git a/codex-prompts/bottleneck-hunter.md b/codex-prompts/bottleneck-hunter.md index 8d0c31fa..bd3eeacd 100644 --- a/codex-prompts/bottleneck-hunter.md +++ b/codex-prompts/bottleneck-hunter.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `bottleneck-hunter` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/bottleneck-hunter/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/deep-company-series.md b/codex-prompts/deep-company-series.md index e4f90c38..66215b62 100644 --- a/codex-prompts/deep-company-series.md +++ b/codex-prompts/deep-company-series.md @@ -1,11 +1,11 @@ --- -description: "AI Berkshire slash entry for 深度公司系列:8 篇长文拆一家公司." +description: "AI Berkshire slash entry for 看懂XX公司(深度公司系列):3-8 篇长文拆一家公司." argument-hint: $ARGUMENTS --- Use the installed AI Berkshire Codex skill `deep-company-series` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/deep-company-series/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/dyp-ask.md b/codex-prompts/dyp-ask.md index 1bc58e59..657f5a92 100644 --- a/codex-prompts/dyp-ask.md +++ b/codex-prompts/dyp-ask.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `dyp-ask` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/dyp-ask/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/earnings-review.md b/codex-prompts/earnings-review.md index fd118022..f38f187b 100644 --- a/codex-prompts/earnings-review.md +++ b/codex-prompts/earnings-review.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `earnings-review` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/earnings-review/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/earnings-team.md b/codex-prompts/earnings-team.md index c3c68f45..285f4705 100644 --- a/codex-prompts/earnings-team.md +++ b/codex-prompts/earnings-team.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `earnings-team` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/earnings-team/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/financial-data.md b/codex-prompts/financial-data.md index ecc2e97a..940a1528 100644 --- a/codex-prompts/financial-data.md +++ b/codex-prompts/financial-data.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `financial-data` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/financial-data/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/industry-funnel.md b/codex-prompts/industry-funnel.md index f0c54730..84f161b9 100644 --- a/codex-prompts/industry-funnel.md +++ b/codex-prompts/industry-funnel.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `industry-funnel` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/industry-funnel/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/industry-research.md b/codex-prompts/industry-research.md index 1a441bbe..832057e4 100644 --- a/codex-prompts/industry-research.md +++ b/codex-prompts/industry-research.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `industry-research` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/industry-research/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/investment-checklist.md b/codex-prompts/investment-checklist.md index 2ca84363..79bdb4f0 100644 --- a/codex-prompts/investment-checklist.md +++ b/codex-prompts/investment-checklist.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `investment-checklist` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/investment-checklist/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/investment-research.md b/codex-prompts/investment-research.md index 170ab392..ab65bab0 100644 --- a/codex-prompts/investment-research.md +++ b/codex-prompts/investment-research.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `investment-research` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/investment-research/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/investment-team.md b/codex-prompts/investment-team.md index a5f8dc3d..0bef4843 100644 --- a/codex-prompts/investment-team.md +++ b/codex-prompts/investment-team.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `investment-team` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/investment-team/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/management-deep-dive.md b/codex-prompts/management-deep-dive.md index 90941227..111d0b81 100644 --- a/codex-prompts/management-deep-dive.md +++ b/codex-prompts/management-deep-dive.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `management-deep-dive` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/management-deep-dive/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/news-pulse.md b/codex-prompts/news-pulse.md index 10f9f39e..1f8edfd9 100644 --- a/codex-prompts/news-pulse.md +++ b/codex-prompts/news-pulse.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `news-pulse` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/news-pulse/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/portfolio-review.md b/codex-prompts/portfolio-review.md index 04e19320..7fa25181 100644 --- a/codex-prompts/portfolio-review.md +++ b/codex-prompts/portfolio-review.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `portfolio-review` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/portfolio-review/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/private-company-research.md b/codex-prompts/private-company-research.md index 6310edd0..aa9f82c9 100644 --- a/codex-prompts/private-company-research.md +++ b/codex-prompts/private-company-research.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `private-company-research` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/private-company-research/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/quality-screen.md b/codex-prompts/quality-screen.md index f0c64b85..2f7b6c46 100644 --- a/codex-prompts/quality-screen.md +++ b/codex-prompts/quality-screen.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `quality-screen` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/quality-screen/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/thesis-drift.md b/codex-prompts/thesis-drift.md index afc9b11a..bfb62030 100644 --- a/codex-prompts/thesis-drift.md +++ b/codex-prompts/thesis-drift.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `thesis-drift` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/thesis-drift/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/thesis-tracker.md b/codex-prompts/thesis-tracker.md index d2599455..339b3d68 100644 --- a/codex-prompts/thesis-tracker.md +++ b/codex-prompts/thesis-tracker.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `thesis-tracker` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/thesis-tracker/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-prompts/wechat-article.md b/codex-prompts/wechat-article.md index 12502fe7..a0d6faa1 100644 --- a/codex-prompts/wechat-article.md +++ b/codex-prompts/wechat-article.md @@ -5,7 +5,7 @@ argument-hint: $ARGUMENTS Use the installed AI Berkshire Codex skill `wechat-article` for this request. -If the skill is not already loaded, read and follow `~/ai-berkshire/codex-skills/wechat-article/SKILL.md`. +If the skill is not already loaded, locate it through the active Codex skill registry or the current repository checkout. Do not assume a fixed home-directory path. User arguments: $ARGUMENTS diff --git a/codex-skills/bottleneck-hunter/SKILL.md b/codex-skills/bottleneck-hunter/SKILL.md index c7847c18..47b2a56a 100644 --- a/codex-skills/bottleneck-hunter/SKILL.md +++ b/codex-skills/bottleneck-hunter/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 供应链瓶颈猎手:AI驱动的全球产 This skill is generated from `skills/bottleneck-hunter.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -116,7 +117,7 @@ Layer 4: ### 2.3 其他趋势的拆解 -对每个确认的超级趋势执行类似拆解。使用WebSearch搜索: +对每个确认的超级趋势执行类似拆解。使用当前客户端可用的联网检索能力搜索: - "{趋势} supply chain bottleneck 2026" - "{趋势} shortage critical component" - "{趋势} capacity constraint" @@ -174,9 +175,9 @@ B级瓶颈(有压力): ### 4.1 对每个S级和A级瓶颈,找出所有相关上市公司 搜索方式: -- WebSearch "{瓶颈环节} supplier listed company" -- WebSearch "{瓶颈环节} manufacturer stock" -- WebSearch "{瓶颈产品} market share company" +- 搜索 "{瓶颈环节} supplier listed company" +- 搜索 "{瓶颈环节} manufacturer stock" +- 搜索 "{瓶颈产品} market share company" ### 4.2 初筛标准(快速过滤) diff --git a/codex-skills/deep-company-series/SKILL.md b/codex-skills/deep-company-series/SKILL.md index ef63434f..98047b0b 100644 --- a/codex-skills/deep-company-series/SKILL.md +++ b/codex-skills/deep-company-series/SKILL.md @@ -1,6 +1,6 @@ --- name: deep-company-series -description: "AI Berkshire skill: 深度公司系列:8 篇长文拆一家公司. Source: skills/deep-company-series.md." +description: "AI Berkshire skill: 看懂XX公司(深度公司系列):3-8 篇长文拆一家公司. Source: skills/deep-company-series.md." --- ## Codex adapter note @@ -8,14 +8,15 @@ description: "AI Berkshire skill: 深度公司系列:8 篇长文拆一家公 This skill is generated from `skills/deep-company-series.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. -# 深度公司系列:8 篇长文拆一家公司 +# 看懂XX公司(深度公司系列):3-8 篇长文拆一家公司 -为 $ARGUMENTS 撰写一个 8 篇深度长文系列,发布在公众号/视频号等公开渠道。**核心 IP 不是"会写",而是"会改"——99% 的财经文章在违反本 skill 的事实核查标准**。 +为 $ARGUMENTS 撰写一个《看懂XX》深度长文系列(3-8 篇,按公司复杂度定,见"篇数适配"),发布在公众号/视频号等公开渠道。**核心 IP 不是"会写",而是"会改"——99% 的财经文章在违反本 skill 的事实核查标准**。 参考样本:`reports/腾讯/《看懂腾讯》/` @@ -24,7 +25,7 @@ This skill is generated from `skills/deep-company-series.md` so Claude Code and ## 一、触发场景 用户希望为一家公司做"教科书级别"的深度研究,并以**系列长文**形式公开发布。区别于一篇研报: -- 8 篇约 12 万字,从认知重置到决策框架完整闭环 +- 3-8 篇(复杂公司 8 篇约 12 万字,简单公司 3 篇约 3 万字),从认知重置到决策框架完整闭环 - 每篇独立成文(适合单篇分享),但贯穿一套估值/管理层/价格判断 - 写给"愿意花 90 分钟读懂一家公司"的读者,不是写给券商客户 @@ -32,7 +33,29 @@ This skill is generated from `skills/deep-company-series.md` so Claude Code and --- -## 二、系列篇目模板(8 篇) +## 二、系列篇目模板 + +### 篇数适配公司复杂度(先定篇数,再定篇目) + +**篇数不固定为 8。** 8 篇是腾讯这种"多业务 + 万亿投资组合 + 20 年管理层故事"的公司才撑得起的容量。写之前先问:这家公司有几个能用"一个尖锐问题"独立成文的主轴?有几个写几篇。 + +| 复杂度 | 特征 | 篇数 | 例子 | +|--------|------|------|------| +| 高 | 多条业务线各自成生意 + 隐藏资产/投资组合 + 管理层史料丰富 | 7-8 篇 | 腾讯 | +| 中 | 2-3 条业务线 + 一个重大时代变量 | 4-6 篇 | — | +| 低 | 主业清晰、核心问题少而集中 | **3 篇** | 快手 | + +**3 篇的标准合并方式**(下面 8 主轴仍是检查清单,全部要覆盖,只是折进 3 篇): + +| # | 篇名方向 | 折入的主轴 | +|---|---------|-----------| +| 01 | 你以为你看懂了 X(开篇 + 生意与护城河) | 认知重置 + 护城河 + 利润引擎概览 | +| 02 | X 的最大变量(当下叙事/隐藏资产) | 隐藏资产 + AI/时代变量 | +| 03 | 多少钱值得买,什么信号必须卖(财务 + 决策终章) | 财务深度 + 管理层要点 + 估值决策 | + +判断信号:如果某篇写到一半发现在"凑内容"(同一事实换说法重复出现),说明篇数定多了——合并。反之,某篇超过 12,000 字还塞不下,拆开。 + +### 8 主轴全模板(复杂公司用;简单公司当检查清单折叠) | # | 篇名模板 | 核心问题 | 字数 | |---|---------|---------|------| @@ -47,6 +70,32 @@ This skill is generated from `skills/deep-company-series.md` so Claude Code and 加一篇 `00-系列说明.md` 作为目录索引,不发表。 +**篇目必须适配公司**:模板是腾讯长出来的,不是套子。某篇没有独立内容(如没有投资组合的公司写不了 04),就替换成该公司特有的主轴(如快手的可灵 AI、电商),或按上面的合并方式折进更少的篇数。判断标准:这一篇能否用"一个尖锐问题"独立成文。 + +--- + +## 二·五、篇内结构模板(从《看懂腾讯》8 篇正文提炼) + +每篇不是自由发挥,有固定骨架。共性结构: + +- **头部引用块**:`> 《看懂X》系列 · 第 0N 篇 · <主轴>`,开篇加阅读时间 +- **第一节永远是钩子**:一组反常识数字 / 一条反常曲线 / 一个让市场担心的数字,不写背景铺垫 +- **每篇末尾**:`## 本篇要点回顾`(5-8 条,数字与正文严格一致)+ `## 下期预告`(用尖锐问题预告,不剧透结论) +- **结尾两行斜体**:`*本文是《看懂X》系列第 0N 篇...*` + `*本系列基于公开信息和价值投资框架研究,不构成任何投资建议。*` + +各篇专属骨架: + +| 篇 | 骨架 | +|---|------| +| 01 开篇 | 股价被市场反复"重新定性"的历史曲线 → 三个常见错觉(每个用财报数据破)→ "需要同时戴几副眼镜"阅读地图表(链接到 02-08)→ 估值钩子数字表(只留悬念不给结论) | +| 02 护城河 | 挑战者失败名单(有名有姓有年份)→ 迁移成本的本质(用户迁不走什么)→ 生态飞轮 → 5 年/10 年后护城河还在吗 → 一个反向思维实验("如果我有 1000 亿能不能复制它") | +| 03 利润引擎 | 反常识数字开场 → 核心产品为何长青(拆到定价权层面)→ 增长空间量化(份额路径 + 反面论据:故事不工作的可能)→ 组织能力(为什么能持续出产品)→ 市场为什么低估 → AI/时代变量对本业务的影响 → 5 年后图景 | +| 04 隐藏资产 | TOP 持仓表(**只统计未并表部分**,并表的单独说明防双算)→ 会计处理差异(哪些进利润表哪些不进)→ "不会卖 vs 可能减持"分类 → 市场为何打折价(逐条)→ 几个意外发现 → 估算折扣的简单方法 | +| 05 AI/时代变量 | 让市场担心的数字 → 分业务拆赢家/输家(严重分化,不给整体结论)→ 关键反问("X 输了单点之争,还能赢吗")→ 5-10 年被绕过的场景推演(逐条反驳或承认)→ 5 个情景 → 未来一年监控信号清单 | +| 06 财务 | 一条反常曲线开场 → 利润表(毛利率变化归因)→ 口径选择(Non-IFRS vs GAAP,为什么)→ EPS 与回购 → 资产负债表(净现金)→ 现金流与 FCF Yield → 股东回报按"1 美元测试"→ ROE 变化是好是坏 → 估值横向+历史比较 → 价值投资 10 项 Checklist 逐条过 → 把财报压缩成三句话 | +| 07 管理层 | 核心团队表 → 关键人物的长期贡献(用具体决策,不用形容词)→ 利益对齐(持股/薪酬占利润比)→ **诚信检验:挑一次真实危机看应对** → 5 年承诺兑现率(逐条核对当年原话)→ 资本配置纪律 → 接班人隐忧(不回避)→ "买人三问"(诚实/能干/热爱)→ 综合评分 | +| 08 决策 | SOTP 分部估值(说清"你买的到底是什么")→ 历史估值分位 + **必须警告"后视镜不是导航仪"** → 未来利润三情景表(乐观/中性/保守 + 隐含增速,标明"是预测不是事实")→ 红线清单(触发≠必卖,但必须重审)→ 赔率思维收尾 | + --- ## 三、写作风格规范 @@ -119,16 +168,17 @@ This skill is generated from `skills/deep-company-series.md` so Claude Code and 3. 用 `/investment-team` 或 `/investment-research` 先生成内部研究底稿 4. 与用户确认 8 篇的核心论点(避免写完才发现方向不对) -### 阶段 2:写作(按 01→08 顺序写,不跳) +### 阶段 2:写作(按 01→末篇顺序写,不跳) - 每篇写完先存 `reports/{公司名}/《看懂{公司名}》/0X-XX.md` +- **目录冲突规则**:若 `reports/{公司名}/《看懂{公司名}》/` 已存在(旧版系列),不覆盖、不混放——新系列写入带日期后缀的新文件夹 `reports/{公司名}/《看懂{公司名}》-{YYYYMMDD}/`,旧文件夹原样保留 - 不立即推 GitHub——等用户审阅 - 用户提修订意见后修改 - 修订完才 git push -### 阶段 3:跨篇一致性扫描(08 篇全部写完后) +### 阶段 3:跨篇一致性扫描(全部篇目写完后) -派 Explore agent 并行扫描 8 篇做以下检查: +派 Explore agent 并行扫描全系列做以下检查: 1. 同一数字(市值、净利润、持股比例)跨篇是否一致 2. 同一术语(FBS、SBC、Non-IFRS)首次出现是否解释 3. 引用关系:02 篇说"详见 06 篇"是否真的对应 @@ -151,7 +201,7 @@ grep -r "<本机用户名>\|/Users/\|<个人身份信息>" reports/ | head ### 1. 先核查事实(不要直接改) -如果用户说"X 数据不对",先用 Bash/Read 找原始数据交叉验证: +如果用户说"X 数据不对",先用本地 shell 和文件读取能力找原始数据交叉验证: - 看 ai-berkshire 项目里同公司的 earnings/财报报告 - 看富途/官方披露 - 给出"用户说的数据 vs 我查到的数据 vs 我之前用的数据"三方对比 diff --git a/codex-skills/dyp-ask/SKILL.md b/codex-skills/dyp-ask/SKILL.md index 04bcd9c0..3c0a72b9 100644 --- a/codex-skills/dyp-ask/SKILL.md +++ b/codex-skills/dyp-ask/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 段永平问答:以他的方式思考. Sourc This skill is generated from `skills/dyp-ask.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. diff --git a/codex-skills/earnings-review/SKILL.md b/codex-skills/earnings-review/SKILL.md index 97304c09..c8191120 100644 --- a/codex-skills/earnings-review/SKILL.md +++ b/codex-skills/earnings-review/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 财报精读:一手资料深度解读. Sourc This skill is generated from `skills/earnings-review.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -46,7 +47,7 @@ This skill is generated from `skills/earnings-review.md` so Claude Code and Code ### 第一步:获取一手资料 -使用 Task 工具启动多个后台 Agent **并行**获取以下原始材料: +按客户端并发能力启动多个独立研究子任务获取以下原始材料;并发不足时分批,不支持子任务时顺序执行: 1. **财报原文**:从公司IR页面、SEC EDGAR(美股10-K/10-Q)、港交所披露易(港股)、巨潮资讯网(A股)获取 2. **业绩电话会纪要/录音**:从 Seeking Alpha、公司IR页面、雪球等获取 diff --git a/codex-skills/earnings-team/SKILL.md b/codex-skills/earnings-team/SKILL.md index 66408380..5746908f 100644 --- a/codex-skills/earnings-team/SKILL.md +++ b/codex-skills/earnings-team/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 财报精读团队:四大师并行解读 + This skill is generated from `skills/earnings-team.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -36,7 +37,7 @@ This skill is generated from `skills/earnings-team.md` so Claude Code and Codex ### 第一步:获取一手资料 -使用 Agent 工具启动后台 Agent **并行**获取以下原始材料: +按客户端并发能力启动独立研究子任务获取以下原始材料;并发不足时分批,不支持子任务时顺序执行: | 资料类型 | 获取来源 | 优先级 | |---------|---------|--------| @@ -69,7 +70,7 @@ This skill is generated from `skills/earnings-team.md` so Claude Code and Codex ### 第三步:启动4个并行研究Agent -使用 Agent 工具在**同一条消息**中启动4个后台 Agent。 +启动4个独立研究角色;尽可能并行,并发不足时分批执行。 --- diff --git a/codex-skills/financial-data/SKILL.md b/codex-skills/financial-data/SKILL.md index 2382a262..bc98ac16 100644 --- a/codex-skills/financial-data/SKILL.md +++ b/codex-skills/financial-data/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 财务数据获取与交叉验证规范. Sourc This skill is generated from `skills/financial-data.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -17,6 +18,77 @@ This skill is generated from `skills/financial-data.md` so Claude Code and Codex 本规范适用于所有涉及企业财务数据的研究。**每个关键数据必须来自两个独立来源,误差>1%须标记。** +这里的“独立来源”指数据生成链彼此独立。两个转载同一份聚合数据的网站只算一个来源;公司公告/交易所文件与第三方数据库可算两个来源。 + +--- + +## 研究准入闸门(先验证能力,再启动研究) + +1. 先执行系统日期命令,记录研究日期和时区。 +2. 用当前客户端实际可用的联网能力,打开一个与标的直接相关的一手页面(公司 IR、交易所或监管文件),确认能读到页面标题、发布日期和正文片段。不要通过检查某个客户端的配置文件来推断联网能力。 +3. 若实时联网不可用: + - 立即停止“最新股价/最新财报/当前估值”研究; + - 明确列出缺失能力和所需材料; + - 只有用户提供带日期的文件后,才可切换为“离线材料分析”; + - 禁止用训练知识、搜索摘要或旧报告冒充最新数据。 +4. 每个子任务都要返回 `data_cutoff`、`source_status` 和 `source_gaps`。主任务发现任一关键子任务未联网或缺少一手材料时,不得给出当前价格区间。 + +--- + +## 时点快照契约(股价、市值、每股指标) + +报告中的当前股价、市值、股本和每股指标必须能还原到同一个时点: + +| 字段 | 必填元数据 | +|------|-----------| +| 当前股价 | 交易所/代码、币种、价格类型(收盘/盘中)、时间戳与时区、来源链接 | +| 总股本 | 基本/稀释口径、对应报告期或公告日、是否扣除库存股、来源链接 | +| 市值 | `股价 × 同口径股本` 的工具输出,不得直接抄聚合站结论 | +| EPS/BVPS/FCF per share | 分子口径、加权平均或期末股数、报告期、是否复权 | + +硬规则: + +1. 股价与股本日期不一致时,先检查期间内回购、增发、拆并股、送转、配股和 ADR 比例变化;无法桥接就标记“不可比”,不得计算当前估值。 +2. 当前市值验算偏差超过 5% 时必须打回;1%–5% 只能以警告状态进入人工复核,不得写成“验证通过”。 +3. 历史每股序列跨越公司行动时,必须同时调整价格与每股分母;只复权价格、不复权 EPS/股本同样会产生假 PE。 +4. 数据源页面没有明确时间戳、币种或口径时,不得作为当前快照的主来源。 + +--- + +## 币种、上市地与 ADR 规则 + +1. 目标价和买入区间始终使用标的实际交易币种,并写明交易所与代码;不得把港股目标价换成美元后省略换算。 +2. 同一张表、同一段估值、同一组情景只使用一种金额口径。跨币种比较时统一到一个展示币种,并在表头或脚注明确:汇率、汇率日期、来源、ADR/ADS 比例。 +3. 公司报告币种与交易币种不同时,先在报告币种完成利润/现金流估值,再单独执行汇率和 ADR 换算。换算步骤必须可复算。 +4. 人民币金额可保留人民币;非人民币金额不强制统一成美元。选择展示币种的原则是减少换算链,而不是追求表面统一。 + +--- + +## 内在价值独立性检查 + +买入区间不得由“当前价打八折”或历史价格位置反推: + +1. 先在看不到当前股价的情况下,根据可持续盈利/自由现金流、资本成本、增长和退出倍数计算内在价值区间。 +2. 再揭示当前价,计算安全边际,并用反向 DCF/反向估值说明市场价格隐含了什么假设。 +3. 至少给出一个关键假设敏感性表。若目标价随当前价同步漂移但基本面假设未变,必须判为估值锚定失败并重算。 +4. 关键输入无法验证时,只输出“可接受估值条件”,不输出精确买入价。 + +--- + +## 行业差异化指标 + +通用指标之外,先识别行业,再加入能解释经济性的专属指标: + +| 行业 | 至少覆盖 | +|------|---------| +| 制造业 | 存货周转、产能利用率、在建产能、资本开支、订单/交付、ROIC、单位成本 | +| 银行/保险 | 净息差、资产质量、不良生成与处置、拨备、资本充足率、久期/偿付能力 | +| 消费/零售 | 同店增长、量价拆分、渠道库存、复购、门店回收期、市场份额 | +| SaaS/平台 | NRR/留存、CAC 回收期、毛利率、变现率、单位经济、网络效应强度 | +| 资源/周期 | 现金成本曲线、储量寿命、产量指引、资本强度、周期中枢假设 | + +市场份额必须写清“产品/类别、地区、时间、分母定义、来源”。不同机构口径不一致时展示区间,禁止拼成一个伪精确值。 + --- ## 数据源优先级 diff --git a/codex-skills/industry-funnel/SKILL.md b/codex-skills/industry-funnel/SKILL.md index 246b5672..371b6ab5 100644 --- a/codex-skills/industry-funnel/SKILL.md +++ b/codex-skills/industry-funnel/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 行业漏斗筛选:从全市场到 3 家的 This skill is generated from `skills/industry-funnel.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. diff --git a/codex-skills/industry-research/SKILL.md b/codex-skills/industry-research/SKILL.md index bea4fc3d..c710a528 100644 --- a/codex-skills/industry-research/SKILL.md +++ b/codex-skills/industry-research/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 行业投资研究:产业链全景扫描 + This skill is generated from `skills/industry-research.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -96,7 +97,7 @@ This skill is generated from `skills/industry-research.md` so Claude Code and Co ## 第三步:全球上市公司扫描 -使用 Task 工具启动后台 Agent,全面搜索该行业所有上市公司。 +启动独立研究子任务,全面搜索该行业所有上市公司;客户端不支持子任务时顺序执行。 ### 搜索清单 - 美股(NYSE/NASDAQ/NYSE American)相关公司 diff --git a/codex-skills/investment-checklist/SKILL.md b/codex-skills/investment-checklist/SKILL.md index f3d85c2a..00656938 100644 --- a/codex-skills/investment-checklist/SKILL.md +++ b/codex-skills/investment-checklist/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 巴菲特价值投资买入前 Checklist. Sour This skill is generated from `skills/investment-checklist.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -43,7 +44,7 @@ This skill is generated from `skills/investment-checklist.md` so Claude Code and ### 第二步:并行数据收集 -使用 Task 工具为**每家公司**启动独立的后台 Agent 进行数据收集(所有公司同时并行启动),每个Agent负责收集: +按客户端并发能力为**每家公司**启动独立数据收集子任务;并发不足时分批执行,不支持子任务时顺序执行。每个子任务负责收集: 1. **盈利能力**:ROE(5-10年趋势)、毛利率、净利率、自由现金流 2. **估值数据**:当前股价、市值、PE(TTM)、前瞻PE、PB、股息率 diff --git a/codex-skills/investment-research/SKILL.md b/codex-skills/investment-research/SKILL.md index c7e5381f..10b698eb 100644 --- a/codex-skills/investment-research/SKILL.md +++ b/codex-skills/investment-research/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 投资研究:巴菲特-芒格-段永平-李 This skill is generated from `skills/investment-research.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -54,18 +55,19 @@ This skill is generated from `skills/investment-research.md` so Claude Code and > - 港股:aastocks(主)+ macrotrends ADR(副) > - A股:东方财富(主)+ 巨潮资讯(副) -使用 Task 工具启动后台 Agent,从网络收集以下数据: +先按 `skills/financial-data.md` 执行实时能力探针。探针通过后,启动独立研究子任务从网络收集以下数据;客户端不支持子任务时由主任务顺序收集: 1. 收入结构:最近财年及近4季度分部收入、增速、毛利率 2. 财务指标:近5年收入、净利润、毛利率、经营利润率、自由现金流、现金储备 -3. 竞争格局:市场份额、主要竞争对手对比 +3. 竞争格局:市场份额、主要竞争对手对比;每个份额写明产品/类别、地区、时间、分母定义和来源 4. 商业模式与护城河:核心竞争优势来源 5. 技术能力:核心技术栈、研发投入 6. 管理层:创始人/CEO履历、持股比例、关键决策记录 7. 行业前景:TAM(总可寻址市场)、增长预测 8. 风险因素:地缘政治、监管、供应链等 -9. 当前估值:市值、PE、PS、PEG、EV/Revenue +9. 当前估值:市值、PE、PS、PEG、EV/Revenue;价格时间戳、交易所、币种、最新股本日期/口径、公司行动和 ADR 比例必须对齐 10. 多空双方核心论点 +11. 行业专属指标:按 `skills/financial-data.md` 的行业矩阵选择制造业、金融、消费、SaaS或资源指标 #### 数据交叉验证(必须执行,使用金融严谨性工具) @@ -77,8 +79,9 @@ This skill is generated from `skills/investment-research.md` so Claude Code and - 最近财年收入和净利润(从公司年报+至少1个第三方源确认) - 现金储备和净现金(现金+短期投资-总债务,注意口径差异) - 管理层持股比例(区分经济权益和投票权,注意AB股结构) +- 价格/股本时点桥接(回购、增发、拆并股、送转、配股、ADR比例变化) -**强制验证步骤(使用Bash调用工具)**: +**强制验证步骤(使用本地 shell 调用工具)**: Step 1 — 市值验算(精确十进制,非浮点): ```bash @@ -105,6 +108,7 @@ python3 tools/financial_rigor.py verify-valuation \ 3. **所有涉及计算的数据必须通过工具验算,禁止LLM心算** 4. 工具输出结果直接嵌入报告附录"关键数据交叉验证记录" 5. 如果工具报告 ❌ 偏差过大,必须排查原因后才能继续分析 +6. 先在隐藏当前股价的条件下计算内在价值,再用当前价计算安全边际和反向估值;不得用“现价打折”生成买入区间 **常见错误防范**: - 市值单位:港币亿 vs 人民币亿 vs 美元亿,容易漏写/多写一个零 diff --git a/codex-skills/investment-team/SKILL.md b/codex-skills/investment-team/SKILL.md index 45eb07dc..28423ab0 100644 --- a/codex-skills/investment-team/SKILL.md +++ b/codex-skills/investment-team/SKILL.md @@ -8,20 +8,21 @@ description: "AI Berkshire skill: 投研团队:四角色并行分析框架. So This skill is generated from `skills/investment-team.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. # 投研团队:四角色并行分析框架 -对 $ARGUMENTS 进行团队化投资研究分析。使用 Team 工具创建真正的多Agent并行研究团队。 +对 $ARGUMENTS 进行团队化投资研究分析。工作流只描述角色、输入、输出和验收条件,不依赖任何客户端专属的团队工具名称。 ## 执行流程 ### 第一步:展示团队框架 -向用户展示以下团队结构,确认后启动: +向用户展示以下团队结构后直接启动;只有标的、上市地或研究深度不明确且会实质改变结果时才请求确认: | 角色 | 职责 | 分析框架 | |------|------|----------| @@ -46,15 +47,29 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code 将评级结果告知每个Agent,影响其研究方式。 -### 第二步:创建团队 +### 第一步¾:实时能力预检(关键 · 失败即停止) -使用 TeamCreate 创建团队: -- team_name: `{公司名}-research`(英文小写,如 `meituan-research`) -- agent_type: `team-lead` +在启动研究角色前,由主任务亲自执行一次真实联网探针:打开该公司的投资者关系页面、交易所披露或监管文件,记录页面标题、发布日期和正文中的一个可核对字段。 -### 第三步:创建4个任务 +- 探针成功:记录 `research_date`、时区、页面 URL 和最新可用数据截止日,再继续。 +- 探针失败或只能看到搜索摘要:停止当前估值研究,说明缺少的能力,并请求用户提供财报/公告文件;不得让后台角色用训练知识补齐“最新数据”。 +- 不检查某个客户端的私有配置文件来推断能力;以实际读取结果为准。 -使用 TaskCreate 创建以下4个任务(每个都要有 subject、description、activeForm): +### 第二步:选择研究深度与执行形态 + +默认使用“标准”深度,并在开头声明: + +| 模式 | 每角色资料范围 | 输出目标 | +|------|---------------|---------| +| 快速 | 1份一手材料 + 1份独立验证来源 | 先判断是否值得深挖,不给伪精确目标价 | +| 标准(默认) | 2份以上一手材料 + 必要的行业/竞品来源 | 完整四视角报告 | +| 深度 | 多期财报、电话会、竞品与行业材料 | 高重要性决策;预期消耗显著增加 | + +将四个角色作为独立研究子任务并行执行。若客户端并发不足,按可用并发数分批;若不支持子任务,则由主任务按同一数据契约顺序执行四个角色。能力差异只能改变调度方式,不能删减验收标准。 + +### 第三步:定义4个独立任务 + +为以下4个任务分别提供 subject、description 和结构化返回字段: #### 任务1:商业模式分析 - subject: `分析{公司名}商业模式、护城河与用户价值` @@ -76,7 +91,9 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code 4. 资产负债表健康度:现金储备、负债率、流动性 5. 估值分析:PE/PS/PB/EV等,与历史及同业对比 6. 安全边际评估:内在价值 vs 当前股价 - 7. **金融严谨性验证(必须使用Bash调用工具,禁止心算)**: + 7. 当前快照对齐:价格时间戳/币种/交易所、最新股本日期与口径、期间公司行动、ADR比例 + 8. 估值独立性:先不看当前价计算内在价值,再做反向DCF和敏感性分析 + 9. **金融严谨性验证(必须使用本地 shell 调用工具,禁止心算)**: - 市值验算:`python3 tools/financial_rigor.py verify-market-cap --price {价格} --shares {股本} --reported {报告市值} --currency {币种}` - 估值验算:`python3 tools/financial_rigor.py verify-valuation --price {价格} --eps {EPS} --bvps {每股净资产}` - 关键数据交叉验证:`python3 tools/financial_rigor.py cross-validate --field {字段} --values '{JSON}' --unit {单位}` @@ -87,12 +104,13 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code - subject: `分析{行业}行业格局与{公司名}竞争态势` - description 包含: 1. 行业规模与增长:市场规模、增速、渗透率 - 2. 竞争格局:主要对手市场份额、竞争策略对比 + 2. 竞争格局:主要对手市场份额、竞争策略对比;份额必须带产品/类别、地区、时间、分母定义和来源 3. 核心竞争者威胁评估:逐个分析主要竞争对手 4. 各细分赛道格局 5. 行业趋势:技术变革、政策影响、新进入者 6. 产业链分析:上中下游价值分配 7. 要求搜索最新行业数据和竞争动态 + 8. 按 `skills/financial-data.md` 的行业指标矩阵补充制造业、金融、消费、SaaS或资源行业的专属经营指标 #### 任务4:风险与管理层评估 - subject: `评估{公司名}投资风险与管理层质量` @@ -106,17 +124,11 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code 7. 长期确定性:10年后公司会怎样?什么可能颠覆其商业模式? 8. 要求搜索最新监管动态、管理层言论等 -### 第四步:启动4个并行Agent - -使用 Task 工具同时启动4个Agent(**必须在同一条消息中并行调用**): +### 第四步:执行研究角色 -每个Agent的配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` -- `team_name`: 对应团队名 -- `name`: 对应角色名(business-analyst / financial-analyst / industry-researcher / risk-assessor) +在客户端允许的并发范围内尽可能同时启动四个独立研究角色;不能一次启动四个时分批执行,不得因此宣称“已完全并行”。 -每个Agent的prompt模板: +每个角色的输入模板: ``` 你是{公司名}投研团队中的"{角色中文名}",负责从{大师名}投资视角分析{公司名}。 @@ -127,19 +139,19 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code {任务description的内容} **研究方法**: -- 使用 WebSearch 搜索最新公开信息(财报、行业报告、新闻) +- 使用当前客户端可用的联网检索能力获取最新公开信息,优先财报、交易所披露和公司公告 - **财务数据必须来自两个独立来源**,按 `skills/financial-data.md` 规范执行(美股:macrotrends+stockanalysis;港股:aastocks+macrotrends;A股:东方财富+巨潮资讯),两源误差>1%须标记 - 确保数据准确,关键数据标注来源 - 分析要深入,不流于表面 +- **联网失败禁止降级续写**:若无法打开一手页面或核对正文,立即返回 `BLOCKED_NO_LIVE_DATA`,列出缺失材料;禁止用训练知识冒充联网结果 +- 返回 `data_cutoff`、`primary_sources`、`independent_sources`、`source_gaps` 和关键数据口径 **输出要求**: - 报告要详尽,使用Markdown表格呈现关键数据 - 每个分析维度要有明确结论和评分 - 报告末尾要有该维度的总体结论 -**完成后**: -1. 使用 TaskUpdate 将任务 #{任务编号} 标记为 completed -2. 通过 SendMessage 把完整分析报告发送给 team-lead(type: "message", recipient: "team-lead") +**完成后**:直接向主任务返回结构化结果,不依赖文件或客户端专属消息协议。 ``` ### 第五步:接收报告并跟踪进度 @@ -148,10 +160,6 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code - 每收到一份报告,更新进度并展示该报告的核心要点(3-5条) - 等待全部4份报告到齐 -### 第六步:关闭团队成员 - -全部报告收到后,向4个Agent发送 shutdown_request(使用 SendMessage,type: "shutdown_request")。 - ### 第七步:汇总最终报告 综合4份分析报告,输出以下结构的最终报告: @@ -193,7 +201,7 @@ This skill is generated from `skills/investment-team.md` so Claude Code and Code ### 第八步:保存报告 -将完整最终报告写入 `~/{公司名}投资研究报告_{日期}.md`(日期格式 YYYYMMDD)。 +将完整最终报告写入 `reports/{公司名}/{公司名}-investment-team-{日期}.md`(日期格式 YYYYMMDD)。若目标文件已存在,使用带时间或递增后缀的新文件名,不覆盖旧报告。 ### 第九步:数据抽检(准出流程) @@ -208,21 +216,23 @@ python3 tools/report_audit.py extract \ python3 tools/report_audit.py verdict \ --results '<填好的JSON>' \ --report <报告文件名> + +# Step 4 — 检查报告是否至少包含两个独立信源 +python3 tools/report_audit.py sources \ + --report <报告文件路径> \ + --min 2 ``` **【准出】** 全部通过 → 报告可发布;**【打回】** 有不通过 → 修正后重审。 -### 第十步:清理团队 - -使用 TeamDelete 清理团队资源。 - ## 重要注意事项 -1. **4个Agent必须并行启动**——在同一条消息中调用4次Task工具 -2. **Agent通过SendMessage汇报**——不是文件协作,是消息通信 -3. **数据准确性**——要求Agent使用WebSearch搜索最新数据,关键数据交叉验证 +1. **四角色必须全部完成**——并发不足时分批执行;不得静默省略某个视角 +2. **角色直接返回结构化结果**——不依赖客户端专属团队/消息 API +3. **数据准确性**——按 `skills/financial-data.md` 的时点、复权、币种、股本和双来源契约执行 4. **结论要明确**——不回避给出买入/观望/回避建议和具体价格区间 5. **所有分析必须有数据支撑**——附数据来源 6. **耐心等待**——4个Agent研究需要几分钟,实时向用户更新进度 7. **反偏见意识**——team-lead在汇总时必须评估:各Agent的分析是否受限于资料充裕度?是否与市场共识过度趋同?最终报告需包含"信息丰富度评级"和"AI研究局限性声明" 8. **信息稀缺时的诚实原则**——宁可在报告中留白标注"数据不足",也不要用推测填满框架伪装确定性 +9. **估值独立性**——先在隐藏当前价的条件下计算内在价值,再比较当前价;目标价随市价漂移但基本面未变时必须重算 diff --git a/codex-skills/management-deep-dive/SKILL.md b/codex-skills/management-deep-dive/SKILL.md index a9368d02..20d8d5fb 100644 --- a/codex-skills/management-deep-dive/SKILL.md +++ b/codex-skills/management-deep-dive/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 管理层纵深研究:买股票就是买人. This skill is generated from `skills/management-deep-dive.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -39,7 +40,7 @@ AI无法和管理层吃饭,但可以通过公开信息做到: ### 第一步:识别关键管理层并启动并行数据收集 -使用 WebSearch 确认以下关键人物: +使用当前客户端可用的联网检索能力确认以下关键人物: | 角色 | 姓名 | 任期 | 背景 | 持股/期权 | |------|------|------|------|----------| @@ -51,7 +52,7 @@ AI无法和管理层吃饭,但可以通过公开信息做到: **注意**:区分"谁在做决策"和"谁的名字在头衔上"。有些公司创始人虽然卸任但仍是灵魂人物(如黄峥之于拼多多)。 -确认关键人物后,使用 Task 工具启动多个后台 Agent **并行**收集以下数据: +确认关键人物后,按客户端并发能力启动多个独立研究子任务收集以下数据;并发不足时分批执行: 1. Agent 1:CEO公开发言与预测记录(股东信、电话会、采访、社交媒体) 2. Agent 2:资本配置决策记录(并购、回购、分红、新业务投资) 3. Agent 3:治理结构与薪酬(股权结构、关联交易、高管薪酬) diff --git a/codex-skills/news-pulse/SKILL.md b/codex-skills/news-pulse/SKILL.md index 23d283b1..37830e19 100644 --- a/codex-skills/news-pulse/SKILL.md +++ b/codex-skills/news-pulse/SKILL.md @@ -8,7 +8,8 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 This skill is generated from `skills/news-pulse.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -51,15 +52,13 @@ This skill is generated from `skills/news-pulse.md` so Claude Code and Codex use 将评级告知每个 Agent,影响其侦察方式。 -### 第三步:创建团队 +### 第三步:实时能力探针 -使用 TeamCreate 创建团队: -- `team_name`: `{公司名}-newspulse`(英文小写,如 `pdd-newspulse`) -- `agent_type`: `team-lead` +主任务先打开一个覆盖异动窗口的一手或权威页面,确认标题、发布日期和正文可读。若联网不可用或只能读取搜索摘要,立即返回 `BLOCKED_NO_LIVE_DATA`;新闻归因禁止退化为训练知识推测。 -### 第四步:创建 4 个侦察任务 +### 第四步:定义 4 个侦察任务 -使用 TaskCreate 创建以下 4 个任务: +定义以下 4 个独立任务,并要求每个任务返回事件时间、来源 URL、发布日期、事实/推断标签和未排除解释: #### 任务 1:公司事件侦察(company-event-scout) @@ -114,13 +113,9 @@ This skill is generated from `skills/news-pulse.md` so Claude Code and Codex use 6. **技术面信号**:是否触及关键支撑/阻力、是否有大宗交易、融资融券异常 7. 关键判断:**是基本面驱动还是情绪/资金面驱动?** -### 第五步:并行启动 4 个 Agent +### 第五步:执行 4 个侦察角色 -**必须在同一条消息中并行调用 4 次 Task 工具**。每个 Agent 配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` -- `team_name`: `{公司名}-newspulse` -- `name`: 对应角色名(company-event-scout / regulatory-watcher / industry-peer-analyst / sentiment-tracker) +在客户端允许的并发范围内尽可能同时执行 4 个独立角色;并发不足时分批执行,不支持子任务时由主任务顺序完成。不得把“分批”写成“完全并行”。 每个 Agent 的 prompt 模板: @@ -137,8 +132,8 @@ This skill is generated from `skills/news-pulse.md` so Claude Code and Codex use {任务description的内容} **侦察方法**: -- 优先使用 WebSearch 搜索时效性查询(关键词加日期或"最近"、"latest"、"2026") -- 关键事件用 WebFetch 精读原始来源(公告原文、财报、监管文件) +- 优先使用当前客户端可用的联网检索能力搜索时效性查询(关键词加具体日期或“最近”) +- 打开并精读原始来源(公告原文、财报、监管文件),不得只依赖搜索摘要 - 对每个事件做"独立信源验证"——传言至少要 2 个独立来源 - **不要被标题党误导**:标题与正文不符的事件要标注"标题误导" @@ -150,16 +145,14 @@ This skill is generated from `skills/news-pulse.md` so Claude Code and Codex use 4. **数据缺口声明**:哪些信息没找到、哪些有疑点、哪些需要等更多信息 5. 严格区分"事实"与"推测",遵循 CLAUDE.md 客观性原则 -**完成后**: -1. 使用 TaskUpdate 将任务标记为 completed -2. 通过 SendMessage 把完整侦察报告发送给 team-lead(type: "message", recipient: "team-lead") +**完成后**:直接向主任务返回结构化侦察结果,不依赖客户端专属消息协议。 ``` ### 第六步:实时跟踪进度 - 每收到一份侦察报告,向用户展示该维度的 3 条核心发现 - 等待全部 4 份到齐 -- 全部到齐后,通过 SendMessage 向 4 个 Agent 发送 shutdown_request +- 全部到齐后核对四个任务的时间窗口和来源状态;缺任一维度时不得给出确定性归因 ### 第七步:team-lead 综合归因 @@ -228,9 +221,9 @@ This skill is generated from `skills/news-pulse.md` so Claude Code and Codex use 写入 `reports/{公司名}/{公司名}-news-{YYYYMMDD}.md`。如果 `reports/{公司名}/` 目录不存在则创建(说明该公司还没建过任何研究报告)。 -### 第九步:清理团队 +### 第九步:完成状态检查 -使用 TeamDelete 清理团队资源。 +确认没有遗留后台任务,并记录本次检索截止时间。 ## 关键原则 diff --git a/codex-skills/portfolio-review/SKILL.md b/codex-skills/portfolio-review/SKILL.md index e7665f59..2e9c2bb2 100644 --- a/codex-skills/portfolio-review/SKILL.md +++ b/codex-skills/portfolio-review/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 组合管理:从\"研究公司\"到\"管理 This skill is generated from `skills/portfolio-review.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -51,7 +52,7 @@ This skill is generated from `skills/portfolio-review.md` so Claude Code and Cod ### 第二步:获取最新数据 -使用 Task 工具启动后台 Agent,通过 WebSearch 为每个持仓并行获取: +按客户端并发能力为每个持仓启动独立研究子任务,通过可用的联网检索能力获取;不支持子任务时顺序执行: 1. 当前股价和估值指标(PE、PB、股息率) 2. 最近一个季度的关键财务变化 3. 近期重大事件 diff --git a/codex-skills/private-company-research/SKILL.md b/codex-skills/private-company-research/SKILL.md index 923763c5..9a4a546e 100644 --- a/codex-skills/private-company-research/SKILL.md +++ b/codex-skills/private-company-research/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 未上市公司研究:多Agent并行深度 This skill is generated from `skills/private-company-research.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -67,15 +68,13 @@ This skill is generated from `skills/private-company-research.md` so Claude Code | **tech-ip-analyst** | 技术栈/专利/研发能力/技术护城河 | "技术壁垒是真是假,能撑多久" | | **signal-miner** | 替代数据挖掘:招聘/专利/诉讼/App数据/供应链 | "常规信息之外,还有什么蛛丝马迹" | -### 第二步:创建团队 +### 第二步:实时能力与材料准入 -使用 TeamCreate 创建团队: -- team_name: `{公司名}-private-research`(英文小写,如 `ant-group-private-research`) -- agent_type: `team-lead` +主任务先读取至少一个可核对正文的一手材料,并记录发布日期。若联网不可用,则仅在用户提供融资文件、公司材料或监管文件后进入离线材料模式;禁止用训练知识补齐最新融资、收入或估值。 -### 第三步:创建6个任务 +### 第三步:定义6个任务 -使用 TaskCreate 创建以下6个任务(每个都要有 subject、description、activeForm): +定义以下6个独立任务(每个都要有 subject、description 和结构化返回字段): --- @@ -799,13 +798,9 @@ This skill is generated from `skills/private-company-research.md` so Claude Code --- -### 第四步:启动6个并行Agent +### 第四步:执行6个研究角色 -使用 Agent 工具同时启动6个Agent(**必须在同一条消息中并行调用**): - -每个Agent的配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` +在客户端允许的并发范围内尽可能并行启动6个独立研究角色;并发不足时分批执行,不支持子任务时由主任务顺序完成。 每个Agent的prompt模板: @@ -824,7 +819,7 @@ This skill is generated from `skills/private-company-research.md` so Claude Code {任务description的内容} **研究方法**: -1. 使用 WebSearch 搜索最新公开信息,每个维度至少搜索3-5次,用不同关键词组合 +1. 使用当前客户端可用的联网检索能力搜索最新公开信息,每个维度使用不同关键词组合,并优先一手材料 2. 搜索关键词策略: - 中文:公司名+收入/估值/融资/用户数/MAU/IPO/招股书/裁员/整改 - 英文:Company Name + revenue/valuation/funding/users/IPO/filing @@ -834,7 +829,7 @@ This skill is generated from `skills/private-company-research.md` so Claude Code - 高可信度:招股书、监管文件、上市公司年报中的关联披露 - 中可信度:晚点LatePost、The Information、36氪、Bloomberg、Reuters、TechCrunch - 辅助验证:知乎、脉脉、Glassdoor、天眼查、企查查 -4. 使用 WebFetch 获取关键文章的全文(不要只看搜索摘要) +4. 打开并读取关键文章全文(不要只看搜索摘要) 5. 对重要数据,至少用2个不同来源交叉验证 **数据标注规范(严格执行)**: @@ -1064,15 +1059,15 @@ Top 3 核心风险及应对策略 将完整最终报告写入 `reports/{公司名}/{公司名}-private-{YYYYMMDD}.md`。 -### 第九步:清理团队 +### 第九步:完成状态检查 -使用 TeamDelete 清理团队资源。 +确认六个维度均已返回;任何关键维度因材料不足被阻塞时,最终报告必须保留缺口,不得补写伪精确结论。 --- ## 重要注意事项 -1. **6个Agent必须并行启动**——在同一条消息中调用6次Agent工具 +1. **六个角色必须全部完成**——并发不足时分批执行,能力差异只能改变调度方式 2. **数据置信度标注**——未上市公司数据来源参差不齐,每个关键数据必须标注来源和置信度 3. **推算要透明**——所有推算过程要展示计算逻辑,不能凭空给数字 4. **交叉验证**——关键数据至少2个来源交叉验证,来源冲突时都列出 diff --git a/codex-skills/quality-screen/SKILL.md b/codex-skills/quality-screen/SKILL.md index 097ad1b2..e8b6803f 100644 --- a/codex-skills/quality-screen/SKILL.md +++ b/codex-skills/quality-screen/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 去劣筛选:7条指标快速排除非一流 This skill is generated from `skills/quality-screen.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -85,7 +86,7 @@ This skill is generated from `skills/quality-screen.md` so Claude Code and Codex **模式判断**: - 如果输入是具体公司名/代码 → **个股模式**,直接进入第二步 - 如果输入是行业/市场/主题 → **批量模式**,先执行以下操作: - 1. 用 WebSearch 搜索该行业/市场/主题下的主要上市公司 + 1. 用当前客户端可用的联网检索能力搜索该行业/市场/主题下的主要上市公司 2. 行业模式:覆盖该行业市值前15-20家上市公司 3. 指数模式:拉取完整成分股列表 4. 主题模式:搜索相关公司,覆盖15-30家 @@ -95,7 +96,7 @@ This skill is generated from `skills/quality-screen.md` so Claude Code and Codex ### 第二步:并行数据收集 -为每家公司启动独立后台Agent,使用 WebSearch 搜索以下数据: +为每家公司启动独立研究子任务,使用当前客户端可用的联网检索能力搜索以下数据;并发不足时分批执行: 1. **ROE**:近10年(或上市以来)的逐年ROE,计算平均值 2. **自由现金流**:近5年的经营现金流和资本开支,计算5年累计FCF @@ -105,6 +106,8 @@ This skill is generated from `skills/quality-screen.md` so Claude Code and Codex 6. **净利率**:近10年净利率趋势,计算均值 7. **总股本变化**:5年前和当前的总股本,计算膨胀比例 +历史股本与每股指标必须按 `skills/financial-data.md` 处理拆并股、送转、配股、增发、回购和 ADR 比例变化;无法统一口径的公司标记“数据不可比”,不得按零变化处理。 + 数据来源优先级:公司年报 > 券商研报 > 财经数据平台 ### 第三步:逐条检验 diff --git a/codex-skills/thesis-drift/SKILL.md b/codex-skills/thesis-drift/SKILL.md index 3b6f74fe..7ca0fb28 100644 --- a/codex-skills/thesis-drift/SKILL.md +++ b/codex-skills/thesis-drift/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 投资论文漂移检测:分清事实变化 This skill is generated from `skills/thesis-drift.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -101,6 +102,8 @@ python3 tools/financial_rigor.py three-scenario --price {价格} --eps {EPS} --s python3 tools/financial_rigor.py calc --expr '{精确算式}' ``` +非估值数值(收入增速、利润率、市场份额、用户数变化等)使用 `calc --expr` 计算变化率;`verify-valuation` 只用于每股估值指标,不要为了满足工具要求强行套用错误子命令。 + 关键财务数据必须至少两处独立来源交叉验证。来源不足、口径不一致、无法复核的数字必须标注为"低置信度 / 待核实"。 ### A4:逐维度判定漂移 @@ -162,6 +165,8 @@ python3 tools/financial_rigor.py calc --expr '{精确算式}' 4. **建议动作如何迁移?** 例如:Watch → Buy、Buy → Hold、Hold → Reduce、Reduce → Exit 5. **下一步需要什么证据?** 下一份财报 / 监管披露 / 管理层说明 / 竞对数据 +如果旧报告或新报告没有明确的“当前建议动作”,先从正文提取可验证的等价状态;仍无法提取时写“旧动作缺失/新动作缺失,无法判断迁移”,不得根据语气自行补成 Buy/Hold/Reduce。 + --- ## 模式B:自动快照对比 diff --git a/codex-skills/thesis-tracker/SKILL.md b/codex-skills/thesis-tracker/SKILL.md index daf76d47..e319b7e6 100644 --- a/codex-skills/thesis-tracker/SKILL.md +++ b/codex-skills/thesis-tracker/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 投资论文追踪:买入后的纪律系统. This skill is generated from `skills/thesis-tracker.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -50,7 +51,7 @@ This skill is generated from `skills/thesis-tracker.md` so Claude Code and Codex ### A0:数据收集 -使用 WebSearch 获取当前股价、估值指标(PE/PB/股息率)、最新财报核心数据,用于填写估值锚点。如果已有该公司的 `/investment-research` 或 `/investment-team` 报告,优先从中读取。 +按 `skills/financial-data.md` 完成实时能力探针后,使用当前客户端可用的联网检索能力获取当前股价、估值指标(PE/PB/股息率)、最新财报核心数据,用于填写估值锚点。如果已有该公司的 `/investment-research` 或 `/investment-team` 报告,只把它作为线索,仍须重新核对时点数据。 使用 `tools/financial_rigor.py verify-valuation` 校验估值数据。 @@ -130,7 +131,7 @@ This skill is generated from `skills/thesis-tracker.md` so Claude Code and Codex ### B2:收集最新数据 -使用 WebSearch 收集: +使用当前客户端可用的联网检索能力收集: 1. 最新财报数据(如果有新的季报/年报) 2. 近期重大事件(管理层变动、监管政策、竞争动态) 3. 当前股价和估值指标 diff --git a/codex-skills/wechat-article/SKILL.md b/codex-skills/wechat-article/SKILL.md index 0f4aa8b0..83b7c0aa 100644 --- a/codex-skills/wechat-article/SKILL.md +++ b/codex-skills/wechat-article/SKILL.md @@ -8,7 +8,8 @@ description: "AI Berkshire skill: 微信公众号文章:作者-编辑-读者 This skill is generated from `skills/wechat-article.md` so Claude Code and Codex users share one canonical workflow. - Treat `$ARGUMENTS` as the user's request in the current Codex thread. -- When the source mentions Claude-only surfaces such as Task, Agent, WebSearch, Bash, Read, or Write, use the closest Codex capability available in this session: subagents when available, web search when needed, shell commands for local tools, and normal file edits for workspace files. +- The canonical workflow is capability-based rather than tied to a specific client's tool names. Use available subagents when helpful; when concurrency is lower, run roles in waves, and when subagents are unavailable, execute the same role contracts sequentially. +- Verify live-data access by opening and reading a current primary source before starting time-sensitive research. If live access fails, stop and request dated user-provided material instead of substituting training knowledge. - Use shared project tools from `tools/` in this repository. Prefer running commands from the repository root with paths like `python3 tools/financial_rigor.py ...`; if the current thread starts outside the repo, locate the actual checkout path first instead of assuming a fixed home-directory path. - Before starting research, run the `date` command to confirm today's date; treat it as the baseline for "latest" data and state the data cutoff date in the report header. Never assume the current date from training data. - Preserve the research quality rules from `AGENTS.md`: cross-check financial data, use exact arithmetic tools for valuation/math, and clearly label uncertainty and source gaps. @@ -48,7 +49,7 @@ This skill is generated from `skills/wechat-article.md` so Claude Code and Codex ### 第二步:深度研究 -使用 Agent 工具**并行**启动2-3个研究Agent,收集足够的素材: +按客户端并发能力启动2-3个独立研究角色收集素材;并发不足时分批执行: **研究Agent A:核心内容研究** - 如果是论文解读:下载论文PDF、提取核心贡献、关键图表、实验结果 @@ -77,7 +78,7 @@ This skill is generated from `skills/wechat-article.md` so Claude Code and Codex ## 阶段二:作者Agent写初稿 -使用 Agent 工具启动**作者Agent**,给出详细的写作指令。 +启动独立的**作者角色**,给出详细的写作指令。 ### 作者Agent的Prompt模板 @@ -129,7 +130,7 @@ This skill is generated from `skills/wechat-article.md` so Claude Code and Codex ## 阶段三:编辑Agent + 读者Agent并行审阅 -初稿完成后,使用 Agent 工具在**同一条消息**中启动编辑Agent和读者Agent。 +初稿完成后,尽可能并行启动编辑角色和读者角色;并发不足时顺序执行。 ### 编辑Agent的Prompt模板 diff --git a/scripts/sync-codex-prompts.py b/scripts/sync-codex-prompts.py index b4da499e..153a49e3 100755 --- a/scripts/sync-codex-prompts.py +++ b/scripts/sync-codex-prompts.py @@ -49,8 +49,9 @@ def prompt_for(source: Path) -> str: "argument-hint: $ARGUMENTS\n" "---\n\n" f"Use the installed AI Berkshire Codex skill `{name}` for this request.\n\n" - f"If the skill is not already loaded, read and follow " - f"`~/ai-berkshire/codex-skills/{name}/SKILL.md`.\n\n" + "If the skill is not already loaded, locate it through the active " + "Codex skill registry or the current repository checkout. Do not " + "assume a fixed home-directory path.\n\n" "User arguments:\n" "$ARGUMENTS\n" ) diff --git a/scripts/sync-codex-skills.py b/scripts/sync-codex-skills.py index e99eddc1..f760fc7e 100755 --- a/scripts/sync-codex-skills.py +++ b/scripts/sync-codex-skills.py @@ -68,11 +68,14 @@ def codex_body(name: str, source_name: str, source_text: str) -> str: f"This skill is generated from `skills/{source_name}` so Claude Code " "and Codex users share one canonical workflow.\n\n" "- Treat `$ARGUMENTS` as the user's request in the current Codex thread.\n" - "- When the source mentions Claude-only surfaces such as Task, Agent, " - "WebSearch, Bash, Read, or Write, use the closest Codex capability " - "available in this session: subagents when available, web search when " - "needed, shell commands for local tools, and normal file edits for " - "workspace files.\n" + "- The canonical workflow is capability-based rather than tied to a " + "specific client's tool names. Use available subagents when helpful; " + "when concurrency is lower, run roles in waves, and when subagents are " + "unavailable, execute the same role contracts sequentially.\n" + "- Verify live-data access by opening and reading a current primary " + "source before starting time-sensitive research. If live access fails, " + "stop and request dated user-provided material instead of substituting " + "training knowledge.\n" "- Use shared project tools from `tools/` in this repository. Prefer " "running commands from the repository root with paths like " "`python3 tools/financial_rigor.py ...`; if the current thread starts " diff --git a/skills/bottleneck-hunter.md b/skills/bottleneck-hunter.md index 5b1cb108..33a91e44 100644 --- a/skills/bottleneck-hunter.md +++ b/skills/bottleneck-hunter.md @@ -101,7 +101,7 @@ Layer 4: ### 2.3 其他趋势的拆解 -对每个确认的超级趋势执行类似拆解。使用WebSearch搜索: +对每个确认的超级趋势执行类似拆解。使用当前客户端可用的联网检索能力搜索: - "{趋势} supply chain bottleneck 2026" - "{趋势} shortage critical component" - "{趋势} capacity constraint" @@ -159,9 +159,9 @@ B级瓶颈(有压力): ### 4.1 对每个S级和A级瓶颈,找出所有相关上市公司 搜索方式: -- WebSearch "{瓶颈环节} supplier listed company" -- WebSearch "{瓶颈环节} manufacturer stock" -- WebSearch "{瓶颈产品} market share company" +- 搜索 "{瓶颈环节} supplier listed company" +- 搜索 "{瓶颈环节} manufacturer stock" +- 搜索 "{瓶颈产品} market share company" ### 4.2 初筛标准(快速过滤) diff --git a/skills/deep-company-series.md b/skills/deep-company-series.md index db54fb8d..e33e5ec2 100644 --- a/skills/deep-company-series.md +++ b/skills/deep-company-series.md @@ -185,7 +185,7 @@ grep -r "<本机用户名>\|/Users/\|<个人身份信息>" reports/ | head ### 1. 先核查事实(不要直接改) -如果用户说"X 数据不对",先用 Bash/Read 找原始数据交叉验证: +如果用户说"X 数据不对",先用本地 shell 和文件读取能力找原始数据交叉验证: - 看 ai-berkshire 项目里同公司的 earnings/财报报告 - 看富途/官方披露 - 给出"用户说的数据 vs 我查到的数据 vs 我之前用的数据"三方对比 diff --git a/skills/earnings-review.md b/skills/earnings-review.md index 738fa618..69c659da 100644 --- a/skills/earnings-review.md +++ b/skills/earnings-review.md @@ -31,7 +31,7 @@ ### 第一步:获取一手资料 -使用 Task 工具启动多个后台 Agent **并行**获取以下原始材料: +按客户端并发能力启动多个独立研究子任务获取以下原始材料;并发不足时分批,不支持子任务时顺序执行: 1. **财报原文**:从公司IR页面、SEC EDGAR(美股10-K/10-Q)、港交所披露易(港股)、巨潮资讯网(A股)获取 2. **业绩电话会纪要/录音**:从 Seeking Alpha、公司IR页面、雪球等获取 diff --git a/skills/earnings-team.md b/skills/earnings-team.md index f40107ef..5bfc9631 100644 --- a/skills/earnings-team.md +++ b/skills/earnings-team.md @@ -21,7 +21,7 @@ ### 第一步:获取一手资料 -使用 Agent 工具启动后台 Agent **并行**获取以下原始材料: +按客户端并发能力启动独立研究子任务获取以下原始材料;并发不足时分批,不支持子任务时顺序执行: | 资料类型 | 获取来源 | 优先级 | |---------|---------|--------| @@ -54,7 +54,7 @@ ### 第三步:启动4个并行研究Agent -使用 Agent 工具在**同一条消息**中启动4个后台 Agent。 +启动4个独立研究角色;尽可能并行,并发不足时分批执行。 --- diff --git a/skills/financial-data.md b/skills/financial-data.md index f156171a..accd5887 100644 --- a/skills/financial-data.md +++ b/skills/financial-data.md @@ -2,6 +2,77 @@ 本规范适用于所有涉及企业财务数据的研究。**每个关键数据必须来自两个独立来源,误差>1%须标记。** +这里的“独立来源”指数据生成链彼此独立。两个转载同一份聚合数据的网站只算一个来源;公司公告/交易所文件与第三方数据库可算两个来源。 + +--- + +## 研究准入闸门(先验证能力,再启动研究) + +1. 先执行系统日期命令,记录研究日期和时区。 +2. 用当前客户端实际可用的联网能力,打开一个与标的直接相关的一手页面(公司 IR、交易所或监管文件),确认能读到页面标题、发布日期和正文片段。不要通过检查某个客户端的配置文件来推断联网能力。 +3. 若实时联网不可用: + - 立即停止“最新股价/最新财报/当前估值”研究; + - 明确列出缺失能力和所需材料; + - 只有用户提供带日期的文件后,才可切换为“离线材料分析”; + - 禁止用训练知识、搜索摘要或旧报告冒充最新数据。 +4. 每个子任务都要返回 `data_cutoff`、`source_status` 和 `source_gaps`。主任务发现任一关键子任务未联网或缺少一手材料时,不得给出当前价格区间。 + +--- + +## 时点快照契约(股价、市值、每股指标) + +报告中的当前股价、市值、股本和每股指标必须能还原到同一个时点: + +| 字段 | 必填元数据 | +|------|-----------| +| 当前股价 | 交易所/代码、币种、价格类型(收盘/盘中)、时间戳与时区、来源链接 | +| 总股本 | 基本/稀释口径、对应报告期或公告日、是否扣除库存股、来源链接 | +| 市值 | `股价 × 同口径股本` 的工具输出,不得直接抄聚合站结论 | +| EPS/BVPS/FCF per share | 分子口径、加权平均或期末股数、报告期、是否复权 | + +硬规则: + +1. 股价与股本日期不一致时,先检查期间内回购、增发、拆并股、送转、配股和 ADR 比例变化;无法桥接就标记“不可比”,不得计算当前估值。 +2. 当前市值验算偏差超过 5% 时必须打回;1%–5% 只能以警告状态进入人工复核,不得写成“验证通过”。 +3. 历史每股序列跨越公司行动时,必须同时调整价格与每股分母;只复权价格、不复权 EPS/股本同样会产生假 PE。 +4. 数据源页面没有明确时间戳、币种或口径时,不得作为当前快照的主来源。 + +--- + +## 币种、上市地与 ADR 规则 + +1. 目标价和买入区间始终使用标的实际交易币种,并写明交易所与代码;不得把港股目标价换成美元后省略换算。 +2. 同一张表、同一段估值、同一组情景只使用一种金额口径。跨币种比较时统一到一个展示币种,并在表头或脚注明确:汇率、汇率日期、来源、ADR/ADS 比例。 +3. 公司报告币种与交易币种不同时,先在报告币种完成利润/现金流估值,再单独执行汇率和 ADR 换算。换算步骤必须可复算。 +4. 人民币金额可保留人民币;非人民币金额不强制统一成美元。选择展示币种的原则是减少换算链,而不是追求表面统一。 + +--- + +## 内在价值独立性检查 + +买入区间不得由“当前价打八折”或历史价格位置反推: + +1. 先在看不到当前股价的情况下,根据可持续盈利/自由现金流、资本成本、增长和退出倍数计算内在价值区间。 +2. 再揭示当前价,计算安全边际,并用反向 DCF/反向估值说明市场价格隐含了什么假设。 +3. 至少给出一个关键假设敏感性表。若目标价随当前价同步漂移但基本面假设未变,必须判为估值锚定失败并重算。 +4. 关键输入无法验证时,只输出“可接受估值条件”,不输出精确买入价。 + +--- + +## 行业差异化指标 + +通用指标之外,先识别行业,再加入能解释经济性的专属指标: + +| 行业 | 至少覆盖 | +|------|---------| +| 制造业 | 存货周转、产能利用率、在建产能、资本开支、订单/交付、ROIC、单位成本 | +| 银行/保险 | 净息差、资产质量、不良生成与处置、拨备、资本充足率、久期/偿付能力 | +| 消费/零售 | 同店增长、量价拆分、渠道库存、复购、门店回收期、市场份额 | +| SaaS/平台 | NRR/留存、CAC 回收期、毛利率、变现率、单位经济、网络效应强度 | +| 资源/周期 | 现金成本曲线、储量寿命、产量指引、资本强度、周期中枢假设 | + +市场份额必须写清“产品/类别、地区、时间、分母定义、来源”。不同机构口径不一致时展示区间,禁止拼成一个伪精确值。 + --- ## 数据源优先级 diff --git a/skills/industry-research.md b/skills/industry-research.md index 155a8c39..b057c426 100644 --- a/skills/industry-research.md +++ b/skills/industry-research.md @@ -81,7 +81,7 @@ ## 第三步:全球上市公司扫描 -使用 Task 工具启动后台 Agent,全面搜索该行业所有上市公司。 +启动独立研究子任务,全面搜索该行业所有上市公司;客户端不支持子任务时顺序执行。 ### 搜索清单 - 美股(NYSE/NASDAQ/NYSE American)相关公司 diff --git a/skills/investment-checklist.md b/skills/investment-checklist.md index aec9bc0e..8dafc2dc 100644 --- a/skills/investment-checklist.md +++ b/skills/investment-checklist.md @@ -28,7 +28,7 @@ ### 第二步:并行数据收集 -使用 Task 工具为**每家公司**启动独立的后台 Agent 进行数据收集(所有公司同时并行启动),每个Agent负责收集: +按客户端并发能力为**每家公司**启动独立数据收集子任务;并发不足时分批执行,不支持子任务时顺序执行。每个子任务负责收集: 1. **盈利能力**:ROE(5-10年趋势)、毛利率、净利率、自由现金流 2. **估值数据**:当前股价、市值、PE(TTM)、前瞻PE、PB、股息率 diff --git a/skills/investment-research.md b/skills/investment-research.md index 85cf02b2..c1174b30 100644 --- a/skills/investment-research.md +++ b/skills/investment-research.md @@ -39,18 +39,19 @@ > - 港股:aastocks(主)+ macrotrends ADR(副) > - A股:东方财富(主)+ 巨潮资讯(副) -使用 Task 工具启动后台 Agent,从网络收集以下数据: +先按 `skills/financial-data.md` 执行实时能力探针。探针通过后,启动独立研究子任务从网络收集以下数据;客户端不支持子任务时由主任务顺序收集: 1. 收入结构:最近财年及近4季度分部收入、增速、毛利率 2. 财务指标:近5年收入、净利润、毛利率、经营利润率、自由现金流、现金储备 -3. 竞争格局:市场份额、主要竞争对手对比 +3. 竞争格局:市场份额、主要竞争对手对比;每个份额写明产品/类别、地区、时间、分母定义和来源 4. 商业模式与护城河:核心竞争优势来源 5. 技术能力:核心技术栈、研发投入 6. 管理层:创始人/CEO履历、持股比例、关键决策记录 7. 行业前景:TAM(总可寻址市场)、增长预测 8. 风险因素:地缘政治、监管、供应链等 -9. 当前估值:市值、PE、PS、PEG、EV/Revenue +9. 当前估值:市值、PE、PS、PEG、EV/Revenue;价格时间戳、交易所、币种、最新股本日期/口径、公司行动和 ADR 比例必须对齐 10. 多空双方核心论点 +11. 行业专属指标:按 `skills/financial-data.md` 的行业矩阵选择制造业、金融、消费、SaaS或资源指标 #### 数据交叉验证(必须执行,使用金融严谨性工具) @@ -62,8 +63,9 @@ - 最近财年收入和净利润(从公司年报+至少1个第三方源确认) - 现金储备和净现金(现金+短期投资-总债务,注意口径差异) - 管理层持股比例(区分经济权益和投票权,注意AB股结构) +- 价格/股本时点桥接(回购、增发、拆并股、送转、配股、ADR比例变化) -**强制验证步骤(使用Bash调用工具)**: +**强制验证步骤(使用本地 shell 调用工具)**: Step 1 — 市值验算(精确十进制,非浮点): ```bash @@ -90,6 +92,7 @@ python3 tools/financial_rigor.py verify-valuation \ 3. **所有涉及计算的数据必须通过工具验算,禁止LLM心算** 4. 工具输出结果直接嵌入报告附录"关键数据交叉验证记录" 5. 如果工具报告 ❌ 偏差过大,必须排查原因后才能继续分析 +6. 先在隐藏当前股价的条件下计算内在价值,再用当前价计算安全边际和反向估值;不得用“现价打折”生成买入区间 **常见错误防范**: - 市值单位:港币亿 vs 人民币亿 vs 美元亿,容易漏写/多写一个零 diff --git a/skills/investment-team.md b/skills/investment-team.md index 00ded8ed..6a91e338 100644 --- a/skills/investment-team.md +++ b/skills/investment-team.md @@ -1,12 +1,12 @@ # 投研团队:四角色并行分析框架 -对 $ARGUMENTS 进行团队化投资研究分析。使用 Team 工具创建真正的多Agent并行研究团队。 +对 $ARGUMENTS 进行团队化投资研究分析。工作流只描述角色、输入、输出和验收条件,不依赖任何客户端专属的团队工具名称。 ## 执行流程 ### 第一步:展示团队框架 -向用户展示以下团队结构,确认后启动: +向用户展示以下团队结构后直接启动;只有标的、上市地或研究深度不明确且会实质改变结果时才请求确认: | 角色 | 职责 | 分析框架 | |------|------|----------| @@ -31,30 +31,29 @@ 将评级结果告知每个Agent,影响其研究方式。 -### 第一步¾:WebSearch 权限预检(关键 · 避免 Agent 静默退化) +### 第一步¾:实时能力预检(关键 · 失败即停止) -在创建团队、启动任何后台 Agent **之前**,必须先确认 WebSearch 权限已放行。 +在启动研究角色前,由主任务亲自执行一次真实联网探针:打开该公司的投资者关系页面、交易所披露或监管文件,记录页面标题、发布日期和正文中的一个可核对字段。 -**为什么必须预检**:本 skill 用 `run_in_background: true` 启动 4 个后台子 Agent,而**后台 Agent 无法向用户弹出交互式权限确认**。若 `WebSearch` 未在 `.claude/settings.local.json` 的 `permissions.allow` 白名单中,子 Agent 的联网搜索会被**静默拦截**,导致其退化为仅凭训练知识(有知识截止日期)作答,却仍按框架输出一份"看起来完整、实则未联网"的伪研究——这是本 skill 最危险的失败模式(见 issue #58)。 +- 探针成功:记录 `research_date`、时区、页面 URL 和最新可用数据截止日,再继续。 +- 探针失败或只能看到搜索摘要:停止当前估值研究,说明缺少的能力,并请求用户提供财报/公告文件;不得让后台角色用训练知识补齐“最新数据”。 +- 不检查某个客户端的私有配置文件来推断能力;以实际读取结果为准。 -**预检步骤**: -1. 用 Bash 检查白名单是否含 WebSearch: - ```bash - grep -l '"WebSearch"' .claude/settings.local.json ~/.claude/settings.local.json 2>/dev/null - ``` -2. 若两处都未命中(即未放行)→ **停下来,不要启动 Agent**,提示用户: - > ⚠️ 检测到 WebSearch 未在权限白名单中。后台研究 Agent 无法联网,会退化成仅凭训练知识作答。请先在 `.claude/settings.local.json` 的 `permissions.allow` 加入 `"WebSearch"`(或运行 `/permissions` 勾选),再重跑本命令。 -3. 命中 → 正常继续。 +### 第二步:选择研究深度与执行形态 -### 第二步:创建团队 +默认使用“标准”深度,并在开头声明: -使用 TeamCreate 创建团队: -- team_name: `{公司名}-research`(英文小写,如 `meituan-research`) -- agent_type: `team-lead` +| 模式 | 每角色资料范围 | 输出目标 | +|------|---------------|---------| +| 快速 | 1份一手材料 + 1份独立验证来源 | 先判断是否值得深挖,不给伪精确目标价 | +| 标准(默认) | 2份以上一手材料 + 必要的行业/竞品来源 | 完整四视角报告 | +| 深度 | 多期财报、电话会、竞品与行业材料 | 高重要性决策;预期消耗显著增加 | -### 第三步:创建4个任务 +将四个角色作为独立研究子任务并行执行。若客户端并发不足,按可用并发数分批;若不支持子任务,则由主任务按同一数据契约顺序执行四个角色。能力差异只能改变调度方式,不能删减验收标准。 -使用 TaskCreate 创建以下4个任务(每个都要有 subject、description、activeForm): +### 第三步:定义4个独立任务 + +为以下4个任务分别提供 subject、description 和结构化返回字段: #### 任务1:商业模式分析 - subject: `分析{公司名}商业模式、护城河与用户价值` @@ -76,7 +75,9 @@ 4. 资产负债表健康度:现金储备、负债率、流动性 5. 估值分析:PE/PS/PB/EV等,与历史及同业对比 6. 安全边际评估:内在价值 vs 当前股价 - 7. **金融严谨性验证(必须使用Bash调用工具,禁止心算)**: + 7. 当前快照对齐:价格时间戳/币种/交易所、最新股本日期与口径、期间公司行动、ADR比例 + 8. 估值独立性:先不看当前价计算内在价值,再做反向DCF和敏感性分析 + 9. **金融严谨性验证(必须使用本地 shell 调用工具,禁止心算)**: - 市值验算:`python3 tools/financial_rigor.py verify-market-cap --price {价格} --shares {股本} --reported {报告市值} --currency {币种}` - 估值验算:`python3 tools/financial_rigor.py verify-valuation --price {价格} --eps {EPS} --bvps {每股净资产}` - 关键数据交叉验证:`python3 tools/financial_rigor.py cross-validate --field {字段} --values '{JSON}' --unit {单位}` @@ -87,12 +88,13 @@ - subject: `分析{行业}行业格局与{公司名}竞争态势` - description 包含: 1. 行业规模与增长:市场规模、增速、渗透率 - 2. 竞争格局:主要对手市场份额、竞争策略对比 + 2. 竞争格局:主要对手市场份额、竞争策略对比;份额必须带产品/类别、地区、时间、分母定义和来源 3. 核心竞争者威胁评估:逐个分析主要竞争对手 4. 各细分赛道格局 5. 行业趋势:技术变革、政策影响、新进入者 6. 产业链分析:上中下游价值分配 7. 要求搜索最新行业数据和竞争动态 + 8. 按 `skills/financial-data.md` 的行业指标矩阵补充制造业、金融、消费、SaaS或资源行业的专属经营指标 #### 任务4:风险与管理层评估 - subject: `评估{公司名}投资风险与管理层质量` @@ -106,17 +108,11 @@ 7. 长期确定性:10年后公司会怎样?什么可能颠覆其商业模式? 8. 要求搜索最新监管动态、管理层言论等 -### 第四步:启动4个并行Agent - -使用 Task 工具同时启动4个Agent(**必须在同一条消息中并行调用**): +### 第四步:执行研究角色 -每个Agent的配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` -- `team_name`: 对应团队名 -- `name`: 对应角色名(business-analyst / financial-analyst / industry-researcher / risk-assessor) +在客户端允许的并发范围内尽可能同时启动四个独立研究角色;不能一次启动四个时分批执行,不得因此宣称“已完全并行”。 -每个Agent的prompt模板: +每个角色的输入模板: ``` 你是{公司名}投研团队中的"{角色中文名}",负责从{大师名}投资视角分析{公司名}。 @@ -127,20 +123,19 @@ {任务description的内容} **研究方法**: -- 使用 WebSearch 搜索最新公开信息(财报、行业报告、新闻) +- 使用当前客户端可用的联网检索能力获取最新公开信息,优先财报、交易所披露和公司公告 - **财务数据必须来自两个独立来源**,按 `skills/financial-data.md` 规范执行(美股:macrotrends+stockanalysis;港股:aastocks+macrotrends;A股:东方财富+巨潮资讯),两源误差>1%须标记 - 确保数据准确,关键数据标注来源 - 分析要深入,不流于表面 -- **联网失败禁止伪装**:若 WebSearch 被拦截/不可用,禁止用训练知识冒充联网结果。必须在报告顶部醒目标注「⚠️ 本报告未能联网,基于训练知识(截止日期 X),置信度降级」,并如实告知 team-lead,由其决定是否中止研究 +- **联网失败禁止降级续写**:若无法打开一手页面或核对正文,立即返回 `BLOCKED_NO_LIVE_DATA`,列出缺失材料;禁止用训练知识冒充联网结果 +- 返回 `data_cutoff`、`primary_sources`、`independent_sources`、`source_gaps` 和关键数据口径 **输出要求**: - 报告要详尽,使用Markdown表格呈现关键数据 - 每个分析维度要有明确结论和评分 - 报告末尾要有该维度的总体结论 -**完成后**: -1. 使用 TaskUpdate 将任务 #{任务编号} 标记为 completed -2. 通过 SendMessage 把完整分析报告发送给 team-lead(type: "message", recipient: "team-lead") +**完成后**:直接向主任务返回结构化结果,不依赖文件或客户端专属消息协议。 ``` ### 第五步:接收报告并跟踪进度 @@ -149,10 +144,6 @@ - 每收到一份报告,更新进度并展示该报告的核心要点(3-5条) - 等待全部4份报告到齐 -### 第六步:关闭团队成员 - -全部报告收到后,向4个Agent发送 shutdown_request(使用 SendMessage,type: "shutdown_request")。 - ### 第七步:汇总最终报告 综合4份分析报告,输出以下结构的最终报告: @@ -194,7 +185,7 @@ ### 第八步:保存报告 -将完整最终报告写入 `~/{公司名}投资研究报告_{日期}.md`(日期格式 YYYYMMDD)。 +将完整最终报告写入 `reports/{公司名}/{公司名}-investment-team-{日期}.md`(日期格式 YYYYMMDD)。若目标文件已存在,使用带时间或递增后缀的新文件名,不覆盖旧报告。 ### 第九步:数据抽检(准出流程) @@ -209,21 +200,23 @@ python3 tools/report_audit.py extract \ python3 tools/report_audit.py verdict \ --results '<填好的JSON>' \ --report <报告文件名> + +# Step 4 — 检查报告是否至少包含两个独立信源 +python3 tools/report_audit.py sources \ + --report <报告文件路径> \ + --min 2 ``` **【准出】** 全部通过 → 报告可发布;**【打回】** 有不通过 → 修正后重审。 -### 第十步:清理团队 - -使用 TeamDelete 清理团队资源。 - ## 重要注意事项 -1. **4个Agent必须并行启动**——在同一条消息中调用4次Task工具 -2. **Agent通过SendMessage汇报**——不是文件协作,是消息通信 -3. **数据准确性**——要求Agent使用WebSearch搜索最新数据,关键数据交叉验证 +1. **四角色必须全部完成**——并发不足时分批执行;不得静默省略某个视角 +2. **角色直接返回结构化结果**——不依赖客户端专属团队/消息 API +3. **数据准确性**——按 `skills/financial-data.md` 的时点、复权、币种、股本和双来源契约执行 4. **结论要明确**——不回避给出买入/观望/回避建议和具体价格区间 5. **所有分析必须有数据支撑**——附数据来源 6. **耐心等待**——4个Agent研究需要几分钟,实时向用户更新进度 7. **反偏见意识**——team-lead在汇总时必须评估:各Agent的分析是否受限于资料充裕度?是否与市场共识过度趋同?最终报告需包含"信息丰富度评级"和"AI研究局限性声明" 8. **信息稀缺时的诚实原则**——宁可在报告中留白标注"数据不足",也不要用推测填满框架伪装确定性 +9. **估值独立性**——先在隐藏当前价的条件下计算内在价值,再比较当前价;目标价随市价漂移但基本面未变时必须重算 diff --git a/skills/management-deep-dive.md b/skills/management-deep-dive.md index b09777a9..a0f22901 100644 --- a/skills/management-deep-dive.md +++ b/skills/management-deep-dive.md @@ -24,7 +24,7 @@ AI无法和管理层吃饭,但可以通过公开信息做到: ### 第一步:识别关键管理层并启动并行数据收集 -使用 WebSearch 确认以下关键人物: +使用当前客户端可用的联网检索能力确认以下关键人物: | 角色 | 姓名 | 任期 | 背景 | 持股/期权 | |------|------|------|------|----------| @@ -36,7 +36,7 @@ AI无法和管理层吃饭,但可以通过公开信息做到: **注意**:区分"谁在做决策"和"谁的名字在头衔上"。有些公司创始人虽然卸任但仍是灵魂人物(如黄峥之于拼多多)。 -确认关键人物后,使用 Task 工具启动多个后台 Agent **并行**收集以下数据: +确认关键人物后,按客户端并发能力启动多个独立研究子任务收集以下数据;并发不足时分批执行: 1. Agent 1:CEO公开发言与预测记录(股东信、电话会、采访、社交媒体) 2. Agent 2:资本配置决策记录(并购、回购、分红、新业务投资) 3. Agent 3:治理结构与薪酬(股权结构、关联交易、高管薪酬) diff --git a/skills/news-pulse.md b/skills/news-pulse.md index d0f9dc23..2d566d13 100644 --- a/skills/news-pulse.md +++ b/skills/news-pulse.md @@ -41,15 +41,13 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 将评级告知每个 Agent,影响其侦察方式。 -### 第三步:创建团队 +### 第三步:实时能力探针 -使用 TeamCreate 创建团队: -- `team_name`: `{公司名}-newspulse`(英文小写,如 `pdd-newspulse`) -- `agent_type`: `team-lead` +主任务先打开一个覆盖异动窗口的一手或权威页面,确认标题、发布日期和正文可读。若联网不可用或只能读取搜索摘要,立即返回 `BLOCKED_NO_LIVE_DATA`;新闻归因禁止退化为训练知识推测。 -### 第四步:创建 4 个侦察任务 +### 第四步:定义 4 个侦察任务 -使用 TaskCreate 创建以下 4 个任务: +定义以下 4 个独立任务,并要求每个任务返回事件时间、来源 URL、发布日期、事实/推断标签和未排除解释: #### 任务 1:公司事件侦察(company-event-scout) @@ -104,13 +102,9 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 6. **技术面信号**:是否触及关键支撑/阻力、是否有大宗交易、融资融券异常 7. 关键判断:**是基本面驱动还是情绪/资金面驱动?** -### 第五步:并行启动 4 个 Agent +### 第五步:执行 4 个侦察角色 -**必须在同一条消息中并行调用 4 次 Task 工具**。每个 Agent 配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` -- `team_name`: `{公司名}-newspulse` -- `name`: 对应角色名(company-event-scout / regulatory-watcher / industry-peer-analyst / sentiment-tracker) +在客户端允许的并发范围内尽可能同时执行 4 个独立角色;并发不足时分批执行,不支持子任务时由主任务顺序完成。不得把“分批”写成“完全并行”。 每个 Agent 的 prompt 模板: @@ -127,8 +121,8 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 {任务description的内容} **侦察方法**: -- 优先使用 WebSearch 搜索时效性查询(关键词加日期或"最近"、"latest"、"2026") -- 关键事件用 WebFetch 精读原始来源(公告原文、财报、监管文件) +- 优先使用当前客户端可用的联网检索能力搜索时效性查询(关键词加具体日期或“最近”) +- 打开并精读原始来源(公告原文、财报、监管文件),不得只依赖搜索摘要 - 对每个事件做"独立信源验证"——传言至少要 2 个独立来源 - **不要被标题党误导**:标题与正文不符的事件要标注"标题误导" @@ -140,16 +134,14 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 4. **数据缺口声明**:哪些信息没找到、哪些有疑点、哪些需要等更多信息 5. 严格区分"事实"与"推测",遵循 CLAUDE.md 客观性原则 -**完成后**: -1. 使用 TaskUpdate 将任务标记为 completed -2. 通过 SendMessage 把完整侦察报告发送给 team-lead(type: "message", recipient: "team-lead") +**完成后**:直接向主任务返回结构化侦察结果,不依赖客户端专属消息协议。 ``` ### 第六步:实时跟踪进度 - 每收到一份侦察报告,向用户展示该维度的 3 条核心发现 - 等待全部 4 份到齐 -- 全部到齐后,通过 SendMessage 向 4 个 Agent 发送 shutdown_request +- 全部到齐后核对四个任务的时间窗口和来源状态;缺任一维度时不得给出确定性归因 ### 第七步:team-lead 综合归因 @@ -218,9 +210,9 @@ description: 公司新闻脉搏:股价异动时快速归因。用 4 个并行 写入 `reports/{公司名}/{公司名}-news-{YYYYMMDD}.md`。如果 `reports/{公司名}/` 目录不存在则创建(说明该公司还没建过任何研究报告)。 -### 第九步:清理团队 +### 第九步:完成状态检查 -使用 TeamDelete 清理团队资源。 +确认没有遗留后台任务,并记录本次检索截止时间。 ## 关键原则 diff --git a/skills/portfolio-review.md b/skills/portfolio-review.md index 7e32f41f..3cc9c5f9 100644 --- a/skills/portfolio-review.md +++ b/skills/portfolio-review.md @@ -36,7 +36,7 @@ ### 第二步:获取最新数据 -使用 Task 工具启动后台 Agent,通过 WebSearch 为每个持仓并行获取: +按客户端并发能力为每个持仓启动独立研究子任务,通过可用的联网检索能力获取;不支持子任务时顺序执行: 1. 当前股价和估值指标(PE、PB、股息率) 2. 最近一个季度的关键财务变化 3. 近期重大事件 diff --git a/skills/private-company-research.md b/skills/private-company-research.md index 682ba0ce..9bd75604 100644 --- a/skills/private-company-research.md +++ b/skills/private-company-research.md @@ -52,15 +52,13 @@ | **tech-ip-analyst** | 技术栈/专利/研发能力/技术护城河 | "技术壁垒是真是假,能撑多久" | | **signal-miner** | 替代数据挖掘:招聘/专利/诉讼/App数据/供应链 | "常规信息之外,还有什么蛛丝马迹" | -### 第二步:创建团队 +### 第二步:实时能力与材料准入 -使用 TeamCreate 创建团队: -- team_name: `{公司名}-private-research`(英文小写,如 `ant-group-private-research`) -- agent_type: `team-lead` +主任务先读取至少一个可核对正文的一手材料,并记录发布日期。若联网不可用,则仅在用户提供融资文件、公司材料或监管文件后进入离线材料模式;禁止用训练知识补齐最新融资、收入或估值。 -### 第三步:创建6个任务 +### 第三步:定义6个任务 -使用 TaskCreate 创建以下6个任务(每个都要有 subject、description、activeForm): +定义以下6个独立任务(每个都要有 subject、description 和结构化返回字段): --- @@ -784,13 +782,9 @@ --- -### 第四步:启动6个并行Agent +### 第四步:执行6个研究角色 -使用 Agent 工具同时启动6个Agent(**必须在同一条消息中并行调用**): - -每个Agent的配置: -- `subagent_type`: `general-purpose` -- `run_in_background`: `true` +在客户端允许的并发范围内尽可能并行启动6个独立研究角色;并发不足时分批执行,不支持子任务时由主任务顺序完成。 每个Agent的prompt模板: @@ -809,7 +803,7 @@ {任务description的内容} **研究方法**: -1. 使用 WebSearch 搜索最新公开信息,每个维度至少搜索3-5次,用不同关键词组合 +1. 使用当前客户端可用的联网检索能力搜索最新公开信息,每个维度使用不同关键词组合,并优先一手材料 2. 搜索关键词策略: - 中文:公司名+收入/估值/融资/用户数/MAU/IPO/招股书/裁员/整改 - 英文:Company Name + revenue/valuation/funding/users/IPO/filing @@ -819,7 +813,7 @@ - 高可信度:招股书、监管文件、上市公司年报中的关联披露 - 中可信度:晚点LatePost、The Information、36氪、Bloomberg、Reuters、TechCrunch - 辅助验证:知乎、脉脉、Glassdoor、天眼查、企查查 -4. 使用 WebFetch 获取关键文章的全文(不要只看搜索摘要) +4. 打开并读取关键文章全文(不要只看搜索摘要) 5. 对重要数据,至少用2个不同来源交叉验证 **数据标注规范(严格执行)**: @@ -1049,15 +1043,15 @@ Top 3 核心风险及应对策略 将完整最终报告写入 `reports/{公司名}/{公司名}-private-{YYYYMMDD}.md`。 -### 第九步:清理团队 +### 第九步:完成状态检查 -使用 TeamDelete 清理团队资源。 +确认六个维度均已返回;任何关键维度因材料不足被阻塞时,最终报告必须保留缺口,不得补写伪精确结论。 --- ## 重要注意事项 -1. **6个Agent必须并行启动**——在同一条消息中调用6次Agent工具 +1. **六个角色必须全部完成**——并发不足时分批执行,能力差异只能改变调度方式 2. **数据置信度标注**——未上市公司数据来源参差不齐,每个关键数据必须标注来源和置信度 3. **推算要透明**——所有推算过程要展示计算逻辑,不能凭空给数字 4. **交叉验证**——关键数据至少2个来源交叉验证,来源冲突时都列出 diff --git a/skills/quality-screen.md b/skills/quality-screen.md index 2a03a4e9..d157a634 100644 --- a/skills/quality-screen.md +++ b/skills/quality-screen.md @@ -70,7 +70,7 @@ **模式判断**: - 如果输入是具体公司名/代码 → **个股模式**,直接进入第二步 - 如果输入是行业/市场/主题 → **批量模式**,先执行以下操作: - 1. 用 WebSearch 搜索该行业/市场/主题下的主要上市公司 + 1. 用当前客户端可用的联网检索能力搜索该行业/市场/主题下的主要上市公司 2. 行业模式:覆盖该行业市值前15-20家上市公司 3. 指数模式:拉取完整成分股列表 4. 主题模式:搜索相关公司,覆盖15-30家 @@ -80,7 +80,7 @@ ### 第二步:并行数据收集 -为每家公司启动独立后台Agent,使用 WebSearch 搜索以下数据: +为每家公司启动独立研究子任务,使用当前客户端可用的联网检索能力搜索以下数据;并发不足时分批执行: 1. **ROE**:近10年(或上市以来)的逐年ROE,计算平均值 2. **自由现金流**:近5年的经营现金流和资本开支,计算5年累计FCF @@ -90,6 +90,8 @@ 6. **净利率**:近10年净利率趋势,计算均值 7. **总股本变化**:5年前和当前的总股本,计算膨胀比例 +历史股本与每股指标必须按 `skills/financial-data.md` 处理拆并股、送转、配股、增发、回购和 ADR 比例变化;无法统一口径的公司标记“数据不可比”,不得按零变化处理。 + 数据来源优先级:公司年报 > 券商研报 > 财经数据平台 ### 第三步:逐条检验 diff --git a/skills/thesis-drift.md b/skills/thesis-drift.md index 56848ea5..578c7046 100644 --- a/skills/thesis-drift.md +++ b/skills/thesis-drift.md @@ -86,6 +86,8 @@ python3 tools/financial_rigor.py three-scenario --price {价格} --eps {EPS} --s python3 tools/financial_rigor.py calc --expr '{精确算式}' ``` +非估值数值(收入增速、利润率、市场份额、用户数变化等)使用 `calc --expr` 计算变化率;`verify-valuation` 只用于每股估值指标,不要为了满足工具要求强行套用错误子命令。 + 关键财务数据必须至少两处独立来源交叉验证。来源不足、口径不一致、无法复核的数字必须标注为"低置信度 / 待核实"。 ### A4:逐维度判定漂移 @@ -147,6 +149,8 @@ python3 tools/financial_rigor.py calc --expr '{精确算式}' 4. **建议动作如何迁移?** 例如:Watch → Buy、Buy → Hold、Hold → Reduce、Reduce → Exit 5. **下一步需要什么证据?** 下一份财报 / 监管披露 / 管理层说明 / 竞对数据 +如果旧报告或新报告没有明确的“当前建议动作”,先从正文提取可验证的等价状态;仍无法提取时写“旧动作缺失/新动作缺失,无法判断迁移”,不得根据语气自行补成 Buy/Hold/Reduce。 + --- ## 模式B:自动快照对比 diff --git a/skills/thesis-tracker.md b/skills/thesis-tracker.md index 836a0eb8..18900ae6 100644 --- a/skills/thesis-tracker.md +++ b/skills/thesis-tracker.md @@ -35,7 +35,7 @@ ### A0:数据收集 -使用 WebSearch 获取当前股价、估值指标(PE/PB/股息率)、最新财报核心数据,用于填写估值锚点。如果已有该公司的 `/investment-research` 或 `/investment-team` 报告,优先从中读取。 +按 `skills/financial-data.md` 完成实时能力探针后,使用当前客户端可用的联网检索能力获取当前股价、估值指标(PE/PB/股息率)、最新财报核心数据,用于填写估值锚点。如果已有该公司的 `/investment-research` 或 `/investment-team` 报告,只把它作为线索,仍须重新核对时点数据。 使用 `tools/financial_rigor.py verify-valuation` 校验估值数据。 @@ -115,7 +115,7 @@ ### B2:收集最新数据 -使用 WebSearch 收集: +使用当前客户端可用的联网检索能力收集: 1. 最新财报数据(如果有新的季报/年报) 2. 近期重大事件(管理层变动、监管政策、竞争动态) 3. 当前股价和估值指标 diff --git a/skills/wechat-article.md b/skills/wechat-article.md index 19c5ef8d..4d6ab792 100644 --- a/skills/wechat-article.md +++ b/skills/wechat-article.md @@ -33,7 +33,7 @@ ### 第二步:深度研究 -使用 Agent 工具**并行**启动2-3个研究Agent,收集足够的素材: +按客户端并发能力启动2-3个独立研究角色收集素材;并发不足时分批执行: **研究Agent A:核心内容研究** - 如果是论文解读:下载论文PDF、提取核心贡献、关键图表、实验结果 @@ -62,7 +62,7 @@ ## 阶段二:作者Agent写初稿 -使用 Agent 工具启动**作者Agent**,给出详细的写作指令。 +启动独立的**作者角色**,给出详细的写作指令。 ### 作者Agent的Prompt模板 @@ -114,7 +114,7 @@ ## 阶段三:编辑Agent + 读者Agent并行审阅 -初稿完成后,使用 Agent 工具在**同一条消息**中启动编辑Agent和读者Agent。 +初稿完成后,尽可能并行启动编辑角色和读者角色;并发不足时顺序执行。 ### 编辑Agent的Prompt模板 diff --git a/tests/test_skill_hardening.py b/tests/test_skill_hardening.py new file mode 100644 index 00000000..7d3bc7e5 --- /dev/null +++ b/tests/test_skill_hardening.py @@ -0,0 +1,156 @@ +import contextlib +import importlib.util +import io +import sys +import tempfile +import types +import unittest +from decimal import Decimal +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from tools import financial_rigor # noqa: E402 +from tools import report_audit # noqa: E402 + + +class AgentNeutralSkillTests(unittest.TestCase): + def test_canonical_skills_do_not_name_client_specific_tools(self): + banned = ( + "TeamCreate", + "TeamDelete", + "TaskCreate", + "TaskUpdate", + "SendMessage", + "WebSearch", + "WebFetch", + "run_in_background", + "team_name", + "Task 工具", + "Agent 工具", + ) + violations = [] + paths = list(sorted((ROOT / "skills").glob("*.md"))) + paths += list(sorted((ROOT / "codex-skills").glob("*/SKILL.md"))) + for path in paths: + text = path.read_text(encoding="utf-8") + for token in banned: + if token in text: + violations.append(f"{path.name}: {token}") + self.assertEqual([], violations) + + def test_live_data_fail_closed_contract_is_present(self): + team = (ROOT / "skills" / "investment-team.md").read_text(encoding="utf-8") + pulse = (ROOT / "skills" / "news-pulse.md").read_text(encoding="utf-8") + self.assertIn("BLOCKED_NO_LIVE_DATA", team) + self.assertIn("BLOCKED_NO_LIVE_DATA", pulse) + + def test_generated_prompts_do_not_assume_checkout_path(self): + for path in sorted((ROOT / "codex-prompts").glob("*.md")): + self.assertNotIn("~/ai-berkshire", path.read_text(encoding="utf-8"), path.name) + + def test_financial_data_contract_covers_known_feedback(self): + text = (ROOT / "skills" / "financial-data.md").read_text(encoding="utf-8") + for phrase in ("时点快照契约", "ADR", "内在价值独立性检查", "产能利用率", "市场份额"): + self.assertIn(phrase, text) + + +class FinancialRigorTests(unittest.TestCase): + def test_exact_calc_avoids_binary_float_drift(self): + with contextlib.redirect_stdout(io.StringIO()): + result = financial_rigor.exact_calc("0.1 + 0.2") + self.assertEqual(Decimal("0.3"), result) + + def test_cross_validate_rejects_one_source(self): + with contextlib.redirect_stdout(io.StringIO()): + result = financial_rigor.cross_validate("revenue", {"annual report": "100"}) + self.assertFalse(result["all_consistent"]) + + def test_cross_validate_accepts_two_consistent_sources(self): + with contextlib.redirect_stdout(io.StringIO()): + result = financial_rigor.cross_validate( + "revenue", {"annual report": "100", "exchange filing": "100.5"}, + tolerance_pct="1", + ) + self.assertTrue(result["all_consistent"]) + + +class ReportAuditTests(unittest.TestCase): + def test_missing_fetched_value_fails_instead_of_passing_empty_audit(self): + item = {"id": 1, "label": "revenue", "reported_value": 100, "unit": "m"} + with contextlib.redirect_stdout(io.StringIO()): + result = report_audit.render_verdict([item]) + self.assertEqual("FAIL", result["verdict"]) + + def test_same_source_twice_is_not_independent(self): + item = { + "id": 1, + "label": "revenue", + "reported_value": 100, + "unit": "m", + "fetched_value": 100, + "fetched_source": "https://example.com/a", + "fetched_value2": 100, + "fetched_source2": "https://www.example.com/b", + } + with contextlib.redirect_stdout(io.StringIO()): + result = report_audit.render_verdict([item]) + self.assertEqual("FAIL", result["verdict"]) + + def test_two_independent_sources_can_pass(self): + item = { + "id": 1, + "label": "revenue", + "reported_value": 100, + "unit": "m", + "fetched_value": 100, + "fetched_source": "https://company.example/a", + "fetched_value2": 100.5, + "fetched_source2": "https://exchange.example/b", + } + with contextlib.redirect_stdout(io.StringIO()): + result = report_audit.render_verdict([item]) + self.assertEqual("PASS", result["verdict"]) + + def test_report_path_must_remain_under_reports(self): + with self.assertRaises(ValueError): + report_audit._resolve_report_path(str(ROOT / "README.md")) + + def test_source_domain_extraction_deduplicates_hosts(self): + domains = report_audit.extract_source_domains( + "[a](https://www.sec.gov/a) https://sec.gov/b https://hkexnews.hk/c" + ) + self.assertEqual(["sec.gov", "hkexnews.hk"], domains) + + +class ScraperPathSafetyTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + playwright = types.ModuleType("playwright") + async_api = types.ModuleType("playwright.async_api") + async_api.async_playwright = object() + sys.modules.setdefault("playwright", playwright) + sys.modules.setdefault("playwright.async_api", async_api) + spec = importlib.util.spec_from_file_location( + "xueqiu_scraper_for_test", ROOT / "tools" / "xueqiu_scraper.py" + ) + cls.scraper = importlib.util.module_from_spec(spec) + spec.loader.exec_module(cls.scraper) + + def test_report_output_is_allowed(self): + path = self.scraper._write_path(ROOT / "reports" / "company" / "out.md") + self.assertEqual((ROOT / "reports" / "company" / "out.md").resolve(), path) + + def test_sibling_prefix_is_rejected(self): + with self.assertRaises(ValueError): + self.scraper._write_path(ROOT / "reports_evil" / "out.md") + + def test_system_temp_is_allowed(self): + path = self.scraper._write_path(Path(tempfile.gettempdir()) / "xueqiu-test.json") + self.assertEqual((Path(tempfile.gettempdir()) / "xueqiu-test.json").resolve(), path) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/financial_rigor.py b/tools/financial_rigor.py index c2ffbf42..d558f83f 100755 --- a/tools/financial_rigor.py +++ b/tools/financial_rigor.py @@ -16,8 +16,10 @@ """ import argparse +import ast import json import math +import operator import sys from decimal import Decimal, Context, ROUND_HALF_EVEN, InvalidOperation @@ -170,6 +172,11 @@ def cross_validate(field_name, source_values: dict, unit="", tolerance_pct=2.0): print(f"交叉验证: {field_name} (Cross-Validation)") print("=" * 60) + if len(source_values) < 2: + print(" ❌ 至少需要两个独立来源,单一来源不能通过交叉验证") + return {"consensus": None, "all_consistent": False} + + tolerance_pct = float(tolerance_pct) values = {k: exact(v) for k, v in source_values.items()} sources = list(values.keys()) nums = list(values.values()) @@ -294,20 +301,41 @@ def exact_calc(expr: str): print("精确计算 (Exact Calculator)") print("=" * 60) - # Safe evaluation: only allow numbers and arithmetic - allowed = set("0123456789.+-*/() eE") - if not all(c in allowed for c in expr.replace(" ", "")): - print(f" ❌ 不安全的表达式: {expr}") - return None - try: - # Replace scientific notation for Decimal compatibility - result = eval(expr, {"__builtins__": {}}, {}) - d_result = exact(result) + tree = ast.parse(expr, mode="eval") + binary_ops = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.Pow: operator.pow, + } + unary_ops = {ast.UAdd: operator.pos, ast.USub: operator.neg} + + def evaluate(node): + if isinstance(node, ast.Expression): + return evaluate(node.body) + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): + return Decimal(str(node.value)) + if sys.version_info < (3, 8) and isinstance(node, ast.Num): + return Decimal(str(node.n)) + if isinstance(node, ast.BinOp) and type(node.op) in binary_ops: + left = evaluate(node.left) + right = evaluate(node.right) + if isinstance(node.op, ast.Pow): + if right != right.to_integral_value() or abs(right) > 100: + raise ValueError("指数必须是绝对值不超过100的整数") + return left ** int(right) + return binary_ops[type(node.op)](left, right) + if isinstance(node, ast.UnaryOp) and type(node.op) in unary_ops: + return unary_ops[type(node.op)](evaluate(node.operand)) + raise ValueError("只允许数字、括号和 + - * / ** 运算") + + d_result = evaluate(tree) print(f" 表达式: {expr}") print(f" 结果: {fmt_number(d_result)}") print(f" 精确值: {d_result}") - return float(d_result) + return d_result except Exception as e: print(f" ❌ 计算错误: {e}") return None @@ -381,26 +409,26 @@ def main(): # verify-market-cap mc = sub.add_parser("verify-market-cap", help="验算市值 = 股价 × 总股本") - mc.add_argument("--price", type=float, required=True) - mc.add_argument("--shares", type=float, required=True, help="总股本") - mc.add_argument("--reported", type=float, required=True, help="报告市值") + mc.add_argument("--price", required=True) + mc.add_argument("--shares", required=True, help="总股本") + mc.add_argument("--reported", required=True, help="报告市值") mc.add_argument("--currency", default="", help="币种") # verify-valuation val = sub.add_parser("verify-valuation", help="验算估值指标") - val.add_argument("--price", type=float, required=True) - val.add_argument("--eps", type=float, default=None) - val.add_argument("--bvps", type=float, default=None, help="每股净资产") - val.add_argument("--fcf-per-share", type=float, default=None) - val.add_argument("--dividend", type=float, default=None, help="每股股息") - val.add_argument("--revenue-per-share", type=float, default=None) + val.add_argument("--price", required=True) + val.add_argument("--eps", default=None) + val.add_argument("--bvps", default=None, help="每股净资产") + val.add_argument("--fcf-per-share", default=None) + val.add_argument("--dividend", default=None, help="每股股息") + val.add_argument("--revenue-per-share", default=None) # cross-validate cv = sub.add_parser("cross-validate", help="多源交叉验证") cv.add_argument("--field", required=True, help="数据字段名") cv.add_argument("--values", required=True, help="JSON: {来源: 数值}") cv.add_argument("--unit", default="") - cv.add_argument("--tolerance", type=float, default=2.0, help="容差百分比") + cv.add_argument("--tolerance", default="2.0", help="容差百分比") # benford bf = sub.add_parser("benford", help="Benford定律检测") @@ -412,12 +440,12 @@ def main(): # three-scenario ts = sub.add_parser("three-scenario", help="三情景估值") - ts.add_argument("--price", type=float, required=True) - ts.add_argument("--eps", type=float, required=True) - ts.add_argument("--shares", type=float, required=True, help="总股本(亿)") - ts.add_argument("--growth", nargs=3, type=float, required=True, + ts.add_argument("--price", required=True) + ts.add_argument("--eps", required=True) + ts.add_argument("--shares", required=True, help="总股本(亿)") + ts.add_argument("--growth", nargs=3, required=True, help="三情景年增速 (乐观 中性 悲观), 如 0.15 0.08 0.0") - ts.add_argument("--pe", nargs=3, type=float, required=True, + ts.add_argument("--pe", nargs=3, required=True, help="三情景目标PE, 如 25 20 15") ts.add_argument("--years", type=int, default=3) ts.add_argument("--currency", default="") @@ -425,18 +453,21 @@ def main(): args = parser.parse_args() if args.command == "verify-market-cap": - verify_market_cap(args.price, args.shares, args.reported, args.currency) + ok = verify_market_cap(args.price, args.shares, args.reported, args.currency) + sys.exit(0 if ok else 1) elif args.command == "verify-valuation": verify_valuation(args.price, args.eps, args.bvps, args.fcf_per_share, args.dividend, args.revenue_per_share) elif args.command == "cross-validate": values = json.loads(args.values) - cross_validate(args.field, values, args.unit, args.tolerance) + outcome = cross_validate(args.field, values, args.unit, args.tolerance) + sys.exit(0 if outcome["all_consistent"] else 1) elif args.command == "benford": values = json.loads(args.values) benford_check(values) elif args.command == "calc": - exact_calc(args.expr) + result = exact_calc(args.expr) + sys.exit(0 if result is not None else 1) elif args.command == "three-scenario": three_scenario_valuation( args.price, args.eps, args.shares, diff --git a/tools/report_audit.py b/tools/report_audit.py index 50c2c310..3ac260b5 100755 --- a/tools/report_audit.py +++ b/tools/report_audit.py @@ -29,6 +29,70 @@ import sys from decimal import Decimal, Context, ROUND_HALF_EVEN from random import Random +from urllib.parse import urlparse + + +_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) +_REPORTS_DIR = os.path.realpath(os.path.join(_ROOT, 'reports')) +_MULTI_LEVEL_SUFFIXES = { + 'co.uk', 'com.cn', 'com.hk', 'com.au', 'co.jp', 'org.cn', 'net.cn', 'gov.cn' +} + + +def force_utf8_stdio() -> None: + """Keep Chinese status output usable under Windows legacy code pages.""" + for stream in (sys.stdout, sys.stderr): + reconfigure = getattr(stream, 'reconfigure', None) + if reconfigure: + reconfigure(encoding='utf-8', errors='replace') + + +def _resolve_report_path(user_path: str) -> str: + """Return a report path only when it resolves inside reports/.""" + resolved = os.path.realpath(user_path) + try: + inside = os.path.commonpath([resolved, _REPORTS_DIR]) == _REPORTS_DIR + except ValueError: # Different drives on Windows. + inside = False + if not inside: + raise ValueError(f'报告路径必须位于 {_REPORTS_DIR} 内: {user_path}') + return resolved + + +def _source_key(source: str) -> str: + """Normalize a source label or URL for independence checks.""" + value = (source or '').strip().lower() + if not value: + return '' + parsed = urlparse(value if '://' in value else f'https://{value}') + host = parsed.hostname or '' + if host.startswith('www.'): + host = host[4:] + if host and '.' in host: + return _registrable_domain(host) + return re.sub(r'[^a-z0-9\u4e00-\u9fff]+', '', value) + + +def _registrable_domain(host: str) -> str: + parts = host.lower().strip('.').split('.') + if len(parts) <= 2: + return '.'.join(parts) + suffix = '.'.join(parts[-2:]) + return '.'.join(parts[-3:]) if suffix in _MULTI_LEVEL_SUFFIXES else suffix + + +def extract_source_domains(text: str) -> list[str]: + """Extract unique linked source domains from Markdown/plain URLs.""" + urls = re.findall(r'https?://[^\s<>\]\[()"\']+', text, flags=re.IGNORECASE) + domains = [] + for url in urls: + host = (urlparse(url.rstrip('.,;:!?,。;:!?')).hostname or '').lower() + if host.startswith('www.'): + host = host[4:] + domain = _registrable_domain(host) if host else '' + if domain and domain not in domains: + domains.append(domain) + return domains _CTX = Context(prec=28, rounding=ROUND_HALF_EVEN) @@ -250,7 +314,7 @@ def _pct_diff(reported: float, fetched: float) -> float: return abs(reported - fetched) / abs(reported) -def render_verdict(results: list, report_name: str = "") -> dict: +def render_verdict(results: list, report_name: str = "", min_sources: int = 2) -> dict: """ 根据核验结果输出准出/打回判决。 @@ -293,10 +357,32 @@ def render_verdict(results: list, report_name: str = "") -> dict: fetched2 = item.get('fetched_value2') source2 = item.get('fetched_source2', '') - # --- 主来源比对 --- + item_id = item.get('id', '?') + + # --- 完整性与来源独立性闸门 --- if fetched is None: - # 没有提供核验值 → 跳过(不计入通过/失败) - print(f' ⬜ [{item["id"]:>2}] {label[:35]:35s} {reported:>12.2f} {unit} → [未提供核验值,跳过]') + print(f' {RED}❌ 不通过{RESET} [{str(item_id):>2}] {label[:35]:35s} → [未提供主来源核验值]') + fail_items.append({ + 'id': item_id, 'label': label, 'reported': reported, + 'unit': unit, 'reason': 'missing_fetched_value', + 'raw_text': item.get('raw_text', ''), + 'line_number': item.get('line_number', 0), + }) + continue + + source_keys = [_source_key(source)] + if fetched2 is not None: + source_keys.append(_source_key(source2)) + source_keys = [key for key in source_keys if key] + if len(set(source_keys)) < min_sources: + print(f' {RED}❌ 不通过{RESET} [{str(item_id):>2}] {label[:35]:35s} → [不足 {min_sources} 个独立来源]') + fail_items.append({ + 'id': item_id, 'label': label, 'reported': reported, + 'unit': unit, 'reason': 'insufficient_independent_sources', + 'source': source, 'source2': source2, + 'raw_text': item.get('raw_text', ''), + 'line_number': item.get('line_number', 0), + }) continue fetched = float(fetched) @@ -355,7 +441,7 @@ def render_verdict(results: list, report_name: str = "") -> dict: print() print('-' * 70) - total = len([r for r in results if r.get('fetched_value') is not None]) + total = len(results) fail_count = len(fail_items) warn_count = len(warn_items) pass_count = total - fail_count - warn_count @@ -373,7 +459,10 @@ def render_verdict(results: list, report_name: str = "") -> dict: for fi in fail_items: print(f' ❌ 第 {fi["line_number"]} 行 | {fi["label"]}') print(f' 报告值:{fi["reported"]} {fi["unit"]}') - print(f' {fi["source"]}:{fi["fetched"]} (偏差 {fi["diff1_pct"]}%)') + if fi.get('reason'): + print(f' 原因:{fi["reason"]}') + else: + print(f' {fi["source"]}:{fi["fetched"]} (偏差 {fi["diff1_pct"]}%)') if fi.get('fetched2') is not None: print(f' {fi["source2"]}:{fi["fetched2"]} (偏差 {fi["diff2_pct"]}%)') print(f' 原文:{fi["raw_text"][:80]}') @@ -403,6 +492,7 @@ def render_verdict(results: list, report_name: str = "") -> dict: # --------------------------------------------------------------------------- def main(): + force_utf8_stdio() parser = argparse.ArgumentParser( description='Report Audit Tool — 研究报告数据抽检工具', formatter_class=argparse.RawDescriptionHelpFormatter, @@ -421,6 +511,9 @@ def main(): ... ]' + Step 4 — 检查报告中的独立信源链接数: + python3 tools/report_audit.py sources --report reports/xxx.md --min 2 + 一步预览(只打印抽检清单,不核验): python3 tools/report_audit.py extract --report reports/xxx.md --dry-run @@ -445,15 +538,26 @@ def main(): vrd.add_argument('--results', required=True, help='JSON 数组,含 fetched_value 等字段') vrd.add_argument('--report', default='', help='报告名称(可选,用于显示)') vrd.add_argument('--output-json', action='store_true', help='将判决结果以 JSON 输出到 stdout') + vrd.add_argument('--min-sources', type=int, default=2, help='每个抽检点要求的独立来源数,默认 2') + + # sources + src = sub.add_parser('sources', help='检查报告中的独立信源链接数') + src.add_argument('--report', required=True, help='reports/ 下的报告文件路径') + src.add_argument('--min', type=int, default=2, dest='minimum', help='最少独立域名数,默认 2') args = parser.parse_args() if args.command == 'extract': - if not os.path.exists(args.report): + try: + report_path = _resolve_report_path(args.report) + except ValueError as e: + print(f'❌ {e}', file=sys.stderr) + sys.exit(1) + if not os.path.exists(report_path): print(f'❌ 文件不存在: {args.report}', file=sys.stderr) sys.exit(1) - with open(args.report, 'r', encoding='utf-8') as f: + with open(report_path, 'r', encoding='utf-8') as f: text = f.read() all_points = extract_data_points(text) @@ -491,8 +595,8 @@ def main(): 'raw_text': p['raw_text'], 'fetched_value': None, # ← 填入主来源核验值 'fetched_source': '', # ← 填入主来源名称 - 'fetched_value2': None, # ← 填入副来源核验值(可选) - 'fetched_source2': '', # ← 填入副来源名称(可选) + 'fetched_value2': None, # ← 填入独立副来源核验值(默认必填) + 'fetched_source2': '', # ← 填入独立副来源名称(默认必填) }) print('抽检清单 JSON(填入 fetched_value 后,传给 verdict 命令):') print() @@ -506,7 +610,9 @@ def main(): sys.exit(1) report_name = args.report or '' - outcome = render_verdict(results, report_name=report_name) + outcome = render_verdict( + results, report_name=report_name, min_sources=max(1, args.min_sources) + ) if args.output_json: print(json.dumps(outcome, ensure_ascii=False, indent=2)) @@ -514,6 +620,25 @@ def main(): # 非零退出码表示打回,方便 CI/脚本判断 sys.exit(0 if outcome['verdict'] == 'PASS' else 1) + elif args.command == 'sources': + try: + report_path = _resolve_report_path(args.report) + except ValueError as e: + print(f'❌ {e}', file=sys.stderr) + sys.exit(1) + if not os.path.exists(report_path): + print(f'❌ 文件不存在: {args.report}', file=sys.stderr) + sys.exit(1) + with open(report_path, 'r', encoding='utf-8') as f: + domains = extract_source_domains(f.read()) + print(f'独立信源域名数: {len(domains)}') + for domain in domains: + print(f' - {domain}') + if len(domains) < max(1, args.minimum): + print(f'❌ 信源不足:至少需要 {max(1, args.minimum)} 个独立域名', file=sys.stderr) + sys.exit(1) + print('✅ 信源数量闸门通过') + else: parser.print_help() diff --git a/tools/xueqiu_scraper.py b/tools/xueqiu_scraper.py index 7d23e719..62227f38 100755 --- a/tools/xueqiu_scraper.py +++ b/tools/xueqiu_scraper.py @@ -23,9 +23,9 @@ --output ../reports/拼多多/段永平雪球发言-PDD相关.md # 其他用户 + 其他关键词 - python3 xueqiu_scraper.py --user-id 6784593966 --keywords 茅台 --output /tmp/out.md + python3 xueqiu_scraper.py --user-id 6784593966 --keywords 茅台 --output reports/茅台/out.md -登录态缓存默认 /tmp/xueqiu_state.json,可用 --state-path 覆盖。 +登录态缓存默认写入系统临时目录,可用 --state-path 覆盖;读写路径仅允许位于仓库 data/、reports/ 或系统临时目录。 """ import argparse @@ -34,9 +34,40 @@ import os import random import re +import sys +import tempfile from datetime import datetime from pathlib import Path -from playwright.async_api import async_playwright +try: + from playwright.async_api import async_playwright +except ImportError: + async_playwright = None + + +_ROOT = Path(__file__).resolve().parents[1] +_TEMP_DIR = Path(tempfile.gettempdir()).resolve() +_ALLOWED_WRITE_DIRS = ((_ROOT / 'data').resolve(), (_ROOT / 'reports').resolve(), _TEMP_DIR) +_ALLOWED_READ_DIRS = (_ROOT.resolve(), _TEMP_DIR) +_DEFAULT_STATE_PATH = str(_TEMP_DIR / 'xueqiu_state.json') + + +def _safe_path(user_path, allowed_dirs): + resolved = Path(user_path).expanduser().resolve() + for allowed in allowed_dirs: + try: + if os.path.commonpath([str(resolved), str(allowed)]) == str(allowed): + return resolved + except ValueError: # Different drives on Windows. + continue + raise ValueError(f'路径不在允许目录内: {user_path}') + + +def _read_path(user_path): + return _safe_path(user_path, _ALLOWED_READ_DIRS) + + +def _write_path(user_path): + return _safe_path(user_path, _ALLOWED_WRITE_DIRS) def is_match(text, keywords): @@ -146,20 +177,23 @@ async def interactive_login(pw, state_path, user_id): print("10 分钟内未检测到登录,退出") await browser.close() return None - await context.storage_state(path=state_path) + state_path = _write_path(state_path) + state_path.parent.mkdir(parents=True, exist_ok=True) + await context.storage_state(path=str(state_path)) print(f"登录态已保存 → {state_path}") return browser, context, page async def load_with_state(pw, state_path, user_id): - if not os.path.exists(state_path): + state_path = _read_path(state_path) + if not state_path.exists(): return None browser = await pw.chromium.launch( headless=True, args=['--no-sandbox', '--disable-blink-features=AutomationControlled'], ) context = await browser.new_context( - storage_state=state_path, + storage_state=str(state_path), user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', locale='zh-CN', viewport={'width': 1280, 'height': 800}, @@ -197,7 +231,9 @@ async def fetch_all_timeline(page, user_id, keywords, progress_path, dump_all_pa all_posts = {} if dump_all_path and os.path.exists(dump_all_path): try: - for e in json.load(open(dump_all_path)): + with open(_read_path(dump_all_path), encoding='utf-8') as cache_file: + cached_entries = json.load(cache_file) + for e in cached_entries: all_posts[e['id']] = e print(f" ↪ 载入已有全量缓存:{len(all_posts)} 条") except Exception as e: @@ -250,7 +286,7 @@ def process(d): start_page = 2 if os.path.exists(progress_path): try: - with open(progress_path) as f: + with open(_read_path(progress_path), encoding='utf-8') as f: prev = json.load(f) start_page = max(2, prev.get('next_page', 2)) for e in prev.get('collected', []): @@ -261,11 +297,15 @@ def process(d): print(f" 进度文件读取失败: {e}") def save_progress(next_page): - with open(progress_path, 'w', encoding='utf-8') as f: + safe_progress = _write_path(progress_path) + safe_progress.parent.mkdir(parents=True, exist_ok=True) + with open(safe_progress, 'w', encoding='utf-8') as f: json.dump({'next_page': next_page, 'collected': list(collected.values())}, f, ensure_ascii=False) if dump_all_path: - with open(dump_all_path, 'w', encoding='utf-8') as f: + safe_dump = _write_path(dump_all_path) + safe_dump.parent.mkdir(parents=True, exist_ok=True) + with open(safe_dump, 'w', encoding='utf-8') as f: json.dump(list(all_posts.values()), f, ensure_ascii=False) consec_fail = 0 @@ -314,7 +354,9 @@ def save_progress(next_page): # 最后一次落盘全量缓存 if dump_all_path: - with open(dump_all_path, 'w', encoding='utf-8') as f: + safe_dump = _write_path(dump_all_path) + safe_dump.parent.mkdir(parents=True, exist_ok=True) + with open(safe_dump, 'w', encoding='utf-8') as f: json.dump(list(all_posts.values()), f, ensure_ascii=False) print(f" 全量缓存 → {dump_all_path}({len(all_posts)} 条)") print(f"\n完成:扫描 {total_posts} 条,命中 {found} 条") @@ -356,8 +398,8 @@ def parse_args(): help='关键词列表,逗号分隔。例:拼多多,PDD,黄峥,Temu') ap.add_argument('--output', type=str, default='', help='markdown 输出路径') ap.add_argument('--raw-json', type=str, default='', help='(可选)命中条目原始 JSON 输出路径') - ap.add_argument('--state-path', type=str, default='/tmp/xueqiu_state.json', - help='登录态缓存文件(默认 /tmp/xueqiu_state.json)') + ap.add_argument('--state-path', type=str, default=_DEFAULT_STATE_PATH, + help=f'登录态缓存文件(默认 {_DEFAULT_STATE_PATH})') ap.add_argument('--dump-all', type=str, default='', help='全量缓存路径:爬取时同时把该用户所有原发言写到这里,用于后续离线多主题分析') ap.add_argument('--from-cache', type=str, default='', @@ -366,7 +408,8 @@ def parse_args(): def filter_from_cache(cache_path, keywords, user_id): - posts = json.load(open(cache_path)) + with open(_read_path(cache_path), encoding='utf-8') as cache_file: + posts = json.load(cache_file) out = [] for p in posts: if is_match((p.get('title','') + ' ' + p.get('text','')), keywords): @@ -388,8 +431,9 @@ async def main(): print(f"从缓存 {args.from_cache} 筛出 {len(collected)} 条(关键词: {keywords})") if not collected: return - Path(args.output).parent.mkdir(parents=True, exist_ok=True) - with open(args.output, 'w', encoding='utf-8') as f: + output_path = _write_path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: f.write(format_md(collected, user_id, keywords)) print(f"Markdown → {args.output}") return @@ -399,12 +443,16 @@ async def main(): return progress_path = args.state_path + f'.progress.{args.user_id}' - raw_json = args.raw_json or f'/tmp/xueqiu_{args.user_id}_raw.json' + raw_json = args.raw_json or str(_TEMP_DIR / f'xueqiu_{args.user_id}_raw.json') print("=" * 60) print(f"雪球爬虫 | user_id={args.user_id} | keywords={keywords} | dump_all={args.dump_all}") print("=" * 60) + if async_playwright is None: + print("缺少可选依赖 playwright。请先安装:pip install playwright && playwright install chromium", file=sys.stderr) + return + async with async_playwright() as pw: session = await load_with_state(pw, args.state_path, args.user_id) if not session: @@ -419,12 +467,15 @@ async def main(): print(f"\n=== 最终: {len(collected)} 条命中 ===") if not collected: return - with open(raw_json, 'w', encoding='utf-8') as f: + raw_json_path = _write_path(raw_json) + raw_json_path.parent.mkdir(parents=True, exist_ok=True) + with open(raw_json_path, 'w', encoding='utf-8') as f: json.dump(list(collected.values()), f, ensure_ascii=False, indent=2) print(f"原始JSON → {raw_json}") if args.output: - Path(args.output).parent.mkdir(parents=True, exist_ok=True) - with open(args.output, 'w', encoding='utf-8') as f: + output_path = _write_path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: f.write(format_md(collected, args.user_id, keywords)) print(f"Markdown → {args.output}")