AI 学习笔记(八):MCP 与 AI Agent 工作流最小实践

前几篇我们把多 Provider 接入、路由和降级打通了,解决的是“调用谁”的问题。
这一篇进入 Phase 3,解决“模型拿到工具后,怎么把一次调用升级成可组合工作流”。

这篇只做最小实践,不谈多代理编排,也不追求“全自动自治”。目标只有 3 个:

  • 用一个最小 MCP Server 暴露 2 个工具
  • 用一个受控 Agent Loop 跑通“理解任务 -> 调工具 -> 汇总结果”
  • 把边界写进工程,而不是把行为全赌在 prompt 上

从 MCP 官方架构看,它不只是“给模型加工具”,而是把 Host、Client、Server 以及 toolsresourcesprompts 这些接口一起标准化。对大多数团队来说,第一步没必要全上,先把 tools 跑顺就已经很有价值。

1. 先把角色分清:MCP、Workflow、Agent 各管什么

按 MCP 官方文档,最常见的链路是:

  • Host:承载 AI 交互的应用,比如 IDE、桌面客户端、服务端工作流
  • Client:Host 里负责连接 MCP Server 的那一层
  • Server:对外暴露能力,能力形态可以是 toolsresourcesprompts

站在工程视角,可以这样理解:

  • MCP 解决“模型如何以标准方式拿到外部能力”
  • Workflow 解决“这类任务按什么固定步骤执行”
  • Agent 解决“在边界内由模型自己决定下一步”

Anthropic 在官方文章里把 workflowagent 区分得很清楚:前者是预先定义好的代码路径,后者是模型动态决定过程。对大多数团队来说,起步阶段更稳的做法通常不是“完全自治”,而是做一个带上限、带日志、带工具白名单的工作流。

一句话总结:

先把工作流做稳,再逐步放权给 Agent。

2. 最小实践先别贪大:只上 tools,先跑本地传输

MCP Server 除了工具,还可以暴露资源和提示模板。但在最小实践里,我更推荐只做 tools,原因很简单:

  • 工具最直接,最容易和现有业务 API 对接
  • 风险边界更清楚,便于做权限与审计
  • 一旦工具调用链稳定,后续再补 resourcesprompts 成本更低

传输层也先别复杂化。MCP 当前常见的两种传输方式是:

  • stdio:本地进程通信,最适合本机开发、IDE 插件、命令行工具
  • Streamable HTTP:适合远程共享 MCP Server,给多个客户端或托管平台复用

如果你只是验证“Agent 能不能调到工具”,优先走 stdio
如果你已经确定要把工具做成团队共享能力,再考虑 HTTP 化。

这个顺序的好处是:先把工具协议、输入 schema、错误处理跑顺,再谈部署形态,不会一开始就被网络、鉴权、连接状态拖住。

3. 一个真正可运行的最小 MCP Server

本地先装两个依赖:

1
npm i @modelcontextprotocol/sdk zod

下面示例直接用 MCP 官方 TypeScript SDK 的 McpServer 思路,暴露两个低风险工具:

  • searchDocs:查询文档
  • createDraft:生成草稿记录,属于 write-safe,不碰真实生产写入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// mcp-server.mjs
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
name: 'workflow-minimal-server',
version: '0.1.0',
});

server.tool(
'searchDocs',
{
query: z.string().min(1),
},
async ({ query }) => {
// 这里替换成你自己的文档检索逻辑
return {
content: [
{
type: 'text',
text: `Found docs for: ${query}`,
},
],
};
}
);

server.tool(
'createDraft',
{
title: z.string().min(1),
outline: z.array(z.string()).min(1),
},
async ({ title, outline }) => {
// 真实场景建议写入草稿区或临时存储,不要直接写生产数据
return {
content: [
{
type: 'text',
text: JSON.stringify({
saved: true,
title,
sectionCount: outline.length,
}),
},
],
};
}
);

const transport = new StdioServerTransport();
await server.connect(transport);

这个版本刻意很小,但已经具备 4 个关键特征:

  1. 工具名和入参 schema 都是显式的。
  2. 工具返回结构统一,Host/Client 更容易消费。
  3. 业务逻辑和模型调用解耦,后续替换实现不影响协议层。
  4. 先用 stdio 跑通,本地调试成本最低。

很多团队的问题不是“能不能写出一个 Server”,而是太早把工具做成“大而全平台”。最小实践里,2 个工具就够了,重点是把输入、输出、权限边界定义清楚。

4. Agent Loop 的关键不是聪明,而是可停止

有了 MCP Server,下一步不是立刻做复杂自治,而是补一个最小 loop,让模型可以在有限轮次里决定是否调用工具。

下面不是某一家 SDK 的真实函数名,而是把各家工具调用流程收敛成统一伪接口,重点看 loop 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// agent-loop.js
export async function runAgent({
model,
userTask,
tools,
executeTool,
maxTurns = 4,
}) {
const messages = [{ role: 'user', content: userTask }];

for (let turn = 1; turn <= maxTurns; turn += 1) {
const response = await model.generate({
messages,
tools,
toolChoice: 'auto',
});

const toolCalls = response.toolCalls || [];

if (toolCalls.length === 0) {
return {
done: true,
turns: turn,
answer: response.text,
};
}

for (const call of toolCalls) {
const result = await executeTool(call);
messages.push({
role: 'tool',
toolCallId: call.id,
content: JSON.stringify(result),
});
}
}

return {
done: false,
turns: maxTurns,
answer: '达到最大轮次,流程已停止。',
};
}

这里最重要的不是模型有多强,而是你有没有把约束写死在工程里:

  • maxTurns,避免无限循环
  • 没有工具调用就立即收敛
  • 工具执行由宿主程序控制,不是让模型直接越权
  • 工具结果回填后再让模型总结,而不是每一步都自动落地真实操作

如果你用的是支持 remote MCP 的平台接口,例如 OpenAI 的 Responses API,也可以直接把远程 MCP Server 作为工具源接进去。但那属于“部署形态升级”,不是最小实践的起点。最小版本先把本地 loop 跑通,收益最大。

5. 真落地时,别漏这 5 个工程细节

5.1 工具分级

至少分三层:

  • read-only:查询、检索、读取状态
  • write-safe:写草稿、生成建议、落临时区
  • write-critical:真实下单、发消息、改生产数据

最小实践里,尽量只开放前两层。

5.2 人在回路

涉及真实变更时,不要让模型直接执行:

  • 先输出计划
  • 人确认
  • 再调用高风险工具
  • 结果回填后再做总结

这比单纯在 prompt 里写“请谨慎操作”可靠得多。

5.3 幂等和重试边界

查询类工具通常可以重试。
写入类工具最好带 operationId 或业务幂等键,否则一次超时就可能变成重复写入。

5.4 可观测性

每轮至少记录:

  • traceId
  • turn
  • toolName
  • latencyMs
  • resultStatus
  • inputDigest 或脱敏后的入参摘要

没有这些字段,出了问题你只能知道“它错了”,不知道“它为什么这么做”。

5.5 传输选择别本末倒置

  • 本地验证、IDE 集成:优先 stdio
  • 共享服务、托管接入:再上 Streamable HTTP

不要一开始就把“远程化”当成核心问题。真正先要跑顺的是 schema、权限、日志和停止条件。

6. 一条更稳的演进路线

如果你准备把项目从“单次模型调用”升级到“可组合工作流”,推荐按这个顺序:

  1. 先做一个本地 stdio MCP Server,只放 1-2 个低风险工具。
  2. 再做一个有 maxTurns 的 Agent Loop,保证流程可终止。
  3. 补齐工具白名单、日志、错误分类和人工确认。
  4. 确定工具协议稳定后,再把共享能力迁到 Streamable HTTP
  5. 最后再考虑多 Agent、计划器/执行器拆分、长期记忆等高级形态。

这个顺序的核心不是“功能最全”,而是每一步都能单独带来收益,而且不会过早把系统做复杂

总结

MCP 把外部能力接入方式标准化,Agent Loop 把多轮决策组织起来,但真正决定系统能不能上线的,仍然是工程边界:

  • 工具是不是显式 schema
  • 流程是不是可停止
  • 权限是不是分层
  • 日志是不是可回放

把这四件事做对,AI 才不是“能调工具的聊天框”,而是可治理、可演进的工作流底座。

下一篇继续 Phase 3:在开发工具里落地一个自动化工作流,从任务触发一路跑到结果回写。

参考资料

本文永久链接: https://www.mulianju.com/learning-notes/ai-learning-notes-mcp-ai-agent-workflow-minimal-practice/