尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用代码文档驯服AI Agent:把意图变成可执行契约

用代码文档驯服AI Agent:把意图变成可执行契约 要让 AI agent 按照意图执行任务真正可靠的办法不是把 prompt 写得更长而是把它想做的事固化成代码文档。最近一个很受关注的想法是让 agent 做我想做的事靠的是代码文档。很多人以为“不听话”是模型能力的问题但实际落地时你会发现绝大多数失控来自同一个地方——你的意图没有变成 agent 可读取、可校验、可回溯的执行契约。一次对话里我们可以把需求写得很详细也能在 agent 跑偏后再补几句修正。但这种交互是线性、口语化、无版本的。同一个任务换一个时间、换一个 agent结果可能就是两套理解。代码文档不一样它可以独立于对话存在能进 git能写测试能被程序检查。它不是给人看的注释而是连接人、模型和程序三者的一种协议。下面我会拆开讲清楚为什么代码文档能解决 agent 任务失控怎样写一份真正有效的 agent 任务文档怎么从单条样例逐步走到批量稳定以及在它失效时该如何排查。1. Agent 不听话不是它笨而是意图没有变成可执行约束1.1 我们习惯怎么指挥 Agent用对话“打补丁”最常见的用法是在对话框里把需求写清楚。比如“把数据里的异常值处理掉然后生成一份报告。”Agent 通常会给你一个看起来合理的流程处理缺失值、删除离群点、输出 Markdown 报告。但你很快会发现它的第一版结果和你脑子里那张表不一定对得上。你补充一句“保留原始列不要覆盖字段。”下一轮它确实保留了但口径又变了。你再补一句它又开始发挥。于是整个对话变成了一场打补丁的游戏。问题不在你要求高而是对话本身不适合承载任务规则。自然语言描述是连续性的你后来补的那句话会覆盖前面的话模型在强上下文里又特别容易“迎合最后一句话”。于是每轮对话都像一次新的理解而不是一次稳定执行。1.2 为什么上下文里的规则这么容易失效从工程角度看有四个原因。第一上下文窗口有限。你在一轮对话里塞的规则越多后续新消息就越容易把前面的注意力冲淡。规则进了上下文但不等于规则会被严格执行。第二自然语言不适合表达边界条件。你说“合理处理”模型不觉得有歧义但它不知道你心里的“合理”包含哪些具体场景。这需要额外澄清而澄清又会增加上下文压力。第三模型倾向于接话而不是执行协议。当你连续提出修正时模型会尽量顺着你的语气走结果就是你推一步它动一步。它没有在“执行一套稳定规则”而是在“生成一个像执行的文本”。第四缺少可验证性。代码坏了有报错prompt 错了没有报错。即使 agent 输出格式错了你也要靠人眼去发现。于是错误没有被拦截还会在下一轮被当成正确结果继续使用。1.3 关键转变从“告诉它”到“交给它一份文档”如果任务规则只存在于聊天记录里它就不是约束只是一种临时的上下文。真正稳定的做法是把规则放进一份独立文档让 agent 在动手之前先读取这份文档把所有关键要求都放在一个不会被最近对话覆盖的位置。这就像一个临时工进场。你只靠口头交代他大概率会按自己的经验干活你给他一份写着目标、边界、输入输出和验收标准的交接文档他至少知道你希望他做到什么程度。Agent 也需要同样的交接文档。代码文档之所以是合适的载体不是因为它能“描述”任务而是因为它能约束任务。文档可以被版本管理可以被测试可以被另一个程序读取。它把“人类意图”从一个模糊的脑内图像变成了可审查、可重放的资产。2. 代码文档的真正作用把“意图”翻译成 Agent 的任务契约2.1 文档的三层功能一份好的 agent 任务文档至少有两个读者人和模型。如果配合自动化验收它还有第三个读者——程序。第一层给人看。团队里的同事看到文档能知道这个自动化任务在做什么为什么存在。第二层给模型读。模型根据文档里的目标、约束、输出格式决定自己执行哪一步。第三层给程序校验。文档里的验收标准可以被转换成断言跑完任务后自动判断输出是否合格。这三层不是割裂的。同一段描述“输入是raw.csv输出是clean.csv保留所有原始列”人看了能理解模型读了能执行程序拿到clean.csv也可以直接检查字段名。当文档能够同时服务于这三种角色它就不再是装饰性注释而是一份可执行契约。2.2 一份 Agent 任务文档应该包含什么不是所有文档都能约束 agent。写得像产品说明一样的文档模型读起来很舒服但执行时还是容易跑偏。真正有效的任务文档建议包含下面几个模块。模块要回答的问题为什么必要任务目标这个任务最终要交出什么结果防止 agent 把过程当成目的输入与输出数据从哪来结果写到哪格式是什么减少自由发挥空间硬性约束哪些操作绝对不能做哪些字段不能改防止意外破坏验收标准输出满足什么条件才算通过让结果可测量失败处理遇到异常时是停止、重试还是跳到下一项避免批量任务被单个错误卡死如果一个任务没有这些信息说明它还不适合交给 agent 自动化。2.3 为什么文档优于 PromptPrompt 也有规则它和文档最大的差别在于生命周期。Prompt 是一次性的。今天写了一段很好的指令明天改了需求你可能直接在上面叠加一段最后连自己都分不清哪些规则还有效。文档则应该像代码一样被维护。每次修改都进 git每次 diff 都看得见谁改了什么、为什么改。这样任务一旦回归你能回滚的不只是代码还有 agent 的“说明书”。文档还更容易复用。你有三张表要做清洗不需要为每张表重写 prompt只需要改文档里的输入路径和字段清单。Prompt 是对话文档是资产。注意这里说的“文档优于 prompt”不是让你彻底抛弃 prompt而是把长周期规则放进文档让 prompt 只承担当前会话的引导。这样模型收到的信息更明确也不会被无关闲聊稀释。3. 最小可行实践让 Agent 先读文档再动手3.1 第一步把需求写成 docs/task.md先别急着调参数先写一份最小的任务文档。内容不复杂但必须包含第 2.2 节列出的模块。下面是一个简单示例。目标是清洗一份 CSV输出干净的表格和摘要。# 清洗销售订单 CSV ## 任务目标 将 data/raw_orders.csv 清洗为 data/clean_orders.csv 并生成 data/summary.json。 ## 输入 - 文件data/raw_orders.csv - 编码UTF-8 - 分隔符逗号 ## 输出 1. data/clean_orders.csv - 包含所有原始列列名不允许修改。 - 新增一列 order_total数值等于 quantity * unit_price。 - order_total 保留两位小数。 2. data/summary.json - 包含字段total_orders、total_revenue、clean_rate。 - 示例格式见本目录 schema/summary_schema.json。 ## 硬性约束 - 不修改原始文件。 - 不删除任何缺失值行缺失值用字符串 UNKNOWN 填充。 - 不使用外部 API。 ## 验收标准 - clean_orders.csv 可以正常读取。 - 每一行的 order_total 等于 quantity * unit_price。 - summary.json 通过 schema/summary_schema.json 校验。 ## 失败处理 - 原始文件缺失立即停止输出错误信息。 - 任何字段不存在立即停止不要自动猜测字段名。注意这份文档没有任何“请你尽量”“大概”“合理”这类词。它尽量把每一个判断点变成可验证事实。这是 agent 文档和普通说明文最大的区别。3.2 第二步让 Agent 在启动时强制加载文档文档写好后要确保 agent 在动手前真的读到它而不是靠你手动粘贴。一个常见做法是在系统提示里直接指定在执行任何任务之前先读取仓库根目录下的 docs/task.md。 阅读完 doc 后用自己的话复述目标与验收标准然后再开始。 如果文档内容有歧义先提问不要自行假设。如果你的 agent 支持工具调用更好的做法是让它通过工具读取文件而不是把文档内容塞进 prompt。这样文档仍然保存在文件系统里后续可以验证它是否被读取也能防止上下文爆炸。实测中很多失控问题都出在“文档存在但 agent 没有读”。所以这一步的关键不是写文档而是把“读文档”变成任务启动的第一动作。3.3 第三步用代码结构补充约束除了task.md代码本身的 docstring 和 schema 也能当约束。尤其是当任务涉及具体函数时docstring 可以帮助 agent 理解接口。def clean_order( input_path: str, output_path: str, fill_missing: str UNKNOWN, ) - tuple[str, dict]: 清洗订单数据。 Args: input_path: 原始 CSV 路径。 output_path: 清洗后 CSV 路径。 fill_missing: 缺失值填充字符串。 Returns: (输出文件路径, summary dict)。 Raises: FileNotFoundError: 输入文件不存在。 这段 docstring 看起来普通但对 agent 而言它比你的口头描述更稳定。只要 agent 能读到源码它就会按函数签名和出口说明来判断结果。如果配合 JSON Schema还可以让结构化输出的校验自动化{ type: object, required: [total_orders, total_revenue, clean_rate], properties: { total_orders: { type: integer, minimum: 0 }, total_revenue: { type: number, minimum: 0 }, clean_rate: { type: number, minimum: 0, maximum: 1 } } }3.4 先跑通单任务再谈批量写文档只是第一步跑出来的结果还得验证。第一次运行建议只用一条样例。文档是否真的被 agent 读取并且在它后续输出中有所体现输出文件是否存在字段是否按约定生成验收标准里的断言是否全部通过有没有出现 agent 自己发明字段、自行解释“合理”这类行为这些检查不复杂但能帮你判断文档是否写清楚了。如果一条样例都跑不稳不要继续扩大批量。批量只会把同一个错误重复很多次。注意不要一上来就把批量数和并发数拉满先用一条样例确认输入、输出和日志都正常。单次跑通只能说明流程没有断不代表规则已经稳定。4. 从单次跑通到稳定批量还差四块拼图4.1 结构化日志让 Agent 的每一步可回溯单次跑通后你很快会发现agent 的任务不能只靠最终结果判断。它中途可能读了哪个文件、调用了哪个工具、丢弃了哪些数据这些信息都值得记录。建议让 agent 在关键步骤输出结构化日志。比如处理完每一批订单后追加一条 JSON 记录{ step: clean, input: data/raw_orders.csv, output: data/clean_orders.csv, rows_before: 1024, rows_after: 1024, status: ok, duration_ms: 1842 }有了日志你就能回答“它到底做了什么”这个问题。否则只凭最终文件很难判断它是否遵守了所有边界约束。4.2 失败重试与熔断Agent 任务进入批量阶段后失败是常态。关键是失败之后怎么处理。我建议先把策略写死在文档里比如单个样本最多重试 2 次。如果两次重试都失败跳过当前样本并记录原因。如果连续失败超过 5 次整个任务停止。并发数从 1 开始逐步增加。每个步骤设置超时时间防止 agent 在某个问题上无限循环。这些参数要写在task.md的失败处理部分让 agent 在进入异常场景时有据可依。不要指望它自己会判断“该不该停止”尤其当任务是一个长循环时无限生成和无限重试都可能导致成本失控。4.3 版本化文档把“任务说明”也当成代码管理到了这一步docs/task.md不该再是随意修改的草稿。它应该和代码一起提交每次任务行为变化都能对应一次 commit。比如你决定把“缺失值填充UNKNOWN”改成“缺失值直接删除”对应的 commit 里应该同时包含docs/task.md里硬性约束的修改验收标准的变化相关测试的更新这样当某个生产任务突然多出几条脏数据时你能通过 git 历史判断是文档变了、模型升级了还是输入变了。要是没有版本化你只能靠记忆猜这很难长期维护。4.4 自动化验收把“看起来对”变成“断言通过”批量任务不能靠人眼逐个看必须有自动化验收。你可以用一个脚本检查输出是否符合文档约定python scripts/validate_output.py \ --input data/clean_orders.csv \ --schema schema/summary_schema.json \ --log logs/task.log脚本里可以检查文件是否存在。列名是否和预期一致。数值公式是否成立。输出 JSON 是否符合 Schema。日志中是否存在status: error。这些检查每一轮跑批后自动执行。只有通过了任务才算完成。没有验收文档写得再漂亮也只是给人看不是给流程用。5. 当你觉得 Agent 又跑偏了按这个顺序排查再好的文档也可能遇到模型不听话、环境变化、参数冲突。遇到跑偏时不要马上改 prompt。按下面顺序排查通常能更快定位。5.1 第一层文档是否真的被模型读取先看日志和上下文确认 agent 是否真的执行了“读取docs/task.md”这一步。有些框架里手动粘贴的文档和工具读取的文档是不同的。如果你只把要求写进了某段很长的 user 消息里后面对话可能会覆盖它。更稳妥的做法是在系统提示里指定文档路径并记录工具调用日志。如果 agent 没读文档问题不是文档写得不好而是启动协议失效。5.2 第二层文档本身是否自洽文档存在歧义是最常见的隐性错误源。比如你在“硬性约束”里写“不删除缺失值行”但在“验收标准”里写“clean_rate 应接近 100%”模型就会困惑到底按哪个执行。文档里出现“大约”“尽可能”“合理”这类词要特别警惕。它们不是约束是给模型的自由发挥空间。检查文档时可以问自己一个没有项目管理背景的新人能照着这份文档交付完全一致的产物吗如果他不能模型大概率也不能。5.3 第三层输入数据和工具权限有时候不是 agent 不听话而是它的手脚被绑住了。输入文件权限不够它想读但读不到。输出目录不存在它创建失败后自动转向别的路径。网络受限它调用外部 API 失败于是改成离线估算。模型版本不支持某些工具它悄悄换了实现方式。这类问题经常伪装成“理解错误”。所以遇到异常输出先看权限、依赖、目录和工具版本再判断是不是意图理解问题。5.4 第四层模型能力和上下文边界最后才需要考虑模型本身。如果文档很长、字段关系复杂、手工规则超过十几条模型可能确实记不住。这时不要把文档写得更长而是拆分任务。比如把“清洗订单”拆成“第一步解析”“第二步校验”“第三步汇总”每一份文档负责一个子任务。上下文窗口不是无限仓库文档太多时信息密度反而会下降。下面这张表可以帮你快速定位方向。现象优先排查方向Agent 完全没按文档执行检查它是否读取了文档启动协议是否失效执行了部分但忽略某些约束检查文档里约束之间是否冲突是否有模糊词尝试读文件但失败检查权限、路径、编码、依赖输出格式不对检查 schema 是否明确是否给了示例批量任务中途停止或无限循环检查失败重试策略、超时、并发参数同一份文档不同次执行结果不一致检查模型版本、上下文是否被污染文档是否被静默修改6. 一个可复用的 Agent 任务文档模板与长期迭代方法6.1 五要素模板把上面所有内容收束起来可以沉淀成一份可复用的模板。每次新的 agent 任务都从这份模板起步。# [任务名称] ## 任务目标 - 要交付什么产物 ## 输入 - 数据源路径 - 依赖文件 - 格式要求 ## 输出 - 文件路径 - 字段列表 - 格式示例 ## 硬性约束 - 不能修改什么 - 必须保留什么 - 禁止调用什么 ## 验收标准 - [ ] 产物存在且可读取 - [ ] 字段名与 schema 一致 - [ ] 关键数值满足公式/范围 - [ ] 日志中无错误状态 ## 失败处理 - 重试次数 - 连续失败阈值 - 超时时间 - 错误日志路径模板的价值不是让你照抄而是提醒你每个任务都必须在动手前回答这些基本问题。回答得越具体agent 的自由发挥空间就越小。6.2 迭代循环文档也是需要维护的代码一次写好一份文档就长期不变这不现实。更合理的路径是选一个足够小的任务。写第一版文档。跑一条样例。检查验收标准。发现偏差优先修订文档而不是临时加一条口头指令。重复第 3 步到第 5 步直到稳定。再扩大批量。这个循环里最重要的习惯是一旦发现某个口头提示能修正 agent 行为你要第一时间把它沉淀进文档。否则你只是在训练这一次会话而不是在建设一个可复用的任务流程。时间久了你会发现真正提升效率的不是模型本身而是你表达意图的能力。文档写得越清楚agent 的行为就越可控你对任务的理解也越深入。6.3 适用边界它解决不了所有“Agent 不听话”最后说一点边界。把意图写进代码文档适合那些有明确产出、可被验收、流程重复出现的任务。比如数据清洗、报告生成、批量校验、代码迁移、接口测试。这些任务有清晰的输入输出有可量化的成功标准文档能起到强约束作用。但它不适合纯开放性的探索、创意写作或需要大量主观判断的场景。你让 agent“写一篇有感染力的文案”文档写得再细也不能替你定义什么是“感染力”。这种情况下与其花时间写文档不如把重点放在人工反馈和逐步迭代上。另外文档不能消除所有错误。即使文档很精确模型也可能因为上下文污染、工具异常或版本变化而跑偏。所以生产环境里日志、测试、人工抽检仍然缺一不可。注意代码文档让 agent 的“出错”从随机变成可回溯这是它最大的价值。但它不会自动让结果变正确它只是让你更容易知道错在哪、为什么错。如果你现在正被“agent 不按我说的做”困扰我的建议很直接别急着换模型也别急着写更长的 prompt先做一份 100 行的docs/task.md把目标、输入输出、约束、验收和失败处理写清楚然后让 agent 先读文档再动手。把它当成一次实验跑一条样例看看结果。你会发现很多失控不是模型太笨而是我们还没学会把意图变成一份可执行的代码文档。这份文档才是你和 agent 之间最稳定的接口。
返回列表