Legwork
用途
决策研究。非学术研究。问题始终围绕“我们应该如何应对 X”展开,交付物的评判标准是其“可用性”,而非“详尽性”。
这决定了以下所有内容。主张的评判标准是其来源是否为该主张的正确类型,而非其所在领域是否受人尊敬。三个来源一致同意,只有在它们本可能分歧时才有意义。如果一次运行无法回答问题,则直接说明,而不是产出模棱两可的长篇大论。
自治原则。 根据上下文推断所需信息,选择一个级别,然后开始。在一行中宣布所选级别并继续执行;不要要求用户选择。用户可以在运行过程中重定向,这比在每个请求上都阻塞询问的成本低得多。仅在遇到严重错误或无法理解的问题时暂停。
级别
深度提升严谨性。它从不提升篇幅。
| quick | standard | deep | |
|---|---|---|---|
| 框架 | 决策加 2-3 个子问题 | 外加命名的证伪者 | 外加二阶角度 |
| 收集 | SERP 片段;仅为了锁定数据点而获取 | 直接获取每个发现的首选来源 | 每个发现至少一个主要来源 |
| 质疑 | 仅独立性分组 | 每个发现进行一次否定性搜索 | 每个发现进行否定性遍历及来源审计 |
| 格式 | brief | brief 或 report | report |
| 门控级别 | --level quick | --level standard | --level deep |
| 预计耗时 | 3-5 分钟 | 8-12 分钟 | 20-40 分钟 |
默认级别为 standard。使用用户指定的级别(quick、standard、deep 或类似“快速扫描”、“深入”等说法),否则使用 $LEGWORK_DEFAULT_MODE 环境变量,否则使用 standard。单独的“深度调研”是该技能的调用短语,而不是对 deep 级别的请求 - 应回退到默认级别。
如果范围界定发现该问题的重要性在实质上高于请求所暗示的,则静默升级一个级别,并在同一开头行中说明。
开头行,然后直接进入阶段 1:
正在运行 standard(约 8-12 分钟)。说“deep”以启用主要来源和否定性遍历。
流水线
四个阶段。完整说明见 methodology.md。
- 框架 - 命名决策、能够解决该决策的子问题,以及哪些证据会改变答案。
- 收集 - 针对每个子问题进行检索,记录每次获取。
- 质疑 - 寻找否定性案例;按独立性对来源进行分组。
- 撰写 - 组装交付物,然后门控。
阶段 2 和 3 按发现交替进行,而非作为严格的阶段门控。
开始之前
锚定日期。 运行 date -u +%Y-%m-%d,并使用该字符串作为本次运行的标识、输出文件夹名称,以及所有可能涉及时间信息的搜索查询中。永远不要依赖你自身对当前年份的感知,也永远不要让子代理自行计算。
检查本次运行是否已完成。 首先读取索引。它是一个调度器:每行代表一次历史运行,摘要列的存在是为了让你不必打开报告就能决定是否需要查看。
python3 ${CLAUDE_SKILL_DIR}/scripts/index.py list --base "$OUTPUT_BASE"
然后从以下四条路径中选择一条,并在你的开头行中说明:
| 索引显示 | 执行操作 |
|---|---|
| 某次运行涵盖了此问题且未过时 | 基于它回答。阅读该报告,而非网络。 |
| 某次运行涵盖了此问题但已标记为过时 | 刷新它 |
| 某次运行接近但回答的是不同问题 | 新建运行,并交叉引用 |
| 没有匹配项 | 新建运行 |
通过先前运行的成果来回答问题是一种成功,而非捷径。 打开报告,阅读与当前问题相关的发现,并保留其置信度评级。明确说明该答案来自某个特定日期的运行。重复执行已完成且仍然有效的研究,正是这个索引旨在避免的浪费。
刷新技术而非重新运行
刷新操作就地更新现有报告 - 相同的文件夹,相同的文件。不要创建第二个文件夹:两个描述同一问题的文件夹,会让读者最终依据他们碰巧打开的那个来行动。
-
继续使用现有的获取日志,而不是开始一个新的。日志中的一切都是已付出的成本。
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py resume --tsv "$OUT/$BASE.tsv"这将打印出已处理的角度、已获取的页面,以及其中哪些页面仍未包含引用。处理未覆盖的角度,并重新验证决定答案的主张;不要重新获取已记录且仍然有效的信息。
-
将每个现在错误的主张移动到
## Superseded部分,并附上日期和原因。永远不要静默删除一个主张。 可能有人已经依据它采取了行动,未来的运行需要知道这个话题已经被覆盖。如果同一个答案被推翻两次,那么请大声说出来——这是文档中该问题不稳定的最强信号。 -
附加一行
## Timeline,说明更改内容。 -
更新索引行。
归档运行
每次运行(包括无法回答的运行)都必须记录在索引中——“我们查找了,但什么都没找到”正是未来会话所需要的信息,以避免再次查找。
python3 ${CLAUDE_SKILL_DIR}/scripts/index.py add --base "$OUTPUT_BASE" \
--folder "$BASE" --topic "Outlook triage for small practices" --level deep \
--one-liner "No native triage below E5; the gap is real but narrow"
摘要行说明的是该次运行得出的结论,而不是它关于什么。六个月后,“关于插件库的笔记”毫无用处;而“没有提交路线,安装只能通过 URL 或根本不行”本身就回答了问题。门控会在存在索引但缺少运行记录时发出警告。
检索策略
两把梯子,先尝试免费的。只升到必要的程度。
搜索:
| 层级 | 调用 | 时机 |
|---|---|---|
| 1 | WebSearch | 始终使用。每个角度三个查询变体。 |
| 2 | platforms.py search --on ... | 答案存在于平台持有的记录中:一个帖子、一个包、一个仓库、一条带日期的新闻、供应商的变更日志。免费,无需密钥,返回的是记录本身而非关于它的页面。 |
| 3 | bd_search.py -m general --engine bing --country XX --language yy | 三个变体后结果仍稀疏,或者问题是地理特定的。使用第二个引擎是付费的理由。 |
| 4 | bd_search.py -m discover --intent "..." | 两个引擎后结果仍稀疏。 |
打开页面:
| 层级 | 调用 | 时机 |
|---|---|---|
| 1 | fetch.py "<url>" --find "term" | 始终首先尝试。免费,也是唯一能产出页面文本的免费传输方式,因此数字可追溯且引用可验证。 |
| 2 | WebFetch | fetch.py 退出码为 3,且该页面不值得付费。 |
| 3 | bd_search.py "<url>" -m scrape | 被阻止:机器人保护、付费墙、403。 |
| 4 | bd_search.py "<url>" -m render | 客户端渲染的壳。 |
| 5 | bd_search.py "<url>" -m pipeline --pipeline NAME | 阻止上述所有方式的平台。按记录计费;-m reddit 是唯一途径而非最后手段的情况。 |
运行 platforms.py list 查看十个免费平台,运行 bd_search.py --help 查看付费模式。遇到退出码 2(认证或配额问题),告知用户运行 brightdata login。不要重试。
搜索结果是一个线索,而不是你要阅读的页面。 在 standard 和 deep 级别,打开你引用的内容:门控将仅依赖 websearch 行的引用视为未打开,这在 standard 级别是警告,deep 级别是错误。Quick 级别设计上优先使用片段,不要求打开。
在回退之前记录失败。 无法打开的页面本身就是关于这次运行的证据——--status blocked——并且收据会将其计算在内。
限制拉回的内容。 在抓取调用中使用 --max-chars 8000,并优先使用 --find 而非盲目截断:在长页面上,前八千个字符通常是导航。
你可以使用谁的来源
研究是指一个局外人能够建立的内容。这个边界在两方面都很重要。
切勿将用户自身的记录呈现为发现。 如果问题涉及用户自己的公司、产品或市场地位,不要访问他们的私人账户——他们的域名注册商、账单、收件箱、内部文件——然后将你在那里找到的内容当作“发现”来报告。他们自己已经知道了。这是循环论证,它夸大表面证据,并掩盖了一个局外人实际上能看到多少信息。应搜索公开的等效信息,并报告一个局外人会找到的信息,包括“什么都没找到”。
可以使用用户给予你的、关于第三方的独家访问权限。 在用户明确提供的情况下,付费订阅、私有数据集或内部数据库在研究竞争对手、供应商或市场时是真正的优势。使用它,像其他任何来源一样记录它,并在“限制”部分说明该发现基于读者可能没有的访问权限。
当一个实体最终没有任何公开踪迹时,这本身就是答案。列出你检查过的内容,说明其存在性或规模无法从外部验证,并且不要用特权访问来填补这个空白。这是针对单个实体而非整个问题的“无法回答”形态。
获取日志
每次检索,无论级别如何,都追加一行:
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py log \
--tsv "$OUT/$BASE.tsv" \
--url "https://acme.example/pricing" \
--kind vendor_pricing \
--angle "what does the incumbent charge" \
--via webfetch \
--date 2026-07-01 \
--title "Pricing" \
--quote "Team plan: 30 US dollars per user per month, billed annually." \
--text-file /tmp/page.txt
两个字段承担重任,且下游无法检查。
--angle 是本次检索所要回答的子问题。协同证据是根据角度而非来源来计数的,因为 legwork 自身的向外扩展会夸大来源数量。诚实记录角度——在单次运行中重复使用同一个字符串会静默地破坏检查,并且没有任何脚本能够判断你做了这件事。
--quote 是使该来源值得引用的逐字句子。在阅读时,为你打算引用的每个来源记录一条引文。 大约一半的发现不包含数字,因此对于这些发现,引文是唯一存在的证据,并且是页面变更后唯一幸存的部分。当页面文本保存在磁盘上时——任何通过 fetch.py 打开的页面,或使用 --out 的 -m scrape 命令——引文会与页面文本进行核对,quote_verified: false 意味着你错误引用了页面。
--from-fetch 从 fetch.py 和 scrape 写入的侧车文件中填充 url、标题、日期和页面文本,因此无需重新输入任何内容:
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py log --tsv "$OUT/$BASE.tsv" \
--from-fetch /tmp/legwork/ab12cd34ef56.json \
--angle "what does the incumbent charge" --kind vendor_pricing \
--query "site:acme.example pricing" --quote "Team plan: 30 US dollars ..."
sources.py log --help 涵盖其余内容:--via 传输方式(包括 api、local 和 mcp)、--text-file 数字提取、--kind。记录你实际使用的传输方式——通过不同的方式重新获取页面以使其可记录,这扭曲了痕迹而非记录它。sources.py kinds 打印哪些来源类型适合哪些主张。
子代理
检索是唯一值得并行化的阶段,每个角度一个子代理。使用 subagent-brief.md 给它们提供简报,该文件包含模板原文、所需的返回结构以及其中每一行的存在理由。子代理没有上下文,因此它所需的一切都必须包含在简报中。
以下三项由你(主模型)保留,而非放在简报中:
- 将模型与角度的形态相匹配。 片段收集和锁定已知数字可以在便宜的模型上良好运行——显式传递覆盖参数,永远不要让子代理意外继承会话模型。但是,deep 级别的主要来源工作,以及任何重建枚举的工作,应保留在编排者的模型上。实测结果:在一次对比中,随模型从廉价到强大,编排者分别打开了 3、8 和 32 个供应商页面,而最弱的模型在报告成功的同时,其网格的每个单元格都只填充了来自单个聚合器的信息。
- 切勿将子代理的转录粘贴到你的综合中。 获取结构化返回,检查角度字符串是否原样返回,记录每一行,然后基于日志工作。
- 范围界定、质疑和综合是判断性工作。 它们属于主模型。
脚本
全部仅依赖标准库。无需虚拟环境。任何 python3 >= 3.9 均可。
| 脚本 | 用途 |
|---|---|
fetch.py "<url>" --find TERM | 免费打开一个页面并保存其文本;被阻止或遇到外壳时退出码为 3 |
platforms.py list | search --on X | 十个免费的平台原生来源,返回记录而非页面 |
sources.py kinds | log | score | 来源类型词汇表、获取日志、每种主张类型的适配度评分 |
sources.py receipt | 从日志中提取用于收据行的检索计数 |
sources.py stale --claim-kind K | 根据该主张类型的半衰期,判断哪些已记录的来源已失效 |
sources.py resume | 先前运行已获取的内容,以便重新运行时跳过 |
independence.py groups | check | 将来源合并为独立意见;对角度感知的协同证据进行计数 |
independence.py portfolio | 整个运行中的来源集中度,而非仅针对单个发现 |
index.py add | list | 研究索引 - 归档运行、查找历史运行、发现过时的运行 |
matrix.py check --report PATH | 对比矩阵的完整性 |
check.py --report PATH --level LEVEL | 可发布性门控 |
finish.py --report PATH --level LEVEL | 门控、过时扫描和归档三合一调用 |
bd_search.py "<query|url>" -m MODE --json | Bright Data 检索回退 |
输出
在运行开始时解析输出基础路径一次:
OUTPUT_BASE="${LEGWORK_OUTPUT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/docs/research}"
BASE="[Topic]_Research_$(date +%Y%m%d)"
OUT="$OUTPUT_BASE/$BASE"; mkdir -p "$OUT"
…
[已截断]