欢迎光临
我们一直在努力

从 LangGraph State 到 MCP、Skills:聊聊 AI Agent 工程化里最容易被忽略的 4 个问题

AI Agent 工程化实践:State、MCP 与 Skill Discovery

    • 前言
    • 一张图理解 Agent 工程化
    • 1. 为什么 Agent 工程化不是“多接几个工具”?
      • 一个简单 Agent 会遇到的真实问题
    • 2. LangGraph State:如何避免长对话中的上下文漂移?
      • 2.1 什么是上下文漂移?
      • 2.2 State 的作用是什么?
      • 2.3 为什么不能只靠聊天记录?
      • 2.4 State 应该拆成哪些部分?
      • 2.5 状态更新比“对话总结”更重要
      • 2.6 用 checkpoint 处理长任务
      • 2.7 让不同节点只看必要信息
      • 2.8 小结
    • 3. MCP Resource Metadata:模型如何知道资源什么时候该用?
      • 3.1 MCP 可以理解成什么?
      • 3.2 Resource Metadata 是什么?
      • 3.3 为什么 Metadata 很关键?
      • 3.4 一个天气资源的 Metadata 示例
      • 3.5 一个智能家居资源的 Metadata 示例
      • 3.6 好的 Metadata 应该包含什么?
      • 3.7 小结
    • 4. Skill、Tool、MCP 到底有什么区别?
      • 4.1 Tool:能执行的具体动作
      • 4.2 Skill:教模型怎么完成一类任务
      • 4.3 MCP:让模型用统一方式连接外部资源和工具
      • 4.4 三者区别表
      • 4.5 一个完整流转例子
      • 4.6 常见误区
        • 误区 1:Skill 就是 Tool
        • 误区 2:有 MCP 就不需要 Skill
        • 误区 3:Skill 越多越好
    • 5. Skill 太多怎么办:Discovery、按需加载和权限控制
      • 5.1 为什么不能把所有 Skill 都塞进 System Prompt?
      • 5.2 问题 1:上下文预算被占用
      • 5.3 问题 2:候选太多,模型更容易选错
      • 5.4 问题 3:相似 Skill 会互相干扰
      • 5.5 Skill Discovery 的推荐流程
      • 5.6 第一步:先做领域分组
      • 5.7 第二步:只召回 Top-K
      • 5.8 第三步:Skill 描述要写正例和反例
      • 5.9 第四步:高风险 Skill 不要自动触发
      • 5.10 第五步:记录使用日志,持续优化
      • 5.11 小结
    • 6. 一个完整例子:周末旅行助手内部如何流转?
      • 6.1 第一步:更新 State
      • 6.2 第二步:Skill Discovery
      • 6.3 第三步:加载相关 Skill
      • 6.4 第四步:通过 MCP 查询资源
      • 6.5 第五步:得到 Observation 并更新 State
      • 6.6 第六步:生成计划并验证
      • 6.7 第七步:用户修改需求
      • 6.8 完整链路总结
    • 7. 落地时可以用的设计清单
      • 7.1 State 设计清单
      • 7.2 MCP Resource Metadata 设计清单
      • 7.3 Skill 设计清单
      • 7.4 Skill Discovery 设计清单
    • 8. 最后总结

前言

最近看到一些遇到一些关于 AI Agent 工程化的问题,我觉得挺有意思的,所以想要详细去介绍一下,下面会讲解的时候会举一些例子,如果表述的不恰当,大家多包涵呢。

这些AI Agent 工程化的问题表面上是在问 LangGraph、MCP、Skills、Skill Discovery,但本质上是在问同一件事:

当大模型不再只是聊天,而是要连续完成任务、调用工具、读取资源、记住状态时,我们应该如何把它组织成一个可靠的系统?

很多人刚开始做 AI 应用时,会觉得 Agent 就是:

大模型 + 工具调用 + Prompt

但真正做复杂一点的任务后,很快会遇到一些问题:

  • 对话变长后,模型忘了用户最初的目标。
  • 用户中途改了需求,模型还在按照旧需求做事。
  • 系统接入了很多外部资源,但模型不知道什么时候该用哪个。
  • Tool、Skill、MCP 混在一起,架构边界越来越乱。
  • Skill 数量越来越多,全部塞进上下文后,模型反而更容易选错。

所以这篇文章想用比较容易理解的方式,聊 4 个 AI Agent 工程化中很重要的问题:

1. LangGraph State:长对话和长任务中,如何避免上下文漂移?
2. MCP Resource Metadata:模型如何知道某个资源什么时候该用?
3. Skill、Tool、MCP:这几个概念到底有什么区别?
4. Skill Discovery:Skill 变多后,如何避免上下文溢出和路由混乱?

为了让这些概念不那么抽象,本文尽量不用很专业的业务场景举例,而是用大家更熟悉的例子,比如:

  • 周末旅行助手
  • 做饭助手
  • 点外卖助手
  • 智能家居助手
  • 购物清单助手

这些例子虽然简单,但背后的工程问题和复杂 Agent 系统一样。

一张图理解 Agent 工程化

可以先看一个简化版 Agent 系统:

#mermaid-svg-uW6tclUjaAkaPSBh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uW6tclUjaAkaPSBh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uW6tclUjaAkaPSBh .error-icon{fill:#552222;}#mermaid-svg-uW6tclUjaAkaPSBh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uW6tclUjaAkaPSBh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uW6tclUjaAkaPSBh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uW6tclUjaAkaPSBh .marker.cross{stroke:#333333;}#mermaid-svg-uW6tclUjaAkaPSBh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uW6tclUjaAkaPSBh p{margin:0;}#mermaid-svg-uW6tclUjaAkaPSBh .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-uW6tclUjaAkaPSBh .cluster-label text{fill:#333;}#mermaid-svg-uW6tclUjaAkaPSBh .cluster-label span{color:#333;}#mermaid-svg-uW6tclUjaAkaPSBh .cluster-label span p{background-color:transparent;}#mermaid-svg-uW6tclUjaAkaPSBh .label text,#mermaid-svg-uW6tclUjaAkaPSBh span{fill:#333;color:#333;}#mermaid-svg-uW6tclUjaAkaPSBh .node rect,#mermaid-svg-uW6tclUjaAkaPSBh .node circle,#mermaid-svg-uW6tclUjaAkaPSBh .node ellipse,#mermaid-svg-uW6tclUjaAkaPSBh .node polygon,#mermaid-svg-uW6tclUjaAkaPSBh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uW6tclUjaAkaPSBh .rough-node .label text,#mermaid-svg-uW6tclUjaAkaPSBh .node .label text,#mermaid-svg-uW6tclUjaAkaPSBh .image-shape .label,#mermaid-svg-uW6tclUjaAkaPSBh .icon-shape .label{text-anchor:middle;}#mermaid-svg-uW6tclUjaAkaPSBh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uW6tclUjaAkaPSBh .rough-node .label,#mermaid-svg-uW6tclUjaAkaPSBh .node .label,#mermaid-svg-uW6tclUjaAkaPSBh .image-shape .label,#mermaid-svg-uW6tclUjaAkaPSBh .icon-shape .label{text-align:center;}#mermaid-svg-uW6tclUjaAkaPSBh .node.clickable{cursor:pointer;}#mermaid-svg-uW6tclUjaAkaPSBh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uW6tclUjaAkaPSBh .arrowheadPath{fill:#333333;}#mermaid-svg-uW6tclUjaAkaPSBh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uW6tclUjaAkaPSBh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uW6tclUjaAkaPSBh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uW6tclUjaAkaPSBh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uW6tclUjaAkaPSBh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uW6tclUjaAkaPSBh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uW6tclUjaAkaPSBh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uW6tclUjaAkaPSBh .cluster text{fill:#333;}#mermaid-svg-uW6tclUjaAkaPSBh .cluster span{color:#333;}#mermaid-svg-uW6tclUjaAkaPSBh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uW6tclUjaAkaPSBh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uW6tclUjaAkaPSBh rect.text{fill:none;stroke-width:0;}#mermaid-svg-uW6tclUjaAkaPSBh .icon-shape,#mermaid-svg-uW6tclUjaAkaPSBh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uW6tclUjaAkaPSBh .icon-shape p,#mermaid-svg-uW6tclUjaAkaPSBh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uW6tclUjaAkaPSBh .icon-shape .label rect,#mermaid-svg-uW6tclUjaAkaPSBh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uW6tclUjaAkaPSBh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uW6tclUjaAkaPSBh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uW6tclUjaAkaPSBh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

用户提出目标

状态管理 State

任务规划 Planner

Skill Discovery

加载相关 Skill

通过 MCP 访问资源和工具

执行动作

观察结果 Observation

最终回答或交付结果

这张图里有几个关键点:

  • State 负责保存当前任务的真实状态。
  • Planner 负责任务拆解。
  • Skill Discovery 负责从很多技能里找出当前最相关的技能。
  • MCP 负责把外部资源和工具标准化地暴露给模型。
  • Observation 不是一句普通聊天内容,而是工具执行后的事实反馈。

如果这些部分没有设计好,Agent 很容易变成一个“看起来很聪明,但做事不稳定”的系统。

1. 为什么 Agent 工程化不是“多接几个工具”?

我们先从一个生活化例子开始。

假设你做了一个“周末旅行助手”,用户说:

我这周末想去杭州玩两天,预算 800 元,不想去太挤的地方,帮我安排一下。

如果只是普通聊天机器人,它可能直接生成一段行程:

第一天去西湖,第二天去灵隐寺,晚上吃本地菜。

这当然能回答,但它不一定真的能完成任务。

一个更像 Agent 的系统,需要做更多事情:

1. 记住用户目标:周末去杭州,两天,预算 800 元,避开拥挤景点。
2. 查询天气:周末是否下雨。
3. 查询交通:高铁、地铁、打车成本。
4. 查询景点:开放时间、距离、热度。
5. 规划路线:每天怎么走更顺。
6. 控制预算:交通、餐饮、门票不要超支。
7. 用户改需求时更新计划。

这就不只是“模型会说话”了,而是一个带状态、资源、工具和计划的系统。

一个简单 Agent 会遇到的真实问题

继续这个旅行助手例子。

用户一开始说:

预算 800 元,玩两天。

过了一会儿又说:

其实我只有周日一天,而且我爸妈也一起去,别安排太累的路线。

如果系统只是把所有聊天记录塞给模型,模型可能会混乱:

  • 它可能还按“两天”规划。
  • 它可能还按“年轻人暴走路线”规划。
  • 它可能忘了预算。
  • 它可能不知道“只有周日一天”是最新需求。

这就是上下文漂移。

所以 Agent 工程化的第一件事,不是多接工具,而是让系统知道:

当前目标是什么?
哪些信息是最新的?
哪些信息已经被用户修改了?
哪些事实来自工具查询?
哪些只是模型的临时假设?

这就是 LangGraph State 这类设计要解决的问题。

2. LangGraph State:如何避免长对话中的上下文漂移?

2.1 什么是上下文漂移?

上下文漂移可以理解成:

对话或任务变长后,模型逐渐偏离真实目标,或者把旧信息、新信息、猜测和事实混在一起。

它在实际 Agent 系统里非常常见。

比如旅行助手中:

第 1 轮:用户说想去杭州两天。
第 2 轮:系统规划了两天路线。
第 3 轮:用户说只剩一天时间。
第 4 轮:用户说要带父母,路线不要太累。
第 5 轮:用户问,那上午去哪比较合适?

如果模型只是看完整聊天记录,它可能会被历史内容干扰。

它看到“杭州两天”,也看到“只剩一天”,还看到“父母一起去”。如果没有清晰的状态管理,它不一定能稳定判断哪个是最新约束。

2.2 State 的作用是什么?

State 可以理解成 Agent 的“结构化工作记忆”。

它不是普通聊天记录,而是把任务中真正重要的信息单独存起来。

比如这个旅行助手,可以维护这样的 State:

{
"user_goal": "规划周日杭州一日游",
"latest_constraints": {
"budget": 800,
"people": ["用户", "父亲", "母亲"],
"duration": "1 day",
"avoid": ["太拥挤的景点", "长时间步行"],
"preference": ["轻松", "适合长辈"]
},
"deprecated_constraints": [
"原计划是两天,但用户后来改成一天"
],
"known_facts": {
"weather": "待查询",
"transport": "待查询"
},
"current_plan": [],
"open_questions": [
"是否需要当天往返?"
]
}

这样后续每个节点都不用重新读完整聊天记录,而是读取这个结构化 State。

2.3 为什么不能只靠聊天记录?

聊天记录的问题是:它是线性的、混杂的。

里面同时有:

  • 用户的真实需求。
  • 用户已经废弃的旧需求。
  • 模型提出过但未确认的建议。
  • 工具返回的事实。
  • 系统临时猜测。
  • 中途失败的方案。

如果全都混在一起,模型就要自己判断哪些内容还有效。

这在短对话里问题不大,但在长任务里会越来越危险。

2.4 State 应该拆成哪些部分?

一个比较清晰的 Agent State 可以拆成:

goal:当前最终目标。
constraints:用户明确约束。
preferences:用户偏好。
facts:已经确认的事实。
assumptions:模型暂时假设。
plan:当前计划。
completed_steps:已经完成的步骤。
evidence:工具或资源返回的证据。
errors:执行中出现的问题。
version:需求版本。

用 Python 类型描述大概是:

from typing import TypedDict

class TravelAgentState(TypedDict):
goal: str
constraints: dict
preferences: list[str]
facts: dict
assumptions: list[str]
plan: list[str]
completed_steps: list[str]
evidence: list[dict]
errors: list[str]
requirement_version: int

2.5 状态更新比“对话总结”更重要

很多系统会做长对话总结,但总结不等于状态管理。

比如下面这种总结其实帮助有限:

用户想去杭州旅行,后来又改了一些需求。

这句话太模糊。

更好的方式是把它更新成结构化状态:

{
"requirement_version": 3,
"goal": "规划杭州周日一日游",
"active_constraints": {
"duration": "1 day",
"people": 3,
"budget": 800,
"walking_intensity": "low"
},
"inactive_constraints": [
{
"value": "2 days",
"reason": "用户后来改为周日一天"
}
]
}

这样系统不会再被旧信息带偏。

2.6 用 checkpoint 处理长任务

如果 Agent 要完成多步任务,最好设置 checkpoint。

旅行助手可以这样:

checkpoint_1:确认用户目标和约束。
checkpoint_2:查询天气和交通信息。
checkpoint_3:筛选候选景点。
checkpoint_4:生成路线。
checkpoint_5:检查预算和强度。
checkpoint_6:输出最终计划。

如果第 4 步发现路线太累,不需要重来所有步骤,只需要回到“筛选景点”或“生成路线”那一步。

2.7 让不同节点只看必要信息

Agent 系统里,不同节点不应该看到完全相同的上下文。

例如:

规划节点:需要看目标、约束、偏好。
查询节点:只需要看查询参数。
验证节点:需要看计划、预算、步行强度。
总结节点:只需要看最终状态和关键证据。

这能减少模型被无关信息干扰。

2.8 小结

LangGraph State 这类设计解决的是:

不要让模型在一大段聊天记录里猜当前状态。
要把当前状态显式、结构化、可更新地保存下来。

一句话:

长任务里,聊天记录是历史,State 才是当前事实。

3. MCP Resource Metadata:模型如何知道资源什么时候该用?

3.1 MCP 可以理解成什么?

MCP 可以简单理解成一种“标准连接方式”。

它让模型可以连接外部资源和工具,比如:

  • 天气信息
  • 地图路线
  • 日历
  • 商品价格
  • 餐厅菜单
  • 家里的智能设备
  • 个人购物清单

如果没有统一协议,每接一个资源都要单独写一套连接逻辑,系统会越来越乱。

MCP 的价值在于:

用统一方式把外部资源、工具和提示暴露给模型。

3.2 Resource Metadata 是什么?

Resource Metadata 就是资源的说明信息。

但它不是给人看的普通介绍,而是给模型做判断用的结构化说明。

它告诉模型:

这个资源是什么?
什么时候适合用?
什么时候不适合用?
需要什么输入?
会返回什么输出?
信息是否实时?
有没有风险或权限限制?

3.3 为什么 Metadata 很关键?

还是看旅行助手。

系统里可能有这些资源:

weather_resource:天气信息
map_resource:地图和路线
calendar_resource:用户日历
restaurant_resource:餐厅信息
ticket_resource:门票信息

用户说:

周日去杭州,帮我安排轻松一点的路线。

模型应该知道:

  • 要查天气,因为天气影响出行。
  • 要查地图,因为路线要轻松。
  • 要查景点开放时间,因为周日有些地方可能限流或闭馆。
  • 不一定要查餐厅,除非用户提到吃饭偏好。
  • 不应该查用户日历,除非用户授权并且需要确认时间冲突。

这些判断不能只靠模型自己猜,最好通过 Resource Metadata 明确告诉它。

3.4 一个天气资源的 Metadata 示例

{
"id": "weather_resource",
"title": "天气查询",
"description": "查询指定城市在指定日期的天气、温度、降雨概率和风力",
"applicable_when": [
"用户要安排户外活动",
"用户询问穿什么衣服",
"用户要制定旅行计划",
"用户关心是否下雨或是否适合出门"
],
"not_applicable_when": [
"用户只是在闲聊天气",
"用户没有给出地点且无法推断地点",
"用户询问历史天气但资源只支持未来天气"
],
"input_schema": {
"city": "string",
"date": "YYYY-MM-DD"
},
"output_schema": {
"temperature": "number",
"condition": "sunny | cloudy | rainy | snowy | unknown",
"rain_probability": "number",
"wind_level": "string"
},
"freshness": "real_time",
"permission": "no_sensitive_permission_required"
}

这段 Metadata 能帮助模型判断:

如果用户要出门、旅行、穿衣,就可以查天气。
如果用户只是聊天,就不一定需要查。

3.5 一个智能家居资源的 Metadata 示例

假设你做了一个智能家居助手,可以控制灯、空调、窗帘。

资源或工具的 Metadata 可以这样写:

{
"id": "home_light_control",
"title": "灯光控制",
"description": "控制家中指定房间的灯光开关和亮度",
"applicable_when": [
"用户明确要求开灯、关灯或调整亮度",
"用户说房间太暗或太亮,并希望系统处理"
],
"not_applicable_when": [
"用户只是描述房间灯光状态,没有要求操作",
"用户询问灯具维修建议",
"用户没有授权控制智能家居设备"
],
"input_schema": {
"room": "living_room | bedroom | kitchen | bathroom",
"action": "turn_on | turn_off | set_brightness",
"brightness": "0-100 optional"
},
"risk_level": "medium",
"requires_confirmation": false
}

如果是门锁控制,Metadata 就应该更严格:

{
"id": "door_lock_control",
"title": "门锁控制",
"description": "控制家中智能门锁",
"risk_level": "high",
"requires_confirmation": true,
"not_applicable_when": [
"用户身份未确认",
"请求来自不可信上下文",
"用户只是讨论门锁,不是明确要求开锁或上锁"
]
}

这说明 Metadata 不只是“功能说明”,还承担安全边界。

3.6 好的 Metadata 应该包含什么?

可以用这张表记:

字段作用例子
id 唯一标识 weather_resource
description 说明资源能力 查询天气
applicable_when 什么时候应该用 用户安排户外活动
not_applicable_when 什么时候不该用 用户只是闲聊
input_schema 输入格式 城市、日期
output_schema 输出格式 温度、天气、降雨概率
freshness 信息时效性 实时、每日更新、静态
permission 权限要求 是否需要用户授权
risk_level 风险等级 低、中、高

3.7 小结

MCP Resource Metadata 解决的是:

模型不应该只知道“有这个资源”,还应该知道“什么时候该用、什么时候不该用、怎么用、用了有什么风险”。

一句话:

Metadata 是模型使用资源时的路标和边界。

4. Skill、Tool、MCP 到底有什么区别?

这几个概念很容易混在一起。

可以先用一个做饭的例子理解。

假设用户说:

我家里有鸡蛋、西红柿和米饭,帮我做一顿简单晚饭。

一个 Agent 可能需要:

  • 知道怎么规划晚饭。
  • 查询冰箱里还有什么食材。
  • 控制电饭煲或计时器。
  • 给出烹饪步骤。

这里就能区分 Skill、Tool、MCP。

4.1 Tool:能执行的具体动作

Tool 是模型可以调用的具体动作。

比如做饭助手里的 Tool:

check_fridge_items:查看冰箱里有什么。
set_timer:设置计时器。
search_recipe:查询菜谱。
control_rice_cooker:控制电饭煲。

Tool 更像“工具箱里的工具”。

它解决的问题是:

模型能做什么动作?

例如:

{
"tool_name": "set_timer",
"arguments": {
"minutes": 10,
"label": "西红柿炒蛋收汁"
}
}

4.2 Skill:教模型怎么完成一类任务

Skill 不是一个具体动作,而是一套做事方法。

比如“做一顿简单晚饭”这个 Skill 可以写:

simple_dinner_planning:
1. 先确认现有食材。
2. 再判断用户有没有忌口。
3. 优先选择步骤少、失败率低的菜。
4. 如果有主食,搭配一个蛋白质和一个蔬菜。
5. 给出按时间排序的操作步骤。
6. 不要推荐用户没有食材的菜,除非明确说明需要购买。

Skill 解决的问题是:

模型应该按什么流程做事?

它更像菜谱、操作手册、工作流。

4.3 MCP:让模型用统一方式连接外部资源和工具

MCP 更像一种连接标准。

它把各种资源和工具统一暴露给模型。

在做饭助手里,MCP server 可能暴露:

Resources:
– fridge_inventory:冰箱库存
– recipe_database:菜谱库
– nutrition_info:营养信息

Tools:
– set_timer:设置计时器
– control_rice_cooker:控制电饭煲
– add_to_shopping_list:加入购物清单

MCP 解决的问题是:

外部资源和工具如何被标准化接入模型?

4.4 三者区别表

概念解决的问题更像什么做饭例子
Tool 能执行什么动作 刀、锅、计时器 设置计时器、查冰箱
Skill 应该怎么完成任务 菜谱、流程手册 如何规划一顿晚饭
MCP 如何连接外部资源和工具 厨房的统一接口和插座标准 连接冰箱库存、菜谱库、电饭煲

4.5 一个完整流转例子

用户说:

我今晚想吃点清淡的,家里有鸡蛋、青菜和米饭,帮我安排一下。

系统内部可以这样工作:

1. 识别任务类型:晚饭规划。
2. Skill Discovery 找到 simple_dinner_planning。
3. 加载 Skill,知道要先确认食材和偏好。
4. 通过 MCP 读取 fridge_inventory。
5. 调用 search_recipe 工具找简单菜谱。
6. 生成晚饭方案。
7. 如果需要计时,调用 set_timer。

对应关系:

Skill:simple_dinner_planning,告诉模型怎么规划。
MCP:连接冰箱库存、菜谱库和计时器。
Tool:search_recipe、set_timer 等具体动作。
State:保存用户偏好、已有食材、当前计划。

4.6 常见误区

误区 1:Skill 就是 Tool

不准确。

Tool 是动作,Skill 是方法。

比如:

set_timer 是 Tool。
如何安排做饭步骤是 Skill。

误区 2:有 MCP 就不需要 Skill

不准确。

MCP 只是把外部资源和工具接进来,但模型仍然需要 Skill 来知道正确流程。

就像厨房里有锅、有刀、有食材,不代表一个人就知道怎么做菜。

误区 3:Skill 越多越好

也不准确。

Skill 太多后,如果没有 Discovery 和治理,会出现:

  • 模型不知道该选哪个。
  • 上下文被 Skill 描述占满。
  • 相似 Skill 互相干扰。
  • 高风险 Skill 被误触发。

这就进入下一个问题:Skill Discovery。

5. Skill 太多怎么办:Discovery、按需加载和权限控制

5.1 为什么不能把所有 Skill 都塞进 System Prompt?

假设你做了一个很强的生活助手,慢慢加了很多 Skill:

做饭规划 Skill
旅行规划 Skill
外卖推荐 Skill
购物清单 Skill
智能家居 Skill
健身计划 Skill
穿衣建议 Skill
预算管理 Skill
日程安排 Skill
电影推荐 Skill
…

一开始只有几个 Skill,直接写进 System Prompt 好像没问题。

但当 Skill 变成几十个、几百个时,就会出问题。

5.2 问题 1:上下文预算被占用

每个 Skill 都需要描述:

这个 Skill 是什么?
什么时候用?
什么时候不用?
输入输出是什么?
有什么风险?

如果几十个 Skill 都写进去,会占用大量上下文。

更糟糕的是,系统可能会截断这些描述。

被截断后,模型可能只看到:

travel_planning:帮助用户安排旅行…
shopping_planning:帮助用户安排购物…
dinner_planning:帮助用户安排晚饭…

但看不到关键边界:

什么时候不用?
是否需要用户确认?
是否能自动下单?
是否涉及付款?

5.3 问题 2:候选太多,模型更容易选错

用户说:

我今晚不想出门,想吃点热乎的,预算 30 元以内。

这个请求可能涉及:

  • 做饭规划
  • 外卖推荐
  • 购物清单
  • 预算管理

如果模型看到 200 个 Skill,它可能会犹豫。

更好的做法是先做 Discovery,只召回少量候选:

候选 Skill:
1. dinner_planning
2. food_delivery_recommendation
3. budget_filtering

然后再让模型选择或组合。

5.4 问题 3:相似 Skill 会互相干扰

比如你有这些 Skill:

food_recommendation:推荐吃什么。
dinner_planning:安排晚饭。
recipe_search:查菜谱。
takeout_selection:选择外卖。

它们都和“吃”有关。

用户只说:

晚上吃什么?

模型很难判断到底该用哪个。

所以 Skill 描述必须写清楚边界。

不推荐:

food_recommendation:
帮助用户解决吃饭问题。

dinner_planning:
帮助用户规划晚饭。

推荐:

recipe_based_dinner:
当用户想在家做饭,并且有食材或愿意购买食材时使用。
不要用于用户明确想点外卖的场景。

takeout_selection:
当用户想点外卖,或不想做饭时使用。
不要用于用户想学习做菜的场景。

5.5 Skill Discovery 的推荐流程

一个更合理的 Skill Discovery 流程是:

#mermaid-svg-mWF2vhVOFrwnmWYk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mWF2vhVOFrwnmWYk .error-icon{fill:#552222;}#mermaid-svg-mWF2vhVOFrwnmWYk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mWF2vhVOFrwnmWYk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mWF2vhVOFrwnmWYk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mWF2vhVOFrwnmWYk .marker.cross{stroke:#333333;}#mermaid-svg-mWF2vhVOFrwnmWYk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mWF2vhVOFrwnmWYk p{margin:0;}#mermaid-svg-mWF2vhVOFrwnmWYk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster-label text{fill:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster-label span{color:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster-label span p{background-color:transparent;}#mermaid-svg-mWF2vhVOFrwnmWYk .label text,#mermaid-svg-mWF2vhVOFrwnmWYk span{fill:#333;color:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk .node rect,#mermaid-svg-mWF2vhVOFrwnmWYk .node circle,#mermaid-svg-mWF2vhVOFrwnmWYk .node ellipse,#mermaid-svg-mWF2vhVOFrwnmWYk .node polygon,#mermaid-svg-mWF2vhVOFrwnmWYk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mWF2vhVOFrwnmWYk .rough-node .label text,#mermaid-svg-mWF2vhVOFrwnmWYk .node .label text,#mermaid-svg-mWF2vhVOFrwnmWYk .image-shape .label,#mermaid-svg-mWF2vhVOFrwnmWYk .icon-shape .label{text-anchor:middle;}#mermaid-svg-mWF2vhVOFrwnmWYk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mWF2vhVOFrwnmWYk .rough-node .label,#mermaid-svg-mWF2vhVOFrwnmWYk .node .label,#mermaid-svg-mWF2vhVOFrwnmWYk .image-shape .label,#mermaid-svg-mWF2vhVOFrwnmWYk .icon-shape .label{text-align:center;}#mermaid-svg-mWF2vhVOFrwnmWYk .node.clickable{cursor:pointer;}#mermaid-svg-mWF2vhVOFrwnmWYk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mWF2vhVOFrwnmWYk .arrowheadPath{fill:#333333;}#mermaid-svg-mWF2vhVOFrwnmWYk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mWF2vhVOFrwnmWYk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mWF2vhVOFrwnmWYk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mWF2vhVOFrwnmWYk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mWF2vhVOFrwnmWYk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mWF2vhVOFrwnmWYk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster text{fill:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk .cluster span{color:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mWF2vhVOFrwnmWYk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mWF2vhVOFrwnmWYk rect.text{fill:none;stroke-width:0;}#mermaid-svg-mWF2vhVOFrwnmWYk .icon-shape,#mermaid-svg-mWF2vhVOFrwnmWYk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mWF2vhVOFrwnmWYk .icon-shape p,#mermaid-svg-mWF2vhVOFrwnmWYk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mWF2vhVOFrwnmWYk .icon-shape .label rect,#mermaid-svg-mWF2vhVOFrwnmWYk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mWF2vhVOFrwnmWYk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mWF2vhVOFrwnmWYk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mWF2vhVOFrwnmWYk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

用户请求

意图粗分类

按领域筛选 Skill

召回 Top-K 候选 Skill

根据正反例消歧

过滤高风险 Skill

加载完整 Skill

执行任务

记录使用结果

5.6 第一步:先做领域分组

不要在所有 Skill 里全局搜索。

先粗分领域:

饮食类:
– recipe_based_dinner
– takeout_selection
– shopping_list_generation

出行类:
– travel_planning
– route_selection
– weather_aware_schedule

家居类:
– light_control
– air_conditioner_control
– cleaning_schedule

娱乐类:
– movie_recommendation
– weekend_activity

用户说:

周末想出去玩,但不想走太多路。

先进入出行类,而不是让所有 Skill 都参与竞争。

5.7 第二步:只召回 Top-K

Top-K 的意思是只选最相关的几个候选。

例如:

用户请求:周末想出去玩,但不想走太多路。

Top-K Skill:
1. travel_planning
2. weather_aware_schedule
3. route_selection

这比把几十个 Skill 全部交给模型更稳定。

5.8 第三步:Skill 描述要写正例和反例

一个好 Skill 不应该只写“我能做什么”,还要写“我不能做什么”。

例如:

travel_planning:
用于用户想安排旅行、周末出行、城市游玩路线的场景。
适合处理预算、时间、同行人、路线强度等约束。
不要用于用户只是询问某个地点的单独信息。
不要用于用户已经明确只需要查天气的场景。

另一个:

weather_query:
用于用户询问天气、穿衣、是否适合出门等场景。
不要用于完整旅行规划,除非它作为 travel_planning 的辅助步骤。

这样模型更容易消歧。

5.9 第四步:高风险 Skill 不要自动触发

有些 Skill 可能会产生真实世界影响。

比如:

place_order:下单。
pay_bill:付款。
unlock_door:开门锁。
send_message:发送消息。
change_schedule:修改日程。

这些 Skill 不应该轻易由模型自动触发。

比如用户说:

帮我看看今晚吃什么。

模型可以推荐外卖,但不能直接下单。

如果用户说:

就点这个吧。

系统也应该确认:

你确认要下单这份套餐吗?价格 28 元,送到默认地址。

这就是权限控制。

5.10 第五步:记录使用日志,持续优化

Skill Discovery 不是写完就结束。

系统应该记录:

某个 Skill 被召回了多少次?
实际被使用了多少次?
用户是否取消或纠正?
执行结果是否成功?
是否经常误触发?
是否长期没人用?

如果一个 Skill 经常被召回但很少使用,说明它的描述可能太宽泛。

如果两个 Skill 经常同时被召回,说明它们可能边界重叠。

如果一个 Skill 长期没人用,可以移出默认列表。

5.11 小结

Skill 太多时,核心不是“怎么塞进上下文”,而是:

怎么让当前任务只看到少量、清晰、可区分、低风险的 Skill。

一句话:

Skill Discovery 的目标不是展示所有能力,而是帮模型缩小选择范围。

6. 一个完整例子:周末旅行助手内部如何流转?

我们把前面的概念串起来。

用户说:

我周日想带爸妈去杭州玩一天,预算 800 元以内,不想走太多路,最好别太晒。

6.1 第一步:更新 State

系统先把用户目标写入 State:

{
"goal": "规划杭州周日一日游",
"constraints": {
"date": "Sunday",
"city": "杭州",
"people": ["用户", "父亲", "母亲"],
"budget": 800,
"walking_intensity": "low",
"avoid": ["暴晒", "长时间步行"]
},
"preferences": ["轻松", "适合长辈"],
"facts": {},
"plan": [],
"requirement_version": 1
}

6.2 第二步:Skill Discovery

系统从很多 Skill 中召回候选:

Top-K Skill:
1. travel_planning
2. weather_aware_schedule
3. route_selection

不会召回:

movie_recommendation
recipe_based_dinner
light_control

因为它们和当前任务无关。

6.3 第三步:加载相关 Skill

加载 travel_planning 后,模型知道旅行规划应该考虑:

时间
预算
同行人
路线强度
天气
交通
景点开放时间
备选方案

加载 weather_aware_schedule 后,模型知道:

如果天气太热或下雨,要优先选择室内、阴凉、步行少的路线。

6.4 第四步:通过 MCP 查询资源

模型通过 MCP 访问资源:

weather_resource:查询周日杭州天气。
map_resource:查询路线距离和交通时间。
place_resource:查询景点开放时间和拥挤程度。

这些资源都有 Metadata,模型知道什么时候该用它们。

6.5 第五步:得到 Observation 并更新 State

假设天气资源返回:

{
"city": "杭州",
"date": "Sunday",
"condition": "sunny",
"temperature": 34,
"rain_probability": 10
}

系统更新 State:

{
"facts": {
"weather": {
"condition": "sunny",
"temperature": 34,
"risk": "hot"
}
}
}

这时路线规划就应该避开暴晒和长距离步行。

6.6 第六步:生成计划并验证

系统生成计划后,不应该直接输出。

还要验证:

预算是否小于 800?
是否适合长辈?
是否步行太多?
是否避开高温暴晒?
是否有备选方案?

如果发现步行时间太长,就回到路线筛选阶段重新规划。

6.7 第七步:用户修改需求

用户又说:

我妈临时不去了,预算改成 500 元。

系统不能只是把这句话追加到聊天记录里。

它应该更新 State:

{
"requirement_version": 2,
"constraints": {
"people": ["用户", "父亲"],
"budget": 500
},
"deprecated_constraints": [
{
"field": "people",
"old_value": ["用户", "父亲", "母亲"],
"reason": "用户更新同行人"
},
{
"field": "budget",
"old_value": 800,
"reason": "用户更新预算"
}
]
}

然后重新检查预算和路线。

6.8 完整链路总结

大模型

MCP 资源

Skill Discovery

State

用户

大模型

MCP 资源

Skill Discovery

State

用户

#mermaid-svg-UqCZwpvlTpiBsE0M{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UqCZwpvlTpiBsE0M .error-icon{fill:#552222;}#mermaid-svg-UqCZwpvlTpiBsE0M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UqCZwpvlTpiBsE0M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UqCZwpvlTpiBsE0M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UqCZwpvlTpiBsE0M .marker.cross{stroke:#333333;}#mermaid-svg-UqCZwpvlTpiBsE0M svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UqCZwpvlTpiBsE0M p{margin:0;}#mermaid-svg-UqCZwpvlTpiBsE0M .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UqCZwpvlTpiBsE0M text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UqCZwpvlTpiBsE0M .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-UqCZwpvlTpiBsE0M .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-UqCZwpvlTpiBsE0M #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-UqCZwpvlTpiBsE0M .sequenceNumber{fill:white;}#mermaid-svg-UqCZwpvlTpiBsE0M #sequencenumber{fill:#333;}#mermaid-svg-UqCZwpvlTpiBsE0M #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-UqCZwpvlTpiBsE0M .messageText{fill:#333;stroke:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UqCZwpvlTpiBsE0M .labelText,#mermaid-svg-UqCZwpvlTpiBsE0M .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .loopText,#mermaid-svg-UqCZwpvlTpiBsE0M .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UqCZwpvlTpiBsE0M .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-UqCZwpvlTpiBsE0M .noteText,#mermaid-svg-UqCZwpvlTpiBsE0M .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-UqCZwpvlTpiBsE0M .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UqCZwpvlTpiBsE0M .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UqCZwpvlTpiBsE0M .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UqCZwpvlTpiBsE0M .actorPopupMenu{position:absolute;}#mermaid-svg-UqCZwpvlTpiBsE0M .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-UqCZwpvlTpiBsE0M .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UqCZwpvlTpiBsE0M .actor-man circle,#mermaid-svg-UqCZwpvlTpiBsE0M line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-UqCZwpvlTpiBsE0M :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

提出旅行目标和约束

根据目标查找相关 Skill

加载旅行规划相关 Skill

查询天气、路线、地点信息

返回 Observation 并更新事实

提供最新状态

写入候选计划

验证预算、强度和天气约束

输出最终旅行方案

这个例子里,几个概念的关系很清楚:

State:保存当前目标、约束、事实和计划。
Skill:告诉模型如何做旅行规划。
MCP:让模型连接天气、地图、地点等资源。
Tool:具体查询天气、路线、地点的动作。
Skill Discovery:从很多技能中选出当前需要的少数几个。

7. 落地时可以用的设计清单

如果你之后要做一个 Agent 系统,可以用下面这个清单检查。

7.1 State 设计清单

  • 是否把当前目标单独保存?
  • 是否区分用户约束、模型假设和工具事实?
  • 用户修改需求时,是否能标记旧需求失效?
  • 是否有 requirement_version?
  • 是否有 checkpoint?
  • 每个节点是否只读取必要状态?

7.2 MCP Resource Metadata 设计清单

  • 资源是否有清晰描述?
  • 是否写了 applicable_when?
  • 是否写了 not_applicable_when?
  • 是否定义输入输出结构?
  • 是否说明信息时效性?
  • 是否标记权限和风险等级?
  • 高风险动作是否需要确认?

7.3 Skill 设计清单

  • Skill 是不是一套做事方法,而不是一个具体工具?
  • Skill 名称是否具体?
  • 是否写清楚什么时候使用?
  • 是否写清楚什么时候不要使用?
  • 是否和其他 Skill 边界重叠?
  • 是否包含高风险动作?

7.4 Skill Discovery 设计清单

  • 是否先按领域分组?
  • 是否只召回 Top-K?
  • 是否有消歧逻辑?
  • 是否过滤高风险 Skill?
  • 是否记录召回和使用日志?
  • 是否定期清理长期不用的 Skill?

8. 最后总结

Agent 工程化真正难的地方,不是让模型回答得更像人,而是让模型在复杂任务中做事更稳定。

这需要几个关键设计:

用 State 管住任务状态。
用 MCP 标准化连接资源和工具。
用 Metadata 告诉模型资源的适用边界。
用 Skill 描述做事方法。
用 Skill Discovery 控制模型看到的技能数量。
用权限和日志保证系统可控、可追踪、可优化。

如果只靠一个很长的 Prompt,系统很快会失控。

更可靠的做法是:

把状态结构化;
把资源标准化;
把技能模块化;
把选择过程收窄;
把高风险动作管住;
把每次执行记录下来。

一句话总结:

AI Agent 不是“模型加一堆工具”,而是一个由状态、资源、工具、技能、路由和权限共同组成的工程系统。

当我们开始这样设计 Agent 时,它才不只是一个能聊天的 Demo,而是更接近一个可以长期维护、持续扩展、稳定工作的系统。

赞(0)
未经允许不得转载:171主机测评 » 从 LangGraph State 到 MCP、Skills:聊聊 AI Agent 工程化里最容易被忽略的 4 个问题
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址