尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI Agent 技能体系设计与实战:从 Prompt 到 agent-skills 的工程化落地

AI Agent 技能体系设计与实战:从 Prompt 到 agent-skills 的工程化落地 前阵子团队内部在做智能体项目复盘翻到我们沉淀了大半年的agent-skills体系觉得这套东西对正在折腾 AI Agent 的朋友应该挺有参考价值。市面上聊 Agent 的文章很多但大多数都在讲框架怎么调、模型怎么选真正把“技能”这件事当成独立工程来打磨的内容不多。我这篇就把我们自己踩过的坑、总结的方法论、以及可以直接照抄的配置结构完整梳理一遍希望能帮你少走几个月的弯路。不管你是刚开始接触 Agent 开发还是已经在生产环境里跑过几个智能体应用这篇文章都值得认真看一遍。我会从技能拆解的思路讲起然后一步步落到实际编码、参数配置和问题排查上全程都基于我们在真实业务里的实践经验不是纸上谈兵。1. 整体设计与思路拆解1.1 为什么需要一套独立的技能体系先说个很现实的问题为什么 Agent 不能只靠一个大模型 Prompt 包打天下我在刚开始做智能体应用的时候也这么想但很快就被现实教育了。当时做了一个文档问答助手Prompt 里写了很长一段“你是一个擅长回答文档问题的助手请根据文档内容作答”看起来没什么问题实际跑起来却频繁出状况——用户问“帮我统计一下这份合同里几个关键日期的差异”模型回答得模棱两可用户让“把上季度的销售额和这季度做个对比”模型干脆答非所问。原因在于大模型本身只是一个推理引擎它缺少“获取外部数据”“操作特定工具”“记住交互状态”这些能力。你把这些能力全部塞进 Prompt一方面会超出上下文窗口的限制另一方面每个能力的调用逻辑和边界条件都挤在一起模型根本不知道该在什么时机触发哪一个。agent-skills的核心思路就是把这些外部能力逐一拆散、模块化每个技能只做一件明确的事技能的描述、入参、出参、触发条件全部结构化。Agent 在运行时先分析用户意图再决定调用哪个技能来完成子任务。这就像把一个全能选手拆成一个团队每个人都有明确的职责分工协作起来自然更高效。1.2 技能拆分的三个层次在设计技能体系的时候我参考了很多开源项目的做法最后总结出三个层次第一层是原子技能它对应一个具体的外部操作不依赖其他技能。比如“执行一段 Python 代码”“调用搜索 API 获取结果”“从 PDF 中抽取指定字段”。这一层是技能体系的地基要保证每个原子技能的输入输出都是清晰定义的。第二层是复合技能它由多个原子技能组合而成解决一个相对完整的业务问题。比如“分析一份财报”这个复合技能内部可能包含“读取 PDF”“抽取财务指标”“对比历史数据”“生成分析结论”四个原子技能的串联调用。第三层是决策技能它负责判断在什么场景下使用哪些技能、以什么顺序调用。这一层离大模型最近本质上是一个加强版的 ReAct 循环但比单纯依赖模型自由发挥要可控得多。1.3 技能与任务解耦的价值上面这套设计解决了我们之前最头疼的一个问题智能体的扩展性。以前每接到一个新需求就要重新写一段完整的 Prompt然后把新的工具调用逻辑揉进去。模型稍微复杂一点的场景Prompt 就膨胀到几千字运行效果还很不稳定。拆成agent-skills之后新增一个能力变成了“注册一个新技能”的问题不再需要改动核心推理逻辑。新技能一旦注册好Agent 在遇到匹配的用户请求时就能自动发现并调用它。这个模式在团队协作中的优势尤其明显——不同人可以并行开发不同技能互不阻塞最后像拼积木一样组装起来。2. 核心细节解析与实操要点2.1 技能的描述规范——决定 Agent 能否正确调用这是整个agent-skills体系里最容易忽略却又最关键的一环。很多人在定义技能的时候只写技能名和函数体觉得能跑通就行。但 Agent 决定何时调用哪个技能靠的是你对技能的文字描述。我们的技能描述有固定的模板技能用途说明 适用场景 不适用场景 输入要求 输出格式。比如“调用搜索 API 获取结果”这个技能我们不只是写“搜索”而是写成技能名web_search 用途当用户需要最新信息、实时数据或超出训练数据截止时间的内容时调用外部搜索接口获取网页结果。 适用场景查天气预报、最新新闻、实时股价、产品价格对比等。 不适用场景用户仅询问常识性问题或模型本身已掌握的内容。 输入要求关键字必填搜索数量可选默认5条 输出格式JSON数组每条包含标题、链接、摘要。上面这段描述看着啰嗦但实际效果非常好。模型在推理时会拿这段描述跟用户的真实意图做匹配信息越清晰匹配越准确。反之如果只有一个“搜索”两个字模型经常会误调用或者该调用的时候不调用。2.2 输入的参数约束与校验技能参数的定义直接决定调用成功率。早期我们用的是简单字符串拼接结果经常出现参数缺漏、格式错乱尤其当用户输入比较复杂时模型提取出的参数质量很不稳定。后来我们换成了 JSON Schema 定义每个技能的参数。以日期查询技能为例{ name: query_date_diff, description: 计算两个日期之间的间隔天数, parameters: { type: object, properties: { start_date: { type: string, format: date, description: 起始日期格式YYYY-MM-DD }, end_date: { type: string, format: date, description: 结束日期格式YYYY-MM-DD } }, required: [start_date, end_date] } }用这种方式定义之后模型会严格按照 Schema 的约束来生成参数大大减少了幻觉参数和格式错误。我们在代码里还加了一层校验如果模型返回的参数格式不符合 Schema会自动触发一次修复循环把错误信息反馈给模型让它重新生成参数。2.3 精确优先级减少无效技能带来的干扰技能库维护到一定规模后我遇到了一个新的性能问题Agent 在决策时需要在几十个技能之间做选择一次推理消耗的 token 大幅增加有时候还会选错。后来我们参考了一些成熟框架的实践引入了一个“相似技能优先级”机制。当多个技能的描述看起来都能解决当前问题时系统会根据技能的历史调用成功率和使用频率给出一个优先级排序Agent 默认优先尝试排序靠前的技能。实测下来这个机制让技能选择的准确率提升了差不多30%。3. 实操过程与核心环节实现3.1 第一个技能从零到一以“PDF关键字段抽取”为例我不打算讲太多抽象理论拿我们项目里实际写过的“PDF关键字段抽取”技能作为例子完整走一遍从设计到落地的过程。先明确需求用户上传一份 PDF 合同需要抽取合同编号、签约日期、合作方名称、合同金额这四个字段。第一步定义技能描述和参数 Schema把 PDF 路径作为输入抽取结果以 JSON 返回。第二步实现核心逻辑我们选了pdfplumber这个 Python 库来做文本提取然后用正则表达式配合规则引擎抽取字段。第三步注册到 Agent 的技能列表里并通过测试用例验证决策能力。代码大致长这样import pdfplumber import json import re def extract_contract_fields(pdf_path: str) - dict: result {} with pdfplumber.open(pdf_path) as pdf: text \n.join(page.extract_text() for page in pdf.pages) result[contract_no] re.search(r合同编号[:\s]*([A-Z0-9\-]), text).group(1) result[sign_date] re.search(r签约日期[:\s]*(\d{4}-\d{2}-\d{2}), text).group(1) result[company_name] re.search(r合作方[:\s]*([\u4e00-\u9fa5]?[\u4e00-\u9fa5]*)?, text).group(1) match re.search(r合同金额[:\s]*([\d,]\.?\d*), text) result[amount] float(match.group(1).replace(,, )) return json.dumps(result, ensure_asciiFalse)这段代码在生产环境里跑下来效果还不错但有几个细节值得加注释PDF 扫描件必须提前接 OCR否则extract_text()拿到的是空字符串正则规则要跟着真实样本持续迭代因为不同来源的合同模板格式差异很大。3.2 注册技能到 Agent以 ReAct 模式为例技能函数实现好之后下一步就是跟 Agent 的决策循环接起来。我们用的是一种类似 ReAct 的框架每一轮推理包括思考、行动、观察三个步骤。技能注册就是告诉 Agent 在“行动”阶段有哪些可选项。注册的核心是把技能的元信息拼装进系统提示词里。我们做了一个技能注册表启动时自动加载所有技能并格式化成模型可读的文本Available Skills: 1. extract_contract_fields(pdf_path: str) - Extracts key contract fields from a PDF document. Use when user uploads a contract file and asks about contract number, date, company name, or amount. 2. web_search(query: str) - Performs a web search...这里有一个实操细节每个技能的描述必须用自己的话重新写一遍不能直接把函数注释搬过去。模型理解自然语言描述的效果远好于理解代码注释。我们曾经试过直接拼接 docstring调用准确率明显下降。3.3 技能编排复合技能的脚本化当一个任务需要多个技能协作时我们用一个编排脚本来控制流程。以“对合同进行风险评估”为例流程是先调用extract_contract_fields抽取关键字段再调用web_search查一下合作方的公开信用信息最后把这些信息汇总给大模型生成评估文本。实现时我们定义了一个简单的 DAG 执行引擎每个技能声明依赖关系引擎按照拓扑顺序依次执行。中间任何一步失败会触发重试或走降级分支。这个编排层规模不大但对稳定性的提升是实打实的。3.4 评估与迭代先做正确性再谈效率技能开发完成后一定要做系统性的评估不能只看一两个 demo 跑通就算完。我们的评估分为两个维度一是成功调用率就是 Agent 在应该调用技能时是否正确调用了参数是否解析正确二是任务完成质量就是拿到技能结果之后大模型生成的最终回答是否正确。我建议每个技能都维护一组覆盖典型场景和边界情况的测试用例。注意测试用例必须包含一些“不该触发技能”的情况因为模型有时候会过度调用。比如用户直接说“我想知道这家公司靠谱不靠谱”没有附上合同文件这时就不应该触发extract_contract_fields而应该触发web_search或直接回答。4. 常见问题与排查技巧实录4.1 模型想用技能但拿不到结果我们最早遇到的高频问题之一是Agent 已经决定调用某个技能但执行结果为空或者超时。排查之后发现大部分是因为技能函数内部没有做异常兜底。以 PDF 抽取为例如果用户上传的文件路径是错误的我们的正则匹配就会抛异常结果整个 Agent 流程中断。后来在技能函数的最外层加了一个统一异常捕获把错误转成一段模型能读懂的说明文字比如 “PDF文件无法读取可能原因是文件格式不正确或缺少文字层”模型就能根据这个提示跟用户做进一步确认。4.2 模型调用技能时参数幻觉模型生成参数时偶尔会编造出一些根本不存在的字段值尤其在输入信息不完整时。我们解决这个问题主要是靠提示词约束加解析后校验两层兜底。提示词里会明确要求“如果用户没有提供必需的参数值请先向用户询问澄清不要自行猜测。”解析后校验是代码层面的兜底一旦参数校验不通过会触发一次自动追问流程。两步叠加之后参数幻觉问题从原来的每日高频降到几乎不再出现。4.3 Token 过多导致决策缓慢技能太多、描述太长会直接推高单轮推理的 token 消耗。我们在测试中量过当技能数量增加到 30 个以上时一个普通请求的 prompt 长度会增加接近4000 token响应速度明显变慢。应对方案是做技能分组。按领域把技能分到不同的组里比如文档处理组、网络检索组、数据计算组Agent 在分析用户请求时先定位到分组再加载组内的技能列表。某些强相关的组可以做到始终加载冷门的组按需加载。这让每轮推理的 token 消耗稳定下降了近40%。4.4 实测效果与经验沉淀我从我们生产环境的日志里随机摘了几个数字供你参考。接入agent-skills体系之后技能调用的整体成功率从最初的67%提升到了91%最终回答的用户满意度提升了大概18个百分点。当然这里的数字跟业务场景、模型底座密切相关但确实能说明一套结构化的技能体系带来的收益是肉眼可见的。还有个经验想特别强调技能描述的语言要尽量具体避免抽象词汇。比如描述一个搜索技能说“查询互联网最新信息”和“当用户需要获取超出你知识截止日期的最新事实时使用搜索引擎查找”的效果完全不同。后者像是在给一个谨慎的同事写交接文档前者就像在讲概念。5. 写在最后的实际建议经历过从“一个巨型 Prompt 包所有功能”到“一套agent-skills体系管理所有外部能力”的转变我现在对 Agent 开发最深的感受是Agent 的体验上限往往不在模型本身的推理能力而在技能层的工程化程度。这句话我想反复强调——大模型这些年的进步确实快但模型再强如果工具调用这一层做得粗糙、边界不清晰、描述含混最终效果一定是不稳定的。如果你刚开始搭自己的一套技能体系我建议不要一上来就做几十个技能而是先拿一个完整业务场景跑通闭环比如“读取文档、抽取信息、检索参考、生成报告”这个组合。跑通之后你再回头审视会发现很多通用能力可以抽出来复用这时候再逐步扩展技能库就能建立一个良性循环。另外多嘴一句技能的描述真的值得反复打磨。我自己的习惯是每写完一个技能描述会假想几个真实用户的提问然后看模型会不会在这个描述下正确触发。描述写不好的技能就算背后的函数写得再漂亮也会沦为一个没人调用的抽屉。记住对智能体来说技能描述就是它的使用说明书得多花时间。
返回列表