
1. 从一段被卡死的Agent交互说起上个月我搭的Agent在处理一份PDF合同摘要时突然“罢工”了。不是模型没能力而是我给它塞了一大堆函数式工具它先调了提取文本的函数又调了OCR的函数再去调归纳总结的函数……一连串步骤里只要中间某次返回的JSON字段格式和预期不符整个链路当场崩掉。后来我把这些散装函数按“技能”维度重新组织让Agent能自动选择、自动编排、自动容错问题迎刃而解。今天这篇我就围绕agent-skills这个主题把什么是Agent Skills、怎么设计、怎么实现、怎么避坑彻底讲透。如果你正在开发AI Agent、做RAG应用或者单纯被“函数太多、模型不会选”折磨过这篇文章应该能给你一套可落地的方案。我尽量用大白话解释但该上的代码和配置一个不少。1.1 为什么常规工具调用撑不住复杂场景先简单说下背景。传统Agent工具Tool通常是“一个函数对应一个功能”模型根据用户意图从函数列表里挑选匹配项填好参数后调用。对于“查询天气”“计算运费”这种单步任务这个模式完全够用。但现实中的需求往往是复合的用户说“帮我把这份财报里的重点提取出来然后生成一张图表”这至少包含文件解析、内容结构化、图表绘制三个环节单靠一个函数做不完。我见过很多团队的第一反应是——把能想到的函数全部塞给模型指望它自己串联。结果模型经常挑错函数或者在一个函数失败后不知道该换哪个。原因很简单模型对每个工具的理解只停留在函数名和参数列表上它并不清楚这个函数适合在什么场景用、和其他工具有什么依赖关系、失败后怎么办。这种“静态工具清单”模式复杂度一上去就会失控。Agent Skills其实是对这种问题的一种系统性解法。2. Agent Skills的核心设计思路你可以把Skills理解为“一组围绕某个能力域的、自包含的指令 工具 行为模式的集合”。它不是一个单点函数而是一个小型的“专业模块”。比如“PDF处理Skill”可以包含文本提取、OCR识别、表格解析、版式分析等多个工具同时内置了“先提取文本若文本层为空再启用OCR”这样的策略而“数据可视化Skill”则包含数据校验、图表类型选择、配色规范等逻辑。2.1 从功能导向到能力域导向传统工具设计是“功能导向”我需要一个函数A就写一个函数A需要函数B再写函数B。Skill设计的核心转变是“能力域导向”先想清楚Agent在某个场景下应该具备什么完整的做事流程再把支撑这个流程的工具、知识、策略打包在一起。拿“行情分析Skill”举例。功能导向的做法是拆成getPrice、getVolume、calcMA、plotKline等七八个函数让模型现场挑。能力域导向的做法是打包成一个skill“你是一个行情分析师当用户提到行情走势时你会经历以下步骤——拉取价格数据、计算技术指标、识别关键形态、输出结论。所有中间工具都由Skill内部管理模型只需要和Skill的入口交互。”这个转变带来的直接好处有四个。第一模型的选择负担大幅下降它只需要判断“当前属于哪类任务”而不是“该调哪个函数”第二Skill内部可以按最优实践固定流程避免模型自由发挥导致链路混乱第三复用性更好“PDF解析Skill”可以在报表项目、合规项目、文档问答里反复使用第四调试和维护更集中某个Skill出问题只需要改这一个模块。2.2 Skill与普通工具的分工边界很多人容易把Skill和Tool搞混我画个不太严格但实用的区分线Tool工具最小的可执行单元通常是一个函数/接口完成一件具体的事情比如“读取指定URL的HTML内容”。Skill技能面向任务的编排单元内部可以调用多个Tool也可以包含规则、提示词、上下文示例以及错误处理策略比如“调研某个主题并输出结构化报告”。在实际落地中我倾向于遵循一个判断原则如果某个环节需要两步以上、且步骤之间存在稳定的先后关系就应该考虑封装成Skill。但也不是越厚越好——如果Skill内聚了大量几乎不会同时触发的功能模型反而不知道该何时调用它。有个折中的分层模式很管用底层保持若干“原子工具”原子性、可复用上层用Skill做“组合编排”。这样既保留灵活性又能让模型在宏观层面做决策。比如底层有个read_url工具上层有个“竞争情报分析Skill”Skill内部自己决定先抓谁、再读谁、怎么交叉验证。2.3 自动与受控之间的平衡设计Skill时一个反复出现的问题是到底让Skill内部全自动跑完还是每一步都停下让用户确认我的建议是分级授权。只读类操作查数据、读文件、分析文本默认自动执行不做中间确认。有副作用但可逆的操作生成临时文件、发送草稿自动执行但保留撤销记录。有不可逆后果的操作删除数据、对外发送、提交订单必须显式确认。这套分层在Skill内部通过一份“执行策略”配置来控制。我见过不少项目把所有操作都塞进“自动模式”导致Agent误删了生产环境的数据这种教训一次就够疼的。3. Skill的组成结构与实现方式明白了设计理念下面看实现。一个标准的Agent Skill我习惯拆成四层任务说明书、上下文与示例、工具集合、执行策略。四层缺一不可。3.1 任务说明书让模型知道Skill“是干嘛的”任务说明书是给模型看的“岗位职责”不是给人看的项目文档。它要讲清楚这个Skill解决什么问题一句话精准描述它不解决什么问题负向边界避免模型误用使用它的典型场景和典型触发词入口参数是什么出口结果长什么样内部工作流的标准顺序。我写说明书时常犯的一个错误是——太抽象。给模型看的说明书一定要具体到“什么情况下做什么事”。举个对比差“本技能用于PDF文档处理。”好“当用户提供PDF文件并要求提取、汇总或翻译其中内容时启用本技能。注意本技能不处理PDF版式编辑如需修改PDF版式请使用文档编辑工具。”优秀的说明书写得像给一位能干但没有常识的新同事看的“交接文档”事无巨细但逻辑清晰。3.2 上下文与示例让模型“对齐行为”光有说明书还不够模型对同一句描述的理解和你的意图可能有偏差。所以必须在Skill里内置少量高质量的few-shot示例示例样本。这些示例的价值比多写三页规则大得多。比如我在一个“自动化测试用例生成Skill”里加入了三个示例输入一个登录接口的缺陷描述示例给出如何派生出边界值、异常流测试用例输入一个支付回调的时序日志示例展示如何识别重复通知场景输入一个用户权限变更的需求文档示例演示如何梳理角色-权限矩阵。模型看到这些后输出风格和思考路径都会向示例靠拢比单纯给它“认真设计用例”的指令靠谱得多。3.3 工具集合底层能力的最小集工具集合是Skill真正“干活”的部分。设计原则是能少一个工具就少一个工具每个工具必须承担不可替代的职责。我踩过一个坑在“数据分析Skill”里同时放了pandas执行工具、SQL查询工具、Excel读取工具结果模型经常拿pandas工具去读Excel或者拿着SQL结果又跑一遍pandas产生大量无意义调用。后来我按数据类型重新划分结构化查询走SQL非结构化表格走Excel解析只有需要统计建模时才用pandas。工具的边界清晰了模型的正确率明显提升。还有一个容易被忽视的细节工具命名。别用“process_data”“handle_file”这种万能名要像API文档一样命名最好能体现动作和对象比如“extract_tables_from_pdf”“summarize_long_text_by_sections”。模型从名字上就能建立初步预期这在函数选择阶段能显著降低误调度率。3.4 执行策略定义“怎么做对”执行策略我把它看作Skill的“大脑”它不直接执行业务动作而是控制执行流程。常见策略包括顺序策略步骤必须按定义好的顺序执行比如“先采集再清洗后建模”。条件策略根据中间结果决定下一步比如“如果文本提取置信度低于阈值则启用OCR”。重试策略某个工具失败后重试几次、间隔多久、换不换备用工具。降级策略主链路失败后是否自动降级到简化模式例如“如果无法联网抓取则使用内置缓存数据”。策略的配置最好和业务代码解耦用声明式配置如JSON来表达。这样调整流程不需要改动代码、重新发布改个配置就能生效对线上调试非常友好。4. 实操从零实现一个“文档问答Skill”纸上谈兵没用我直接带你走一遍完整实现。这个案例我选的是“文档问答Skill”目标是让Agent能读取多种格式的文档PDF、Word、Markdown回答关于文档内容的问题。它足够典型也足够有实用价值。4.1 环境选型与依赖我在这套实现里用的是Python 3.11 LangChain原因是生态成熟、调试直观。如果你用的是其他Agent框架核心思路完全一致只是API风格有差异。我先把依赖列一下pip install langchain langchain-openai pypdf python-docx tiktoken chromadb这里有个关键选型向量库我选了Chroma因为它是嵌入式本地库部署简单适合单机场景。如果项目后续要支撑多用户高并发再迁移到Milvus或pgvector也不难——把向量抽象层封装一下即可。4.2 Skill的编排定义我用JSON来声明这个Skill的元信息和执行策略{ skill_name: document_qa, description: 当用户提供本地文档或链接并要求根据内容回答问题时使用。支持PDF、Word、Markdown格式。, negative_hint: 不处理纯图片OCR识别需求请调用image_ocr_skill。, workflow: [ {step: load_document, tool: document_loader, retry: 2}, {step: split_chunks, tool: text_splitter, params: {chunk_size: 800, overlap: 100}}, {step: build_index, tool: vector_store, mode: recreate_if_not_exist}, {step: retrieve_and_answer, tool: qa_chain, top_k: 4} ], fallback: { load_fail: 提示用户重新上传并建议使用PDF文本层完整的文件。, no_answer: 回答未在文档中找到相关信息并列出最相近的三个段落供参考。 } }这份配置直接体现了前面说的四层结构description和negative_hint是任务说明书workflow是执行策略tools引用了具体工具集合而具体示例我放在单独的示例文件里。4.3 核心代码实现下面看代码。先实现文档加载工具from langchain.document_loaders import PyPDFLoader, TextLoader from langchain_community.document_loaders import Docx2txtLoader class DocumentLoader: def load(self, path: str): ext path.rsplit(., 1)[-1].lower() loader_map { pdf: PyPDFLoader, txt: TextLoader, md: TextLoader, docx: Docx2txtLoader } loader_cls loader_map.get(ext) if not loader_cls: raise ValueError(fUnsupported file type: {ext}) loader loader_cls(path) return loader.load()这个模块我写得很薄因为真正的难点不在加载而在加载之后。你实际用的时候会碰到各种“非标准文件”——扫描版PDF、加密PDF、损坏的docx。加载器本身解决不了这些问题所以我在Skill里预设了异常钩子如果PDF加载出来是空白页自动触发OCR备用流程。再来是切分和索引from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings def _build_index(docs, persist_dir./chroma_db): splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap100, separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_documents(docs) vector_store Chroma.from_documents( documentschunks, embeddingOpenAIEmbeddings(), persist_directorypersist_dir ) vector_store.persist() return vector_store切分参数这里有个值得展开的细节。chunk_size800用来适配主流嵌入模型的输入限制同时也避免单个片段上下文过长导致检索噪音。chunk_overlap100是强制加的否则跨边界的关键信息会被截开比如一个完整句子被切到两半检索就找不到全貌。separators的顺序也很重要——中文文档里“。”和“”的断句效果比单纯按字符切要好得多。4.4 回答链路与记忆QA链路的实现直接决定最终回答质量。我不能只用简单的“检索到文本就扔给大模型”得做重排和溯源def qa_chain(vector_store, question, k4): retriever vector_store.as_retriever(search_kwargs{k: k}) docs retriever.get_relevant_documents(question) # 简单重排和问题重叠度最高的片段排前面 def overlap_score(d): q_tokens set(question.lower().split()) d_tokens set(d.page_content.lower().split()) return len(q_tokens d_tokens) / max(len(q_tokens), 1) docs.sort(keyoverlap_score, reverseTrue) context_text \n\n.join([f[片段{idx}] {d.page_content} for idx, d in enumerate(docs)]) prompt f请仅依据以下文档片段回答问题。若片段中没有相关信息请明确回答未找到相关信息。 {context_text} 问题{question} 回答 return call_llm(prompt), docs重排这步很多人会跳过但我实测下来简单overlap重排就能把回答准确率提升5到10个百分点。原因很直观向量召回的前k个片段未必是“最关键”的重排把和问题字面重叠最高的片段拉到最前面大模型在生成时会以它为重心。溯源也很重要。我让返回结果带上docs列表前端展示是“回答 引用片段”让用户能核实内容这对企业场景几乎是刚需没有溯源的回答在企业里没人敢信。4.5 测试用例与验收标准Skill写完了怎么才算“能用”我建议定一套最小验收集给一份10页的标准PDF问三个事实型问题要求全部准确给一份手写扫描件PDF无文本层验证降级OCR流程能否兜住问一个文档里完全不存在的主题验证“未找到相关信息”的兜底回答是否礼貌且不瞎编给一个Word文档验证加载器和PDF是否共用同一链路。这套测试集不需要很重但每改一次Skill内部的提示词、切分参数或重排策略都跑一遍。我见过太多人改完一个参数自我感觉良好结果把另一个场景调挂了。回归测试在这个场景下不是仪式感是真真切切省时间。5. Agent Skills的进阶玩法与场景矩阵基础版做通之后Skill的价值才刚刚开始显现。我把进阶玩法分为三个层次从低到高分别是多Skill协同、Skill自省与自优化、Skill共享生态。5.1 多Skill协同让Agent学会“找谁能干”单一Skill能解决单一任务域但真实用户需求往往横跨多个域。比如“用户上传一份市场调研问卷的Excel表格要求写成一份带趋势分析的汇报文档”——这至少涉及表格解析、统计分析、报告生成三个Skill。多Skill协同的关键是顶层做一层“Skill路由器”。你可以用一个轻量模型如带简单分类头的模型或直接让大模型做意图分类判断当前请求属于哪个或哪几个Skill域然后再把控制权交给对应Skill。这里有个常见的错误让每个Skill都尝试处理所有输入相互覆盖结果模型不知道听谁的。路由器的作用就是明确“这个任务归谁管”。我在代码里维护一个技能注册表SKILL_REGISTRY { document_qa: DocumentQASkill, data_analyst: DataAnalystSkill, report_writer: ReportWriterSkill, chart_builder: ChartBuilderSkill, }路由逻辑很简单先让模型输出一个JSON指定“意图列表”和“执行顺序”然后按图执行。你可能会问这不还是让模型做决策有什么区别区别在于——模型现在做的是“宏观任务编排”而不是“微观函数选择”前者比后者简单得多稳定得多。5.2 Skill自省加一层“结果检查员”「Skill自省」这个词听着高级其实就是在Skill里加了一个“检查员”角色每次执行完不用原Skill自己评判而是用一个独立的提示词模板或另一个模型实例来检查结果质量发现问题就自动触发重做或修正。比如文档问答Skill中检查员的任务是回答是否完全基于引用片段防止幻觉引用片段是否包含足够的信息支撑回答防止证据不足是否有更合适的片段被漏掉了防止召回不足如果检查不通过Skill会重新走“检索—生成”链路最多重试一次。我踩过的最大的坑是让Skill“自己监督自己”——同一个模型既生成又检查等于没有检查。独立检查角色虽然多一次模型调用但质量提升非常可观值得。另外如果模型自查发现“文档本身信息不足”不要硬答直接走fallback。现实中很多Agent的问题不是不会说而是“硬说”。把“承认不知道”作为一个有效的出口会让系统表现得诚实且值得信赖。5.3 Skill生态像“插件市场”一样复用Skill的终极形态我认为是“可打包流通的”。针对具体行业场景比如“投行尽职调查资料整理Skill”“电商客服售后处理Skill”“医疗文献循证综述Skill”——这些都是高度可复制的模块。如果你的团队有多个Agent项目我强烈建议从第一天就建立一个内部“Skill仓库”。每个Skill有版本号、有维护者、有验收测试记录。这样新项目不用从零造轮子直接把已有Skill拉进来适配即可。长期看这个仓库会变成团队最大的技术资产之一。6. 踩坑记录与排查实录再好的设计落地都可能翻车。我把自己踩过和帮别人排查过的常见问题整理成一份速查表按“症状—原因—解法”的格式给出。症状常见原因排查方法与解决Skill调用率极低模型宁可瞎猜也不用Skill任务说明书过于抽象模型没意识到这是自己的活重写description加入触发词、典型场景和负向边界补充few-shot示例模型反复用错工具Skill内部状态混乱工具集合边界不清、工具命名含糊重新梳理工具职责确保每个工具和其余工具无重叠工具名按“动词对象”规范命名回答内容偏题、幻觉严重检索上下文太杂、没有重排、检查员缺失加粗召回片段、增加overlap重排、引入独立结果检查员一个文档在某个环境正常、另一个环境报错依赖缺失或文件编码差异给Skill配独立依赖清单加载路径统一用相对路径在测试环境跑最小回归集输出格式不稳定时好时坏未在Skill里定义输出Schema强制JSON Schema输出并在提示词末尾给一次格式示例重试不见效同一错误反复出现重试的根因没解决盲目重试区分“瞬时错误”网络抖动和“逻辑错误”参数传错后者不重试直接改写流程6.1 一个真实问题的完整排查过程有个朋友上个月问我“我的Agent接了网页摘要Skill之后总是返回空内容但单独跑工具是好的。”我让他把工作流日志发我看了一眼发现是网页正文提取那一步用了通用正则很多网站的内容是动态渲染的纯正则拿不到。解法不是换正则而是给Skill加了一个“渲染等待 无头浏览器”的降级链路先试静态解析如果正文为空再上Playwright一种自动化浏览器工具渲染后提取。同时设置5秒超时避免坏页面拖死整个流程。这个案例说明很多Skill故障不是代码质量差而是“默认假设”太强——假设页面都是静态的、PDF都是带文本层的、表格都是规范格式的。把“降级链路”作为Skill的一等公民设计进去才能应对真实世界的脏数据。说到爬虫级的HTML解析我补一句在选择解析库时优先考虑如BeautifulSoup4配合lxml解析器如果要做渲染级的页面提取再上Playwright。不少团队上来就无脑上Playwright性能损耗大多数场景其实是杀鸡用牛刀。6.2 监控与可观测性Skill体系的“仪表盘”Skill越用越多可观测性问题就越突出。我的建议是最少记录四类日志路由日志哪个请求被路由到哪个Skill置信度多少内部步骤日志Skill内部每个workflow步骤的输入输出摘要、耗时、token消耗降级/失败日志什么原因触发降级、重试了几次、最终是否成功用户反馈日志用户对最终结果点了“有帮助”还是“无帮助”。有了这些日志你能快速定位“90%的请求都路由到了同一个Skill”这种偏斜问题也能算出每个Skill的真实成本。没有监控Skill体系就像蒙眼开车出问题只能靠碰运气。7. 我个人踩过几次坑之后的设计原则最后分享几条我用真金白银换来的设计原则。第一Skill的粒度宁粗勿细。一个能完整交付一个任务域的Skill比五个只有半步功能的Skill更可靠。模型不需要在微观层面做太多决策宏观上判断“这是什么类型的任务”就够了。如果你发现某个Skill拆开后各自都撑不起完整任务那就反着来——把它们合并。第二提示词与代码同样重要但要分层管理。Skill里的提示词、说明书和工具实现一定要分开存放独立版本。我见过最糟糕的情况是提示词硬编码在业务代码里想调一句话得动代码、发版。拆开后调试效率能提升一个数量级。第三别用“万能Prompt”代替工具。你可以在提示词里要求模型“仔细分析文档”但如果文档格式复杂、内容冗长纯Prompt解决不了根本问题。把复杂任务拆成“工具可以做、规则可以约束、模型才能专注思考”的组合系统才不会脆弱。第四永远留一条人工兜底。自动化程度再高的Skill也要给操作者一个“发回人工”的出口。很多时候业务方比模型更懂上下文。Skill能处理90%的常规请求那10%的复杂疑难交给人工处理不丢人反而给Agent的误操作加了一道保险。这些原则不一定适用于所有项目但对我来说它们把Agent架构从“能跑”推进到了“稳定、可维护、能交付”。希望这篇agent-skills拆解能帮你少走几段弯路。