一文带你彻底搞定 Skills 实战开发(含可落地代码 + 指令模板)
query_sales_data(**call_args) 这种写法非常优雅,它利用 Python 的解包机制,直接把 LLM 提取的 JSON 映射到函数参数上。开发 AI Agent 的过程,本质上是将人类的“业务 SOP”转化为“代码逻辑(Skills)”和“自然语言逻辑(Prompt)”的过程。有了 Skills,它才能真正连接业务,成为干活的“实干家”。好了,这就是我今天想分享的内容。强
大家好,我是玄姐。
导读:如果说大模型(LLM)是 AI Agent 的“大脑”,那么 Skills(技能/工具)就是它的“双手”。没有 Skills,大模型只是一个博学的“空谈家”;有了 Skills,它才能真正连接业务,成为干活的“实干家”。

本文将带你从零构建一套生产级可用的 Skills 实战架构,不讲空话,直接上代码和模板。
1. 为什么要重新定义 "Skills"?
在早期的 Prompt Engineering 中,我们试图通过“角色扮演”让模型具备某种能力。但在 Enterprise(企业级)落地中,这远远不够。
真正的 Skills(技能)必须包含三个核心要素,缺一不可:
-
Schema(元数据定义):明确告诉 LLM,“我能干什么?我的参数有哪些?类型是什么?”
-
Executable(执行逻辑):真正跑在服务器上的 Python/Java/Go 代码,用于查询数据库、调用 API 或处理文件。
-
Instruction(引导指令):能够让 LLM 在正确场景下、以正确参数唤醒该技能的 Prompt 策略。
2. 架构设计:Agent 如何“拿”起工具?
在编写代码之前,我们需要理解 Agent 调用 Skills 的标准生命周期(Observe-Think-Act):
-
注册 (Registration):将 Skill 的描述(Description)和参数结构(Schema)注入到 System Prompt 或 API 的 tools 字段中。
-
路由 (Routing):LLM 根据用户意图,判断是否需要调用工具,以及调用哪一个。
-
执行 (Execution):系统拦截 LLM 的结构化输出(如 JSON),映射到具体的本地函数并执行。
-
回环 (Feedback):将执行结果(Result)再次封装成文本,喂回给 LLM,让其生成最终回复。
3. 核心实战:手把手写一个 "Enterprise Skill"
假设我们要开发一个“企业数据查询技能” (EnterpriseDataSkill),用于查询公司内部的销售数据。我们将使用 Python 和 Pydantic 来实现标准化的 Schema 定义。
第一步:定义 Skills 的“骨架” (Schema)
使用 Pydantic 可以自动生成标准 JSON Schema,这是各大模型(OpenAI/Claude/DeepSeek)通用的“接口语言”。
from pydantic import BaseModel, Fieldfrom typing import Optional, Literal
# 1. 定义参数结构class SalesQueryArgs(BaseModel): region: str = Field(..., description="查询的销售区域,例如:'华东', '北美'") quarter: str = Field(..., description="查询的季度,格式如 '2024-Q1'") product_line: Optional[str] = Field(None, description="可选,特定的产品线名称")
# 2. 定义 Skill 元数据SKILL_METADATA = { "name": "query_sales_data", "description": "查询公司特定区域和季度的销售业绩数据。当用户询问'XX地区上个季度卖了多少'时使用此工具。", "parameters": SalesQueryArgs.model_json_schema()}
第二步:编写 Skills 的“肌肉” (Implementation)
这是实际的业务逻辑代码。在生产环境中,这里通常涉及数据库连接或 API 调用。
import json
def query_sales_data(region: str, quarter: str, product_line: str = None): """ 模拟数据库查询逻辑 """ print(f"DEBUG: 正在查询数据库... Region={region}, Quarter={quarter}")
# 模拟返回结果 mock_db = { "华东": {"2024-Q1": 1500000}, "北美": {"2024-Q1": 2000000} }
amount = mock_db.get(region, {}).get(quarter, 0)
result = { "status": "success", "data": { "region": region, "quarter": quarter, "amount": amount, "currency": "CNY" } }
# 注意:Skills 的返回值通常必须是 String 类型,以便喂回给 LLM return json.dumps(result, ensure_ascii=False)
第三步:闭环调用 (The Loop) -- 让模型“动”起来
有了“骨架”和“肌肉”,现在我们需要给 Agent 注入“神经信号”。
我们将使用 Anthropic SDK 实现一个标准的 ReAct (Reason + Act) 闭环。这里的核心在于处理 tool_use (模型请求) 和 tool_result (执行反馈) 的“乒乓球”交互。
代码实战:
import anthropicfrom typing import List, Dict, Any
# 假设 client 已初始化client = anthropic.Anthropic(api_key="YOUR_API_KEY")
def run_sales_agent(user_query: str): print(f"👤 User: {user_query}")
# 1. 动态生成工具定义 (直接复用第一步的 Pydantic Schema) # 这是自动化关键:代码改了,Schema 自动变,不用手动维护 JSON tools_schema = [{ "name": "query_sales_data", "description": SKILL_METADATA["description"], "input_schema": SalesQueryArgs.model_json_schema() }]
# 2. 初始化对话上下文 messages = [{"role": "user", "content": user_query}]
# --- Round 1: 发球 (Claude 思考) --- response = client.messages.create( model="claude-3-5-sonnet-20240620", max_tokens=1024, tools=tools_schema, messages=messages )
# 3. 拦截:判断模型是否想“拿”工具 if response.stop_reason == "tool_use": # 必须将 Claude 的"思考过程"完整加入历史,否则上下文会断裂 messages.append({"role": "assistant", "content": response.content})
# 提取核心信息 tool_block = next(b for b in response.content if b.type == "tool_use") tool_id = tool_block.id # 身份证:用于后续匹配结果 call_args = tool_block.input # 参数包:{'region': '华东', 'quarter': '2024-Q1'}
print(f"🤖 Agent: 正在调用工具 -> query_sales_data({call_args})")
# --- Round 2: 接球 (本地执行) --- # 这里执行我们在第二步写好的业务函数 try: # **关键点**:使用 **kwargs 自动解包参数,直接传给函数 execution_result = query_sales_data(**call_args) except Exception as e: execution_result = f"Error: {str(e)}"
print(f"✅ System: 执行成功,数据已获取。")
# --- Round 3: 扣杀 (反馈结果) --- # 构造符合 Anthropic 协议的反馈块 tool_result_message = { "role": "user", "content": [{ "type": "tool_result", "tool_use_id": tool_id, # 必须与请求 ID 严格对应 "content": execution_result }] } messages.append(tool_result_message)
# 让 Claude 根据数据生成最终的自然语言回答 final_response = client.messages.create( model="claude-3-5-sonnet-20240620", max_tokens=1024, tools=tools_schema, # 保持工具定义一致 messages=messages )
return final_response.content[0].text
# 如果模型不需要工具,直接返回文本 return response.content[0].text
# === 🚀 运行效果 ===# output = run_sales_agent("帮我查一下华东区2024年第一季度的业绩")# print(f"💬 Final Answer: {output}")
核心逻辑拆解:
-
Schema 自动化: 注意代码中的
input_schema: SalesQueryArgs.model_json_schema()。这意味着你以后只要修改 Pydantic 类(比如增加一个字段),工具定义会自动更新,无需手动改 JSON,这是工程化的关键细节。
-
ID 绑定: Claude 的
tool_use_id是唯一的。你必须把它原封不动地塞回tool_result中,这样模型才能知道“这个结果是对应刚才那次查询的”。 -
参数解包:query_sales_data(**call_args) 这种写法非常优雅,它利用 Python 的解包机制,直接把 LLM 提取的 JSON 映射到函数参数上。
4. 指令工程:让模型精准调用的“秘籍”
很多开发者代码写得很好,但模型就是不调,或者参数乱填。核心原因在于 Context Engineering(上下文工程)没做好。
以下是一套经过实战验证的 System Prompt 注入模板:
📝 通用 Skill 注入模板
# Role你是一个配备了专业工具的 AI 业务助手。你的目标是协助用户查询数据和解决问题。
# Tools Capability你拥有以下工具(Skills)的访问权限:
## 1. query_sales_data- **功能描述**: {SKILL_DESCRIPTION}- **参数要求**: {JSON_SCHEMA}- **触发时机**: 当且仅当用户明确询问有关“销售额”、“业绩”或“营收”等定量数据时调用。对于一般性闲聊(如“你好”),请勿调用此工具。
# Constraints (关键约束)1. **参数推断**: 如果用户没有提供必要的参数(如只说了“查一下销售额”,没说哪个区),你必须先追问用户,而不是编造参数。2. **格式严格**: 调用工具时,必须输出严格的 JSON 格式。3. **事实导向**: 永远依据工具返回的数据回答,不要使用你预训练知识中的过时数据。
💡 技巧点拨:
-
Negative Prompting (负向约束): 明确告诉它“什么时候不要调”,能显著降低幻觉率。
-
Parameter Inference (参数推断): 强制模型在缺参时进行“追问(Slot Filling)”,而不是瞎猜,这是提升用户体验的关键。
5. 落地避坑指南 (Best Practices)
在将 Skills 部署到生产环境时,这三个坑一定要避开:
-
上下文爆炸 (Context Window):
-
-
问题: 如果你挂载了 50 个工具,光是工具定义的 JSON Schema 就会把 Token 撑爆。
-
解法: 使用 RAG 技术动态加载 Skills。先根据用户 Query 检索出最相关的 Top-5 工具,再将这 5 个工具的定义注入 Prompt,示例代码如下:
-
import jsonimport numpy as npfrom typing import List, Dictfrom sentence_transformers import SentenceTransformerfrom sklearn.metrics.pairwise import cosine_similarity# 1. 模拟一个包含大量 Skills 的工具库# 在真实场景中,这里通常是一个数据库或配置文件ALL_SKILLS_REPOSITORY = [ { "name": "query_sales_data", "description": "查询公司特定区域和季度的销售业绩数据、营收报表。", "schema": {"type": "function", "function": {"name": "query_sales_data", "parameters": {...}}} }, { "name": "check_weather", "description": "查询此时此刻的全球各城市天气情况、温度、降雨概率。", "schema": {"type": "function", "function": {"name": "check_weather", "parameters": {...}}} }, { "name": "send_feishu_message", "description": "向飞书/Lark群组或个人发送通知消息、提醒。", "schema": {"type": "function", "function": {"name": "send_feishu_message", "parameters": {...}}} }, { "name": "search_internal_wiki", "description": "搜索公司内部Wiki知识库,查找技术文档、HR政策等。", "schema": {"type": "function", "function": {"name": "search_internal_wiki", "parameters": {...}}} }, # ... 假设这里还有 50 个工具 ...]class SkillRetriever: def __init__(self, skills: List[Dict]): self.skills = skills # 加载一个轻量级的 Embedding 模型 print("正在加载 Embedding 模型 (首次运行可能较慢)...") self.encoder = SentenceTransformer('all-MiniLM-L6-v2') # 预计算所有 Skill Description 的向量并缓存 descriptions = [skill["description"] for skill in skills] self.skill_embeddings = self.encoder.encode(descriptions) print(f"✅ 已索引 {len(skills)} 个 Skills。") def retrieve(self, user_query: str, top_k: int = 2) -> List[Dict]: """ 根据用户输入,动态检索最相关的 Top-K 个工具 """ # 1. 将用户 Query 向量化 query_embedding = self.encoder.encode([user_query]) # 2. 计算余弦相似度 (Cosine Similarity) similarities = cosine_similarity(query_embedding, self.skill_embeddings)[0] # 3. 获取 Top-K 索引 # argsort 返回的是从小到大的索引,所以取最后 k 个并反转 top_k_indices = np.argsort(similarities)[-top_k:][::-1] # 4. 返回对应的 Skill 对象 selected_skills = [] print(f"\n🔍 用户意图: '{user_query}'") print(f"🎯 命中工具 (Top {top_k}):") for idx in top_k_indices: score = similarities[idx] skill = self.skills[idx] print(f" - {skill['name']} (相似度: {score:.4f})") selected_skills.append(skill) return selected_skills# --- 测试运行 ---# 初始化检索器retriever = SkillRetriever(ALL_SKILLS_REPOSITORY)# 场景 A: 用户想查数据relevant_skills_a = retriever.retrieve("帮我看看上个季度华东区的业绩怎么样", top_k=2)# 场景 B: 用户想发通知relevant_skills_b = retriever.retrieve("给项目组发个消息,说服务器修好了", top_k=2)
-
错误处理 (Error Handling):
-
-
问题: Skill 代码报错了(比如数据库超时),直接把 Python Traceback 丢给 LLM?
-
解法: 永远要在 Skill 内部捕获异常,并返回一段“人类可读”的错误描述。
-
代码示例:
-
try: # 业务逻辑except TimeoutError: return "系统提示:数据源连接超时,请建议用户稍后重试。"
-
安全性 (Security):
-
-
问题: 允许 LLM 执行
delete_user或drop_table? -
解法: 读写分离。对于高危操作(增删改),必须在 Skill 执行前增加一步 "Human-in-the-loop" (人工确认) 环节。
-
6. 结语
开发 AI Agent 的过程,本质上是将人类的“业务 SOP”转化为“代码逻辑(Skills)”和“自然语言逻辑(Prompt)”的过程。
掌握了 Schema 定义 + 业务代码 + 指令模板这套组合拳,你就掌握了构建强大 Agent 的钥匙。
好了,这就是我今天想分享的内容。如果你对构建企业级 AI 原生应用新架构设计和落地实践感兴趣,别忘了点赞、关注噢~
—1—
加我微信
扫码加我👇有很多不方便公开发公众号的我会直接分享在朋友圈,欢迎你扫码加我个人微信来看👇

加星标★,不错过每一次更新!
⬇戳”阅读原文“,立即预约!
更多推荐




所有评论(0)