<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent 开发 on Code Plato</title><link>https://CodePlato3721.github.io/zh/categories/agent-%E5%BC%80%E5%8F%91/</link><description>Recent content in Agent 开发 on Code Plato</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Sun, 13 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://CodePlato3721.github.io/zh/categories/agent-%E5%BC%80%E5%8F%91/index.xml" rel="self" type="application/rss+xml"/><item><title>CLAUDE.md 编写简明指南</title><link>https://CodePlato3721.github.io/zh/post/claude.md-%E7%BC%96%E5%86%99%E7%AE%80%E6%98%8E%E6%8C%87%E5%8D%97/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://CodePlato3721.github.io/zh/post/claude.md-%E7%BC%96%E5%86%99%E7%AE%80%E6%98%8E%E6%8C%87%E5%8D%97/</guid><description>&lt;img src="https://pub-deacd49348914a49b1254b01f351ef0d.r2.dev/2026/09/a-concise-guide-to-writing-claude-md/cn/banner.png" alt="Featured image of post CLAUDE.md 编写简明指南" /&gt;&lt;p&gt;这个指南同样也适用于 &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/p&gt;&#10;&lt;h2 id="claudemd-有什么用"&gt;CLAUDE.md 有什么用&#10;&lt;/h2&gt;&lt;p&gt;首先我们要知道 CLAUDE.md 有什么用。因为模型就像一个新生儿。你每一次跟它对话，它都不知道你是谁，它是谁，你们前面说了什么。它都是通过你传给它的上下文得知这一切的。当然，Claude Code 有一些预设的上下文，所以它知道自己是 Claude Code，一个帮你写代码的工具。&#10;所以每一次新的会话开始的时候，它对你的项目是一无所知的。之所以它工作得还不错，是因为 Claude Code 自己的预设上下文教它在遇到一个项目改动前，应该做什么来了解项目的信息。&#10;所以你会发现它似乎很了解你的项目，但是又常常会重复造轮子。因为它有一套快速理解项目的方法论，但是还是做不到了解你项目的所有细节。当然你也不会希望它这么做，否则太烧 token 了。&#10;这个时候就需要有人用简短的语言向它介绍这个项目的背景、架构等等信息。这样做的好处是：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;它就不会去遍历你的项目，可以节省 token&lt;/li&gt;&#10;&lt;li&gt;它不会做很明显违背你的项目代码意图的事情，或者重复造轮子&#10;这些简短的语言就会被放在 &lt;code&gt;CLAUDE.md&lt;/code&gt; 中，或者 &lt;code&gt;AGENTS.md&lt;/code&gt; 中。这个指南同时也适用于 &lt;code&gt;AGENTS.md&lt;/code&gt;。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="编写原则"&gt;编写原则&#10;&lt;/h2&gt;&lt;p&gt;一份好的 CLAUDE.md 应该像：&#10;&lt;strong&gt;新员工的最短上手指南：&lt;/strong&gt; 想象着你在带一个新员工做一个任务。努力用最短的语言告诉他足够的知识。只要能够达到他上手可以干活，而且不至于把你的代码库搞砸就行。&#10;&lt;strong&gt;用词犹如简历一般简洁：&lt;/strong&gt; 我前一段时间在找工作，曾经向 HR 学习过怎么写简历。然后我就绞尽脑汁地把我长达 4 页的简历压缩成了 2 页。几乎删除了所有冗余的词语。每个句子都精简到无法再减少。你也应该这么写 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&#10;&lt;strong&gt;高度抽象的知识：&lt;/strong&gt; 你不需要告诉 &lt;code&gt;Claude Code&lt;/code&gt;、&lt;code&gt;Codex CLI&lt;/code&gt; 代码缩进是多少格，或者连接数据库参考什么文件的第几行。很多事情交给 hook 去做，代码的名字和行数都是会变的。你要告诉模型的是一些高度抽象的设计理念。&#10;一般来说，一份好的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 长度应该在 200 行以内。&lt;/p&gt;&#10;&lt;h2 id="种类"&gt;种类&#10;&lt;/h2&gt;&lt;p&gt;包括项目根目录下的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 在内，其实有 3 种 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/strong&gt;，作用范围是全局。你的所有项目都会用到它。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt;&lt;/strong&gt;，作用范围是项目。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;./subdirectory/CLAUDE.md&lt;/code&gt;&lt;/strong&gt;，子文件夹也可以有 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="框架"&gt;框架&#10;&lt;/h2&gt;&lt;p&gt;并没有一个严格的、完美的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 框架。但是我参考了一些比较好的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 和相关文章，总结出了一个比较合理的 &lt;code&gt;CLAUDE.md&lt;/code&gt;：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;一句话介绍&lt;/li&gt;&#10;&lt;li&gt;架构&lt;/li&gt;&#10;&lt;li&gt;技术栈&lt;/li&gt;&#10;&lt;li&gt;命令&lt;/li&gt;&#10;&lt;li&gt;约定&lt;/li&gt;&#10;&lt;li&gt;边界&lt;/li&gt;&#10;&lt;li&gt;领域文档映射表&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="一句话介绍"&gt;一句话介绍&#10;&lt;/h3&gt;&lt;p&gt;简单介绍这个项目是什么，大概的功能是什么。例子：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;这是一个多智能体编排框架，用于协调并行运行的 Claude Code 子智能体，在 FastAPI + React 代码库上自动化执行开发/QA 工作流。&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="架构"&gt;架构&#10;&lt;/h3&gt;&lt;p&gt;简单介绍项目的架构，比如：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Architecture&#10;- Controller 保持轻量 —— 业务逻辑放在 `app/Services/` 中&#10;- 数据库访问只能通过 `app/Repositories/`。禁止在 controller 中直接使用 Eloquent。&#10;- `app/Http/Resources/` 中的 API resources 负责规范每一个 JSON 响应的结构。&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="技术栈"&gt;技术栈&#10;&lt;/h3&gt;&lt;p&gt;简单介绍项目的技术栈，类似：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Tech Stack&#10;- FastAPI，Python 3.11&#10;- PostgreSQL 15（SQLAlchemy 2.0 异步）&#10;- Celery + Redis 用于后台任务处理&#10;- Poetry 用于依赖管理&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="命令"&gt;命令&#10;&lt;/h3&gt;&lt;p&gt;一些项目的常用命令，比如：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Commands&#10;- 开发服务器：`uvicorn app.main:app --reload`&#10;- 运行测试：`pytest -x -v`&#10;- 数据库迁移：`alembic upgrade head`&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="约定"&gt;约定&#10;&lt;/h3&gt;&lt;p&gt;无法被 linter 包含的抽象约定，类似：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Conventions&#10;- 布尔类型的变量/属性以 `is`、`has` 或 `should` 开头&#10;- 所有日期时间统一以 UTC 格式存储和传递&#10;- 事件名称遵循 `domain.action` 的命名格式&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="边界"&gt;边界&#10;&lt;/h3&gt;&lt;p&gt;文件修改的边界：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Boundaries&#10;- `legacy/` — 老版支付系统，只做紧急 bug 修复，不引入新模式或重构&#10;- `src/generated/` — Prisma/GraphQL 自动生成&#10;- `vendor/`, `third_party/` — 第三方代码，通过升级依赖版本解决问题，不直接修改&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="领域文档映射表"&gt;领域文档映射表&#10;&lt;/h3&gt;&lt;p&gt;领域名词和文档的对应关系：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Domain Doc Map&#10;| 提及 | 阅读 |&#10;|---|---|&#10;| billing, stripe, payment, subscription, invoice | docs/billing.md |&#10;| auth, login, session, oauth, jwt | docs/auth.md |&#10;| migration, schema, drizzle, kysely | docs/db-migrations.md |&#10;| feature flag, rollout, kill switch | docs/feature-flags.md |&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们把这几个例子拼起来，看一个好的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 应该像这样：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;这是一个多智能体编排框架，用于协调并行运行的 Claude Code 子智能体，在 FastAPI + React 代码库上自动化执行开发/QA 工作流。&#10;&#10;## Architecture&#10;- Controller 保持轻量 —— 业务逻辑放在 `app/Services/` 中&#10;- 数据库访问只能通过 `app/Repositories/`。禁止在 controller 中直接使用 Eloquent。&#10;- `app/Http/Resources/` 中的 API resources 负责规范每一个 JSON 响应的结构。&#10; &#10;## Tech Stack&#10;- FastAPI，Python 3.11&#10;- PostgreSQL 15（SQLAlchemy 2.0 异步）&#10;- Celery + Redis 用于后台任务处理&#10;- Poetry 用于依赖管理&#10;&#10;## Commands&#10;- 开发服务器：`uvicorn app.main:app --reload`&#10;- 运行测试：`pytest -x -v`&#10;- 数据库迁移：`alembic upgrade head`&#10;&#10;## Conventions&#10;- 布尔类型的变量/属性以 `is`、`has` 或 `should` 开头&#10;- 所有日期时间统一以 UTC 格式存储和传递&#10;- 事件名称遵循 `domain.action` 的命名格式&#10;&#10;## Boundaries&#10;- `legacy/` — 老版支付系统，只做紧急 bug 修复，不引入新模式或重构&#10;- `src/generated/` — Prisma/GraphQL 自动生成&#10;- `vendor/`, `third_party/` — 第三方代码，通过升级依赖版本解决问题，不直接修改&#10;&#10;## Domain Doc Map&#10;| 提及 | 阅读 |&#10;|---|---|&#10;| billing, stripe, payment, subscription, invoice | docs/billing.md |&#10;| auth, login, session, oauth, jwt | docs/auth.md |&#10;| migration, schema, drizzle, kysely | docs/db-migrations.md |&#10;| feature flag, rollout, kill switch | docs/feature-flags.md |&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="要不要写-never"&gt;要不要写 Never&#10;&lt;/h3&gt;&lt;p&gt;你可能在别的指导上看到一个段落叫 Never，用来记录曾经犯过的错。听起来很好。但是我不太建议增加这个部分。因为 Never 的特点是 &lt;strong&gt;只有添加的动机，没有删除的动机&lt;/strong&gt;。导致这个部分越来越长，甚至 Claude Code 自己都会去加，而且没有人会去删除它。时间久了就会变成一个&lt;strong&gt;历史事故墓地&lt;/strong&gt;。&#10;我建议的做法是：当问题出现了，把出问题的模式反过来，写成一种正向的规则。比如“不要在 &lt;code&gt;webhook/stripe.ts&lt;/code&gt; 里做同步数据库写入”，改成“只在 &lt;code&gt;repository/&lt;/code&gt; 内做数据库操作”。&lt;/p&gt;&#10;&lt;h2 id="修剪"&gt;修剪&#10;&lt;/h2&gt;&lt;p&gt;就算你按照以上的框架编写了 &lt;code&gt;CLAUDE.md&lt;/code&gt;，对于一个大项目，或者维护周期较长的项目，或者这个项目已经有了 &lt;code&gt;CLAUDE.md&lt;/code&gt;，你还是会发现这个文件的长度无法控制在 200 行以内。那么就要进入修剪步骤。&#10;修剪就是把东西从 &lt;code&gt;CLAUDE.md&lt;/code&gt; 中移除出去。具体的移动方法和路径有以下几种：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;自定义子智能体：&lt;code&gt;.claude/agents&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;规则文件夹：&lt;code&gt;.claude/rules&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;子目录 CLAUDE.md：&lt;code&gt;./subdirectory/CLAUDE.md&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;固定工作流：&lt;code&gt;.claude/skills/&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;其他文档：&lt;code&gt;docs/&lt;/code&gt;&#10;你会发现我给它们编了号。这是因为文档的抽取是有优先级的。顺序是从具体到抽象，从精准到泛化。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="自定义子智能体"&gt;自定义子智能体&#10;&lt;/h3&gt;&lt;p&gt;把所有关于子智能体的指导文档都抽取到 &lt;code&gt;.claude/agents&lt;/code&gt; 下，比如：&#10;你可以定义一个专门用来跑集成测试的 agent。文件名叫 &lt;code&gt;integration-tester.md&lt;/code&gt;。内容：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;---&#10;name: integration-tester&#10;description: Runs integration tests&#10;tools: Bash&#10;model: sonnet&#10;---&#10;&#10;You run and diagnose integration tests. You should follow these steps.....&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="规则文件夹"&gt;规则文件夹&#10;&lt;/h3&gt;&lt;p&gt;具体的规则文件可以放在 &lt;code&gt;.claude/rules&lt;/code&gt; 中。分为带路径和不带路径两种。不带路径的优先级等同于 &lt;code&gt;CLAUDE.md&lt;/code&gt;，比如：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# Security Rules&#10;&#10;- All user input is validated at the API boundary&#10;- Secrets and API keys are read from environment variables only&#10;- SQL queries always use parameterized statements&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;带路径的会在满足指定路径的条件下才被加载，比如：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;---&#10;paths:&#10; - &amp;#34;tests/**/*.py&amp;#34;&#10; - &amp;#34;**/*.spec.ts&amp;#34;&#10;---&#10;&#10;# Unit Testing Rules&#10;&#10;- One assertion concept per test&#10;- Test names describe behavior, not implementation&#10;....&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;规则文件和领域文档映射表：&lt;/strong&gt; 你可能会有疑问：“规则文件和领域文档映射表会不会重复定义了相同的东西？”是的，确实会出现这个问题。比如，你可能会定义 &lt;code&gt;billings/&lt;/code&gt; 路径下的文件应该遵循 billings 相关的文档，然后在领域文档映射表中也有一行 &lt;code&gt;| billings | docs/billings.md |&lt;/code&gt;，这样确实是重复了。&#10;解决的办法就是把文档分为 &lt;strong&gt;规则&lt;/strong&gt; 和 &lt;strong&gt;背景&lt;/strong&gt;。规则文件强制性高，放到 &lt;code&gt;.claude/rules&lt;/code&gt; 中。背景文件相对较弱，放到 &lt;code&gt;docs/&lt;/code&gt; 中。&lt;/p&gt;&#10;&lt;h3 id="子目录-claudemd"&gt;子目录 CLAUDE.md&#10;&lt;/h3&gt;&lt;p&gt;针对某些子目录的规则可以移动到这些目录下，常见的场景有 &lt;code&gt;sql/&lt;/code&gt;、&lt;code&gt;domains/&lt;/code&gt;、&lt;code&gt;adapters/&lt;/code&gt; 文件夹。在这些目录下的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 不必遵循特定的框架。但是还是要保持简短。&lt;/p&gt;&#10;&lt;h3 id="固定工作流"&gt;固定工作流&#10;&lt;/h3&gt;&lt;p&gt;如果有一些固定的、需要按顺序做的操作，就尽量做成项目级的 skill，然后放到 &lt;code&gt;.claude/skills/&lt;/code&gt; 下。&lt;/p&gt;&#10;&lt;h3 id="其他文档"&gt;其他文档&#10;&lt;/h3&gt;&lt;p&gt;其他的文档放到 &lt;code&gt;docs/&lt;/code&gt; 目录下。这个目录下放一些比较泛化的文档。比如更具体的 &lt;code&gt;architecture.md&lt;/code&gt;，或者把 ADR（Architecture Decision Record）文档放到 &lt;code&gt;docs/adr/&lt;/code&gt; 文件夹下，比如 &lt;code&gt;docs/adr/0001-migrate-to-drizzle.md&lt;/code&gt;。&lt;/p&gt;&#10;&lt;h2 id="如何开始"&gt;如何开始&#10;&lt;/h2&gt;&lt;p&gt;如果你还没有一份 &lt;code&gt;CLAUDE.md&lt;/code&gt; 或者 &lt;code&gt;AGENTS.md&lt;/code&gt;，那么按照以下步骤开始做：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;运行 &lt;code&gt;/init&lt;/code&gt; 生成一份初稿&lt;/li&gt;&#10;&lt;li&gt;开始根据以上方法来修改初稿&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;未来也要记得定时运行 &lt;code&gt;/doctor&lt;/code&gt; 来优化 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/p&gt;&#10;&lt;h2 id="参考文献"&gt;参考文献&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://www.humanlayer.dev/blog/writing-a-good-claude-md" target="_blank" rel="noopener"&#10; &gt;https://www.humanlayer.dev/blog/writing-a-good-claude-md&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://chipp.ai/engineering/claude-md-architecture" target="_blank" rel="noopener"&#10; &gt;https://chipp.ai/engineering/claude-md-architecture&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://dev.to/nishilbhave/claudemd-best-practices-the-complete-2026-guide-435j" target="_blank" rel="noopener"&#10; &gt;https://dev.to/nishilbhave/claudemd-best-practices-the-complete-2026-guide-435j&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://medium.com/@arad.haghi/best-practices-for-structuring-claude-code-projects-1b7144a683bc" target="_blank" rel="noopener"&#10; &gt;https://medium.com/@arad.haghi/best-practices-for-structuring-claude-code-projects-1b7144a683bc&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="关于作者"&gt;关于作者&#10;&lt;/h2&gt;&lt;p&gt;我是代码Plato。&lt;/p&gt;&#10;&lt;p&gt;我相信，人类的创造力才是 AI Coding 的真实之树，而代码与模型不过是投射在洞穴墙上的影子。&lt;/p&gt;&#10;&lt;p&gt;微博：@代码Plato&#10;主页：https://weibo.com/u/1041257881&lt;/p&gt;&#10;</description></item><item><title>我们什么时候不需要MCP</title><link>https://CodePlato3721.github.io/zh/post/%E6%88%91%E4%BB%AC%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%B8%8D%E9%9C%80%E8%A6%81mcp/</link><pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate><guid>https://CodePlato3721.github.io/zh/post/%E6%88%91%E4%BB%AC%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%B8%8D%E9%9C%80%E8%A6%81mcp/</guid><description>&lt;img src="https://pub-deacd49348914a49b1254b01f351ef0d.r2.dev/2026/09/when-you-don-t-need-mcp/cn/banner.png" alt="Featured image of post 我们什么时候不需要MCP" /&gt;&lt;h2 id="问题"&gt;问题&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;经常看到招聘 JD 上写着 MCP，似乎每个人都要会 MCP 才能做 agent 开发&lt;/li&gt;&#10;&lt;li&gt;又有人说 MCP 太重了，没什么人用 MCP&lt;/li&gt;&#10;&lt;li&gt;我们又会看到很多教程说，使用 MCP 统一接口很好用&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;我们平时看到的教程都是介绍 MCP 是什么，为什么要用 MCP。说了很多，其实大家还是不能直观地理解 MCP 的取舍。我今天就反过来说说什么时候不需要 MCP，来帮助大家更直观地感受 MCP。&lt;/p&gt;&#10;&lt;h2 id="mcp是什么"&gt;MCP是什么&#10;&lt;/h2&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;MCP（Model Context Protocol，模型上下文协议）&lt;/strong&gt; 是 Anthropic 发起的一个开放协议，用来让 AI 应用（比如 Claude Code、Claude Desktop、OpenClaw 这些 agent）用&lt;strong&gt;统一的方式&lt;/strong&gt;去发现和调用外部工具、读取外部资源&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;这是字面上的概念。实际上你可以把 MCP 看作是暴露给 agent 的一种资源。在 MCP 出现之前，如果你想让一个 AI 应用连上 Google Drive、GitHub、Slack 这些服务，&lt;strong&gt;每个 AI 应用&lt;/strong&gt;都得为每一个外部服务单独写一套集成代码。&#10;MCP 相当于给这个连接关系定义了一个&amp;quot;标准插座&amp;quot;。&lt;/p&gt;&#10;&lt;h2 id="不用mcp用什么"&gt;不用MCP用什么&#10;&lt;/h2&gt;&lt;p&gt;如果不用 MCP，我们还有很多别的东西可以选择。最重要的是两种：&#10;&lt;strong&gt;function calling：&lt;/strong&gt; 2023 年 OpenAI 推出了 function calling 的能力。其实很简单，就是你先把一段函数签名传给 LLM。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-arduino" data-lang="arduino"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;查询某个城市当前的天气&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;LLM 知道有一个 tool 之后，如果 LLM 在执行过程中觉得需要调用这个外部工具，那么执行结果的 &lt;code&gt;content&lt;/code&gt; 内会多出一个 &lt;code&gt;tool_use&lt;/code&gt; 对象，并且 &lt;code&gt;stop_reason&lt;/code&gt; 也会写 &lt;code&gt;tool_use&lt;/code&gt;。就像：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;new york&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;stop_reason&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后你就自己写代码去实现这个函数调用。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; response.stop_reason &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tool_use &lt;span style="color:#f92672"&gt;=&lt;/span&gt; response.content[&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... 调用查天气的函数&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然实际工作中没这么土，你可以用 LangChain 之类的框架。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;CLI：&lt;/strong&gt; 其实也是一种 &lt;strong&gt;function call&lt;/strong&gt;，只不过你就只暴露一种 tool，而且直接告诉 LLM 这就是个 bash，你想执行什么命令就自己写命令。好处就是你不需要告诉 LLM bash 能干啥，要怎么用。因为 LLM 在阅读了大量资料后，它本来就知道怎么写 bash 命令。&lt;/p&gt;&#10;&lt;p&gt;但是使用 &lt;strong&gt;CLI&lt;/strong&gt; 有一定风险，权限控制不好做。&lt;/p&gt;&#10;&lt;h2 id="模型视角感受"&gt;模型视角感受&#10;&lt;/h2&gt;&lt;p&gt;首先你必须知道一件事情：模型根本不知道你调用的是 MCP 还是 function calling。&#10;比如，当你注册查询天气这个工具的时候，如果使用 MCP 注册，那么模型看到的 tools 是这样的：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Get the current weather for a specified city&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你用 function calling，模型看到的 tools 是这样的：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Get the current weather for a specified city&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们现在来玩一个游戏，你能看出这两个 JSON 的区别吗？答案是它们没有区别。所以不需要纠结用 MCP 跟 function calling 会对模型造成什么影响。对模型来说，根本就没有区别。&lt;/p&gt;&#10;&lt;h3 id="mcp的缺点"&gt;MCP的缺点&#10;&lt;/h3&gt;&lt;p&gt;那么 MCP 既然这么好，为什么不都用 MCP 呢？因为 MCP 有缺点：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;上下文开销大：&lt;/strong&gt; 一旦连接上一个 MCP server，这个 server 暴露的&lt;strong&gt;所有&lt;/strong&gt;工具的完整 schema（名字、参数、描述）都会被提前塞进模型的上下文窗口，不管这次对话用不用得上。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;消耗服务器资源：&lt;/strong&gt; MCP Server 是一个持续存在、需要一直跑着的东西。你启动的 MCP Server 多了，服务器的 CPU、内存都会被消耗。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;运维负担：&lt;/strong&gt; MCP server 既然是一个独立进程，你需要自己负责它崩溃后重启、多个 server 间的启动顺序，以及内部状态的持续维护。&lt;/p&gt;&#10;&lt;h3 id="mcp的优点"&gt;MCP的优点&#10;&lt;/h3&gt;&lt;p&gt;MCP 听起来很糟糕啊。那为什么那么多公司还是继续使用 MCP 呢？因为它虽然有很多缺点，但是它最大的优点就是统一了项目资源的集成接口。统一接口这件事情本身就有 2 个好处：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;调用代码简洁：&lt;/strong&gt; 这东西不做软件的人感觉不出它的好处。做软件的人听到统一接口都知道它好。你原来几千行的屎山代码，可能突然就变成十几行了。接口一多，bug 就埋藏得多、埋藏得深，维护起来就没完没了。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;接入第三方资源简单：&lt;/strong&gt; 所有第三方资源都做成了 MCP，这下接入资源不用再花费很多精力去做定制化了。这件事情其实也没什么技术含量，就跟统一货币、度量衡一样。关键是得有人去做这件事情。&lt;/p&gt;&#10;&lt;h2 id="结论"&gt;结论&#10;&lt;/h2&gt;&lt;p&gt;如果你的需求是以下其中一种：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;你想用第三方做的工具&lt;/li&gt;&#10;&lt;li&gt;你在用你们团队多平台公用的工具&lt;/li&gt;&#10;&lt;li&gt;你想做多角色权限区分&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;那么你可以考虑使用 MCP，否则的话，使用其他轻量化方法，比如 function calling 就够了。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="关于作者"&gt;关于作者&#10;&lt;/h2&gt;&lt;p&gt;我是代码Plato。&lt;/p&gt;&#10;&lt;p&gt;我相信，人类的创造力才是 AI Coding 的真实之树，而代码与模型不过是投射在洞穴墙上的影子。&lt;/p&gt;&#10;&lt;p&gt;微博：@代码Plato&#10;主页：https://weibo.com/u/1041257881&lt;/p&gt;&#10;</description></item></channel></rss>