找回密码
 立即注册
首页 业界区 业界 Agent构建:声明式优于硬编码

Agent构建:声明式优于硬编码

石娅凉 6 小时前
AI Agent 实操指南:用一份 Markdown 文件指挥 AI 干活

你大概已经用 AI 写过不少代码了——让它生成一个函数、解释一段逻辑、写个正则。但你有没有遇到过这种情况:
项目里有个老模块,散落在十几个文件里,全是回调地狱。你想让 AI 帮你重构成 async/await,顺便补上测试。结果呢?AI 改了三个文件就开始"失忆",改完 A 忘了 B 的依赖,测试一跑全红。
问题不在 AI 的能力,而在于你跟它的协作方式。
你一直在跟 AI 对话,但你真正需要的是给它一份工作手册
这就是 Agent 思维的起点。
什么是 Agent?一句话版本

传统用法:你说一句,AI 做一步。你是指挥官,AI 是传话兵。
Agent 用法:你给一个目标和规则,AI 自己拆解任务、调用工具、验证结果、遇到问题还能自我修正。
用公式表达:
  1. Agent = LLM(推理能力)+ 工具调用(读写文件、跑命令)+ 工作流(按步骤执行)+ 约束规则(什么能做什么不能做)
复制代码
关键区别不是 AI 变聪明了,而是你给了它上下文和边界
核心理念:声明式优于硬编码

传统开发里,你用代码控制逻辑——写一堆 if/else、状态机、流程引擎来编排 AI 的行为。这叫命令式(Imperative)
但在 Agent 场景下,还有另一条路:用自然语言文档控制 AI 的行为。这叫声明式(Declarative)
你不写代码去约束 AI,而是写一份 Markdown 文件,告诉它:

  • 你的角色是什么
  • 工作流程是什么
  • 哪些事绝对不能做
  • 遇到问题怎么处理
为什么 Markdown 能管用?因为 Markdown 的标题层级、列表、缩进,天然地把信息组织成了清晰的层次结构。大模型在处理这种结构化文本时,能更准确地理解"哪些是规则、哪些是步骤、哪些是禁区",大幅减少歧义。一份层次清晰的 .md 文件,对 AI 来说就像一份合同——结构越清晰,它的遵循度越高。
这份文件,我们统一叫它 AGENTS.md
你可能见过 CLAUDE.md(Claude Code 专用)或 .cursorrules(Cursor 专用)。这些本质上都是同一件事——给 Agent 写工作手册。AGENTS.md 是一个更通用的命名约定,不绑定任何厂商。随着 VS Code、Cursor、Kiro 等工具对 Agent 模式的支持越来越成熟,一个标准化的文件名会让你的配置跨工具复用。
底层到底发生了什么?

在你动手之前,有必要先理解 Agent 的运行机制。别担心,不复杂。
你日常使用的 AI 编码工具(VS Code + Copilot、Cursor、Kiro 等),其实已经内建了一个 Agent。这个 Agent 的"骨架"是用代码写死的——一个循环程序(通常叫 Orchestrator / 宿主程序),负责:

  • 接收你的指令
  • 把指令连同 AGENTS.md 的内容一起发给大模型
  • 解析大模型的回复——如果回复里包含工具调用请求(tool_call),就去执行对应的操作(读文件、写文件、跑命令等)
  • 把执行结果喂回给大模型
  • 重复 2-4,直到任务完成
大模型本身虽然经过了指令微调,具备了推理和规划能力,但它终究只能"输出文本"——它自己不会读文件、不会跑命令、不会发请求。外面这层硬编码的宿主程序,才是让它"有手有脚"的关键。
而 AGENTS.md 的角色,就是在第 2 步被注入——它成为大模型的"系统指令"的一部分,影响大模型在整个任务过程中的每一次决策。
动手:在 VS Code 里配置你的第一个 Agent

不需要装额外插件,不需要命令行工具。你只需要 VS Code + 一个支持 Agent 模式的 AI 扩展(比如 GitHub Copilot、Kiro、Continue 等)。
项目结构

在你的项目根目录下,创建一个 .agents/ 目录。每个 Agent 是一个独立的 .md 文件:
  1. your-project/
  2. ├── .agents/
  3. │   ├── modernizer.md      ← 代码现代化专家
  4. │   ├── reviewer.md        ← 代码审查专家
  5. │   └── bug-fixer.md       ← Bug 修复专家
  6. ├── src/
  7. │   └── ...
  8. ├── tests/
  9. │   └── ...
  10. └── package.json
复制代码
为什么每个 Agent 一个文件,而不是全塞进一个 AGENTS.md?
道理跟你不会把所有业务逻辑写进一个 index.js 一样——关注点分离。当你有 5 个不同职责的 Agent 时,独立文件让你能精确地告诉 AI:"这次用 modernizer.md 的规则来干活",而不是让它在一份巨大的文档里自己猜该遵守哪段。
你也可以在项目根目录放一个 AGENTS.md 作为全局规则(比如代码风格、Git 提交规范),让所有 Agent 都遵守。子目录的 Agent 文件则定义各自的专属职责。这种分层设计,跟你在软件架构里做的模块解耦是一回事。
写你的第一份 Agent 文件

假设你的任务是:把项目里的老式回调代码重构成 async/await。
创建 .agents/modernizer.md:
  1. # 代码现代化 Agent
  2. ## 角色
  3. 你是一个 Node.js 代码重构专家。你的任务是将项目中的回调风格异步代码
  4. (Callbacks、.then() 链)重构为 async/await 语法。
  5. ## 工作流程
  6. ### 1. 扫描
  7. - 搜索项目中所有包含 `.then(`、`callback`、`fs.readFile` 等模式的文件
  8. - 输出一份待重构文件清单,按依赖关系排序(被依赖最少的先改)
  9. ### 2. 逐个重构
  10. - 每次只改一个文件
  11. - 改完后立即运行 `npm test`
  12. - 如果测试通过,继续下一个文件
  13. - 如果测试失败,最多尝试修复 3 次
  14. ### 3. 遇到阻塞
  15. - 如果某个文件 3 次修复都失败,回滚该文件的改动
  16. - 记录失败原因,继续处理下一个文件
  17. - 最后汇总所有阻塞点,报告给我
  18. ## 规则
  19. - 不要修改 package.json 的 dependencies 或 version 字段
  20. - 不要重构没有对应测试用例的文件(没测试 = 不动)
  21. - 每个文件改完后必须跑测试,不能攒着一起跑
  22. - 如果不确定某个改动是否安全,先问我
  23. ## 代码风格
  24. - 使用 try/catch 包裹 await 调用
  25. - 错误处理保持与原代码一致的粒度
  26. - 保留原有注释,必要时更新
复制代码
这不是 prompt,这是工作规范。AI 每次执行任务时都会读取这份文件,就像新员工入职时读 wiki 一样。
给 Agent 下达任务

在 VS Code 的 AI 聊天窗口中,你可以这样说:
  1. 请按照 .agents/modernizer.md 中的工作流程,开始重构 src/ 目录下的异步代码。
复制代码
一个好的 Agent 会这样响应:
我已读取 modernizer.md。开始扫描阶段。
发现以下 7 个文件包含回调风格代码:

  • src/utils/fileHelper.js(无外部依赖,优先处理)
  • src/services/userService.js(依赖 fileHelper)
  • ...
开始处理第 1 个文件:src/utils/fileHelper.js
它不是在"聊天",它在执行工作流
当任务变大:子 Agent 与上下文隔离

上面的重构案例还算简单——一个 Agent 就能搞定。但现实中,任务往往更复杂。
比如你要"根据 OpenAPI 文档实现所有 API 接口"。这份文档可能有几千行,涉及 30 个接口。如果让一个 Agent 把文档全读进去再写代码,它的上下文窗口很快就会被撑满——这就是上下文爆炸。AI 读了太多杂乱信息后,会开始"软忽略"早期的指令,质量急剧下降。
解决方案:拆任务,派子 Agent
子 Agent 是怎么被创建的?

这是整个 Agent 架构里最精彩的部分。你可能会好奇:一份 Markdown 文件,怎么就能让 AI "生"出另一个 AI?
答案是:本质上就是一次 tool_call
还记得前面说的宿主程序吗?它给大模型提供了一系列工具(读文件、写文件、跑命令等)。其中有一个特殊的工具,类似于 spawn_agent——"启动一个新的 Agent"。
当主 Agent 在执行任务时,如果它判断"这个子任务太大了,我应该派个人去专门处理",它就会输出一段 JSON:
  1. {
  2.   "tool": "spawn_agent",
  3.   "args": {
  4.     "task": "阅读 docs/api-spec.yaml,提取所有接口的路径、方法、请求/响应 schema,输出精简的 JSON 摘要"
  5.   }
  6. }
复制代码
宿主程序收到这个 tool_call 后,会做几件事:

  • 创建隔离工作目录:宿主程序(不是大模型)自动在一个临时区域为这个子 Agent 新建一个独立文件夹(比如 .agents/workspaces/api-parser-a3f1,带随机后缀避免冲突)。这个目录就是子 Agent 的"沙盒"——它的临时文件、中间产物都放在这里,不会污染主项目,也不会跟其他并行的子 Agent 互相干扰。
  • 启动新的大模型会话:开一个全新的对话实例,把任务描述注入为系统指令。这个新实例的上下文是干净的——它只知道自己被分配的任务,不背负主 Agent 的历史包袱。
  • 子 Agent 独立执行:在自己的沙盒里读文档、处理数据、生成结果。
  • 返回结果,销毁实例:子 Agent 完成后,宿主程序把结果作为那次 tool_call 的返回值喂回给主 Agent。子 Agent 的会话随即被宿主程序销毁,释放上下文资源。谁来销毁?始终是宿主程序——大模型自己没有管理进程生命周期的能力。
对主 Agent 来说,整个过程就像调用了一个耗时稍长的函数——传入参数,拿到返回值,继续干活。
在 AGENTS.md 里怎么写,才能引导 AI 启动子 Agent?

关键在于:你要在工作流中明确描述什么条件下应该拆分任务,以及子任务的边界是什么。AI 不会无缘无故地去 spawn 子 Agent——它需要你在"宪法"里给出清晰的指引。
来看一个具体的例子。我们扩展前面的重构场景,加入 API 实现的需求:
  1. # API 实现 Agent
  2. ## 角色
  3. 你负责根据 OpenAPI 文档实现所有后端 API 接口。
  4. ## 工作流程
  5. ### 阶段一:文档解析(委派子 Agent)
  6. 这个阶段的工作量大且独立,必须委派给一个子 Agent 处理:
  7. - 子 Agent 的任务:阅读 docs/api-spec.yaml,提取所有接口的路径、方法、
  8.   请求参数和响应 schema,输出为精简的 JSON 摘要
  9. - 子 Agent 应在自己的隔离工作区中完成,不要在主项目目录下创建临时文件
  10. - 等待子 Agent 返回 JSON 摘要后,再进入阶段二
  11. ### 阶段二:逐个实现
  12. - 基于阶段一返回的 JSON 摘要(不要再去读原始 yaml 文件)
  13. - 按接口逐个实现
  14. - 每实现一个接口,写一个对应的集成测试
  15. - 每个接口实现后跑一次测试
  16. ### 阶段三:汇总
  17. - 列出所有已实现的接口和测试覆盖情况
  18. - 标注未能实现的接口及原因
  19. ## 规则
  20. - 阶段一必须通过子 Agent 完成,不要自己直接读取完整的 API 文档
  21. - 如果 API 文档超过 3000 行,可以按模块拆分为多个子 Agent 并行解析
复制代码
注意"阶段一"的写法——它没有用任何代码或 JSON 去"调用"子 Agent,而是用自然语言描述了:

  • 什么时候该拆:"这个阶段的工作量大且独立"
  • 子 Agent 该做什么:"阅读 yaml,提取 schema,输出 JSON 摘要"
  • 隔离要求:"在自己的隔离工作区中完成"
  • 同步机制:"等待子 Agent 返回后,再进入阶段二"
大模型读到这些指令后,会自己决定在合适的时机发出 spawn_agent 的 tool_call。你用声明式的语言描述了"意图",宿主程序和大模型配合完成了"执行"。
为什么要隔离工作目录?

你可能注意到了,子 Agent 不是在主项目目录里干活,而是在 .agents/workspaces/ 下的独立文件夹里。这不是多此一举,而是解决了两个实际问题:

  • 防止并行冲突:如果你同时派了 3 个子 Agent 去解析 API 文档的不同模块,它们都往同一个 api-summary.json 里写,结果就是互相覆盖、数据错乱。独立目录让每个子 Agent 有自己的"工位",互不干扰。
  • 保持主项目干净:子 Agent 的中间产物(临时文件、草稿、调试日志)不会散落在你的 src/ 目录里。任务完成后,宿主程序可以根据策略清理这些工作目录,或者保留作为审计日志。
主 Agent 和子 Agent 怎么沟通?

这是多 Agent 协作中最关键的问题。目前主流有两种模式:
模式一:同步等待(最常见)

主 Agent 发出 spawn_agent 的 tool_call 后,自己就挂起了——就像你在代码里调了一个 await。子 Agent 独立运行,完成后把结果返回。宿主程序把这个结果作为 tool_call 的 response 喂回给主 Agent,主 Agent 被唤醒,继续往下走。
在主 Agent 看来,这就是一次普通的函数调用:传入任务描述,拿到执行结果。
模式二:异步协作(适合超大任务)

宿主程序同时启动多个子 Agent 并行执行,不让主 Agent 傻等。所有子 Agent 完成后,宿主程序收集各自的结果,一次性或分批喂回给主 Agent。
需要注意的是,大模型本身是无状态的(每次都是请求-响应),所以"异步"和"并行"都是宿主程序在调度,不是大模型自己在"同时想两件事"。主 Agent 和子 Agent 之间的协调,本质上都是宿主程序在中间传话。
在 AGENTS.md 里怎么写沟通规则?

你可以在 Agent 文件中明确规定汇报机制。比如:
  1. ## 子 Agent 沟通协议
  2. ### 任务委派时
  3. - 向子 Agent 明确说明:任务目标、输入数据位置、期望的输出格式
  4. ### 结果接收时
  5. - 子 Agent 必须返回结构化的 JSON 结果,包含:
  6.   - status: "success" | "partial" | "failed"
  7.   - result: 实际产出(数据、文件路径等)
  8.   - issues: 遇到的问题列表(如果有)
  9. - 如果 status 为 "partial" 或 "failed",主 Agent 应评估是否需要重试或换策略
  10. ### 进度汇报(长时间任务)
  11. - 子 Agent 每完成一个主要步骤后,
  12.   通过系统的 HTTP 工具向 http://localhost:3000/progress 发送进度通知
  13. - 通知格式:{"agent": "api-parser", "step": "3/7", "message": "已解析用户模块"}
复制代码
注意最后一段——进度汇报机制也是声明式的。你不需要在代码里写死 Webhook 地址或 Slack 通知逻辑。只要宿主程序提供了基础的 HTTP 请求工具,你在 Markdown 里写一句"用 HTTP 工具发个通知",AI 就会在合适的时机自己拼装请求去调用。
这就是声明式的魅力:连"怎么汇报工作"这件事,都不用硬编码
顺便聊聊 SKILL.md:Agent 的"技能手册"

你可能注意到,AGENTS.md 关注的是"谁来做、按什么流程做、遇到问题怎么办"——这是编排层
但有些知识是跨 Agent 通用的,比如:

  • 怎么读取 PDF 文件的内容(毕竟它不是纯文本)
  • 怎么调用公司内部的 API 网关
  • 怎么按照团队的 UI 规范生成前端组件
这类"怎么做某件具体的事"的知识,适合写进 SKILL.md
打个比方:

  • AGENTS.md 是岗位职责书——定义这个人负责什么、工作流程是什么
  • SKILL.md 是操作手册——定义某项具体技能的步骤和规范
Agent 在执行任务时,会根据需要去"翻阅"相关的 SKILL 文件。比如重构 Agent 在处理文件读写时,可能会参考一份关于"Node.js 文件操作最佳实践"的 SKILL.md。
这篇文章不展开 SKILL.md 的细节,但记住这个分层:Agent 负责编排,Skill 负责执行细节。两者配合,才是完整的 Agent 架构。
安全网:反思层与人在回路

让 AI 自主干活,你难免会担心:万一它改出 bug 怎么办?万一它执行了不该执行的操作呢?
反思层:让 Agent 审查自己

在 AGENTS.md 里加一段"自检"规则:
  1. ## 质量保障
  2. - 在提交任何改动前,切换到"资深代码审查者"的视角重新审视你的改动
  3. - 检查清单:
  4.   - 是否引入了新的副作用?
  5.   - 错误处理是否完整?
  6.   - 是否有遗漏的边界情况?
  7. - 如果发现问题,先修复再提交
  8. - 如果不确定,标记为 TODO 并告知我
复制代码
你甚至可以更进一步——让主 Agent 在提交代码前,专门 spawn 一个"Reviewer Agent"来审查:
  1. ## 代码审查流程
  2. 在提交任何代码变更前,启动一个临时的审查子 Agent。
  3. 它应站在 SRE(运维工程师)的角度审查你的 Patch:
  4. - 是否有性能隐患?
  5. - 是否有安全风险?
  6. - 是否会影响现有服务的稳定性?
  7. 只有当审查 Agent 返回 "LGTM" 时,才能提交改动。
复制代码
人在回路:关键操作必须人类确认

在真实的企业场景中(修改数据库、发送邮件、部署上线),我们不敢让 Agent 100% 自动化。
好的 Agent 工具都支持"人在回路"机制:当 Agent 要执行高危操作时,宿主程序会暂停执行,弹出确认框等你点"Yes"。
需要说清楚的是:这个拦截是宿主程序硬编码实现的,不是靠 Markdown 声明就能保证的。你在 AGENTS.md 里写"删除文件前要问我",AI 大概率会遵守,但这属于"软约束"——大模型有可能忽略。真正的安全保障,是宿主程序在工具层面做的拦截:比如当 AI 调用 delete_file 工具时,宿主程序无论如何都会先弹确认框。
所以最佳实践是双保险:宿主程序层面做硬拦截(这是底线),同时在 AGENTS.md 里也写上规则(让 AI 在决策阶段就尽量避免高危操作):
  1. ## 需要人类确认的操作
  2. 以下操作在执行前必须暂停并等待我的确认:
  3. - 删除任何文件
  4. - 修改数据库 schema
  5. - 向外部服务发送请求
复制代码
这跟 Prompt Engineering 有什么区别?

你可能觉得:这不就是写了个长 prompt 吗?
区别在三个地方:
PromptAGENTS.md生命周期一次性,用完即弃持久化,每次任务都生效作用范围单次对话整个项目,所有对话协作方式你指挥,AI 执行你定规则,AI 自主决策Prompt 是"这次帮我做个什么"。AGENTS.md 是"你在这个项目里应该怎么工作"。
一个是临时工的任务单,一个是正式员工的岗位手册。
写在最后

Agent 不是什么遥远的未来技术。它就是:
把你脑子里"我会怎么做这件事"的过程,写成一份结构化的文档,然后让 AI 照着做。
你一直在用 AI 当计算器——给一个问题,要一个答案。但 Agent 模式下,AI 是你的同事——你给它一个目标、一套规范、一些工具,它自己去把事情办了。
核心思维转变:

  • 以前:你写代码来实现功能
  • 现在:你写 AGENTS.md 来描述"如何实现功能的过程"
现在打开 VS Code,在项目里建一个 .agents/ 目录,把你最头疼的那个重复性任务写成一份 Agent 文件。
不需要完美,先写一版,跑一次,看看 AI 怎么理解你的意图,然后迭代。
这就是 Agent 开发的正确打开方式——不是写代码,而是写规则;不是编程,而是管理。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册