使用AI智能体、子智能体、技能及MCP的五大最佳实践
目录
§0 📖 本文在系列中的位置 §1 🏗️ 天真的架构——以及它为何失效 §2 ✅ 更好的架构——基于 MCP 的多智能体系统§3 📉 构建之前:生产力现实核查 §3b 📐 需求先行——AI 无法消除的瓶颈
- §4 📊 最佳实践 0 —— 选择合适的模型并保持精准
- §7 📋 最佳实践 1 —— 明确具体:在构建前定义智能体行为
- §8 🤖 最佳实践 2 —— 考虑上下文隔离与可重用性
- §9 🔒 最佳实践 3 —— 确保 MCP 调用安全
- §10 🛡️ 最佳实践 4 —— 引导智能体响应的安全性和质量
- §11 ✅ 质量与安全最佳实践总结
本指南针对开发者在使用 AI 智能体、子智能体、技能和 MCP 服务器时应遵循的五大最佳实践进行了实操说明——从选择合适的模型和编写精确提示词,到通过 SDD 定义智能体行为、利用 Claude Code 子智能体进行上下文隔离、确保 MCP 调用安全,以及通过护栏引导智能体响应质量。
§0 📖 本文在系列中的位置
本文假设你已经了解什么是 MCP,并至少使用或构建过一个智能体。
如果你是初学者,《让我们用 Quarkus 创建一个 AI MCP 服务器》涵盖了协议基础知识和你的第一个工具。
关于你无法控制的第三方 MCP 服务器的安全威胁模型,《MCP 末日的 5 位骑士》是姊妹篇——它涵盖了当你无法修改服务器代码时需要审计的内容。
本文将承接上述内容,深入探讨架构、生产模式以及仅在规模化时才会出现的问题。
§1 🏗️ 天真的架构——以及它为何失效
大多数智能体实现起步方式都一样:一个模型,几个硬编码在智能体中的工具或 API 调用,以及一个试图让一切运转起来的大型系统提示词。架构如下:
naive-architecture
[LOADING...]
这种方式在演示时有效,但在生产环境中会失败,原因如下:
§2 ✅ 更好的架构——基于 MCP 的多智能体系统
解决方案是分解——即将从单体服务转向微服务的原则应用于智能体系统。由一个监督智能体处理意图路由,专门的子智能体处理特定领域。MCP 服务器为外部系统提供标准化、限定范围的访问权限。每个组件职责单一,且爆炸半径边界明确。
multi-agent-mcp-architecture
[LOADING...]
这种架构的优势显而易见:一个只能读取订单的子智能体,无论模型被告知什么,都无法删除订单。一个限定在“读取”范围的 MCP 服务器无法被强制执行写入。只负责路由的监督者无法直接接触任何外部系统。范围限制是由架构强制执行的,而不仅仅是靠指令。
但这种架构引入了三个天真架构所掩盖的问题类别。本文后续部分将探讨这些问题及其解决方法。
以下各节均标注了其解决的问题类别,以及该模式是适用于此类系统的用户(使用你未构建的智能体和 MCP 服务器)还是创作者(构建他人依赖的工具和架构)。我们大多数人兼具这两种角色——请顺着阅读或直接跳转到你当前面临的问题。
§3 📉 构建之前:生产力现实核查
在投入多智能体架构之前,有必要基于数据来设定预期。一项 2025 年的 METR 随机对照试验(RCT)研究了 16 名经验丰富的开发者,涉及 246 项真实任务,结果发现 AI 工具使开发者的工作效率降低了 19%,尽管这些开发者认为自己提高了 20% 的效率 [1]。Faros AI 发现,尽管 AI 采用率高达 75%,但在 10,000 多名开发者中,DORA 指标没有可衡量的改善 [2]——个人收益被未改变的瓶颈所抵消。
METR 研究的是 Cursor 和 Claude,而非 MCP 智能体,因此下表是我们对该研究的解读。但当开发者在智能体模式下使用 Cursor 时,它执行了与子智能体相同的“规划 → 工具调用 → 观察 → 迭代”循环。METR 记录的失败模式同样适用。不同之处在于,在多智能体系统中,一个错误不会停留在一次对话中,而是会通过工具调用和智能体边界传播。更好的架构并不能消除这些问题,但它使这些问题变得可见、可测试且可修复。
重点不是智能体无效,而是导致开发者在使用 Cursor 时效率降低的失败模式在智能体系统中结构上更严重,因为错误在工具调用和子智能体边界之间累积,而不是局限于单一的建议。
§3b 📐 需求先行——AI 无法消除的瓶颈
AI 让编码变得廉价,但思考依然昂贵。在构建任何智能体之前,必须有人弄清楚系统应该做什么——这依然是人类的工作。正如 Simon Martinelli 所言:“AI 没有消除复杂性,它只是重新分配了它。精力不再花在写代码上,而是花在理解应该构建什么上。”
给智能体输入模糊的需求,你会很快得到一个功能正常但做错事的代码。上游的清晰度决定了下游提示词、规范和护栏的有效性。
AI 统一流程 (AI Unified Process)
由 Java Champion Simon Martinelli 提出的 AI 统一流程 (AIUP) 将规范(而非代码)置于核心。其核心工件是系统用例:对系统从外部表现出的行为进行精确、可测试的描述。代码、测试和文档均由同一规范生成。需求变更?先更新规范,代码随之更新。
IREB AI4RE —— AI 时代的软件需求工程
国际需求工程委员会 (IREB) 提供 AI4RE:一项关于在需求工程中负责任地使用 AI 的微证书。它涵盖了需求获取、文档编写、验证、大语言模型 (LLM)、提示词工程以及 AI 的局限性。无先决条件,可自学。
两者相辅相成:AI4RE 帮助你编写更好的规范;AIUP 确保这些规范驱动系统,而不是在编码开始后就被遗忘。
理解代码的智能体:OpenCode 中的 LSP
OpenCode 是一款开源 AI 编程智能体,它将其子智能体连接到语言服务器协议 (LSP) 服务器。当子智能体编辑文件时,OpenCode 会查询 LSP 服务器并将诊断信息直接反馈到智能体的上下文中:类型错误、未定义变量、缺失方法——这与你的 IDE 提供的反馈相同。它还可以查询符号、导航到定义以及检查调用层级和 AST 结构。
它内置了超过 30 种 LSP 服务器(Java、TypeScript、Go、Rust、Python 等)。
🏆 最佳实践
五项实践涵盖了最常见的失败模式。请按顺序应用——每一项都会使下一项更有效,且当智能体背后的需求从一开始就很明确时,所有实践的效果都会更好。
§4 📊 最佳实践 0 —— 选择合适的模型并保持精准
模型选择会影响一致性、指令遵循能力以及对模糊性的处理方式。但下表展示了更重要的一点:提示词完成了大部分工作。一个约束良好的提示词配合较弱的模型,其表现始终优于给予模糊指令的更强模型。
以下是同一请求在有无约束下的对比:
claude.ai —— 模糊提示词,无约束 用户: 编写一个 Java MCP 工具来根据 ID 获取订单。 CLAUDE:
claude.ai —— 约束提示词,明确需求 用户: 编写一个 Java MCP 工具来根据 ID 获取订单。要求:验证 UUID 格式,返回类型化的 DTO(而非 JPA 实体),如果未找到则抛出领域异常,并编写一个精确的工具描述,告知模型此工具不应用于何处。 CLAUDE:
模型大小提高了上限,提示词精度提高了下限。为任务选择合适的模型,但永远不要用模型选择来代替提示词纪律。
SDD(规范驱动开发,Specification-Driven Development) 是一种在编写任何代码或提示词之前,先编写简短、结构化规范的实践。可以将其视为代理(Agent)领域的 TDD(测试驱动开发)。该规范定义了范围、禁止的操作、工具、输出格式、升级条件和测试用例。它指导着系统提示词、具体实现和测试套件。相同的规范,相同的行为,始终如一。
一个行为不端的普通函数会产生明显的报错。而一个行为不端的代理往往会“默默地”成功——它返回了某些内容、调用了工具或生成了输出。其失败之处在于它所做的选择。如果没有规范,你就没有任何衡量标准;有了规范,任何偏离行为都是一个测试失败,而不是生产事故。
规范长什么样
以下是一个订单支持子代理的示例。你在编写任何代码之前写下它,将其提交到仓库,并像审查设计文档一样与团队一起评审:
specs/order-support-agent.yaml
每个字段都直接映射到一个约束。forbidden_actions 成为系统提示词中的硬性阻断项。tools 是实现中注册的精确列表——不多也不少。test_cases 成为你的自动化测试套件。当代理行为不端时,你要先更新规范,然后再更新实现。规范是产物,代码是其当前的表达形式。
使用 Claude Code 将规范转化为代码
一旦规范提交到仓库,你就可以直接在提示词中将其交给 Claude Code。核心准则:告诉 Claude 在编写任何内容之前先阅读规范,将其约束在规范允许的范围内,并要求它在任何模糊不清的地方进行询问,而不是擅自假设。
Claude Code —— 基于规范实现
用户:
CLAUDE:
注意 Claude Code 的行为:它逐字从规范字段中推导系统提示词,仅注册列出的三个工具,为每个测试用例编写一个测试,并在对模糊项做出假设前停下来询问。后一种行为正是约束发挥的作用——如果没有“如果模糊则询问”的要求,Claude Code 本会悄悄地选择一种解释并继续执行。
💡 先规范,后代码 —— 永远如此
使用 Claude Code 和代理时,最常见的失败模式是在定义范围之前就要求实现。你得到的是一段能跑通但功能完全错误的代码。先写规范,与团队评审,然后再生成。
§8 🤖 最佳实践 2 —— 考虑上下文隔离与可重用性
一个处理所有事情的代理会积累上下文噪音,产生级联错误,且无法进行独立测试。Claude Code 为此提供了两种机制:用于上下文隔离和并行执行的子代理(Subagents),以及用于可重用、版本化能力的技能(Skills)。
真实案例:QuestDB PR 审查技能
QuestDB 的开源仓库发布了一个 review-pr 技能,展示了生产级技能的规模:它通过 gh CLI 脚本获取 PR 数据,然后启动 8 个并行子代理,每个子代理分别负责不同的关注点(正确性、并发性、性能、资源管理、测试、代码质量、PR 元数据、Rust 安全性),运行强制验证步骤以消除误报,并输出结构化报告。技能与子代理协同工作——正是其设计的初衷。
→ 在 GitHub 上查看完整的 QuestDB review-pr 技能
💡 子代理的隔离既是一种安全性属性,也是一种架构约束
并行子代理之间不共享状态或上下文。行为不端的子代理无法影响其兄弟节点。但这也意味着:如果任务 B 需要任务 A 的输出,它们必须串行运行,而不是并行。请相应地进行分解设计。
§9 🔒 最佳实践 3 —— 保护你的 MCP 调用
连接 MCP 服务器意味着将你的凭据、文件系统和外部 API 的访问权限托付给它。以下是 MCP 诞生第一年内被证实的真实事件,展示了信任被滥用时会发生什么:
这一模式在所有案例中保持一致:经典漏洞(注入、路径遍历、认证缺失)出现在快速编写且未经安全审查的新型 AI 工具中。这些漏洞证实了新的 AI 原生世界受制于与传统软件相同的安全原则。协议是新的,但错误不是。
工具投毒 (Tool Poisoning) —— 对用户隐藏、对模型可见的指令
这一点值得单独列出,因为它非常隐蔽。一个恶意的 MCP 工具可以在工具的 description 字段中嵌入指令——当 LLM 读取工具元数据时,这些指令对模型可见,但在任何面向用户的 UI 中却不显示。模型会像对待其系统提示词的一部分一样遵循这些隐藏指令。
工具投毒 —— 真实文档记录示例 (Invariant Labs, 2025年4月)
⚠ “Rug Pull”(撤资/欺诈)攻击
Invariant Labs 的演示展示了这一点:攻击者控制的“休眠”MCP 服务器首先宣传一个无害的工具,在建立用户信任后将其替换为恶意工具。根本问题在于,工具的底层代码和行为可以在未经通知或重新验证的情况下被 MCP 客户端修改——而标准客户端在工具被“批准”后,通常不会在每次后续调用时重新获取和重新验证工具的完整定义。
真正有效且无需编写任何代码的缓解措施:
📌 像对待第三方库一样对待 MCP 服务器 —— 因为它们就是第三方库
你绝不会在未经审查的情况下将一个随机的 npm 包引入生产服务。MCP 服务器与你的应用程序代码具有相同的信任级别,拥有对你的凭据、文件系统和外部 API 的访问权限。审查标准至少应达到相同高度。### §10 🛡️ 最佳实践 4 --- 指导 AI 代理的响应安全与质量
在代理(Agent)的语境下,护栏(Guardrails)并非简单的内容过滤器,而是承载系统稳定性的架构。一旦缺乏检查,产生的后果将不仅仅是糟糕的文本,而是数据记录被删除、凭据泄露,或是无人察觉的错误答案。护栏必须部署在多个关键点:输入到达模型前、工具执行前以及输出生成后。
2025 年的数据显示,39% 的公司报告称其 AI 代理访问了未经授权的系统,32% 的公司发现代理导致了敏感数据的泄露。这并非理论推测,而是源于配置错误的权限和缺失的输出验证。[SailPoint, 2025]
你可能还没用过的最简单护栏:CLAUDE.md
最容易设置的护栏是在项目根目录下创建一个 CLAUDE.md 文件。Claude 会在每次会话开始时读取它,并将其作为常驻指令执行——这是一种在任何内容输入前限制其行为的有效方式。
CLAUDE.md --- Java 代理的代码质量与安全护栏
将 CLAUDE.md 视为版本控制下的系统提示词。范围、输出规则、PII 处理、升级策略——所有内容集中在一个文件中,像代码一样被审查,并可供整个团队阅读。虽然它不能替代生产环境中的程序化护栏,但对于开发工作流而言,它能立即填补大部分漏洞。
当 CLAUDE.md 不够用时:钩子(Hooks)
CLAUDE.md 是由模型解读的,这意味着它可能被绕过。曾有开发者要求 Claude Code 记录其 Azure OpenAI 配置,结果 Claude 将真实的 API 密钥硬编码在 markdown 文件中并推送到公共仓库,11 天后产生了 3 万美元的欺诈性账单。即使 CLAUDE.md 中写着“严禁硬编码密钥”,模型仍可能在权衡后将其视为建议而非强制。
钩子(Hooks)则不同。 它们是在特定生命周期点运行的 Shell 命令——例如工具运行前(PreToolUse)、运行后(PostToolUse)或会话启动时。退出代码 2 将直接阻塞操作。这里没有模型的逻辑推理,也没有协商余地。
.claude/settings.json --- 使用钩子拦截硬编码密钥
~/.claude/validators/block_secrets.py
hookify 插件可以简化 JSON 编辑过程。你只需描述规则,它会自动生成钩子:
Claude Code --- 使用 hookify 创建钩子
用户:
📌 从警告开始,升级为阻塞
初始阶段使用
action: warn以了解触发条件,避免干扰工作流。一旦验证该模式能准确捕捉目标且无误报,再升级为action: block。有关钩子作为护栏的详细指南及更多规则示例,请参阅 Claude Code Hooks: Guardrails That Actually Work。📌 对输入而非仅输出进行 DLP(数据防泄漏)
进入模型的 PII 可能最终出现在日志、嵌入向量、微调流水线或缓存中。Carlini 等人的研究(经斯坦福 SAIL 分析证实)发现,现代大模型在特定提示下能够可靠地记忆并复述训练数据——这意味着一旦数据进入模型,可能就永远无法完全清除。[Stanford SAIL, 2025] 请在输入层进行拦截,事后清理并非有效的恢复策略。
§11 ✅ 质量与安全最佳实践总结
上述每一项最佳实践都针对特定的失效模式。以下是综合参考表,每个关注点对应一份清单,用于快速审计任何代理系统。
代理 (Agents)
子代理 (Subagents)
技能 (Skills)
MCP 服务器 (MCP Servers)
// 总结
一个所有功能硬编码的单一代理在演示时有效,但在扩展时会失败。多代理 MCP 架构——由监督者路由至专门的子代理,并由受限的 MCP 服务器提供支持——在结构上强制执行边界,而非仅仅依赖指令。但只有在扎实的工程配套下,架构才能发挥作用:版本化的提示词配置、实施前的 SDD 规范、具有明确范围和工具约束的子代理、作为版本控制能力包的技能、输入输出双层的护栏,以及将第三方 MCP 服务器视为第三方库——进行审查、固定版本并审计。采用该架构是正确的方向,而这些模式则是使其安全运行的关键。 😅