让 AI 读文档,而不是读源码——OpenWiki 是怎么做到的
编码智能体(coding agent,如 Claude Code、Codex 这类能读写文件、调用工具的 AI 编程助手)进入一个陌生仓库时,要先读大量源码才能建立上下文。这个过程慢、耗 token,而且每换一个 agent、每开一个新会话都要重来一遍。
OpenWiki 换了个思路:让一个 agent 把仓库通读一次,把结果沉淀成一份结构化文档,再在仓库里留下指路标,让后来的 agent 直接读这份文档,而不必重新啃源码。本文先介绍它是什么、怎么用,再重点剖析它如何驱动 LLM 把这份文档写出来——看它每一轮到底给 LLM 发了什么、LLM 又回了什么。
OpenWiki 出自 LangChain公司。这家公司因其同名开源框架LangChain而广为人知,Langchain是当前采用最广的 LLM 应用开发框架,GitHub 上有约 142k Star、上千个预置集成;它的 agent 编排框架 LangGraph 每月下载数千万次,被 Uber、LinkedIn、JPMorgan、Klarna、Replit 等公司用在生产环境。OpenWiki 背后是一家在 AI 应用领域已经蹚出成熟工程实践的团队——后文会看到,它并没有从零造轮子,而是把自家的 agent 运行时(DeepAgents,同样基于 LangGraph)直接复用了过来。
OpenWiki 是什么
功能与特点
OpenWiki 是一个命令行工具(CLI,Command-Line Interface),职责是为代码仓库生成并维护文档,且这套文档「为 agent 而设计」。它有几个定义性的特点:
-
Agent 驱动生成。 文档不是模板套出来的,而是由一个 LLM agent 实地检查仓库、识别技术域与业务域后写成的。OpenWiki 自己不写文档内容,它负责给 agent 下达任务、提供上下文、约束行为。 -
一切结论有据可查。 系统提示词里最硬的一条约束是「不许臆造」——文件、模块、API、业务规则、行为,每一个重要论断都必须有源码、现有文档或 git 历史作证。这是把 LLM 幻觉压到最低的根基。 -
产出统一落在 openwiki/目录。 生成的文档以quickstart.md为入口,再按需要分出 architecture、workflows、operations 等分节页,页与页之间用稳定链接串起来。 -
自动为其他 agent 埋好指路标。 每次运行都会在仓库顶层的 AGENTS.md/CLAUDE.md里插入或刷新一段固定的 OpenWiki 引用,告诉进入仓库的编码 agent「先读 openwiki/quickstart.md」。这是 OpenWiki「为 agent 而生」的关键一环。 -
多模型供应商。 开箱支持 OpenRouter(默认)、Anthropic、OpenAI、OpenAI 兼容网关、Baseten、Fireworks,每家都可自定义模型 ID。 -
增量维护,且能识别「无需改动」。 --init从零建立文档,--update依据 git 变更做外科手术式的局部刷新。运行前后会对openwiki/做内容哈希比对,内容没变就不写元数据,避免定时任务空转。 -
面向两类读者。 既要让零基础的人从 quickstart 看懂项目,也要让未来的 agent 靠它少读源码就能做出高质量改动;既讲技术实现,也讲业务逻辑,并解释「为什么这么设计」而不只是「有哪些文件」。
init / update 如何让编码 agent 掌握项目代码
OpenWiki 让 agent「掌握」代码,靠的不是让每个 agent 都去读全量源码,而是一条「先沉淀、再复用」的链路,由两个命令和一处注入构成。
--init:从零沉淀。 首次运行时假定 openwiki/ 还没有可用文档,agent 从头建立仓库清单(现有文档、入口、构建配置、主要域目录、测试、数据结构、运维脚本),结合 git 历史理解重要文件和工作流是怎么演化来的,然后先写 quickstart,再写分节页。首版只求准确、可导航,不求面面俱到——初始运行最多写 8 页文档。
--update:增量刷新。 后续运行只看「上次成功运行以来仓库发生了什么」。OpenWiki 从 openwiki/.last-update.json 里取上次的 git 提交号,对比出这段时间的提交与未提交改动,只刷新受这些改动直接影响的页面。更新是保守的:能改一句话就不重写一段,没有相关变更时干脆什么都不动。
注入引用:把文档接入 agent 的工作流。 生成文档只是第一步,真正让「后来的 agent」用上它的,是那段被强制写进顶层 AGENTS.md / CLAUDE.md 的引用。编码 agent 进入仓库、按惯例读取这两个指令文件时,就会被引导先看 openwiki/quickstart.md,顺着链接找到它当前任务相关的架构、工作流、运维笔记。
这就是「让 AI 读文档而非读源码」的价值所在:源码是给机器执行的,信息密度低、意图隐晦;而 OpenWiki 产出的文档把「这个系统做什么、怎么组织、为什么这么设计」提炼成了高信号材料,稳定、可链接、解释了动机。后来的 agent 读它,比重新扫一遍源码更快、更省 token,也更不容易理解偏差。
使用方式
安装后首次以交互模式运行时,OpenWiki 会引导完成一次配置:选择推理供应商、填入 API key、选定模型,并可选地配置 LangSmith 追踪。这些配置和密钥保存在本机的 ~/.openwiki/.env,不进仓库。
日常调用有七种形式:
|
|
|
|---|---|
openwiki |
|
openwiki "message" |
|
openwiki --init [message] |
|
openwiki --update [message] |
|
openwiki -p "message" |
--print 同义) |
openwiki --modelId <id> |
|
openwiki --help |
|
--init 和 --update(不带 --print)在终端里运行时会在成功后自动退出,因此同一个 CLI 既能当一次性工具用,也能当常驻的交互工具用。若要让文档持续保持最新,可以接入仓库自带的 GitHub Actions 工作流示例,让它每天自动开一个包含文档更新的 PR。
OpenWiki 方案剖析
技术方案
OpenWiki 的实现可以看成三层,各层职责清晰、互相解耦。理解了这三层的分工,就理解了它的技术架构。
业务层——OpenWiki 自身。 它只关心一件事:「写什么文档、怎么写、不准动什么」。这一层的核心资产是一份把文档工程师纪律编码进去的系统提示词、一层 CLI 与凭据管理外壳、运行前采集的 git 证据,以及固定的文档骨架约定。它本身不实现任何工具,也不管「怎么当一个 agent」。
运行时层——DeepAgents。 「怎么当一个 agent」这件事被完全外包给了 DeepAgents(LangChain 出品的生产级 agent 运行时框架)。它提供了一整套开箱即用的能力:一组文件系统与 shell 工具、驱动 agent 一轮轮自动调用工具的执行循环、派发子代理做并行研究的能力、长对话自动摘要、提示词缓存、待办清单管理,以及可插拔的后端抽象。OpenWiki 把这套基建整体拿来用。
模型层——供应商客户端。 最底层是各家 LLM 的接入客户端,负责「怎么和模型说话」(HTTP、SDK、流式解析)。OpenWiki 通过 LangChain 的 Anthropic / OpenAI / OpenRouter 客户端对接不同供应商。
围绕这三层,有几个设计决策值得单独说明,它们共同塑造了 OpenWiki 的行为边界:
-
虚拟根隔离。 agent 拿到的文件系统被限定在目标仓库内,仓库根被映射成 /。所有文件操作都走这个虚拟根,agent 既碰不到仓库外的东西,也不必关心宿主机的真实绝对路径。这是「安全地跑在真实仓库上」的底层保障。 -
会话持久化。 每次运行的对话状态被写入本机的一个 SQLite 检查点存储(checkpointer),会话线程用仓库路径的哈希做键。这让运行具备可恢复性,也让 update 能延续上次的上下文。 -
供应商解析与回退。 用哪个供应商、哪个模型,按「显式配置 → 环境变量 → 默认值」的优先级解析。针对 OpenRouter,还内置了一条模型回退链:主模型遇到服务端错误(5xx)时,自动改用备选模型重试,避免单点失败拖垮整次运行。 -
内容快照防空转。 运行前后各对 openwiki/目录算一次 SHA-256 内容哈希(内容哈希快照,排除元数据文件本身)。只有两次哈希不同、即文档确实被改动过,才写入.last-update.json。这一步专门为定时任务而设:文档已经是最新时,update 不会平白无故刷新元数据、制造无意义的提交。
OpenWiki 如何与 LLM 交互(以 init 流程为例)
这一节不看源码,而是抓取 OpenWiki 与 LLM 之间真实往返的 HTTP 请求和应答,从外部观察它如何驱动 LLM 干活。下面以一次 init 运行为例。在示例仓库中,这次 init 共往返了 112 轮。
一次请求包含什么
OpenWiki 走的是 Anthropic Messages API 的请求结构,每次发给 LLM 的请求由三个平级的顶层字段组成:
-
system(系统提示词): 给 agent 定角色、定纪律的长文本。每一轮完全相同(抓包中逐轮 sha256 一致),这次 init 会话里约 1.8 万字符。 -
tools(工具定义): 9 个工具的 JSON Schema。每一轮也完全相同,OpenWiki 不做动态裁剪,每轮全量发送。 -
messages(对话历史): 唯一逐轮增长的字段,承载「至今为止发生的全部对话」。
也就是说,日常说的「user prompt」「assistant prompt」并不是独立字段,它们都是 messages 数组里的元素。真正每轮变化的只有 messages。
messages 如何逐轮累加
OpenWiki 用 LangGraph(LangChain 的有状态 agent 编排库)的检查点机制持久化对话,每一轮都把完整历史重新发给 LLM,而不是只发增量。累加规律很规整:
轮 1 messages = [ user(init 任务指令 + git 上下文 + 仓库根 + 运行守则) ]轮 2 messages = [ user(同上), assistant(思考 + 工具调用), user(工具结果) ]轮 3 messages = [ …轮 2 全部…, assistant(思考 + 工具调用), user(工具结果) ] … 每多一轮,就在末尾追加一对 [assistant, user]轮 112 messages = 前 111 轮的全部内容 + 第 111 轮的工具执行结果
其中,首条 user 消息(本次任务的完整指令)在各轮完全不变;每一轮 assistant 的应答之后,紧跟一条 user 消息,装的是上一轮工具调用的执行结果,并通过 ID 与对应的工具调用配对。
一个直接推论:随着轮次增加,messages 越来越长,而每轮的应答只是「下一步的思考加几个工具调用」,长度基本恒定。所以到了后期,请求体的长度远远超过应答的长度。为压住由此带来的成本,运行时对 Anthropic 类模型自动加了提示词缓存——固定不变的 system 和 tools、以及每轮共享的历史前缀,缓存后重复轮次只按缓存读计费。
应答里有什么
LLM 的应答由内容块(content block)拼成,对 OpenWiki 而言分两类:
-
思考或文字。 模型的推理过程或自然语言回复。OpenWiki 对这部分不做动作,它只是流式展示给用户看。 -
工具调用。 携带工具名和参数,例如列目录、读取某个文件、跨文件搜索。这才是驱动流程往前走的部分——运行时执行这些调用,把结果塞回下一轮的 messages,再发起下一轮请求。
112 轮里发生了什么
单看一轮请求-应答,只是「模型调了几个工具、拿到几段结果」。但把 112 轮连起来看,会浮现出一条清晰的认知路径:模型正是靠这条路径,从对仓库一无所知,一步步逼近到能写出准确文档的程度。它大致分四个阶段,层层递进——每一步读到的东西,都为下一步该读什么提供线索。
第一步:从「路标文件」建立全局印象。 模型收到 init 指令后,并不急着扎进源码,而是先列根目录,再读那几个信息密度最高的文件——CLAUDE.md、AGENTS.md、README.md 和构建配置(如 package.json、settings.gradle)。这些文件用几百行就交代了「这是个什么项目、有哪些模块、怎么构建」,让模型花最小的代价拿到一张粗略的全局地图。这一步对应系统提示词里的运行纪律:不逐文件全读,优先看仓库树、配置和 README 这类高信号文件。
第二步:把地图转成计划。 有了全局印象,模型用待办工具把后续动作固化成一份清单:建仓库清单 → 派子代理并行调研各主要域 → 写临时的 _plan.md → 写 quickstart → 写分节页 → 刷新根指令文件 → 写 .last-update.json。这一步的意义在于把「读懂仓库」这件模糊的大任务,拆成有先后、可勾选的小步骤,避免模型在几十轮里迷失方向或重复劳动。
第三步:分域深入,并行细读。 计划里最花时间的是「理解各主要域」。模型不自己一个个串行读,而是按子代理纪律派出少量只读子代理,每个领一个窄任务(如数据采集、网络通信、UI/API 层),在各自独立的上下文里深入读该域的代表性文件,读完只回传「带源码路径的精炼发现 + 遗留问题」。主代理不必看子代理读过的每一行,只接收压缩后的结论——这既并行提速,又避免把主线程的上下文撑爆。至于「为什么这么设计」这类文件树给不出的信息,则靠 git 历史补齐:模型对高信号文件选择性地看提交记录,理解某个模块是怎么演化来的。
第四步:综合落笔,自我收尾。 手上攒够了各域的结构化发现后,模型切换到写作:先写 quickstart 作为入口,再写分节页,所有写入都由主代理统一完成以保证口径一致。收尾阶段它还会回看 openwiki/ 目录树、删掉临时的 _plan.md、把 OpenWiki 引用写进顶层指令文件。
四个阶段合起来,是一条「先鸟瞰、再规划、后分域细读、最后综合」的漏斗式路径——每一层都比上一层更深、更聚焦,而不是从头到尾平铺地把源码读一遍。如此一轮轮自动往复,直到第 112 轮携带全部历史收尾。整个过程无人干预——agent 的这种自治循环,来自运行时框架内建的「理解 → 执行 → 验证,干到完成才停」的行为准则。
prompt 解析
上面反复提到「系统提示词」和「工具定义」。这一节拆开看它们到底由什么构成。需要说明的是,下面的解析取自一次 chat 会话的抓包:各段纪律在 init / update / chat 三种模式下基本一致,只有末尾一小段「模式专属行为」随模式变化。
system prompt
系统提示词是两段拼接的结果:前半段来自 OpenWiki 自己,后半段来自 DeepAgents 框架的内置默认提示词。OpenWiki 没有改写框架那半段,而是把自己的专业纪律追加在前面,于是 agent 同时具备「文档专家」的针对性约束和「通用 agent」的自治能力。
prompt 内容的来源: 前半段是
src/agent/prompt.ts中createSystemPrompt()的模板字符串,其中随命令变化的「模式专属行为」由同文件的createModeInstructions()生成;后半段由 DeepAgents 的createDeepAgent在接收systemPrompt参数时自动追加(src/agent/index.ts创建 agent 处传入前半段),原文在deepagents库内置的默认提示词里。
前半段——OpenWiki 自定义的文档工程师纪律,可归纳为十来个主题:
-
角色定义。 把模型设定成技术作家、软件架构师、产品分析师三重身份,产出物是 openwiki/下的文档;核心红线是不许臆造,每个论断都要有证据。 -
运行纪律。 用虚拟路径、禁传宿主绝对路径;不逐文件全读,按目录和扩展名定点发现;首版求准不求全、保持聚焦;不搜仓库外。这些约束共同控制 token 消耗、防止在大仓库里迷路。 -
子代理纪律。 多域仓库可用子代理并行做只读研究,数量按仓库大小设上限;子代理只许查看和总结、不许写文件,主代理负责综合与全部写入。 -
规划纪律。 正式动笔前先写临时的 _plan.md列出计划与证据,收尾前必须删除,不能留在最终文档里。 -
git 纪律。 用 git 解释「为什么存在」而非只是「有什么」;init 看近期提交理解演化,update 只看上次成功运行以来的提交。 -
现有文档纪律。 把已有的 README、文档、runbook 当一手材料,有用就总结加链接而非照搬;现有文档与源码冲突时以源码为准并指出可能过时之处。 -
根指令文件纪律。 强制让顶层 AGENTS.md/CLAUDE.md引用 quickstart,只动顶层文件、用固定的段落模板增量替换而非重复堆叠。 -
CLI 参考。 列出 7 种命令形式,并要求被问及用法时优先实跑 openwiki --help验证后再作答。 -
安全隐私规则。 不读、不记录任何密钥、凭据、 .env等敏感物;除了顶层两个指令文件的 OpenWiki 段,不改openwiki/以外的任何源码。 -
文档目标与质量规则。 定义什么是好文档:零基础可入门、未来 agent 可少读源码、兼顾技术与业务、解释动机、稳定链接、像人写的而非文件清单;同时避免建空目录和薄页,宁可在宽页面里用小标题,也不为拆而拆。 -
必需结构。 quickstart 必须是入口并链接到所有主要分节;只有仓库足够大才建分节目录;在 .last-update.json记录最后一次成功更新。 -
模式专属行为。 这是随命令变化的一小段——chat 模式下 agent 只答不写、不主动改文档;init 模式假定从零开始建结构;update 模式则要求外科手术式的最小改动。
后半段——DeepAgents 内置的通用 agent 行为规范,与具体任务无关:要求沟通简洁直接、不带「Sure!」这类客套前缀;准确优先于迎合用户;按「理解 → 执行 → 验证」的循环持续推进直到完成;长任务给简短进度更新;并给出待办工具、文件系统工具、子代理工具各自的使用说明。这一段是 agent 能自主一轮轮跑下去的行为底座。
tool prompt
OpenWiki 自己一个工具都没定义。 创建 agent 时它传入的是空工具列表,9 个工具全部由运行时框架及其依赖自动注入。按职责可分成三类:文件系统类(在虚拟根内读写探索)、执行类(在宿主机跑 shell)、编排类(管理任务与派发子代理)。
prompt 内容的来源: OpenWiki 侧只有一行
tools: [](src/agent/index.ts的createDeepAgent调用处),确实不定义任何工具。9 个工具的 JSON Schema 由框架注入,源头在node_modules里:文件系统 7 个工具和task来自deepagents库,write_todos来自langchain库的待办中间件。抓包里tools字段则是它们最终发给 LLM 的完整 Schema。
|
|
|
|
|---|---|---|
ls |
|
ls |
read_file |
|
|
write_file |
|
edit_file 改已有文件而非新建」 |
edit_file |
|
old_string → new_string,支持全部替换;编辑前必须先读 |
glob |
|
*/**/?),返回匹配的路径 |
grep |
|
|
execute |
|
|
task |
|
|
write_todos |
|
|
前六个文件系统工具都走虚拟根,只能在目标仓库内操作、路径必须以 / 开头;execute 是例外,它跑在宿主真实环境。这些工具的定义与执行逻辑都来自框架,OpenWiki 只是消费方——它把「agent 用什么工具、工具怎么实现」这件事完全交给了运行时层。
user prompt
user prompt 是「本次任务指令 + 每轮工具执行结果」的组合,随命令不同而不同:
prompt 内容的来源: 任务指令由
src/agent/prompt.ts的createUserPrompt()按命令分支生成,其中 git 证据的采集逻辑在src/agent/utils.ts的createGitSummary();附在末尾的仓库根路径和运行守则由src/agent/index.ts的createRunUserMessage()拼上。每轮的工具执行结果不是模板,而是工具实际运行后由框架回填的。
-
init: 携带一份 git 现状快照(工作区状态、当前提交、近期提交清单、未提交改动),要求从零生成文档。 -
update: 携带上次更新的元数据和自那以后的 git 变更摘要,要求只刷新受影响的页面。 -
chat: 直接转发用户的消息。
除任务指令外,非交互运行还会附上仓库根路径和一段运行守则,反复强调虚拟路径规则、执行工具需先切目录、不得越界搜索——把「在哪干、怎么用工具」钉死,防止 agent 走偏。
知识补充
Anthropic Messages API 简介
Anthropic 的 Messages API 是 OpenWiki 与模型交互的底层协议。官方文档:https://docs.anthropic.com/en/api/messages。
一次请求(POST /v1/messages)的请求体由若干顶层字段构成,与本文相关的核心字段是:
|
|
|
|
|---|---|---|
model |
|
claude-sonnet-5 |
messages |
|
user 与 assistant 交替出现,承载至今为止的全部往返 |
max_tokens |
|
|
system |
|
messages(这与 OpenAI 把系统消息放进 messages[0] 的做法不同) |
tools |
|
|
stream |
|
|
messages 数组里每个元素有 role(user / assistant)和 content 两个字段。content 的关键之处在于它不一定是纯文本,而可以是一个内容块(content block)数组,块的类型包括:
-
text:普通文字。 -
thinking:模型的思考过程(开启扩展思考时出现)。 -
tool_use:模型发起的工具调用,带id、name(工具名)和input(参数)。 -
tool_result:工具执行结果,出现在user消息里,必须带tool_use_id与对应的tool_use.id配对——这是 API 的硬性要求,用来告诉模型「这是你刚才那个调用的返回」。
正是这套内容块机制,让「模型请求调用工具 → 调用方执行 → 结果回传」的循环能在 messages 里表达出来,也解释了前文交互分析中 assistant 应答为何能同时含思考与工具调用、每轮 user 消息又为何装的是工具结果。此外,对成本敏感的长会话,API 支持提示词缓存(prompt caching):把固定不变的前缀(如 system 和 tools)标记为缓存断点,后续请求命中缓存的部分只按更低的费率计费。
开源组件 DeepAgents
DeepAgents 是 LangChain 出品的生产级 agent 运行时框架。定位是:给它一个模型加一个后端,就还你一个开箱即用、自带文件系统、子代理、待办、摘要、缓存的自治 agent。OpenWiki 正是它的一个「文档专家」预设。官方文档:https://docs.langchain.com/oss/python/deepagents/overview;API 参考:https://reference.langchain.com/python/deepagents;源码仓库:https://github.com/langchain-ai/deepagents。
它为 OpenWiki 提供的能力包括:
-
工具系统与执行引擎。 文件系统工具、shell 执行工具、子代理工具的完整实现,含虚拟路径解析、权限检查、输出截断、超时控制;以及驱动「模型要调工具 → 框架执行 → 结果回填 → 再问模型」不断循环的执行引擎。 -
子代理编排。 派发拥有独立上下文的子代理做并行、隔离的只读研究,让主代理专注综合与落笔。 -
上下文管理。 对话过长时自动摘要旧消息、过大的工具结果自动驱逐,防止上下文爆炸——这是 init 能跑上百轮而不崩的关键。 -
待办管理与提示词缓存。 提供结构化待办工具给 agent 做自我规划,并为 Anthropic 类模型自动加缓存断点以省钱。 -
多种后端抽象。 OpenWiki 选用的是本地 shell 后端,框架另有沙箱等其他后端可选。
OpenWiki 与 DeepAgents 的分工可以一句话概括:DeepAgents 负责「怎么当一个 agent」,OpenWiki 负责「当一个写文档的 agent 时该守什么规矩」。如果没有 DeepAgents,OpenWiki 就得自己实现工具系统、执行循环、上下文管理和子代理编排——那才是真正的工作量。
夜雨聆风