ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI Agent技能模块化实践:从提示词约束到结构化调用

AI Agent技能模块化实践:从提示词约束到结构化调用 从去年开始我就在折腾各种 AI Agent 的应用前后试过不少框架和编排方案踩过不少坑。最近终于把一套叫agent-skills的项目思路理顺了——简单说就是把智能体需要的能力拆成一个个可复用、可组合、可独立测试的“技能模块”让模型不再面对一团模糊的指令而是像工具箱一样按需取用。这篇文章我会把这个项目的设计思路、实现细节、以及我在实际调试中踩过的坑全部摊开来讲希望能给正在做 Agent 落地的朋友省点时间。先说清楚它解决了什么问题。早期我做 Agent 的时候最常见的痛点是任务一复杂大模型就开始“自由发挥”。你让它处理一个订单查询它可能自己脑补出一套参数也可能把无关工具乱调一通。根子在于我们对模型的行为约束太依赖系统提示词里的“叮嘱”可模型并不真正理解边界。agent-skills 的核心思路就是把每个原子能力封装成带明确 schema、带校验逻辑、带错误处理机制的功能模块让 Agent 在规划和执行时走“结构化调用”的路径而不是靠模型临场发挥。适合的人群很明确正在做 AI 应用开发、想摆脱纯 prompt 工程局限、需要让智能体完成多步骤真实任务的工程师。1. 项目设计总览从“给模型念经”到“给模型装工具箱”1.1 为什么靠提示词约束行为不够用在进入细节之前我想先聊聊我在这个项目里第一个想明白的事为什么传统 prompt 工程解决不了 Agent 的行为可靠性问题。你可以把大模型想象成一个非常聪明但极其“自信”的实习生。你光在嘴上告诉他“处理退款时一定要先查订单状态”他大概率会点头说好但真到干活的时候各种意想不到的情况还是会出现——他可能先调了退款接口才发现订单不存在也可能因为上一个对话里出现过类似字段就自行套用。这不是模型不聪明而是自然语言的约束本质上是一个“建议”不是一个“强约束”。我在早期项目里试过特别极端的做法把行为规范写得像法律条文一样长系统提示词拉到几十K还用了各种 few-shot 示例。结果呢延迟上去了token 成本翻倍模型偶尔还是会犯低级错误而且提示词越长模型对后面内容的遵循度越低。这个现象不是个别模型的毛病是普遍规律。后来我意识到与其费劲在语言层约束不如在机制层约束——让模型做选择而不是让模型做发明。这就是 agent-skills 这个项目的出发点把模型擅长的“意图理解”和“任务规划”留下来把那些需要精确执行的步骤全部变成结构化的、预定义好的技能调用。1.2 技能模块的四个设计原则agent-skills 在设计技能模块时我给自己定了四个原则这四个原则后来帮我在落地时省了非常多麻烦。第一个原则是单一职责。一个技能只做一件事。比如“查询订单状态”是一个技能“查询订单状态并计算退款金额”就不应该是一个技能。你可能会觉得这样拆分会导致技能数量爆炸但实战下来其实不会因为 Agent 本来就有规划能力它能够把复合任务拆解成多个技能的组合调用。单一职责最大的好处是技能逻辑能做深、做透参数边界清晰而且复用率极高。第二个原则是显式声明。每个技能都必须有明确的描述、参数 schema、返回值 schema、以及调用约束。这个声明不是写给开发者看的注释而是让模型在规划阶段就能读到的“API 文档”。模型看到一份结构清晰、语义明确的技能清单时它能更好地理解每个工具何时该用、何时不该用。第三个原则是防御式实现。技能内部必须有完整的参数校验、状态检查和错误处理。你要把技能当成一个公共接口来设计不信任任何来自模型的参数输入因为模型生成的参数偶尔会让人觉得它在“瞎猜”。第四个原则是可观测性。每个技能调用都要有完整的日志耗时、参数、返回值、错误信息。没有这一步你真到排查问题的时候会欲哭无泪。这三个字看起来是废话但我在很多项目中看到的情况是日志是打了但打的是“谁调用过”而不是“调用时模型给了什么参数、技能因为什么原因拒绝了”这两者差别天壤之别。1.3 技能注册表模型与执行层之间的桥梁有了技能模块还需要一个机制把它们组织起来这就是技能注册表。在 agent-skills 里注册表是一个中央存储包含所有可用技能的元数据。Agent 在每次任务的规划阶段会拿到注册表里所有技能的描述和参数 schema然后依据用户请求选择需要调用的技能。我把这个过程类比成你不可能把所有工具都摆在桌面上但你必须有一份目录知道哪个抽屉里放着什么工具。注册表的实现可以很轻量——一个 Python 字典就能跑起来。但真正需要注意的是元数据的质量。我见过太多人在这里偷懒技能描述写得含糊不清比如“处理订单相关操作”模型根本不知道这个技能具体干什么自然也不会在合适的时机调用。元数据写得好不好直接决定了 Agent 的技能调用准确率这一点我会在后面的章节里展开讲。2. 核心细节拆解一个技能模块应该长什么样2.1 技能模块的数据结构设计所有技能基于一个统一的基类来实现这个基类定义了一个技能最基本的生命周期。我先把代码贴出来再逐步拆解每条设计背后的思路。from typing import Any, Dict, Optional import json import time import traceback from dataclasses import dataclass, field from enum import Enum class SkillStatus(str, Enum): SUCCESS success USER_ERROR user_error # 参数错误、状态不满足等 SYSTEM_ERROR system_error # 内部异常、依赖服务故障 dataclass class SkillResult: status: SkillStatus data: Any None message: str latency_ms: float 0.0 skill_name: str raw_params: Dict[str, Any] field(default_factorydict) def to_dict(self) - Dict[str, Any]: return { status: self.status.value, data: self.data, message: self.message, latency_ms: self.latency_ms, skill_name: self.skill_name, raw_params: self.raw_params, } class BaseSkill: 所有技能模块的基类。 子类需要实现四个类属性 - name: 技能唯一标识用 snake_case - description: 给模型看的技能描述要写清楚何时用、何时不用 - parameters: JSON Schema 格式的参数声明 - required_parameters: 必填参数列表 name: str description: str parameters: Dict[str, Any] {} required_parameters: list [] def __init__(self, registry: Optional[Any] None) - None: self.registry registry # 技能内部维护一个轻量级的调用历史便于排查问题 self._last_call_meta: Dict[str, Any] {} def validate_params(self, raw_params: Dict[str, Any]) - Dict[str, Any]: 校验并规范化参数。基类提供默认实现类型检查和必填检查。 errors [] for field_name in self.required_parameters: if field_name not in raw_params or raw_params[field_name] in (None, ): errors.append(f缺少必填参数: {field_name}) if errors: raise ValueError(; .join(errors)) # 这里可以根据 parameters 的 schema 做更细的类型检查 return raw_params def precheck(self, params: Dict[str, Any]) - Optional[str]: 执行前的状态前置检查。返回 None 表示通过返回字符串表示拒绝原因。 这个回调设计出来就是为了拦截那些“参数合法但业务状态不允许”的调用。 例如查询订单前先检查订单号格式是否符合规范。 return None def execute(self, params: Dict[str, Any]) - SkillResult: 技能核心逻辑。子类必须实现。 raise NotImplementedError def run(self, raw_params: Dict[str, Any]) - Dict[str, Any]: 统一入口。负责参数校验、前置检查、执行、异常捕获和日志记录。 start time.time() self._last_call_meta {raw_params: raw_params} try: validated self.validate_params(raw_params) block_reason self.precheck(validated) if block_reason is not None: result SkillResult( statusSkillStatus.USER_ERROR, messageblock_reason, skill_nameself.name, raw_paramsraw_params, ) self._last_call_meta[result] result.to_dict() return result.to_dict() result self.execute(validated) if not isinstance(result, SkillResult): result SkillResult(statusSkillStatus.SUCCESS, dataresult, skill_nameself.name) except ValueError as e: result SkillResult( statusSkillStatus.USER_ERROR, messagestr(e), skill_nameself.name, raw_paramsraw_params, ) except Exception as e: # 兜底捕获所有异常避免技能内部错误直接击穿 Agent 主流程 result SkillResult( statusSkillStatus.SYSTEM_ERROR, messagef技能内部异常: {e}, skill_nameself.name, raw_paramsraw_params, ) self._last_call_meta[traceback] traceback.format_exc() finally: result.latency_ms round((time.time() - start) * 1000, 2) self._last_call_meta[result] result.to_dict() return result.to_dict()这段代码初看很简单但里面有几个我认为值得单独拎出来说的设计决策。第一个是SkillResult的存在。它统一了所有技能的返回格式Agent 主流程拿到后不需要再做一堆 if-else 判断就能规范化处理。我把状态分成三类SUCCESS、USER_ERROR、SYSTEM_ERROR。这个分类特别重要——USER_ERROR表示技能拒绝了这次调用属于“正常业务拦截”比如参数缺失、订单不存在SYSTEM_ERROR表示技能内部出现了未预期的问题比如数据库连接失败。Agent 的主流程对这两种错误的处理策略完全不同前者通常会把错误信息直接反馈给模型让它调整参数重新尝试后者则应该把控制权交还给人或走降级逻辑。第二个是precheck这个方法。很多人会问参数校验还不够吗为什么要多一个前置检查我的经验是参数校验只能保证类型对、必填项有值但很多业务约束是参数层面看不出来的。举个例子你有一个退款技能参数校验只能检查退款金额是不是数字但“这笔订单是否已经发货”“这个订单是否已经处理过退款”是需要查业务状态才能知道的。把这些检查统一放到precheck里能让主流程在真正执行技能前就把绝大多数的无效调用拦截下来既节省计算资源也避免产生脏数据。第三个是raw_params的透传。这是我在调试过无数次之后才加上的。很多 Agent 框架只会在日志里记录最终的执行结果但排查问题的时候你更需要知道“模型刚才传了什么进来”。你放开了看模型传入原始参数经常会有一些奇怪的内容比如多传了某个无关字段、把字符串数量当成金额之类的只有把这些原始参数完整记录下来你才能定位是模型理解错了还是技能描述写得有歧义。2.2 技能描述怎么写模型才听得懂这是 agent-skills 里最容易被低估的部分。很多人在做技能的时候把精力全花在实现逻辑上描述随手写一句“查询订单”就完事了。结果模型在需要查询订单的时候就是不调这个技能。问题往往就出在描述上。我给自己定了一个“技能描述五要素”模板技能做什么、什么时候用、什么时候不要用、常见参数示例、返回内容说明。拿查询订单技能举例好的描述应该是这样description: 根据订单号或买家ID查询订单的当前状态。当用户询问订单是否发货、物流到哪了、 退款进度时使用此技能。注意此技能只用于查询不执行任何修改操作 如果用户要求取消订单请调用 cancel_order 技能而不是使用本技能。你可能会觉得这描述是不是太啰嗦了我的实测结论是不会。模型在规划阶段是同时读取所有技能的描述的足够明确的正例和反例能让模型更准确地做出选择。尤其是“什么时候不要用”这个部分它对抑制模型的“乱调”行为帮助极大。参数的 JSON Schema 同样重要。我会要求每个参数都有清晰的description并且明确标注格式。比如“订单号”字段不能只写type: string而要写清楚“订单号格式为 17 位数字例如 20250807123456789”。模型看到这个描述之后在生成参数时会更倾向于照格式来而不是自己脑补格式。2.3 技能怎么与 Agent 主流程协作一套技能系统只有独立模块还不够它必须和 Agent 的决策主流程形成闭环。在 agent-skills 里主流程走的是标准的“规划-执行-反馈”循环。规划阶段Agent 拿到用户的请求结合注册表里的技能列表生成一个调用计划执行阶段Agent 按计划依次调用技能反馈阶段技能返回的结果被格式化之后再喂回给模型让模型决定下一步动作。这里有一个我踩了很多坑的细节技能返回结果的格式必须“面向模型友好”。我见过很多项目直接把 Python 字典序列化成 JSON 然后塞给模型结果模型在后续推理时总是搞错字段。原因很简单后端返回的数据结构和业务语言的表达是有差异的比如订单状态的内部编码是1、2、3模型并不知道2代表已发货。所以在 agent-skills 里技能返回给模型之前会经过一层“转译”把内部编码转换成自然语言描述比如把{status: 2}转成{status: 已发货物流公司为顺丰运单号 SF1234567890}。这一步简单但极其有效模型的后续决策准确率明显提升。另外还有一个容易被忽视的点技能的返回要适度。有时候后端接口会返回几十个字段但模型真正决策时只需要其中两三个关键字段。如果把所有字段都塞回去模型很容易被无关信息干扰。我建议在技能内部就把返回字段整理成模型最关心的一组信息宁可少一点不要太多。这个思路和写 API 给前端用的逻辑有点相反值得专门习惯一下。3. 实战复现从定义技能到跑通 Agent 调用闭环3.1 准备一个真实的业务案例为了把这套东西讲明白我拿一个虚拟的电商客服场景来做完整演示。场景包含四个技能查询订单、取消订单、查询商品库存、创建售后工单。这四个技能覆盖了“查询类”和“执行类”两种典型形态足够说明问题。先定义四个技能的注册信息用一个字典集中管理def get_skill_registry(): return { query_order: { module: skills.ecommerce.query_order.QueryOrderSkill, description: 根据订单号或买家ID查询订单当前状态。 当用户询问我的订单到哪了发货了吗退款到哪了时使用。 只做查询不做任何修改。, parameters: { type: object, properties: { order_id: {type: string, description: 17位数字订单号}, buyer_id: {type: string, description: 买家用户ID}, }, required: [], }, }, cancel_order: { module: skills.ecommerce.cancel_order.CancelOrderSkill, description: 取消一个未发货且未进入退款流程的订单。 当用户明确要求取消订单时使用。 如果订单已发货不要使用此技能应引导用户走退货流程。, parameters: { type: object, properties: { order_id: {type: string, description: 17位数字订单号}, reason: {type: string, description: 取消原因}, }, required: [order_id], }, }, query_stock: { module: skills.ecommerce.query_stock.QueryStockSkill, description: 查询商品SKU的实时库存数量。 当用户询问还有货吗能拍吗时使用。 注意库存数据为实时值不缓存。, parameters: { type: object, properties: { sku_id: {type: string, description: 商品SKU编码}, }, required: [sku_id], }, }, create_after_sale: { module: skills.ecommerce.create_after_sale.CreateAfterSaleSkill, description: 为用户创建售后服务工单支持退货、换货、维修三种类型。 当用户要求退货、换货、维修时使用。 创建前需要确认用户已登录且订单在售后保障期内。, parameters: { type: object, properties: { order_id: {type: string, description: 17位数字订单号}, type: {type: string, enum: [return, exchange, repair]}, reason: {type: string, description: 用户填写的售后原因}, }, required: [order_id, type, reason], }, }, }从这段代码可以看到几个我在实操中形成的习惯。第一个是required未必全都填满。拿query_order来说订单号和买家 ID 至少传一个就能查如果强制两个都必填会降低技能对真实场景的适配性。第二个是我把模块路径写成了字符串而不是直接导入类。这样做的目的是延迟加载——技能数量多了之后全量导入会让 Agent 的冷启动变慢按需加载能明显改善启动速度。3.2 技能实现示例查询订单与取消订单注册表里配置的都是元信息真正的业务逻辑在具体的技能类里实现。这里我写两个有代表性的例子。查询订单技能的实现class QueryOrderSkill(BaseSkill): name query_order description 根据订单号或买家ID查询订单当前状态。 parameters { type: object, properties: { order_id: {type: string, description: 17位数字订单号}, buyer_id: {type: string, description: 买家用户ID}, }, required: [], } required_parameters [] def validate_params(self, raw_params): if not raw_params: raise ValueError(至少需要提供 order_id 或 buyer_id 中的一个) if order_id not in raw_params and buyer_id not in raw_params: raise ValueError(至少需要提供 order_id 或 buyer_id 中的一个) if order_id in raw_params and not isinstance(raw_params[order_id], str): raise ValueError(order_id 必须是字符串) return raw_params def precheck(self, params): if order_id in params: order_id params[order_id] if len(order_id) ! 17 or not order_id.isdigit(): return f订单号 {order_id} 不是有效的17位数字订单号请核实后重试 return None def execute(self, params): # 这里在实际项目中会查询数据库或调用订单服务 # 我这里用 mock 数据演示返回格式 order_id params.get(order_id, MOCK20250807001) # 模拟一个查询结果 db_result { order_id: order_id, status_code: 2, status_text: 已发货, logistics_company: 顺丰速运, tracking_no: SF1234567890, items: [{sku_id: SKU1001, name: 无线鼠标, qty: 1}], created_at: 2025-08-01 10:30:00, } # 转译成模型友好的返回内容 return SkillResult( statusSkillStatus.SUCCESS, data{ order_id: db_result[order_id], 订单状态: db_result[status_text], 物流信息: f{db_result[logistics_company]}运单号 {db_result[tracking_no]}, 商品列表: , .join( f{item[name]} x{item[qty]} for item in db_result[items] ), 下单时间: db_result[created_at], }, )取消订单技能的实现重点看precheck逻辑怎么拦截不合规调用class CancelOrderSkill(BaseSkill): name cancel_order description 取消一个未发货且未进入退款流程的订单。 parameters { type: object, properties: { order_id: {type: string, description: 17位数字订单号}, reason: {type: string, description: 取消原因}, }, required: [order_id], } required_parameters [order_id] def precheck(self, params): order_id params[order_id] if len(order_id) ! 17 or not order_id.isdigit(): return f订单号 {order_id} 不是有效的17位数字订单号 # 模拟检查订单状态 status self._mock_get_order_status(order_id) if status in (已发货, 已完成): return f订单 {order_id} 当前状态为 {status}无法取消建议走退货流程 if status 已取消: return f订单 {order_id} 已经处于取消状态请勿重复操作 return None def execute(self, params): order_id params[order_id] # 执行取消逻辑成功后返回结果 return SkillResult( statusSkillStatus.SUCCESS, data{ order_id: order_id, 取消结果: 成功, 当前状态: 已取消, 取消原因: params.get(reason, 用户主动取消), }, )注意precheck里返回的字符串它是会直接被模型读到的。所以我特意用了“无法取消建议走退货流程”这样包含下一步指引的描述。模型读到这个信息后就能合理地调整自己的话术和建议而不是硬套技能规则。我自己在实测中最大的体会是前置检查里的一句话胜过系统提示词里的十句安抚话术因为它是精确的、当场生效的、上下文相关的。3.3 主流程调度让模型学会“想清楚再动手”技能模块就位之后就需要一个调度器来驱动整个循环。我建议在这个阶段不要一上来就接复杂的 Agent 框架先用最朴素的方式跑通逻辑定义好对话历史、技能清单、以及模型调用的函数封装。这里我给出一个基于 OpenAI 函数调用风格的调度器骨架思路是通用的你换成任何支持工具调用的模型都能复刻。class AgentRuntime: def __init__(self, skill_registry): self.skills {} for name, meta in skill_registry.items(): module_path, cls_name meta[module].rsplit(., 1) module importlib.import_module(module_path) skill_cls getattr(module, cls_name) self.skills[name] skill_cls(registryself) self.skill_schemas [] for name, skill in self.skills.items(): self.skill_schemas.append({ type: function, function: { name: skill.name, description: skill.description, parameters: { **skill.parameters, required: skill.required_parameters, }, }, }) def chat(self, user_message, historyNone): history history or [] messages [{role: system, content: 你是一个电商客服助手...}] history [ {role: user, content: user_message} ] # 第一轮让模型决定是否需要调用技能 response self._call_llm(messages, toolsself.skill_schemas) # 如果模型要求调用技能 while response.get(tool_calls): tool_calls response[tool_calls] new_messages [] for call in tool_calls: fn_name call[function][name] fn_args json.loads(call[function][arguments] or {}) # 执行技能并记录结果 result self.skills[fn_name].run(fn_args) new_messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse), }) # 把工具结果拼回对话历史让模型继续推理 messages messages new_messages response self._call_llm(messages, toolsself.skill_schemas) return response[content], messages这个骨架特别适合刚开始搭建 Agent 的团队代码量小依赖少逻辑透明出现问题时你可以快速定位是模型规划错了、参数生成错了、还是技能执行错了。我见过不少团队一上来就接重型 Agent 框架结果问题被框架层层封装排查成本非常高反而不如先用小骨架把业务跑通再按需引入框架能力。3.4 模型参数选择与多技能协作的小实验在跑通基本流程之后我建议做一个小实验验证技能的协作能力。准备一段用户消息“帮我查一下订单 20250807123456789 的物流顺便看看这个订单里的无线鼠标还有没有库存。”这个请求需要模型连续调用两个技能先查订单拿到 SKU 编码再查库存。第一版测试时我的模型把这两个调用合并成了一个query_order调用这是常见的错误类型。后来我在技能描述里加了更明确的引导比如在query_stock的描述中强调“此技能需要 sku_id如果用户只提供了订单号请先调用 query_order 获取订单中的 sku_id再调用本技能”。这个提示看起来是给模型的其实也规范了模型的多步规划行为。另一个值得注意的调参经验是模型温度。技能调用这个场景下我把temperature固定设成 0因为参数生成和技能选型不需要任何创造力温度高了只会带来随机性。有些模型的函数调用模块还支持tool_choice参数你可以显式约束模型“必须调用某个技能”或者“不需要调用技能”在特定场景里比如必须走工具的敏感操作可以直接锁死省掉模型自由裁量的风险。4. 常见问题与排查技巧实录4.1 模型不调用技能或者乱调用技能怎么办这是我在各种 Agent 项目里被问到最多的问题。遇到这个情况不要第一反应就去改提示词按下面这个顺序排查基本能定位。先看日志里的技能描述。很多时候模型不调用技能是因为它根本没理解这个技能是干什么的或者技能描述里出现了和业务术语不一致的表达。比如你做的是电商系统内部叫“工单”模型可能只懂得“售后单”描述里如果写“创建工单”模型就可能在犹豫时错过它。试试在描述里同时保留两个术语。再看参数 schema 是否够清楚。如果模型频繁生成错误参数多半是参数说明写得像摆设。一个常见错误是只写type: string不写格式和示例模型只能猜。加上格式说明和示例之后参数准确率会有立竿见影的提升。最后看返回结果里USER_ERROR的比例。如果技能经常返回拒绝说明模型对使用边界的理解还不够。此时不要急着调描述先看看模型到底传了什么参数进来是不是存在共性的理解偏差然后针对性地在描述里把“反面场景”写清楚。4.2 技能返回报错信息模型还是反复试同一个错误这个现象非常典型技能拒绝了调用返回了明确的错误信息但模型不听劝下一次还是用一模一样的参数重试陷入了死循环。我早期遇到这个问题的时候非常头大以为是模型能力不够后来发现根源在返回信息的表达方式。技能返回的错误内容模型真正能利用的往往是最后一句。如果你把错误日志、堆栈信息、内部状态码全部抛出去给模型看不仅噪音大还可能让模型误导。正确做法是返回信息只写两段内容第一段是“发生了什么”第二段是“建议怎么做”。例如message: 订单号 2025080712345 不是有效的17位数字订单号请核实后重试这里“请核实后重试”就是给模型的明确行动指引。我在所有技能的错误返回里统一加上了这个模式循环重试的问题减少了大概六成。剩下的还是会出现但频率低了很多。如果再遇到顽固循环可以在调度层加一个计数器同一个技能连续失败 N 次就中断任务把控制权交还给人。4.3 技能调用链路变长如何做性能优化当技能数量多到一定程度之后Agent 的响应延迟会明显上升。我实测过一次用户请求如果涉及三个技能调用延迟可能从 2 秒飙到 8 秒以上。这里有两个优化方向值得尝试。第一个是技能注册表的筛选前置。模型不需要在每次规划时都看到全部技能可以先按意图粗分类只让模型看到相关的技能组。比如用户说“我要退货”就只暴露订单类、售后类技能库存类技能完全不展示。这不仅减少 token还能降低模型选错技能的概率。第二个是技能的预加载和连接复用。如果技能内部要访问外部服务务必把 HTTP 连接做成复用数据库连接池也要调好。一个技能内如果串行调了三次外部 API单技能耗时就会翻三番回头看一下日志里的各技能耗时分布往往能发现性能瓶颈就在某个不起眼的技能内部。4.4 线上环境的日志与监控要怎么做最后分享一点线上运维经验。技能执行日志不能只打在一个地方至少要保留两个维度按技能维度统计的调用量、成功率、平均耗时以及按请求维度贯串全链路 trace。前者用来监控技能健康度后者用来排查具体用户问题。我在 agent-skills 的实践中给每个技能都增加了结构化日志输出除了基本信息外还强制带上技能名和调用耗时。最后通过日志系统可以快速生成每天的技能调用报表。当你看到某个技能的成功率低于 95% 时就要赶紧去查是模型参数传错、业务状态拦截太多、还是服务本身在报错。这套观测体系建立之后Agent 的稳定性才算真正有保障不然你永远都是“凭感觉”在优化。回过头来看agent-skills 这套做法最大的价值其实不是它用了多复杂的架构而是把“模型自由发挥”的空间收窄到了合理的范围。模型负责理解意图、拆解任务技能负责精确执行、守住边界两者各司其职整个系统的可靠性才能上来。我在这条路上踩过的坑不少特别是技能描述规范和返回信息格式化这两块是我反复试错后才总结出来的经验强烈建议你现在就要重视起来。如果你也正在做 Agent 应用不妨从最小的一两个技能入手试试这套思路相信你会很快感受到结构化技能比纯提示词约束稳得多。
返回列表