面向大模型的文档应该怎么写?
在编写文档之前,首先需要明确一个问题:
这份文档是写给人看的,还是写给大模型看的?
传统文档通常以方便人阅读为主要目标,而面向大模型的文档除了需要具备可读性,还需要考虑上下文长度、任务拆分、模块依赖、并行协作和信息检索等问题。
因此,面向大模型编写文档时,应重点遵循以下原则。
一、以功能模块为文档边界
从文档组织的角度来看,单体式的组织方式并不可取。
例如,一个用户模块涉及的接口、业务逻辑、数据模型和页面代码,如果全部分散在不同的包、目录和文档中,大模型在处理用户模块时,就需要同时查找并读取多个位置的信息。
这样不仅会增加上下文消耗,还容易遗漏关键内容。
相比之下,按照微服务或模块化的思路组织文档更加合适。
每个功能模块都应该拥有相对独立的生态,能够在模块内部完成自洽。例如,一个用户模块应当尽可能完整地描述:
- 模块的业务目标;
- 模块包含的功能;
- 模块的数据结构;
- 模块提供的接口;
- 模块依赖的外部能力;
- 模块与其他模块的协作关系。
这也是为什么文档应当按照“功能模块”的维度进行编写。
每一个功能模块都应尽可能完整、自洽,使大模型在处理该模块时,不需要读取整个系统的所有文档。
如果当前功能依赖其他模块、外部接口、外部数据或外部终端,可以通过契约描述这些依赖,而不必把依赖方的全部实现细节复制到当前文档中。
为了避免单个文档过大,建议每份功能模块文档控制在 900 行以内。
二、契约先行
在功能模块正式开发之前,应当优先定义契约。
契约用于明确不同模块、不同系统和不同开发角色之间如何进行协作。
需要契约的场景主要包括:
- 功能模块之间的依赖;
- 前端与后端之间的协作;
- 子模块之间的并行开发;
- 系统与外部接口之间的调用;
- 系统与外部数据源之间的交互;
- 主 Agent 与 SubAgent 之间的任务协作。
例如,一个模块依赖外部接口时,只需要在契约中定义:
- 接口地址;
- 请求方式;
- 请求参数;
- 返回结果;
- 错误码;
- 异常处理方式。
当前模块不需要了解外部接口内部是如何实现的。
契约的价值在于,它能够让不同模块在不了解彼此内部实现的情况下,仍然按照统一约定进行开发和协作。
只有先明确契约,子模块之间才能真正实现并行开发,SubAgent 才能发挥并发协作的作用。
因此,契约不仅用于解决模块之间的协作问题,也可以用于缓解大模型上下文过大、信息混乱和任务相互干扰的问题。
三、大模型如何拆分和执行文档
面对一个大型系统时,不应该让一个 Agent 从头到尾读取并完成所有任务。
更合适的方式是采用主 Agent 与 SubAgent 协作的模式。
3.1 主 Agent 的职责
主 Agent 主要负责:
- 理解整体需求;
- 完成系统架构分析;
- 识别功能模块;
- 分析模块之间的依赖;
- 定义公共契约;
- 拆分任务;
- 将任务分配给不同的 SubAgent;
- 汇总和检查最终结果。
3.2 SubAgent 的职责
SubAgent 主要负责:
- 处理具体功能模块;
- 根据契约实现对应功能;
- 完成代码、测试或文档;
- 返回执行结果;
- 汇报发现的问题和依赖。
3.3 建立索引文档
当系统文档较多时,还需要建立一份统一的索引文档。
索引文档不需要包含所有实现细节,而是用于描述:
- 系统包含哪些功能模块;
- 每个模块对应哪一份文档;
- 模块之间存在哪些依赖关系;
- 每个模块包含哪些任务;
- 每个任务应由哪个 Agent 执行;
- 执行任务前需要读取哪些文档。
主 Agent 可以根据索引文档定位相关内容,并将具体任务派发给对应的 SubAgent。
这就是 Task 任务机制的核心逻辑:
大模型不是一次性读取并实现整个系统,而是根据任务索引,按需加载文档并完成对应任务。
四、解决上下文爆满和回答不准确的问题
大模型的上下文容量是有限的。
不同模型可能支持 200K、1M 或其他规模的上下文,但无论上下文有多大,都不应该把整个系统的所有需求、设计、接口、代码和测试一次性全部放入上下文中。
上下文过大会带来以下问题:
- 重要信息被大量无关内容稀释;
- 模型难以准确定位当前任务;
- 不同模块的信息容易相互干扰;
- 回答结果更容易遗漏或产生偏差;
- Token 消耗明显增加;
- 任务执行效率下降。
4.1 游戏地图渲染的例子
可以用游戏中的地图渲染机制来理解这个问题。
玩家进入一个大型游戏世界时,系统通常不会一次性渲染整张地图,而是根据玩家当前的位置和视角,只加载附近一定范围内的内容。
例如,玩家只能看到周围十米的区域,那么系统只需要优先渲染这十米范围内的地图。随着玩家移动,再继续加载新的区域。
大模型处理文档时也应该采用类似方式:
当前任务需要什么内容,就加载什么内容,而不是一次性加载整个系统。
主 Agent 根据索引文档判断当前任务涉及哪些模块,再加载对应模块的文档、契约和必要的上下文。
SubAgent 只处理自己负责的任务,不需要了解所有模块的实现细节。
通过这种按需加载、局部处理和契约协作的方式,可以有效降低上下文压力,并提高大模型输出结果的准确性。
五、推荐的文档组织结构
一个适合大模型处理的项目,可以按照以下结构组织:
docs/
├── index.md # 系统总索引
├── architecture.md # 总体架构说明
├── contracts/ # 公共契约
│ ├── user-api.md
│ ├── order-api.md
│ └── payment-api.md
├── modules/ # 功能模块文档
│ ├── user/
│ │ ├── overview.md
│ │ ├── requirements.md
│ │ ├── design.md
│ │ └── tasks.md
│ ├── order/
│ │ ├── overview.md
│ │ ├── requirements.md
│ │ ├── design.md
│ │ └── tasks.md
│ └── payment/
│ ├── overview.md
│ ├── requirements.md
│ ├── design.md
│ └── tasks.md
└── tasks/
├── task-index.md
└── dependency-map.md
各类文档的作用如下:
| 文档或目录 | 作用 |
|---|---|
index.md |
帮助主 Agent 快速了解系统结构 |
architecture.md |
描述整体架构和模块边界 |
contracts/ |
保存模块之间的接口契约 |
modules/ |
保存各个功能模块的完整文档 |
tasks/ |
记录任务拆分、执行顺序和依赖关系 |
六、总结
面向大模型编写文档,核心并不是单纯追求文档越详细越好,而是要让文档具备以下特点:
- 按照功能模块进行组织;
- 每个功能模块内部尽可能自洽;
- 模块之间通过契约进行协作;
- 单个文档控制在合理大小以内;
- 使用索引文档管理大量文档;
- 由主 Agent 负责分析和任务分发;
- 由 SubAgent 负责具体模块的并行执行;
- 根据当前任务按需加载上下文;
- 避免一次性将整个系统放入大模型上下文。
最终目标是让大模型像游戏加载地图一样,只读取当前任务真正需要的信息,通过契约连接不同模块,通过任务机制完成并行协作,从而减少上下文压力,提高输出结果的准确性和稳定性。