AI 应用工程:Tool、MCP、Skill 与 Workflow 如何接入 Agent?——搭建一个可运行的需求影响面分析 Agent
近期落地接入时发现一个共性认知偏差:很多人把 Tool/MCP/Skill/Workflow 一概当成 Agent 插件,统一放在一个列表里,依靠模型自行走完完整流程。可业务一旦需要交付物校验、上下文拼接、强制审批等逻辑,这种简单堆砌的实现方式就会出现各类问题。
这篇就用一个可以直接运行的最小项目,把四类能力的接入点和参与时刻讲清楚——它们不是四种并列组件,而是分别落在不同的工程责任上。
项目描述:跑通一次“需求影响面分析”任务,并输出一份带校验字段的 JSON 报告。
完整DEMO项目地址:https://gitcode.com/ligang2585116/agent-demo
开篇:先分清接入位置
上一篇 已经讲过职责边界。本篇展开前,先把四类能力放在同一张表里对照。
| 维度 | Tool | MCP | Skill | Workflow |
|---|---|---|---|---|
| 本质 | 模型可调用的一个动作/函数 | 连接外部能力的标准协议 | 按需加载的任务方法论(SKILL.md) | 包住 Agent 的确定性代码流程 |
| 解决的问题 | 模型自己做不了的事(查数据、执行操作) | 外部能力如何被统一发现与调用 | 领域知识不常驻 Prompt,任务匹配时才加载 | 关键环节不允许模型自由裁量 |
| 接入位置 | ToolRegistry,暴露给模型 | 应用侧适配层:Client 把 Tool 转成 Function Tool,Resource 由应用拼入 Context | 摘要进初始上下文,正文经activate_skill按需进入 | Agent 循环之外的应用代码 |
| 谁触发 | 模型决定调用 | Tool 由模型调;Resource 由应用主动读 | 模型判断任务匹配后激活 | 代码无条件执行,模型无法感知或跳过 |
| 形态 | 代码(函数 + Schema) | 协议 + 进程(Server / Client) | 文档(SKILL.md+ 可选资源) | 代码(普通流程逻辑) |
| 本篇示例 | resolve_project_owner | search_code、architecture://project-map | impact-analysis/SKILL.md | 输入校验、Zod 报告校验、审批门禁 |
这张表是本文的观察框架,不是某一家厂商的统一分类。MCP Resource 是否进入 Context 由 Host 决定,协议不会自动完成。1
几组容易混淆的对比
Tool vs MCP:不是并列关系。MCP 是连接协议,不是和本地 Function Tool 同层级的“另一种 Tool”。mcp-client.ts把 MCP Tool 适配进ToolRegistry之后,模型看到的是同一套 Function Tool 列表,根本分不出哪个来自本地、哪个来自 MCP Server。区别只在工程侧:本地 Tool 是你写的函数,MCP Tool 是别的进程或别的团队通过标准协议提供的能力。
Tool vs Skill:Tool 提供‘手’,Skill 提供‘操作手册’。 Tool 返回的是数据或执行结果;Skill 激活后返回的是指令文本,告诉模型该按什么步骤做、输出什么格式。本篇里 Skill 的激活机制本身也借助了一个 Tool——activate_skill——但这只是交付方式:Skill 仍然是SKILL.md资产,激活 Tool 不是把 Skill 降格成普通动作。
Skill vs Workflow:两者都写“流程”,约束力完全不同。这是接入时最关键、也最容易混的一处:
SKILL.md里写的“先搜代码 → 再查依赖 → 输出 JSON”是建议,模型可以不遵守;workflow.ts里的if (highRisk && !approved) throw是强制,模型连感知它的机会都没有。
判断一条规则该放 Skill 还是 Workflow,只问一句:这条规则被违反了,能接受吗?能接受 → Skill;不能接受 → Workflow。
Tool 是手,MCP 是接手的标准插座,Skill 是操作手册,Workflow 是流水线上的质检关卡。前三者服务于模型的自主决策,最后一个专门限制模型的自主决策。
后面六节按接入顺序展开:先搭 Harness,再依次接入本地 Tool、MCP、Skill、Workflow,最后跑通一次完整 Run。
一、先搭一个最小 Agent Harness(把循环写出来)
先把问题收窄:最小 Harness 到底要做什么?
对本篇而言,它只保留两项职责:
- 把可调用能力整理成模型可见的 Tool 定义;
- 解析模型返回的 Tool 调用,把结果回填,再继续循环,直到出现最终文本或触发最大迭代次数。
下面这段是最核心的结构(摘自agent-demo/src/agent.ts的写法):
// 关键点:模型只“请求调用”,执行发生在应用侧for(letiteration=0;iteration<this.maxIterations;iteration+=1){if(turn.type==="final")returnturn.text;constresults=awaitPromise.all(turn.calls.map((call)=>tools.execute(call)));turn=awaitmodel.continue(results);}为什么要把“执行发生在应用侧”写进代码结构?因为 Tool Calling 的协议里,本质是“模型提出动作 → 应用执行 → 应用回填 → 再请求”。2
Harness 不做 Workflow 的校验、不做 Skill 的激活决策、不做 MCP 发现;它只负责把“动作循环”跑通,并给上层流程提供一个稳定入口。
二、注册第一个本地 Function Tool(让模型能查负责人)
本地 Function Tool 解决的是:模型要能提出“查询项目负责人”的调用请求。
在示例里,这个 Tool 叫resolve_project_owner,入参只有一个projectName,输出包含owner与team。核心代码在agent-demo/src/local-tools.ts。
接入方式也很直接:把 Tool 注册到ToolRegistry,同时把输入 schema 暴露给模型 Adapter。
本节只强调两点工程边界:
- Tool Registry 是应用侧的动作目录,模型只是看见 Tool 的描述与参数结构;
- 强制校验不放在 Tool。如果你把“必须校验/必须审批”也做成 Tool,那么模型可以不调用,Workflow 的门禁就会失效。
这一点在第五节 Workflow 会反过来体现出来:校验发生在应用代码里,不交给模型选择。
三、用 MCP 接入外部能力(Tool 发现与调用在应用侧完成)
接入 MCP 时,有三个问题必须回答:Server 暴露什么、Client 发现与调用什么、接入后模型看到什么。本节依次展开。
本篇选择最简单的本地 stdio 方式:Harness 启动子进程作为 MCP Server,再由 MCP Client 做连接、分页发现和调用。3
1) MCP Server:暴露两个 Tool + 一个 Resource
示例 MCP Server 在agent-demo/src/mcp-server.ts,它提供:
- Tool:
search_code - Tool:
get_project_dependencies - Resource:
architecture://project-map
Server 侧注册 Tool 的写法遵循 SDK 文档的示例范式(registerTool+inputSchema+ handler)。3
2) MCP Client:listTools/listResources + callTool/readResource
Harness 在agent-demo/src/mcp-client.ts里做了两类工作:
listTools:把工具元数据发现出来(本篇实现了 cursor 分页的遍历)。callTool:把模型提出的 Tool Call 转发给 Server 并执行,然后把返回内容转成 Harness 统一的 ToolResult。
在这里要特别区分两条错误路径:
工具业务失败可能表现为isError: true,但 JSON-RPC 协议故障会导致请求直接 reject/throw;应用侧需要分别处理。4
3) Tool schema 映射:MCP inputSchema → 模型 function parameters
将 MCP Tool 适配到模型 function tool,本质上是做一个“应用侧转换层”——对应开篇表里 MCP 的「接入位置」一行:
- MCP 的
inputSchema是 JSON Schema,可以直接作为模型 function tool 的参数结构使用; - 但协议消息结构、返回内容格式、以及工具执行确认都不在 MCP 里解决,需要由 Harness 自己处理。1
Resource 则完全不同:Resource 的进入上下文由应用控制。本篇在 Workflow 里先读architecture://project-map,再把它拼进任务 Context。1
四、加载impact-analysisSkill:只常驻 Catalog,激活时再注入完整指令
Skill 的目的,是让“任务方法”以SKILL.md形式可复用,而不是把所有规则写进一个巨长 Prompt。
示例里,Skill 在/skills/impact-analysis/SKILL.md,它遵循开放规范:YAML frontmatter + Markdown 正文,name与description是必填字段。5
1) Progressive Disclosure:先 Metadata,再 Instructions,再按需资源
在示例实现里,Catalog 只包含name与description(以及位置等最少信息),激活时才把完整SKILL.mdbody 注入上下文。这对应官方客户端指南描述的 progressive disclosure 机制。6
2) 为什么要有activate_skill(name)这一层?
如果模型能直接读文件,可以让它自己去读SKILL.md;但为了统一教学示例,本篇走专用激活 Tool 路径:
- 模型只给出一个
activate_skill的调用请求; - Harness 用受控的 Skill Map 查找并读取对应 Skill 内容后,再返回给模型;
- 支持配置松耦合:Skill 资产在哪里、怎么组织,由 loader 决定。7
同时,本篇 loader 做了真正的 YAML frontmatter 解析,而不是用正则硬拆字段,以避免多行值/引用等边界导致字段丢失。8
五、Workflow:把不可跳过的校验与审批写进应用代码
这一节回答一个看似反直觉的问题:既然 Skill 里已经写了分析步骤和输出要求,为什么还要 Workflow?
原因很简单:Skill 指令是否被执行,取决于模型“理解并选择”;Workflow Gate 必须由代码强制执行,否则模型可能直接输出一段未经校验的文本就结束 Run,绕过你预期的交付路径。
示例把 Workflow 写成普通 TypeScript 函数,并固定一个顺序:
validateInput → 读取 MCP Resource(architecture://project-map) → runAgent(Tool 调用循环发生在这里) → validateReport(Schema 校验发生在这里) → highRisk → approvalGate.confirm → publishvalidateReport使用 schema 校验输出字段,失败直接抛出错误,不进入发布路径。approvalGate也同样是代码层面控制:高风险报告被拒绝时,Workflow 不会“继续产出结果”。9
这里直接对应官方对 Workflow 的定义取向:Workflow 是预定义的路径/门禁;Agent 则是模型动态决定过程与 tool usage。9
六、跑通一次:离线模式先验证“接入点都真的执行了”
在本地按以下步骤跑通:
cd"agent-demo"npminstall--registry=https://registry.npmjs.orgnpmtestnpmrun demonpm run demo默认是离线模式(不需要OPENAI_API_KEY)。你会看到一条清晰的 Trace(示例输出):
MODEL_MODE=offline Workflow: validate input MCP:readarchitecture://project-map Model: activate impact-analysis Model: call search_code Model: call get_project_dependencies Model: call resolve_project_owner(checkout-web)Model: call resolve_project_owner(merchant-console)Model: call resolve_project_owner(finance-admin)Model: produce report Workflow: validate report Workflow: approval required Workflow: publish注意这里的“验证重点”不是评测模型好不好,而是证明工程链路真的闭合:
- MCP Client 发现与调用确实发生;
- Skill 被激活并注入了完整指令;
- Workflow 的校验与审批确实发生;
- 最终输出满足你在 Workflow 里定义的报告 schema。
如果你想切到 Live 模式,只需要把 Adapter 切到 OpenAI Responses API,并配置环境变量。示例项目里也提供了MODEL_MODE=live分支,但默认以 offline 作为可复现基线。10
七、替换 Model/Server/业务时,哪些模块可以保留?
这一节用“替换对象”倒推职责边界,帮助你避免把 demo 变成不可迁移的样板。
1) 换模型 Provider(只替换 Model Adapter)
你需要替换的是:
- provider 专属的 Tool Call/Result Item 解析与组装;
- 但 Harness 的“动作循环”、Tool Registry、Workflow、MCP 适配层都可以保留。
这也是本篇选择 Model Adapter 的原因:把 Provider 差异限制在 Adapter 边界内。2
2) 换 MCP Server(只替换 Server 配置 + Tool Allowlist)
你需要调整的是:
- Server 启动方式、暴露工具集合、以及 Resource 的 URI;
- Harness 仍然沿用同一套 listTools/callTool/readResource 的调用结构(尤其要保持错误路径处理一致)。4
3) 换业务场景(替换本地 Tool、Skill 与 Workflow Gate)
当业务规则发生变化,最先动的是:
- 本地 Function Tool 与其校验 schema;
- Skill(方法与输出模板);
- Workflow 的结果校验与高风险判定。
这三块变化的规模通常比你想象的小,因为它们都与“交付物 schema 与强制门禁”直接绑定。
4) 但请记住:这仍然不是生产 Runtime
这个最小例子能完成一次任务,但它缺少第四篇要讲的生产能力:如何在中断后恢复、如何追踪执行、如何在真实故障里评测与改进。
所以收束一句:接入链路跑通了 ≠ 生产系统完成了。
适用/不适用边界
适用,把本文作为:
- 新团队建立 Agent 接入基线(先跑通再扩展);
- 需要把“工具、外部能力、任务方法、门禁路径”拆清楚的工程训练。
不适用,把本文当作:
- 完整生产 Runtime(没有 checkpoint/memory/evaluation/observability);
- 多厂商跨协议的通用 SDK 教程(本篇锁定了具体的版本基线,用于可复现)。
下一篇我会接着回答:为什么一次任务跑通之后,仍不能直接进入企业生产环境。
来源索引(用于支撑关键技术断言)
MCP Tool/Resource 边界:Tool 是工具动作,Resource 由应用控制如何进入 Context:
https://modelcontextprotocol.io/specification/2025-11-25/server↩︎ ↩︎ ↩︎OpenAI Function Calling:模型提出 tool call、应用执行与回填循环由应用侧完成:
https://developers.openai.com/api/docs/guides/function-calling↩︎ ↩︎MCP v1:stdio/Streamable HTTP transport 与本地子进程集成方式:
https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/docs/server.md↩︎ ↩︎MCP Tool 错误语义:
isErrorvs JSON-RPC 协议故障不同处理路径:https://modelcontextprotocol.io/specification/2025-11-25/server/tools↩︎ ↩︎Agent Skills 开放规范:Skill 目录包含
SKILL.md,frontmatter + Markdown,必填name/description:https://agentskills.io/specification↩︎Agent Skills Progressive Disclosure:Catalog(metadata)→ 激活(instructions)→ 资源按需进入:
https://agentskills.io/client-implementation/adding-skills-support↩︎Skill 激活机制与专用激活工具模式:
https://agentskills.io/client-implementation/adding-skills-support↩︎YAML frontmatter 解析与分离 metadata/body:官方客户端实现指南与参考解析器(按需解释):
https://agentskills.io/client-implementation/adding-skills-support↩︎Workflow 与 Agent 的架构区分(Workflow 预定义路径,Agent 动态路径):
https://www.anthropic.com/engineering/building-effective-agents↩︎ ↩︎OpenAI 官方 TypeScript SDK 与 Responses API 作为主要接口:
https://github.com/openai/openai-node、releasev6.49.0(调研基准日 2026-07-27) ↩︎