ultradoc — 回答源自源码,而非记忆
ultradoc 通过检索有依据的证据并对其进行推理,来回答关于开源项目的超精确问题。确定性引擎(scripts/ultradoc.mjs,零依赖 Node)用代码进行搜索和索引;你的工作是读取检索到的证据并撰写精确、带引用的答案。每个声明必须指向一个真实的来源。这会被强制执行:如果任何引用未能解析到检索到的证据,ultradoc check 将失败。
核心规则:不要根据你对库自身知识的记忆来回答。你的训练数据是过时的,并且会幻觉 API。只根据
ultradoc从实际仓库、Issues、PRs、文档和网络中检索到的证据来回答。如果证据没有涵盖到,请说明并检索更多——永远不要猜测。
脚本
一个已提交、无依赖的单个文件:node scripts/ultradoc.mjs <command>。无需 npm install,无需 API 密钥。运行 --help 查看全部命令。关键命令:
ask --repo <url|path> --q "<问题>" [--sources ...] [--package <p>] [--semantic] [--docs-url <u>]克隆(任何 git URL,缓存在/tmp/ultradoc/<slug>中)、建立索引、从所有选定来源检索,并将一个证据档案(EVIDENCE.md、evidence.json、meta.json)写入到一个运行文件夹中——该文件夹持久化保存在克隆目录旁的<clone>/.ultradoc/runs/<id>(一个稳定的、基于提交的、跨问题复用的主目录),除非你传递了--out。默认来源:code,issues,prs,docs(当仅凭仓库无法回答问题时,添加web,so;对于“X 是什么时候添加/更改的”问题,添加releases,history;对于社区 Q&A,添加discussions——需要 gh CLI)。 在单体仓库中,--package <name|dir>将代码/文档检索范围限定到一个工作区包。code|issues|prs|docs|releases|history|discussions|so --repo <...> --q "..."—— 深入一个来源,将证据打印到标准输出(不生成档案)。使用这些命令来扩展一个薄弱的领域。history在克隆上运行 git pickaxe(log -S/-G)——首次调用远程仓库时会获取一次完整历史记录。web --repo <...> [--q "..."] [--url <u,...>]—— 无密钥的网络发现(SearXNG → DuckDuckGo → 你的 WebSearch)+ 获取/提取。传递--url来锚定一个你通过自己的 WebSearch 找到的特定页面。overview --repo <...> [--refresh]—— 生成(一次)仓库的缓存 Markdown 摘要:它是什么、工作区包、布局、公共 API 和文档地图。缓存在克隆旁边,并在提交未更改时重用——阅读它可以在不重新索引的情况下,跨多个问题定位。这是导航地图,不是可引用的证据。doc --repo <...> [--package <p>] [--sources ...]—— 构建一个有依据的参考文档:一个确定性的章节大纲(概述、安装、公共 API/每包、配置、架构),每章节检索一个档案并合并成一个单一的evidence.json,以及一个DOC.todo.md工作清单。你将带引用的散文写入DOC.md;check像验证答案一样验证它。持久化保存在<clone>/.ultradoc/doc/中。参见下面的“生成文档”。check --run <档案目录>—— 根据档案的evidence.json验证ANSWER.md(或doc运行的DOC.md)的引用。非零退出码 ⇒ 无根据。--answer <file>验证特定文件。index --repo <...>—— 构建/打印结构索引(用于调试/检查);列出单体仓库的已发现工作区包。semantic up|down|status—— 可选的本地向量后端(见下文)。
工作流程
你端到端地拥有这个任务:返回一个基于证据、带引用的答案,并且永远不要交回一个只有一半检索结果的档案。但检索和验证工作是一组独立的、几乎零成本的调用——当你的运行环境可以时,并行化它们(在一次消息中批量处理独立的深入查询;如果可用,分发给子智能体或工作流)否则就内联进行。迭代多轮直到证据完整;在完成之前不要返回。参见 references/orchestration.md。
-
确定目标。 识别项目及精确问题。如果你只有一个名称,找到规范的仓库 URL(使用你的网络搜索,或者如果不明确,询问用户)。记下用户关心的任何版本/分支(
--ref)。同一个仓库的多个问题? 运行一次
overview --repo <url>并阅读它打印出的缓存的OVERVIEW.md:它映射了仓库(包、布局、公共 API、文档),这样后续问题就可以重用同一个克隆+索引,而不是从头开始重新定位。永远不要引用它——它是导航,不是证据。单体仓库? 如果
ask/index/overview报告了工作区包(例如,socialgouv/code-du-travail-numerique→@cdt/frontend,@socialgouv/modeles-social,……),选择名称/目录与问题所涉及子系统相匹配的包,并添加--package <name|dir>,这样检索就不会淹没在其他包中。如果没有包名明显匹配,首先运行一次无范围限定的ask,查看哪些包是命中次数最多的,然后重新运行并将其范围限定到该包。未知的包名会大声失败并列出存在的包。 -
检索。 首先,像开发者一样思考,为问题推导出 2–3 个查询变体——引擎会逐字搜索,因此措辞很重要:
- 自然语言措辞("retry backoff"),
- 代码库可能使用的标识符形式(
retryBackoff,retry_backoff,MAX_RETRIES), - 如果用户引用了,就是字面上的错误消息、选项或标志名称。
多部分问题? 现在就将其拆分为子问题——每个子问题都需要自己的证据,或者最终需要明确的“未知”。跟踪每个子问题的覆盖情况。
引擎已经处理了复数和重音("retries"/"retry","délai"/"delai"),将标识符拆分为子标记,并提升以关键词命名的文件——因此,将你的变体用于同义词和标识符("heartbeat" vs
ping,"pool" vsconnectionLimit),而不是用于同一单词的屈折变化。并且知道何时词汇搜索会薄弱:它需要实际出现在仓库中的单词。对于概念性或“为什么这样设计”的问题,首先使用docs,discussions,web(或--semantic),而不是期望code来承载答案。使用最佳变体和匹配的来源运行
ask:node scripts/ultradoc.mjs ask --repo <url> --q "<精确问题>" --sources code,issues,prs,docsultradoc 从其自己的 README/清单中自动发现项目的官方文档 URL(并优先考虑仓库内的规范
docs/树),然后据此进行锚定——仅当需要覆盖时才传递--docs-url <url>。当问题是概念性的或仓库内容稀疏时,添加web,so;当问题是关于何时/为何/哪个版本发生了更改时,添加releases,history。该命令会打印档案路径。然后将剩余的变体 × 深入来源视为扇出——在一次消息中将这些独立的深入查询作为并行调用发出(或者每个单元一个子智能体)。参见references/orchestration.md。 -
读取档案。 打开运行文件夹中的
EVIDENCE.md。每个项目都有一个 ID([E1],[E2],……)、一个来源ref和一个片段。这就是你的证据——阅读实际的代码/Issue/PR/文档文本。 -
深入挖掘空白。 当一个子问题不到 ~2 个相关条目,或者没有条目实际包含所询问的符号/行为时,它就是薄弱的。不要等到一个档案看起来薄弱了才去查询这些变体——从一开始就并行运行变体查询(深入查询几乎是免费的:克隆和索引已缓存)。将它们扇出到各个来源:
code --repo <...> --q "<符号或行为>"以提取更多代码区域。issues/prs以阅读相关的讨论和进行中的更改。- 当问题是关于何时/为何/哪个版本发生了更改时,使用
releases(变更日志 + 发布说明)和history(git pickaxe)。 discussions用于社区 Q&A 和设计线程(需要 gh CLI)。web(或你的 WebSearch →web --url <u>)用于外部参考。 遵循references/retrieval-playbook.md了解如何迭代,以及references/orchestration.md了解如何并行运行这些深入查询。
写作前进行分类。 检索是面向召回率的,因此档案将包含仅共享关键词的不相关内容。评判标准:只有当项目的片段命名了所询问的符号/行为或描述了相同的机制时,它才与问题相关——仅共享一个关键词是不够的。列出通过该测试的证据 ID,并忽略其余的——不相关的条目不能仅仅因为存在而被引用。如果分类后少于 ~2 个条目支持核心主张,请返回并检索更多。
重新查询而不是重新阅读。 如果前 3 个代码条目不相关,不要继续向下阅读列表——使用步骤 2 中下一个标识符形状的变体重新运行
code --q。连续两个不相关的档案意味着措辞不正确,而不是仓库缺少答案。循环直到干涸。 按轮次深入(限制 ~3 轮)。当一轮没有出现新的相关证据时停止,或者当每个子问题都有 ≥2 个支持条目时停止——然后进行分类并写作。在限制后仍得不到支持的子问题是一个明确的未知数,绝不能从记忆中填充。
-
写作答案。 在同一个运行文件夹中创建
ANSWER.md。要精确且简洁。为每个事实声明引用它所属的证据 ID,例如重试使用带抖动的指数退避 [E1],PR #79 正在重写此功能 [E7]。参见references/citation-format.md。当版本很重要时,将答案锚定到meta.json中显示的提交。涵盖步骤 2 中的每个子问题;将证据未确定的事情标记为明确的未知——不要从记忆中填充。 -
验证(两层)。
- 结构:
node scripts/ultradoc.mjs check --run <档案目录>。如果任何引用未能解析到证据,或答案没有引用,它就会失败。修复并重新运行直到通过。 - 语义(对抗性支持检查):
node scripts/ultradoc.mjs verify --run <档案目录>会写入一个声明↔证据工作清单(VERIFY.todo.json+VERIFY.md)。作为怀疑论者评判每一对:默认判定为unsupported/refuted,除非被引用的片段字面上支持该声明,将裁决(verdict)设置为 supported · partial · refuted · unsupported(+ 简短注释)。每对运行一个怀疑论者——当可用时分发给子智能体(每个返回其裁决),否则内联裁决每一对——并将所有裁决收集到一个单一的verdicts.json中(参见references/orchestration.md),然后:
这会在以下情况失败:未解决/无来源(结构)以及任何被反驳(refuted)或不支持(unsupported)的声明——填补引用解析了但实际上并不支持该声明的空白。修复声明(重新引用、弱化、删除或检索更好的条目)并重新验证直到通过。还要根据node scripts/ultradoc.mjs verify --apply verdicts.json --run <档案目录> node scripts/ultradoc.mjs check --semantic --run <档案目录>references/answer-rubric.md进行自我审查,以确保完整性/时效性/未知性。
- 结构:
-
呈现。 向用户提供基于证据的答案及其引用和链接(文件:行号、Issue/PR 编号、来自证据的文档/SO/Web URL),以及用于验证的提交。
生成文档
当用户想要一个整个项目(或整个包)的文档而不是一个答案时,使用 doc——它是在章节大纲上扇出的相同有根据的循环:
- 构建脚手架。
node scripts/ultradoc.mjs doc --repo <url> [--package <p>]。引擎构建一个确定性的提纲(概述、安装/使用、公共 API 或每个工作区包一个章节、配置、架构),为每个章节检索一个档案,将它们合并成一个带有全局[E#]ID 的evidence.json,并写入DOC.todo.md(每章节工作清单)+DOC.plan.json。添加--sources code,docs,issues,prs以使各章节基于比仅代码+文档更多的来源。它持久化保存在<clone>/.ultradoc/doc/下。 - 写作每个章节。 读取
DOC.todo.md和EVIDENCE.md,然后在运行文件夹中写入DOC.md:每个提纲条目一个章节,每个声明引用[E#]。证据薄弱的章节是一个扇出单元——深入查询它(code|docs|issues …)或将空白标记为明确的未知;永远不要凭记忆写作。每章节的写作工作可以并行化——参见references/orchestration.md。 - 验证并呈现。
node scripts/ultradoc.mjs check --run <doc-dir>(以及verify+check --semantic用于支持门控,与步骤 6 完全一样——两者都会自动检测DOC.md)。修复直到有依据,然后呈现锚定到其提交的DOC.md。
可选的语义模式(完全本地,无需 API 密钥)
确定性的 Tier-1 搜索(ripgrep + 符号索引)是默认设置,不需要任何东西。对于更模糊、概念性的问题,你可以启用本地语义搜索:
node scripts/ultradoc.mjs semantic up # docker compose: Qdrant + Ollama (+ SearXNG)
node scripts/ultradoc.mjs ask --repo <url> --q "..." --semantic
一切都在本地 Docker 容器中运行——无密钥,无数据离开机器。如果栈未启动,--semantic 会记录一条通知并回退到 Tier 1。参见 references/semantic-setup.md。
参考
references/orchestration.md—— 如何跨调用/子智能体并行化检索深入查询和验证,包含返回 + 裁决汇编契约以及顺序回退。references/retrieval-playbook.md—— 如何选择来源并迭代以得到完整答案。references/citation-format.md——check强制执行的引用语法。references/answer-rubric.md—— 在呈现之前的语义自我审查。references/provider-apis.md—— 如何按主机获取 Issues/PRs,无需密钥。