Agent 如何触发 Skill?Skill 和 Tool 的区别详解

Agent 如何触发 Skill?Skill 和 Tool 的区别

先说个让人困惑的问题

刚接触 Agent 开发的时候,我经常被两个概念搞混:Tool 和 Skill。

文档里写的是"Agent 可以调用 Tool",但实际开发时又会看到"Skill 编排"、"Skill 触发"。这俩到底啥关系?是同一个东西换了个名字,还是真的有层级差异?

后来在 NVC 项目里踩了不少坑,才算真正搞明白。今天把这块梳理清楚,顺便聊聊 Agent 触发 Skill 的几种模式。

Tool:最小执行单元

Tool 是 Agent 与外部世界交互的最小单元。说白了,它就是一个可以被调用的函数

python 复制代码
# 一个典型的 Tool 定义
def search_database(query: str, limit: int = 10) -> list[dict]:
    """搜索数据库中的记录
    
    Args:
        query: 搜索关键词
        limit: 返回结果数量限制
        
    Returns:
        匹配的记录列表
    """
    results = db.search(query, limit=limit)
    return results

Tool 有几个关键特征:

特征 说明
原子性 只做一件事,不会拆成更小的步骤
声明式描述 必须有清晰的函数签名和 docstring
被动调用 自己不会主动执行,等别人来调
无状态 调用之间不保留状态(理想情况下)

LLM 能用 Tool,靠的是 Function Calling 机制。LLM 看到 Tool 的描述,理解参数含义,然后决定要不要调、怎么传参。

复制代码
用户: "帮我查一下最近的订单"
         ↓
LLM: 看到有 search_orders Tool,参数是 user_id 和 date_range
         ↓
LLM: 生成 function call: search_orders(user_id="xxx", date_range="last_7_days")
         ↓
系统: 执行函数,返回结果
         ↓
LLM: 把结果组织成自然语言回复用户

这个过程很直白,但有个问题:如果完成一个任务需要多步操作呢?

比如"帮我订一张明天去上海的机票",拆解下来可能是:

  1. 查询航班信息
  2. 筛选合适的航班
  3. 检查座位 availability
  4. 创建订单
  5. 处理支付

每一步都是一个 Tool 调用,但它们之间有先后依赖,还有条件分支(没座位怎么办?支付失败怎么办?)。这时候就需要更高层的抽象了。

Skill:编排好的执行流程

Skill 是一组编排好的多步骤执行流程。如果说 Tool 是单个乐器,那 Skill 就是一段乐谱。

python 复制代码
# 一个 Skill 的伪代码示例
def book_flight_skill(user_request: str) -> BookingResult:
    """订机票的完整流程"""
    
    # Step 1: 解析意图
    intent = parse_travel_intent(user_request)
    
    # Step 2: 查询航班
    flights = search_flights(
        origin=intent.origin,
        destination=intent.destination,
        date=intent.date
    )
    
    # Step 3: 筛选推荐
    recommended = filter_and_rank(flights, user.preferences)
    
    # Step 4: 确认选择(可能需要用户交互)
    selected = confirm_selection(recommended)
    
    # Step 5: 创建订单
    order = create_order(selected, user.payment_info)
    
    # Step 6: 返回结果
    return BookingResult(order=order, confirmation=order.confirmation_code)

Skill 的特征和 Tool 形成对比:

维度 Tool Skill
粒度 原子操作 复合流程
是否有状态 无状态 可以维护上下文
调用方式 直接函数调用 流程编排
是否可组合 是(被 Skill 组合) 是(可以包含其他 Skill)
错误处理 单步异常 流程级容错和重试

一个 Skill 内部可能调用多个 Tool,也可能调用其他 Skill。这种嵌套组合的能力,让 Agent 的行为变得灵活而强大。

Agent 触发 Skill 的三种模式

理解了 Tool 和 Skill 的区别,接下来看 Agent 是怎么触发 Skill 的。这里有三种典型模式:

模式一:LLM 直接触发 Tool(Function Calling)

最简单的情况,LLM 直接决定调用哪个 Tool:
Tool LLM User Tool LLM User #mermaid-svg-1hjVFSeYUSAn8dmx{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-1hjVFSeYUSAn8dmx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1hjVFSeYUSAn8dmx .error-icon{fill:#552222;}#mermaid-svg-1hjVFSeYUSAn8dmx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1hjVFSeYUSAn8dmx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1hjVFSeYUSAn8dmx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1hjVFSeYUSAn8dmx .marker.cross{stroke:#333333;}#mermaid-svg-1hjVFSeYUSAn8dmx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1hjVFSeYUSAn8dmx p{margin:0;}#mermaid-svg-1hjVFSeYUSAn8dmx .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1hjVFSeYUSAn8dmx text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1hjVFSeYUSAn8dmx .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-1hjVFSeYUSAn8dmx .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-1hjVFSeYUSAn8dmx #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-1hjVFSeYUSAn8dmx .sequenceNumber{fill:white;}#mermaid-svg-1hjVFSeYUSAn8dmx #sequencenumber{fill:#333;}#mermaid-svg-1hjVFSeYUSAn8dmx #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-1hjVFSeYUSAn8dmx .messageText{fill:#333;stroke:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1hjVFSeYUSAn8dmx .labelText,#mermaid-svg-1hjVFSeYUSAn8dmx .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .loopText,#mermaid-svg-1hjVFSeYUSAn8dmx .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .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-1hjVFSeYUSAn8dmx .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-1hjVFSeYUSAn8dmx .noteText,#mermaid-svg-1hjVFSeYUSAn8dmx .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-1hjVFSeYUSAn8dmx .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1hjVFSeYUSAn8dmx .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1hjVFSeYUSAn8dmx .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1hjVFSeYUSAn8dmx .actorPopupMenu{position:absolute;}#mermaid-svg-1hjVFSeYUSAn8dmx .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-1hjVFSeYUSAn8dmx .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1hjVFSeYUSAn8dmx .actor-man circle,#mermaid-svg-1hjVFSeYUSAn8dmx line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-1hjVFSeYUSAn8dmx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} "今天天气怎么样"分析意图,决定调用天气 Toolget_weather(city="北京"){"temp": 28, "condition": "晴"}"北京今天 28 度,晴天"

这种模式适合单步任务,简单直接。但问题是:LLM 本身不执行复杂流程,它只负责"点菜",不负责"做菜"。

模式二:编排层触发 Skill(Orchestrator 调度)

当任务复杂度上来后,需要一个编排层来管理流程:
#mermaid-svg-Osuz6MN7C4LUEvXp{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-Osuz6MN7C4LUEvXp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Osuz6MN7C4LUEvXp .error-icon{fill:#552222;}#mermaid-svg-Osuz6MN7C4LUEvXp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Osuz6MN7C4LUEvXp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Osuz6MN7C4LUEvXp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Osuz6MN7C4LUEvXp .marker.cross{stroke:#333333;}#mermaid-svg-Osuz6MN7C4LUEvXp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Osuz6MN7C4LUEvXp p{margin:0;}#mermaid-svg-Osuz6MN7C4LUEvXp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster-label text{fill:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster-label span{color:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster-label span p{background-color:transparent;}#mermaid-svg-Osuz6MN7C4LUEvXp .label text,#mermaid-svg-Osuz6MN7C4LUEvXp span{fill:#333;color:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp .node rect,#mermaid-svg-Osuz6MN7C4LUEvXp .node circle,#mermaid-svg-Osuz6MN7C4LUEvXp .node ellipse,#mermaid-svg-Osuz6MN7C4LUEvXp .node polygon,#mermaid-svg-Osuz6MN7C4LUEvXp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Osuz6MN7C4LUEvXp .rough-node .label text,#mermaid-svg-Osuz6MN7C4LUEvXp .node .label text,#mermaid-svg-Osuz6MN7C4LUEvXp .image-shape .label,#mermaid-svg-Osuz6MN7C4LUEvXp .icon-shape .label{text-anchor:middle;}#mermaid-svg-Osuz6MN7C4LUEvXp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Osuz6MN7C4LUEvXp .rough-node .label,#mermaid-svg-Osuz6MN7C4LUEvXp .node .label,#mermaid-svg-Osuz6MN7C4LUEvXp .image-shape .label,#mermaid-svg-Osuz6MN7C4LUEvXp .icon-shape .label{text-align:center;}#mermaid-svg-Osuz6MN7C4LUEvXp .node.clickable{cursor:pointer;}#mermaid-svg-Osuz6MN7C4LUEvXp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Osuz6MN7C4LUEvXp .arrowheadPath{fill:#333333;}#mermaid-svg-Osuz6MN7C4LUEvXp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Osuz6MN7C4LUEvXp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Osuz6MN7C4LUEvXp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Osuz6MN7C4LUEvXp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Osuz6MN7C4LUEvXp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Osuz6MN7C4LUEvXp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster text{fill:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp .cluster span{color:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp 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-Osuz6MN7C4LUEvXp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Osuz6MN7C4LUEvXp rect.text{fill:none;stroke-width:0;}#mermaid-svg-Osuz6MN7C4LUEvXp .icon-shape,#mermaid-svg-Osuz6MN7C4LUEvXp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Osuz6MN7C4LUEvXp .icon-shape p,#mermaid-svg-Osuz6MN7C4LUEvXp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Osuz6MN7C4LUEvXp .icon-shape .label rect,#mermaid-svg-Osuz6MN7C4LUEvXp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Osuz6MN7C4LUEvXp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Osuz6MN7C4LUEvXp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Osuz6MN7C4LUEvXp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 订机票
查订单
退款
用户请求
Orchestrator
意图识别
book_flight Skill
query_order Skill
refund Skill
search_flights Tool
create_order Tool
process_payment Tool
query_database Tool
validate_refund Tool
initiate_refund Tool

Orchestrator 是一个独立的调度层,它:

  • 接收用户请求
  • 识别意图,决定调用哪个 Skill
  • 把 Skill 的执行结果返回给用户

这种模式下,LLM 可以完全不参与 Skill 的触发过程。Orchestrator 用规则引擎或简单模型就能搞定意图识别,速度快、成本低。

模式三:LLM 触发 Tool 组合(多轮 Agent Loop)

这是最灵活的模式,LLM 在一个循环中逐步调用 Tool,自己决定下一步做什么:

python 复制代码
def agent_loop(user_request: str, max_iterations: int = 10):
    """Agent 执行循环"""
    messages = [{"role": "user", "content": user_request}]
    tools = get_available_tools()
    
    for i in range(max_iterations):
        # LLM 决定下一步
        response = llm.chat(messages, tools=tools)
        
        # 如果 LLM 决定结束
        if response.finish_reason == "stop":
            return response.content
        
        # 如果 LLM 要调用 Tool
        if response.tool_calls:
            for tool_call in response.tool_calls:
                result = execute_tool(tool_call)
                messages.append({
                    "role": "tool",
                    "content": result,
                    "tool_call_id": tool_call.id
                })
        
        # 继续循环,让 LLM 看到 Tool 结果后决定下一步
    
    return "达到最大迭代次数"

这种模式下,LLM 既是决策者又是执行者。它根据每一步的结果,动态决定下一步做什么。灵活性最高,但也最不可控------LLM 可能陷入循环,或者调用了不该调用的 Tool。

NVC 项目实战:模式路由与工具映射

在实际项目中,我踩过一个坑:让 LLM 自己选 Tool,准确率只有 70% 左右。用户说"我想练习观察",LLM 有时候会调成 rag_search 而不是 practice_start

后来我们设计了一套模式路由 + 意图预路由机制,把 Skill 的触发从"LLM 自己选"变成了"系统根据场景分配"。

NVC 的 10 个 Tool

NVC 项目定义了 10 个 Tool,每个都是一个独立的 Spring Bean:

Tool 名称 职责
RagSearchTool RAG 知识检索,从 pgvector 向量库中搜索 NVC 相关知识
WikiSearchTool Wiki 搜索,查找用户生成的 NVC 实践笔记
WikiWriteTool Wiki 写入,异步生成 NVC 实践 Wiki
ProfileQueryTool 用户档案查询,获取用户能力画像
ProfileUpdateTool 用户档案更新,记录职业、偏好等信息
DashboardQueryTool 练习数据查询,统计练习次数、得分等
EvaluateNvcTool NVC 表达评估,判断是否符合四步法
PracticeStartTool 开始练习,初始化练习会话
ScenarioGenerateTool 场景生成,AI 生成冲突场景
ScenarioSearchTool 场景搜索,从场景库中检索

ModeRouter:三种模式路由

NVC 支持三种对话模式,每种模式下 Agent 可用的 Tool 集合和行为策略不同:

java 复制代码
// 三种模式路由器
FreeDialogRouter    // 自由对话模式,默认 DIALOGUE_GUIDE,每 5 轮触发评估
ScenarioRouter      // 场景驱动模式,CREATED 阶段生成场景,每 5 轮检查低分触发评估
StructuredRouter    // 结构化四步模式:观察→感受→需求→请求

这样做的好处是:LLM 看到的 Tool 列表是精简的、模式相关的 。在结构化练习模式下,LLM 根本看不到 dashboard_query 这个 Tool,自然就不会调错。

IntentRouter:绕过 LLM 的意图预路由

更进一步,对于一些明确的意图,我们甚至不让 LLM 参与决策:

java 复制代码
// IntentRouter.java - 在 LLM 调用前,通过正则匹配快速识别高置信度意图
public AgentResult execute(PracticeContext context) {
    IntentMatch match = intentRouter.match(context.getUserMessage());
    if (match != null && match.getConfidence() >= 0.8) {
        return toolExecutor.executeDirectly(match.getToolName(), match.getArgs());
    }
    return reactLoop(context);
}

IntentRouter 的匹配规则:

意图 匹配模式 对应 Tool
profile_update "我是/我的职业是/帮我记录到档案" ProfileUpdateTool
profile_query "看看档案/查看档案" ProfileQueryTool
dashboard_query "练习数据/练习统计" DashboardQueryTool

这个设计的考量是:高频的档案和数据查询可以用正则快速匹配,省掉 LLM 推理的 2-3 秒延迟。匹配不上再走 ReAct 循环。

完整流程

把上面的组件串起来:
#mermaid-svg-acNsN27asT6kcFG8{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-acNsN27asT6kcFG8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-acNsN27asT6kcFG8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-acNsN27asT6kcFG8 .error-icon{fill:#552222;}#mermaid-svg-acNsN27asT6kcFG8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-acNsN27asT6kcFG8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-acNsN27asT6kcFG8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-acNsN27asT6kcFG8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-acNsN27asT6kcFG8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-acNsN27asT6kcFG8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-acNsN27asT6kcFG8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-acNsN27asT6kcFG8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-acNsN27asT6kcFG8 .marker.cross{stroke:#333333;}#mermaid-svg-acNsN27asT6kcFG8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-acNsN27asT6kcFG8 p{margin:0;}#mermaid-svg-acNsN27asT6kcFG8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-acNsN27asT6kcFG8 .cluster-label text{fill:#333;}#mermaid-svg-acNsN27asT6kcFG8 .cluster-label span{color:#333;}#mermaid-svg-acNsN27asT6kcFG8 .cluster-label span p{background-color:transparent;}#mermaid-svg-acNsN27asT6kcFG8 .label text,#mermaid-svg-acNsN27asT6kcFG8 span{fill:#333;color:#333;}#mermaid-svg-acNsN27asT6kcFG8 .node rect,#mermaid-svg-acNsN27asT6kcFG8 .node circle,#mermaid-svg-acNsN27asT6kcFG8 .node ellipse,#mermaid-svg-acNsN27asT6kcFG8 .node polygon,#mermaid-svg-acNsN27asT6kcFG8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-acNsN27asT6kcFG8 .rough-node .label text,#mermaid-svg-acNsN27asT6kcFG8 .node .label text,#mermaid-svg-acNsN27asT6kcFG8 .image-shape .label,#mermaid-svg-acNsN27asT6kcFG8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-acNsN27asT6kcFG8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-acNsN27asT6kcFG8 .rough-node .label,#mermaid-svg-acNsN27asT6kcFG8 .node .label,#mermaid-svg-acNsN27asT6kcFG8 .image-shape .label,#mermaid-svg-acNsN27asT6kcFG8 .icon-shape .label{text-align:center;}#mermaid-svg-acNsN27asT6kcFG8 .node.clickable{cursor:pointer;}#mermaid-svg-acNsN27asT6kcFG8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-acNsN27asT6kcFG8 .arrowheadPath{fill:#333333;}#mermaid-svg-acNsN27asT6kcFG8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-acNsN27asT6kcFG8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-acNsN27asT6kcFG8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-acNsN27asT6kcFG8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-acNsN27asT6kcFG8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-acNsN27asT6kcFG8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-acNsN27asT6kcFG8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-acNsN27asT6kcFG8 .cluster text{fill:#333;}#mermaid-svg-acNsN27asT6kcFG8 .cluster span{color:#333;}#mermaid-svg-acNsN27asT6kcFG8 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-acNsN27asT6kcFG8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-acNsN27asT6kcFG8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-acNsN27asT6kcFG8 .icon-shape,#mermaid-svg-acNsN27asT6kcFG8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-acNsN27asT6kcFG8 .icon-shape p,#mermaid-svg-acNsN27asT6kcFG8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-acNsN27asT6kcFG8 .icon-shape .label rect,#mermaid-svg-acNsN27asT6kcFG8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-acNsN27asT6kcFG8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-acNsN27asT6kcFG8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-acNsN27asT6kcFG8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命中且置信度>=0.8
未命中
用户消息
IntentRouter
正则匹配?
直接执行 Tool
ModeRouter
选择对话模式
AgentLoop + 模式 Tool 集
LLM 推理
执行 Tool
返回结果

这套架构在 NVC 项目里跑了大半年,意图识别准确率从 70% 提升到了 95% 以上。

几个容易踩的坑

坑一:Tool 描述写得太抽象

python 复制代码
# 反例:LLM 看不懂
def process_data(data, config):
    """处理数据"""
    pass

# 正例:描述具体
def filter_orders(orders: list, status: str, date_range: str) -> list:
    """根据状态和日期范围筛选订单
    
    Args:
        orders: 订单列表
        status: 订单状态,可选值: pending, paid, shipped, completed, cancelled
        date_range: 日期范围,格式: "2024-01-01~2024-01-31"
        
    Returns:
        符合条件的订单列表
    """
    pass

Tool 的描述是 LLM 理解它的唯一依据。写得越具体,LLM 调用得越准。

坑二:Skill 没有错误处理

python 复制代码
# 反例:Skill 里没有容错
def book_flight_skill(request):
    flights = search_flights(request.origin, request.destination)
    selected = flights[0]  # 直接取第一个,万一没有航班呢?
    create_order(selected)
    
# 正例:每一步都有错误处理
def book_flight_skill(request):
    flights = search_flights(request.origin, request.destination)
    if not flights:
        return Result.error("没有找到符合条件的航班")
    
    selected = flights[0]
    order_result = create_order(selected)
    if order_result.is_error():
        return Result.error(f"下单失败: {order_result.error_message}")
    
    return Result.success(order_result.data)

Skill 是流程级的抽象,必须考虑各种异常情况。Tool 可以抛异常让上层处理,但 Skill 必须自己兜底。

坑三:Agent Loop 没有退出条件

python 复制代码
# 反例:可能无限循环
while True:
    response = llm.chat(messages, tools=tools)
    if response.tool_calls:
        execute_tools(response.tool_calls)
    
# 正例:设置最大迭代次数
for i in range(MAX_ITERATIONS):
    response = llm.chat(messages, tools=tools)
    if response.finish_reason == "stop":
        return response.content
    if response.tool_calls:
        execute_tools(response.tool_calls)
    
return "抱歉,处理过程中遇到问题,请稍后重试"

LLM 有时候会陷入循环,反复调用同一个 Tool。没有退出条件,Agent 就会一直跑下去。

总结

Tool 和 Skill 的区别,本质上是粒度的区别:

  • Tool 是原子操作,一个 Tool 只做一件事
  • Skill 是复合流程,一个 Skill 协调多个 Tool 完成一个任务

Agent 触发 Skill 的三种模式,各有适用场景:

  • LLM 直接触发 Tool:适合简单任务,一个 Tool 就能搞定
  • 编排层触发 Skill:适合流程固定、对可靠性要求高的场景
  • LLM 触发 Tool 组合:适合复杂、需要动态决策的场景

NVC 项目的实践表明:不是所有场景都需要 LLM 来决策。用 ModeRouter 限制 LLM 的选择范围,用 IntentRouter 处理明确意图,反而能获得更好的效果。

Agent 的核心不是"让 LLM 做所有事",而是在合适的地方用合适的机制。LLM 擅长理解和生成,但不擅长精确执行。把 LLM 放在它擅长的位置,其他事情交给确定性的代码,这才是好架构。

相关推荐
用户469368483203 小时前
kimi-code 深度掌握系列文章-Plan Mode 与 Goal Mode:结构化的自主执行(九)
agent
leeyi3 小时前
Memory 三层设计:为什么 [“session“,id] 最后写成了四元组(第78篇-E64)
aigc·agent·ai编程
为你学会写情书3 小时前
Agent Skills 完全指南:从目录规范到渐进式加载的工程实践
agent
Ai拆代码的曹操3 小时前
Agent 做错了怎么办?Self-Critique 机制拆解
后端·agent·ai编程
ckjoker4 小时前
我把Java多模态链路从0跑通了,结果先被4个坑狠狠干了一顿
后端·agent
提笔了无痕5 小时前
Agent 上下文管理详解、Context设计与构建
数据库·oracle·agent·context
weixin_431600445 小时前
为什么 Agent REPL 要上 Ink:好处、用法与内部设计
前端·学习·ai·agent·ai编程
小当家.1055 小时前
工具并行调用原理与实现:CompletableFuture 实战
java·agent·线程池·工具·并行
苏灿烤鱼6 小时前
AI 论文档案库|大模型与 Agent 周报
人工智能·agent