news 2026/8/31 11:08:56

用代码文档约束AI智能体:从行为规范到检索增强实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用代码文档约束AI智能体:从行为规范到检索增强实践

实际开发中,越来越多的团队开始把“补测试、改接口、写组件”这类任务交给 AI 智能体(agents)完成。但很多人会遇到同一个问题:智能体改代码很积极,改出来的结果却不符合项目约定。有人把原因归结为模型能力不够,其实在大多数情况下,真正的问题是项目里缺少一份能让智能体读懂、能检索命中、能明确约束行为的代码文档。

这里要说的不是“把文档写得更详细”这种笼统建议,而是把代码文档当作智能体的行为规范来管理。文档的作用对象不再只有人,还包括模型和工具循环。只有当文档中的规则、边界、示例和反例能被智能体在合适的时机读到,它才可能做你真正想让它做的事。

这篇博客围绕“如何用代码文档让智能体按你的意图执行”这条主线展开。先讲智能体为什么依赖文档;再调整写文档的思路;然后给一个最小落地示例;接着处理上下文有限时的检索问题;最后讲如何验证、排查,以及生产环境里的文档治理方式。

1. 为什么智能体总在做别的事:问题根源在文档

1.1 智能体执行任务的真实机制:模型加工具循环

要理解智能体为什么“不听话”,先要看清楚它的执行机制。智能体本质上是一个大语言模型加工具循环:系统提示、用户请求、工具返回结果共同构成上下文,模型根据这些内容生成下一步动作,动作可以是文本回复,也可以是调用函数、读取文件、执行命令。

这个机制决定了三件事:

  • 智能体没有对项目的“常识”,它只知道上下文里写了什么。
  • 如果项目约定没有出现在上下文里,它只能根据训练数据中的常见模式去推测。
  • 推测的结果往往是最常见的写法,而不是你的团队约定。

代码文档在这里的作用不是“参考资料”,而是智能体做出决策时的外部约束。只要关键规则在上下文中可见,模型就更有可能在生成阶段走对方向,而不是事后让人类修改。这个理解很重要,因为很多人是在“生成之后人工检查”这个环节补规则,而不是在“生成之前注入规则”。

1.2 文档缺失时,智能体会用训练数据的常见模式“脑补”

当文档缺失或模糊时,智能体会自动填补空缺。这里的“填补”不是严格推理,而是概率补全:它选出训练数据里最常出现的模式。举几个具体例子:

  • 项目规定所有日志使用结构化 JSON 输出,但上下文里没有这条规则,智能体直接生成console.logprint
  • 项目规定金额统一用整数分long传输,文档没写,智能体用Double表示,并在边界产生精度问题。
  • 项目规定业务校验失败要返回 4xx 错误码,文档没写,智能体为了“尽快跑通测试”在catch里吞掉异常。

这些问题的共同点是:智能体的输出在语法上没有问题,甚至能通过基础测试,但不符合项目的长期约定。靠人 review 能发现一部分,却无法覆盖每一次生成。文档就是把这些约定变成模型可依赖的显式信号的载体。

1.3 代码文档的定位变化:从人的说明书,变成智能体的行为规范

传统代码文档面向人,读者能结合背景知识补全上下文,能容忍含蓄表达。智能体文档则不同,它的阅读者是一个“没有项目背景、但能严格按文字执行”的模型。两类读者的需求差异很大:

维度给人看给智能体看
阅读方式浏览、搜索、跳转按上下文顺序读取,或检索命中
重点信息背景、动机、设计思路规则、边界、示例、反例
表达风格可以含蓄、可留白要直接、无歧义
默认假设开发者有项目背景智能体可能完全不认识项目
过期影响开发者发现并修正智能体会继续按过期规则执行

这里有一个常见误区:以为“给智能体写文档”意味着把文档改造成机器人语言。实际上,文档仍然要给人看,只是需要额外增加一层“机器可提取的决策规则”。修改思路不是把文档写得像提示词,而是让每条规则都能被清晰检索、独立理解、直接执行。

2. 让文档成为智能体的“行为接口”:写文档的思路调整

2.1 智能体需要的是决策规则,不是功能列表

很多 README 的功能列表写得详细,智能体却仍然不知道该怎么做。原因在于功能列表回答的是“系统有什么”,而智能体真正需要的是“遇到什么情况怎么做”。以支付服务为例:

  • 功能列表:支持余额查询、转账、退款。
  • 决策规则:金额从外部传入时,单位必须是分,使用long接收,禁止使用Double

智能体执行任务时,真正需要的是后者。它不需要你重复描述业务价值,而是需要一组可以在生成代码时直接引用的判断条件。

建议每份文档至少回答四类问题:

  1. 边界:这个模块允许做什么,禁止做什么。
  2. 约束:命名、类型、格式、事务边界、日志规范等。
  3. 示例:期望的输入输出,最好带代码片段。
  4. 反例:常见错误写法,明确写着“不要这样做”。

这四类信息不是给人看的补充材料,而是智能体生成过程中需要直接决策的关键点。

2.2 一份 README 的改造前后对比

改造前的 README 通常长这样:项目名、技术栈、启动命令、几个接口路径。它不是没有用,只是智能体读了之后仍然不知道写代码时要遵守哪些规则。

改造后可以增加“行为约束”和“开发规则”章节。下面是一个最小模板:

# Payment Service ## 行为约束 - 金额统一使用整数分(long)传递,禁止使用 Double。 - 对外接口使用 JSON,字段命名使用 camelCase。 - 所有写操作必须在事务内执行,禁止在事务内调用远程接口。 - 入参校验失败返回 4xx,并携带 errorCode 字段。 ## 错误处理 - 业务校验失败返回 4xx,响应体包含 errorCode 和 message。 - 非预期异常必须记录完整堆栈后抛出,禁止 catch 后不处理。 ## 反例 - 不要这样做:`double balance = 10.5;` 正确做法:`long balanceInCents = 1050L;`

这段文档的作用在智能体处理“新增一个转账接口”时非常明显。如果检索系统把“行为约束”这一块命中了,模型就可能在设计接口时直接使用long而不是Double;如果文档没写,模型大概率会按训练数据里的常见方式生成,后续再被 review 打回。

注意,这个模板要结合实际项目补充,不能只抄结构。每一条规则都需要能被测试验证,例如“字段命名使用 camelCase”可以用静态检查或接口测试校验。

2.3 文档放哪里:位置和命名直接影响检索命中

文档位置不是小问题。智能体读取上下文时,通常会优先读 README,但不会主动翻遍每个子目录。如果规则分散在十几个 markdown 文件里,又没有明显命名,检索或上下文注入时很容易遗漏。

推荐按下面这个结构组织:

repository-root/ ├── README.md ├── docs/ │ ├── behavioral-rules.md │ ├── api-contracts.md │ └── examples/ │ └── payment-codecs.md ├── src/ └── tests/

README.md只放项目概览和入口,详细规则放到docs/下,用文件名直接表达规则领域。behavioral-rules.md这种命名在智能体检索时比doc1.mdhelp.md更容易命中。每个文档块尽量控制在能独立理解的大小,避免一个章节超过几千字,否则检索命中后也会占用大量上下文。

3. 落地示例:用一份智能体可读文档驱动“补测试”任务

3.1 任务场景与仓库结构设计

接下来看一个能直接复现的落地示例:让智能体为支付服务补充一组单元测试。任务输入是“代码仓库 + 规则文档”,输出是“符合项目约定的测试文件”。

这个场景足够小,能看出文档是否存在、是否被读到的差异。仓库结构按第 2 章设计:

payment-service/ ├── README.md ├── docs/ │ ├── behavioral-rules.md │ └── examples/ │ └── payment-codecs.md ├── src/ │ └── PaymentService.java └── tests/ └── PaymentServiceTest.java

给智能体的任务描述可以是:

为 PaymentService 补充单元测试。 开始前先读取 docs/behavioral-rules.md。 测试代码必须遵守其中所有约束。

这里的任务描述只有一句话,真正约束行为的是文档。换句话说,文档是“行为接口”,任务描述只是入口。

3.2 行为规则文档的关键内容

docs/behavioral-rules.md除了上一节的“金额单位”“错误处理”规则,还需要增加测试场景下模型最容易出错的部分:

## 单元测试约定 - 测试用例必须包含正常分支、边界分支和异常分支。 - 金额边界测试使用 Long.MAX_VALUE / 0L / 负数,禁止只测正数。 - mock 远程调用时,必须模拟成功和超时两种结果。 - 断言必须检查具体字段值,禁止只检查返回值是否为 null。 - 反例:不要写只验证“不抛异常”的测试,那会给实现放水。

这里的关键是“反例”。大模型生成测试时,默认倾向是优先保证代码通过,而不是暴露问题。如果文档没有“禁止只验证不抛异常”这类反例,生成的测试很可能缺乏保护力。文档写出反例后,模型才有机会在生成阶段意识到“要通过负向用例表达约束”。

3.3 把文档注入智能体上下文的三种实践

学习环境里,最简单的方式是把文档拼进系统提示或任务提示:

你是该项目的开发智能体。 开始任务前,先读取 docs/ 目录下的规则文档。 必须遵守 docs/behavioral-rules.md 中的约束。 如果规则与用户请求冲突,以规则文档为准,并说明原因。

如果智能体工具支持自动读取仓库文件,也可以直接把规则文档路径加入检索范围。对于小型仓库,还可以在终端里把文档聚合到一个上下文文件:

cat docs/behavioral-rules.md docs/examples/payment-codecs.md > .agent/context.md

再让智能体读取.agent/context.md。这种方式适合本地手动实验,不推荐作为生产方案,因为一旦仓库变大,全文塞入会消耗大量上下文,而且长文档会让模型忽略关键部分。真正需要的是第 4 章的检索方式。

4. 上下文有限时,如何让关键文档被精确命中

4.1 全文塞入、检索增强、工具调用怎么选

当规则文档较少时,全文塞入简单直接;但项目进入一定规模后,全文塞入会带来两个问题:token 消耗高,以及长文本中关键规则容易被模型忽略。这时需要在“检索增强”和“工具调用”之间选型。

方式适合场景优点缺点
全文塞入文档少、上下文窗口大信息完整,实现简单长文档浪费 token,关键规则可能被淹没
检索增强文档多、按需取用精准节省上下文需要检索组件,存在召回遗漏风险
工具调用智能体主动读取指定文档实时、按需、灵活依赖智能体判断能力,可能少读文件

在高效智能体(efficient agents)的讨论中,上下文管理是关键议题。一个常见建议是:不要试图把所有知识塞进上下文,而是把“决策规则”做成可检索片段,让模型在需要时命中。针对代码文档这个场景,最推荐的是“检索增强 + 工具调用”组合:任务开始时先检索,命中后把相关规则注入上下文。

4.2 最小检索实现:先把 Markdown 拆成可检索块

前面提到,检索是对“文档块”操作的。第一步是把 Markdown 按标题拆成多个块。下面是一个最小 Python 实现,用于扫描目录下所有 markdown 文件:

import os from dataclasses import dataclass @dataclass class DocChunk: title: str path: str text: str def load_markdown_chunks(root: str) -> list[DocChunk]: chunks = [] for base, _, files in os.walk(root): for name in files: if not name.endswith(".md"): continue path = os.path.join(base, name) with open(path, encoding="utf-8") as f: lines = f.read().splitlines() title = name text_lines = [] for line in lines: if line.startswith("## "): if text_lines: chunks.append(DocChunk(title, path, "\n".join(text_lines))) text_lines = [] title = line[3:].strip() else: text_lines.append(line) if text_lines: chunks.append(DocChunk(title, path, "\n".join(text_lines))) return chunks

这个实现把二级标题作为切块边界,每个块包含标题、文件路径和正文。实际项目中,切块策略要根据文档结构调整:如果三、四级标题也承载重要规则,可以递归切更细;如果文档短,可以直接按文件切。

第二步是给查询评分。为了降低门槛,先用关键词评分:

def score(chunk: DocChunk, query: str) -> int: text = chunk.text.lower() terms = [t.lower() for t in query.split()] return sum(1 for t in terms if t in text) def retrieve(query: str, chunks: list[DocChunk], top_k: int = 3): scored = sorted(chunks, key=lambda c: score(c, query), reverse=True) return scored[:top_k]

真实项目可以用 BM25、向量检索或混合检索替换这里的评分函数。核心思路不变:把文档切块、建立索引、根据任务查询返回最相关的规则片段。

4.3 检索结果的优先级排序

检索命中只是第一步,返回顺序也很重要。如果一条规则是“禁止使用 Double”,另一段示例代码恰好演示了double balance,模型看到上下文时容易被示例带偏。因此要将约束类规则放在检索结果前面。

可以给文档块增加权重标记:

def rank(chunk: DocChunk, query: str) -> float: base_score = score(chunk, query) if "行为约束" in chunk.title or "禁止" in chunk.text: base_score += 2.0 if "示例" in chunk.title: base_score -= 0.5 return base_score

优先级排序的原则是:行为约束先于功能描述,反例先于示例,规则先于背景。这样即使 Model 在长上下文中只读前几段,也能优先看到硬性约束。这里的权重调节只是为了说明方向,落地时要基于评估集反复调整。

5. 验证智能体是否按文档执行:从“看结果”到“看过程”

5.1 用 Playwright 回放行为,验证页面规则是否生效

如果智能体的任务涉及网页交互,Playwright 是验证行为是否符合文档的好选择。比如文档规定“支付页金额输入只允许整数分”,你可以让智能体生成页面代码后,用 Playwright 跑一遍行为测试,检查金额输入是否真的拒绝小数:

import { test, expect } from '@playwright/test'; test('支付页金额输入禁止出现小数', async ({ page }) => { await page.goto('/pay'); await page.fill('#amount', '10.5'); await page.click('#submit'); const message = await page.locator('.error-message').textContent(); expect(message).toContain('金额单位应为分'); });

这类测试验证的不是“页面能不能打开”,而是“规则是否真正落到行为上”。它既检查智能体的实现结果,也间接检查文档是否被有效读取。如果文档规则命中,Playwright 测试大概率通过;如果不命中,测试会直接失败。

5.2 关键决策点设置中断检查,避免错误路径走到底

长任务里,智能体可能在某个分支选错方向,再继续执行会造成更大返工。深度智能体中断(interrupt)机制对这种场景很有用:在关键决策点暂停,让模型先说明自己的执行计划,再进入实际修改。

一个可落地的做法是,在任务提示中要求智能体在动手前输出“规则检查报告”:

执行前请先回答: 1. 本任务涉及的规则文件路径是什么? 2. 需要遵守的关键约束有哪些? 3. 当前计划中可能存在哪些违反约束的风险点?

在开发环境,人可以检查报告后决定是否继续;在自动化环境,可以把报告内容写入日志,由脚本判断关键约束是否被提及。这个验证点应该放在“生成代码之前”,而不是“生成之后”,因为改代码的成本远大于改计划。

5.3 构建小型评估集,持续度量“按文档执行”的达标率

文档写得好不好,不能靠感觉。建议维护一个十个左右任务组成的评估集,每个任务包含:任务描述、规则文档、期望行为、禁止行为。每次修改文档后,把评估集跑一遍,记录通过率。

下面是一个最简单的评估记录表:

任务文档是否命中是否遵守金额类型是否遵守错误处理结果
新增转账接口通过
补充支付测试失败
修复退款接口部分通过

评估集的价值在于回归。你改了某份文档后,可能某个任务变好,另一个任务变差。没有评估集,很难发现这种此消彼长。这也是“building effective agents”这条实践里最容易被忽略的一环:把智能体的行为当成被测系统,而不是一次性脚本。

6. 常见问题排查:文档写了,智能体还是乱来

6.1 现象一:指令冲突时,智能体选了错误指令

表现:系统提示或任务描述说“使用 double”,规则文档说“金额用 long”,智能体最终选择了 double。

原因:模型面对冲突指令时,往往会优先响应更靠近问题末尾的指令,或者更显式的指令。如果规则文档没有声明自身优先级,模型无法判断哪条才是权威。

检查方式:打印进入模型的完整上下文,看规则文档是否真的被读取,以及它和任务描述之间是否直接矛盾。

处理方式:在规则文档开头增加优先级声明:

## 优先级 本文档约束优先于任务描述和系统提示中的通用建议。 当出现冲突时,以本文档为准。

这是最简单也最有效的补救。毕竟任务描述由人临时编写,规则文档才是团队的长期共识。

6.2 现象二:关键约束被忽略

表现:文档里明确写了“金额使用 long”,智能体生成的代码仍然用 double。

原因有两类:一是约束文档没有进入检索结果,或者进入后排在上下文末尾被截断;二是文档中只有一句抽象描述,没有配合反例和示例,模型没有把这条规则当作“必须遵守的硬约束”。

检查方式:把智能体执行任务的日志输出打开,查看检索返回了哪些块,观察上下文是否被截断。

处理方式:先确认文档块大小,把“行为约束”章节拆成独立小文件;再对约束块加权重,确保检索命中后排在前面;最后补充反例,让模型看到“不要这样做”和“正确做法”的对比。

6.3 现象三:文档更新后,智能体仍按旧规则执行

表现:团队把支付金额单位从“元”改成“分”,更新了文档,但智能体生成的代码仍按旧的“元”逻辑处理。

原因:智能体工具可能缓存了旧文档;或者.agent/context.md这类聚合文件没有重新生成;或者检索索引没有随文档更新。

检查方式:先确认实际注入上下文的文件内容,再看检索索引的构建时间。不要只看代码仓库里的文档已经改了,要确认“智能体读到的版本”已经改了。

处理方式:把文档更新纳入持续集成流程。文档变更后自动重建检索索引,并触发第 5 章里的评估集任务,用自动化方式验证新旧规则是否都生效。

6.4 排查链路与检查清单

遇到“文档写了,智能体还是乱来”这类问题,按下面的顺序排查:

  1. 任务描述是否把目标说清楚,有没有和文档冲突。
  2. 文档文件路径是否正确,智能体是否真的读取了。
  3. 检索结果里有没有包含关键规则,排序是否符合预期。
  4. 上下文是否足够长,关键规则有没有被截断。
  5. 规则之间是否存在冲突。
  6. 聚合文件或检索索引是否已经更新。
  7. 查看智能体中间日志,确认从哪一步开始偏离规则。

这套检查顺序适用于多数场景。按照“输入 -> 路径 -> 检索 -> 上下文 -> 冲突 -> 缓存 -> 过程日志”的顺序排查,比直接怀疑模型能力更有效率。

7. 生产环境中的文档治理:写一次,多处复用

7.1 文档变更触发回归评估

学习环境里可以手动更新文档、手动跑评估集。生产环境不行,因为一次规则调整可能影响多个智能体任务,人工验证成本太高。推荐做法是:把文档更新和智能体评估都纳入 CI。

简单流程可以是:

  1. 开发者修改docs/下的规则文档。
  2. 推送后 CI 自动重建检索索引。
  3. CI 用修改后的文档触发预定义评估集。
  4. 查看每个任务的“按文档执行”达标率。
  5. 未通过任务直接阻塞合并。

这个流程把文档当成可测试的代码资产,而不是随时可能过期的文本。没有这一步,“文档驱动智能体”只能停留在小规模实验,无法进入正式发布流程。

7.2 文档分级:哪些规则只对部分智能体开放

生产环境还有一个容易被忽略的点:不是所有文档都应该暴露给所有智能体。比如安全敏感规则、内部部署细节、数据库账号约定,如果全部注入上下文,会带来信息泄露风险。

建议按文档敏感度分级:

级别示例使用方式
公开约定命名规范、错误码格式所有智能体可检索
内部规则模块边界、数据库字段约定仅相关模块智能体可使用
机密信息密钥、生产环境地址禁止写入任何智能体上下文

分级后再控制智能体的工具权限,可以避免“文档写得很清楚,但权限被滥用”的问题。这个设计应该在文档第一版就规划,而不是等人数变多后再补。

7.3 落地前可复用的检查清单

最后,整理一份可以直接拿去用的检查清单:

  • 文档中是否包含行为约束、示例和反例。
  • 每条规则是否能用测试或静态检查验证。
  • 文档是否按模块拆分,块大小是否适合检索。
  • 检索索引是否会随文档更新自动重建。
  • 规则文档是否声明了相对任务描述的优先级。
  • 是否设置了关键决策点的中断检查。
  • 是否维护了最小评估集,并能度量达标率。
  • 文档是否按公开、内部、机密分级。
  • 生产流程里,文档变更是否触发回归评估。
  • 智能体日志是否能还原出“读了哪些文档、在哪一步偏离”。

真正的做法不是把所有规则一次性写全,而是从最小可用的规则集开始,跑通“文档 -> 检索 -> 执行 -> 验证 -> 回归”这条链路,再逐步补充规则。每一次规则调整都当成一次代码变更来对待,智能体的可预测性才会逐步提高。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 11:08:34

MATLAB/Simulink新能源汽车整车模型搭建与仿真优化实践

MATLAB/Simulink做新能源汽车整车模型,这几年从课程设计一路火到了企业预研。很多人一上来就想着搭一个“满配”模型,结果不是卡在找不到模块,就是仿真一跑内存飙满,或者输出的速度曲线根本不合常理。这篇文章不打算绕弯子&#x…

作者头像 李华
网站建设 2026/8/31 11:07:18

MiniMAX H3提示词模板:3段式SKILL实现电影级视频生成

从“提示词写不好”到“AI 直接出电影级特效”,中间到底差了什么?很多人第一反应是差一个更强的模型,或者差一张更贵的显卡。但最近的 MiniMAX H3 相关讨论里,一个更明显的信号是:官方把“电影级特效提示词”做成了标准…

作者头像 李华
网站建设 2026/8/31 11:07:12

小模型如何重塑AI成本格局:从token计费到模型路由实践

从年初开始,身边不少团队都从“非大模型不用”转向了“小模型真香”。原因并不复杂:大模型 API 的 token 成本在真实业务量面前会快速膨胀,而短信通知、客服意图识别、日志分类、内容审核初筛这类高频场景,根本不需要每次都动用最…

作者头像 李华
网站建设 2026/8/31 11:03:28

分治、排序与随机化:斯坦福算法专项第一课深度解析

斯坦福算法专项的第一门课,主题是分治、排序和随机化,主讲人 Tim Roughgarden,有中文配音版本可以看。这门课在算法学习圈里口碑很稳,不是因为它讲了多少炫酷技巧,而是它把一个程序员最容易绕开的底层能力讲清楚了&…

作者头像 李华
网站建设 2026/8/31 11:00:56

MFC定时器更新列表框:从WM_TIMER消息机制到UI线程安全实践

许多刚接触 MFC 的开发者,第一次做 Windows 上位机界面时,几乎都会遇到一个看似简单、实际坑不少的需求:界面上有一个列表框,需要在程序运行过程中定时往里面追加新的内容。这个需求听起来很朴素,但它背后涉及了 Windo…

作者头像 李华