1. 从“会聊天”到“会干活”:为什么你的大模型总在空转?
每次看到“AI智能体”、“AI Agent”这些词,我都有点哭笑不得。从业内视角看,太多人把大模型当成了一个更聪明的聊天机器人,以为给它一个任务,它就能像科幻电影里的AI管家一样,自动把事情办得漂漂亮亮。但现实往往是,你让它“帮我分析一下这个季度的销售数据”,它可能给你生成一段分析报告的文字,却不会真的去数据库里拉取数据、计算环比增长率、生成可视化图表。你让它“监控服务器日志,发现异常就告警”,它可能只会复述一遍告警逻辑,而不会去执行一个定时任务,调用API,发送告警信息。
问题的核心在于,我们混淆了“理解意图”和“执行任务”这两个完全不同的能力层级。当前的大语言模型(LLM),在“理解意图”上已经非常出色,它能听懂你的自然语言指令,甚至能拆解出复杂的步骤。但它的“执行能力”几乎为零——它没有手,没有脚,无法操作软件,无法调用外部API,无法读写数据库。它被困在文本的牢笼里,空有一身“武艺”,却无法施展。
这就是“Skills”(技能)概念出现的背景。Skills不是魔法,而是一种工程化的思想:将人类在特定领域的操作经验,封装成一个个可被大模型理解和调用的标准化“工具”或“函数”。你可以把它想象成给大模型打造一个“工具箱”。大模型负责理解你的需求,并从工具箱里挑选合适的工具(Skill),然后按照工具的使用说明书(函数定义)去“使用”它。这个“使用”的过程,实际上是由背后的代码逻辑来完成的。
所以,当我说“让大模型真正‘会干活’”时,我指的是构建一个系统:大模型作为“大脑”,负责决策和规划;而一个个封装好的Skills作为“四肢”,负责具体执行。没有Skills,大模型就是一个光说不练的“战略家”;有了Skills,它才能成为一个能落地、能交付结果的“实干家”。
2. Skills的本质:不是代码,是经验的“乐高积木”
很多人一听到“封装”,第一反应就是写代码、定义API。这没错,但只对了一半。Skills封装的核心,远不止技术实现,更在于对领域经验的抽象和标准化。
2.1 一个Skill的完整构成
一个设计良好的Skill,应该包含以下四个层次:
意图描述(自然语言):用人类能理解的话告诉大模型“这个技能是干什么的”。例如:“这是一个用于查询城市天气的技能。” 这部分直接决定了LLM能否在合适的场景下想起并调用这个技能。
输入/输出规范(结构化接口):明确告诉大模型,使用这个技能需要提供哪些参数,以及会返回什么格式的结果。这就像函数的签名。例如:
- 输入:
{“city_name”: “string”} - 输出:
{“weather”: “string”, “temperature”: “number”, “humidity”: “number”}
- 输入:
执行逻辑(代码/配置):技能背后真正的执行体。它可能是一段Python函数,一个HTTP API调用,一个数据库查询语句,甚至是一个自动化脚本的触发。这部分对LLM是“黑盒”,LLM不关心内部如何实现,只关心调用它并得到结果。
安全与错误处理边界:定义这个技能在什么情况下不能使用,以及执行失败时该如何反馈。例如,天气查询技能需要检查城市名是否有效;数据库操作技能需要严格的权限校验。这部分是保障系统稳定性的关键,必须在设计时就考虑进去。
2.2 与普通API/微服务的区别
你可能会问,这和我直接调用一个天气API有什么区别?区别在于“认知层”。
- 普通API调用:需要开发者明确知道在代码的哪一行、什么条件下、以什么参数去调用哪个API。这是“硬编码”的,逻辑是固定的。
- Skill调用:开发者只需要告诉LLM“有什么工具可用”,以及“工具的说明书”。LLM根据与用户的动态对话,自主判断“此时此刻是否需要使用工具”以及“使用哪个工具、传入什么参数”。这是“动态规划”的,逻辑是灵活的。
举个例子,用户说:“我明天要去北京出差,不知道要不要带伞。” 一个集成了天气Skill的智能体,其内部思考链可能是:
- 理解用户意图:查询北京明天的天气,重点是降水情况。
- 检索可用Skills:发现有一个“查询城市天气预报”的Skill。
- 规划执行:调用该Skill,参数
city_name设为“北京”。 - 处理结果:收到
{“weather”: “小雨”, “temperature”: 18, ...},然后组织语言回答:“北京明天有小雨,建议您带伞。”
这个过程里,“调用天气API”这个动作是由LLM自主决策触发的,而不是开发者预先写死的。这就是Skills带来的根本性变革:将固定的工作流,转变为由自然语言驱动的、动态的智能工作流。
2.3 经验封装的维度:从简单到复杂
Skills可以封装不同复杂度的经验:
- 原子操作:如“发送邮件”、“查询数据库单条记录”、“生成一个随机数”。这是最基本的工具。
- 业务流程:如“新用户注册流程”(包含验证邮箱、创建数据库记录、发送欢迎邮件等多个原子操作的组合)。
- 专业判断:如“初步审核贷款申请材料完整性”(基于规则和简单模型,输出“通过”、“缺失XX材料”、“拒绝”等结构化结果)。
- 交互式任务:如“引导用户完成产品配置”,这个Skill可能需要与用户进行多轮对话,动态收集信息。
关键在于,无论多复杂,对外呈现给LLM的,都应该是一个清晰的“意图描述”和“输入输出规范”。LLM不需要知道“审核贷款材料”背后是100条规则还是一个小型神经网络,它只需要知道“给你一堆材料,你能告诉我缺什么”。
3. 手把手实战:从零封装你的第一个Skill
理论说再多,不如动手做一遍。我们以一个非常实用且常见的场景为例:封装一个“企业知识库问答”Skill。这个技能允许智能体根据用户问题,自动从你公司的内部文档(如Confluence、Wiki、PDF手册)中查找相关信息并生成回答。
3.1 第一步:定义技能蓝图(做什么?)
在写任何代码之前,我们先明确这个Skill的“说明书”。
- 技能名称:
query_company_knowledge_base - 意图描述(给LLM看):“当用户询问与公司产品、制度、流程、历史等相关的问题时,使用此技能从公司内部知识库中搜索最相关的信息片段,用于辅助生成回答。”
- 输入参数:
query(字符串,必需): 用户的问题或需要查询的关键词。top_k(整数,可选,默认3): 返回最相关的信息片段数量。
- 输出格式:
results(数组): 一个包含top_k个检索结果的数组。每个结果是一个对象,包含:content(字符串): 检索到的信息文本。source(字符串): 信息来源(如文档标题、URL)。relevance_score(浮点数): 相关性得分(0-1之间)。
这个定义非常关键。它让LLM明白:1)什么时候该用这个技能;2)用的时候需要提供什么;3)用了之后会得到什么。
3.2 第二步:构建技能引擎(怎么做?)
这是技能的“黑盒”实现部分。我们采用目前最主流、效果也相对较好的技术方案:文本嵌入向量检索。
环境准备与核心工具选型:
- 编程语言:Python,生态丰富。
- 向量数据库:选用ChromaDB。理由:轻量、易用、纯Python、支持内存和持久化模式,非常适合中小规模知识库和快速原型验证。如果知识库极大(百万级以上文档),可以考虑Qdrant或Weaviate。
- 文本嵌入模型:选用text-embedding-3-small。理由:OpenAI的API,效果稳定,接口简单,且
text-embedding-3系列在性价比和效果上取得了很好的平衡。如果要求完全本地化,可以选用BAAI/bge-small-zh-v1.5这类开源模型,但需要自己部署嵌入服务。 - 文档加载与切分:使用LangChain的
DocumentLoader和TextSplitter。虽然我们强调不重复造轮子,但LangChain在文档处理这块的封装确实能省去大量琐碎工作。
核心实现代码拆解:
首先,是知识库的构建(只需运行一次或定期更新):
# knowledge_base_builder.py import os from langchain_community.document_loaders import DirectoryLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings import chromadb from chromadb.config import Settings # 1. 加载文档(假设所有txt文档放在./docs目录下) loader = DirectoryLoader('./docs', glob="**/*.txt", loader_cls=TextLoader) documents = loader.load() # 2. 切分文档(防止单个文档太长,超出模型上下文) text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个片段约500字符 chunk_overlap=50 # 片段间重叠50字符,保持上下文连贯 ) texts = text_splitter.split_documents(documents) # 3. 初始化嵌入模型和向量数据库 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") client = chromadb.PersistentClient(path="./chroma_db") collection = client.get_or_create_collection(name="company_knowledge") # 4. 将文档转换为向量并存入数据库 for i, text in enumerate(texts): # 生成向量 embedding = embeddings.embed_query(text.page_content) # 存入数据库,元数据记录来源 collection.add( ids=[f"doc_{i}"], embeddings=[embedding], metadatas=[{"source": text.metadata.get("source", "unknown")}], documents=[text.page_content] ) print("知识库构建完成!")注意:
chunk_size的设置是门艺术。太小会丢失上下文,太大会降低检索精度并增加成本。需要根据你的文档类型(技术文档、会议纪要、Q&A)进行微调。对于技术文档,500-800是个不错的起点。
接下来,是Skill本身的实现:
# company_knowledge_skill.py import chromadb from chromadb.config import Settings from langchain_openai import OpenAIEmbeddings class CompanyKnowledgeSkill: def __init__(self, db_path="./chroma_db"): self.embeddings = OpenAIEmbeddings(model="text-embedding-3-small") self.client = chromadb.PersistentClient(path=db_path) self.collection = self.client.get_collection(name="company_knowledge") def query(self, query: str, top_k: int = 3) -> dict: """ 技能的核心函数,对应我们定义的接口。 """ # 1. 将用户查询转换为向量 query_embedding = self.embeddings.embed_query(query) # 2. 在向量数据库中搜索最相似的片段 results = self.collection.query( query_embeddings=[query_embedding], n_results=top_k ) # 3. 格式化输出,符合我们定义的规范 formatted_results = [] if results['documents']: for i in range(len(results['documents'][0])): formatted_results.append({ "content": results['documents'][0][i], "source": results['metadatas'][0][i].get('source', 'N/A'), "relevance_score": round(results['distances'][0][i], 4) # Chroma返回的是距离,越小越相似 }) return {"results": formatted_results} # 技能的使用示例(在智能体框架中,这个`query`方法会被调用) if __name__ == "__main__": skill = CompanyKnowledgeSkill() answer = skill.query("我们公司的年假制度是怎样的?", top_k=2) print(answer)3.3 第三步:集成与测试(怎么用?)
现在,我们需要把这个Skill“安装”到智能体框架中,让LLM能调用它。以目前比较流行的Dify或LangChain的Agent框架为例,集成方式本质上是将技能“注册”进去。
在Dify中,你可以在“工具”或“技能”配置页面,通过“自定义工具”功能,填入我们之前定义的“意图描述”、“输入参数”和“输出格式”,并将API端点指向我们上面写的query方法(需要包装成HTTP服务)。
在纯代码的LangChain Agent中,你可以这样注册:
from langchain.agents import Tool from company_knowledge_skill import CompanyKnowledgeSkill knowledge_skill = CompanyKnowledgeSkill() # 将技能包装成LangChain Tool company_knowledge_tool = Tool( name="QueryCompanyKnowledge", func=knowledge_skill.query, # 这里绑定我们的核心方法 description="当用户询问与公司产品、制度、流程、历史等相关的问题时,使用此工具从公司内部知识库中搜索最相关的信息片段。输入应为一个明确的查询问题。" ) # 然后将这个tool加入到Agent的工具列表中测试环节至关重要,你需要模拟各种提问:
- 正向测试:“公司的产品定价策略是什么?” -> 应成功触发技能并返回相关文档片段。
- 边界测试:“今天天气怎么样?” ->不应触发此技能。这依赖于LLM对“意图描述”的理解能力。
- 模糊测试:“我怎么请假?” -> 应能触发,并检索到休假流程相关文档。
- 压力测试:输入一个知识库中完全不存在的生僻词,观察其返回结果(应为空或低分片段),并确保不会导致系统错误。
4. 进阶:设计可组合、可维护的Skills体系
当你封装了十几个、几十个Skills后,管理就成了大问题。如何让Skills体系不变成一团乱麻?
4.1 技能的分类与命名规范
混乱始于命名。建议建立一套命名规范:
- 按领域前缀:
finance_(财务)、hr_(人事)、it_(IT)、sales_(销售)。 - 按操作类型:
get_(查询)、create_(创建)、update_(更新)、calculate_(计算)、analyze_(分析)。 - 按资源对象:
_user、_order、_document。
例如:hr_get_leave_balance(查询剩余年假)、sales_create_customer_record(创建客户记录)。清晰的命名能帮助LLM和开发者快速理解技能用途。
4.2 技能的版本管理与依赖
Skills不是一成不变的。API会变,业务逻辑会变。你需要为Skill引入版本管理。
- 在技能描述或元数据中明确版本号,如
version: "1.2.0"。 - 当技能更新时,考虑向后兼容。如果必须做破坏性更新(如输入参数变更),最好创建一个新技能(如
query_knowledge_v2),并在一段时间内并行支持旧版。 - 明确技能的依赖,比如某个数据分析Skill依赖于“数据查询Skill”的输出作为输入。这有助于在编排复杂任务时理解执行链。
4.3 技能的可发现性与元信息
除了基本的意图描述,为每个Skill添加丰富的元信息,能极大提升智能体调用它的准确性和效率:
- 使用示例:提供2-3个典型的调用示例(输入和输出)。
- 适用场景与限制:明确说明在什么情况下推荐使用,什么情况下不适用(例如,“本技能仅支持查询2023年之后的数据”)。
- 权限等级:标注该技能所需的最低权限(如“员工级”、“经理级”、“系统级”),智能体在调用前可以进行初步的权限校验。
- 执行成本/耗时:对于可能消耗大量Token或执行时间较长的技能,可以给出预估,供LLM在规划时权衡。
4.4 技能的编排与组合:实现复杂工作流
单个Skill能力有限,真正的威力在于组合。智能体应该能够自动串联多个Skills来完成复杂任务。
例如,用户说:“帮我分析一下上周销售冠军的业绩,并给他写一封表扬邮件。”
- LLM规划:这需要两个技能,先
sales_get_top_performer(获取销售冠军信息),再email_send_praise(发送表扬邮件)。 - LLM执行:调用第一个技能,获得输出
{“name”: “张三”, “performance”: 150%}。 - LLM编排:将第一个技能的输出(张三的名字和业绩)作为第二个技能的输入参数,生成邮件内容并发送。
这个过程中,LLM扮演了“工作流引擎”的角色。为了让它更好地做到这一点,我们在设计Skills时就要有“可组合性”意识:
- 输出标准化:尽可能让输出是结构化的JSON,方便后续技能解析。
- 错误码统一:定义一套通用的错误码和消息格式,方便上层处理。
- 提供“技能图谱”:可以维护一个文件,描述技能之间的输入输出关系,辅助LLM进行规划。
5. 避坑指南:Skills开发中常见的“雷区”
封装Skills听起来美好,但踩坑是必经之路。下面是我从实际项目中总结的几个关键陷阱。
5.1 意图描述模糊:让LLM“猜不透”
问题:技能描述写成“处理数据”或“执行操作”。这太宽泛了,LLM无法准确判断何时该调用。反面案例:description: “这是一个有用的工具。”正确做法:描述要具体,包含触发条件和核心动作。使用“当……时,用于……”的句式。正面案例:description: “当用户需要将中文文本翻译成英文时,使用此工具。输入是中文文本,输出是对应的英文翻译。”
5.2 忽视权限与安全:打开潘多拉魔盒
问题:技能直接封装了删除数据库、发送全员邮件、审批付款等高危操作,却没有做任何权限校验。一旦LLM被恶意诱导或误解指令,后果严重。案例:一个delete_user技能,仅凭用户名就执行删除。解决方案:
- 技能层面:在技能内部,必须集成严格的权限验证逻辑。可以校验调用者的身份Token、角色,或检查操作对象是否属于其权限范围。
- 架构层面:区分“高危技能”和“普通技能”。对于高危技能,可以采用“人机协同”模式,即LLM提出执行请求,由用户二次确认后再执行。
- 输入校验:对所有输入参数进行严格的类型、范围、合法性校验,防止SQL注入、命令注入等攻击。
5.3 过度依赖LLM的“理解力”:把复杂逻辑扔给提示词
问题:试图用一个超级复杂的提示词,让LLM去完成本应由代码处理的精确逻辑。比如,让LLM解析一段非标准格式的日志,并提取出特定字段。后果:输出不稳定,格式容易出错,难以调试,且Token消耗大。正确思路:Skills应该封装确定性的、精确的逻辑。把解析、计算、判断等硬核工作放在Skill的代码实现里。LLM应该只负责它擅长的部分:理解用户自然语言意图,并将其“翻译”成对确定性技能的调用。
- 该LLM做的:理解“帮我找出上个月销售额超过10万的客户”。
- 该Skill做的:接收结构化的查询条件
{“time_range”: “last_month”, “min_sales”: 100000},执行优化过的数据库查询,返回结构化的客户列表。
5.4 技能“僵尸化”:缺乏监控与迭代
问题:技能上线后就没人管了。不知道它被调用的频率、成功率、耗时,也不知道返回的结果是否有效。后果:技能可能早已失效(如依赖的API已变更),或效果很差,但无人知晓,成为系统中的“僵尸服务”,影响智能体整体表现。必备的监控指标:
- 调用量:每个技能每天的调用次数。
- 成功率:调用成功(返回有效结果)的比例。
- 平均耗时:从调用到返回的延迟。
- 输入输出采样:定期记录一些典型的输入和输出,用于评估技能是否仍符合预期。
- 错误日志:详细记录每一次失败的原因(参数错误、网络超时、权限不足等)。
基于这些数据,你才能知道哪些技能是高频核心资产需要重点维护,哪些技能是冗余可以下线,哪些技能需要优化性能或准确率。
6. 从Skills到智能体:构建真正“会干活”的AI应用
封装好Skills,只是拥有了工具箱。如何让智能体(Agent)熟练地使用这些工具,才是最终目标。这涉及到智能体的“大脑”配置。
6.1 为智能体选择正确的“大脑”(LLM)
不是所有LLM都擅长工具调用。你需要关注模型的几个关键能力:
- 工具调用(Function Calling)的可靠性:这是基础。模型必须能严格按照你提供的工具描述来生成格式正确的调用请求。GPT-4系列、Claude 3系列、DeepSeek最新版本在这方面表现都很出色。
- 长上下文规划能力:对于需要串联多个工具的复杂任务,模型需要有足够的上下文窗口来记住整个计划、已执行步骤的结果和剩余任务。
- 拒绝不当请求的“判断力”:一个好的智能体应该知道什么时候不该使用工具。比如用户询问敏感信息或提出不合理请求时,它应该礼貌拒绝,而不是强行调用一个可能出错的技能。
6.2 设计高效的智能体提示词(Prompt)
智能体的提示词是其“操作系统”。除了常见的系统指令(“你是一个有帮助的助手…”),针对工具调用,必须明确:
- 工具列表与规范:清晰列出所有可用工具及其详细描述、参数。这是最重要的部分。
- 输出格式指令:严格要求模型以指定格式(如JSON)返回工具调用请求和最终答案。
- 推理链鼓励:鼓励模型“一步一步思考”,先解释它计划做什么、为什么选择这个工具,然后再执行。这不仅能提高准确性,也方便调试。
- 错误处理指引:告诉模型当工具调用失败时该怎么办(例如,“如果查询失败,请向用户说明无法获取信息,并询问是否想换一种方式提问”)。
6.3 实现闭环:让智能体从结果中学习
一个初级的智能体只会机械地调用工具。一个高级的智能体应该能根据工具返回的结果,动态调整后续策略。
- 结果验证:技能返回了结果,智能体应该能初步判断这个结果是否合理、是否回答了用户问题。如果结果为空或质量很差,它应该尝试换一个关键词重新查询,或者向用户澄清问题。
- 多技能协同策略:当第一个技能返回的结果不完整时,智能体应能自动触发第二个、第三个技能来补充信息。例如,先通过
search_company_directory找到员工邮箱,再通过check_calendar_availability查看其日程。 - 状态管理:对于多轮对话中的复杂任务,智能体需要记住之前已经调用过哪些工具、得到了什么结果,避免重复操作或陷入循环。
6.4 评估与迭代:智能体不是一次成型的
上线不是终点。你需要一套评估体系来衡量智能体是否真的“会干活”。
- 单技能准确率:针对每个技能,测试智能体在典型场景下是否能正确触发并传入正确参数。
- 端到端任务成功率:设计一系列真实的用户任务(如“预订会议室并通知团队成员”),看智能体能否独立完成。
- 人工审核与反馈:在初期,对智能体的执行过程和结果进行人工抽样审核,标注错误。这些数据可以用于优化提示词,甚至微调模型(如果使用可微调的模型)。
- A/B测试:尝试不同的提示词模板、不同的工具描述方式,甚至不同的大模型,通过实际用户交互数据选择效果最好的组合。
说到底,让大模型“会干活”,是一个系统工程。它需要我们将模糊的人类经验,拆解、提炼、封装成一个个边界清晰、定义明确的Skills;需要我们对智能体进行精心“调教”,让它学会在正确的时间、以正确的方式使用这些工具;更需要我们建立监控和迭代机制,让这个系统越用越聪明。这条路没有捷径,但每一步都踩在实处,每一次封装都是对你业务逻辑的一次深度梳理,其价值远不止于一个AI应用本身。当你看到智能体流畅地串联起多个技能,独立完成一个曾经需要多人协作的任务时,你就会明白,拒绝重复造轮子,拥抱Skills化的智能体开发,是通往下一代人机协同的必经之路。