大模型应用开发:Skill技能封装与结构化调用实战指南
1. 背景与核心概念在探索大模型应用开发时你是否遇到过这样的困惑明明给模型下达了清晰的指令但它的输出却总是“差点意思”比如你希望它帮你分析一份财报它却只给出了泛泛的总结或者你希望它扮演一个严格的代码审查员它却表现得像个和事佬。问题的根源往往不在于模型本身的能力而在于我们与模型“沟通”的方式不够精确和结构化。这正是Skill技能概念要解决的核心问题。简单来说Skill 是对大模型能力的一种封装和标准化描述。它通过一套精心设计的指令、示例和约束条件将我们模糊的、口语化的需求转化为模型能够精准理解和执行的“任务蓝图”。1.1 什么是 Skill我们可以将 Skill 理解为大模型领域的“函数”或“API”。在传统编程中一个函数如calculateTax(income)封装了特定的计算逻辑你只需要传入参数就能得到预期的结果。Skill 的作用类似它封装了引导大模型完成特定任务的“逻辑”。一个完整的 Skill 通常包含以下几个核心要素角色与目标明确告诉模型“你是谁”以及“你要做什么”。例如“你是一位经验丰富的网络安全专家负责分析这段代码中的潜在漏洞。”详细指令清晰、无歧义地描述任务的具体步骤、格式要求和输出规范。例如“请按以下格式输出1. 漏洞类型2. 代码行号3. 风险描述4. 修复建议。”示例提供高质量的输入-输出对Few-shot Learning让模型通过示例更准确地把握任务边界和风格。这是让模型“学会”新任务的关键。约束与边界规定模型不应该做什么避免其自由发挥导致结果偏离预期。例如“不要生成任何解释性文字只输出分析结果表格。”1.2 为什么需要 Skill它能解决什么问题在没有 Skill 概念之前我们与模型的交互更像是“一次性提示工程”。每次遇到新任务都需要重新构思和调试一大段提示词Prompt这个过程低效、不可复用且难以保证质量的一致性。Skill 的引入带来了根本性的改变标准化与复用性将针对特定任务的优质提示词固化为 Skill可以在不同场景、不同对话中反复调用实现“一次定义处处使用”。提升效果与可控性通过精心设计的指令和示例Skill 能显著提升模型在特定任务上的表现减少输出中的“幻觉”和无关内容使结果更可控、更可靠。降低使用门槛普通开发者或业务人员无需深究提示工程的细节只需调用已定义好的 Skill如“财报分析专家”、“代码审查员”就能获得专业级的输出。构建复杂应用的基础在智能体Agent系统中一个复杂的任务可以被拆解为多个子任务每个子任务由一个专门的 Skill 来处理。Skill 成为了构建模块化、可编排的AI应用的核心组件。1.3 Skill 的常见应用场景内容生成与润色新闻稿写作、广告文案创作、多语言翻译、文本风格转换如正式转口语。信息提取与分析从合同、财报、研报中提取关键条款和数字进行情感分析、主题归纳。代码相关任务代码生成、代码解释、代码审查、单元测试生成、Bug调试。专业领域问答扮演法律顾问、医疗咨询、金融分析师等角色提供专业领域的解答。数据处理与格式化将非结构化的文本数据转换为结构化的JSON、CSV或表格。2. 环境准备与版本说明本文的讲解和示例将主要围绕OpenAI GPT 系列模型的 API 使用展开因为其生态对 Skill/Function Calling 的支持最为成熟和典型。但 Skill 的设计思想是模型无关的同样适用于 Claude、DeepSeek、通义千问等主流大模型。核心环境与工具Python: 推荐 3.8 及以上版本。本文示例代码基于 Python。OpenAI Python SDK:openai库版本 1.0.0 及以上。新旧版本API差异较大请务必注意。API 密钥: 你需要一个有效的 OpenAI API 密钥并确保账户有可用额度。IDE/编辑器: 任意你熟悉的即可如 VS Code, PyCharm。虚拟环境推荐: 使用venv或conda管理项目依赖避免包冲突。安装依赖在你的项目目录下创建并激活虚拟环境后安装必要的包pip install openai python-dotenv其中python-dotenv用于安全地管理环境变量如API密钥。项目结构建议llm-skill-demo/ ├── .env # 存储环境变量如 OPENAI_API_KEY ├── requirements.txt # 项目依赖列表 ├── skills/ # 存放所有Skill定义模块 │ ├── __init__.py │ ├── financial_analyst.py │ └── code_reviewer.py ├── main.py # 主程序调用Skill └── utils.py # 工具函数如调用模型重要版本说明OpenAI 在 2023年6月左右发布了 Chat Completions API 的function calling能力这是实现结构化 Skill 的关键。随后该能力演进为tools工具调用功能更强大。本文示例将基于较新的tools参数进行演示它兼容并扩展了functions的功能。请确保你的openaiSDK 版本足够新以支持此特性。3. 核心语法、配置与原理拆解要理解 Skill 的实现我们需要深入两个层面一是如何用自然语言描述一个 Skill提示词工程二是如何通过 API 将其“告知”模型并获取结构化结果函数/工具调用。3.1 Skill 的提示词结构剖析一个高质量的 Skill 提示词远不止一句“你是一个翻译”。它应该是一个包含上下文、指令、格式和示例的完整系统消息System Message。示例定义一个“SQL查询生成器”Skill# 这是一个存储在 skills/sql_generator.py 中的 Skill 定义 SQL_GENERATOR_SYSTEM_PROMPT 你是一个专业的SQL查询生成专家。你的任务是根据用户的自然语言描述生成准确、高效且安全的MySQL查询语句。 ## 你的能力 1. 理解用户关于数据库查询的意图。 2. 根据提供的数据库表结构Schema生成对应的SELECT、INSERT、UPDATE或DELETE语句。 3. 优先使用参数化查询或指明参数位置以避免SQL注入风险。 4. 对复杂的查询进行优化建议。 ## 输出格式 你必须严格按照以下JSON格式输出不要包含任何其他解释性文字 { sql: 生成的SQL语句用{{}}标注需要用户提供的参数例如SELECT * FROM users WHERE id {{user_id}}, explanation: 对生成的SQL语句的简要解释说明其作用和关键部分, potential_risk: 指出该查询可能存在的风险如性能问题、无WHERE条件的DELETE等或需要特别注意的地方 } ## 数据库表结构Schema {table_schema} ## 示例 用户”帮我查一下上个月销售额超过10000的所有订单按销售额降序排列“ 你 { sql: SELECT order_id, customer_id, total_amount, order_date FROM orders WHERE order_date 2023-10-01 AND order_date 2023-11-01 AND total_amount 10000 ORDER BY total_amount DESC, explanation: 此查询从orders表中筛选出2023年10月份总金额超过10000的订单并按照总金额从高到低排序。, potential_risk: 如果orders表数据量极大在order_date和total_amount上建立复合索引可提升性能。 } --- 现在请开始你的工作。记住只输出JSON。 拆解说明角色与目标开篇明义“SQL查询生成专家”。能力范围清晰界定任务边界生成哪些SQL类型考虑安全。输出格式强制结构化输出JSON这是实现程序自动化处理的关键。{{}}用于标记动态参数。上下文{table_schema}是一个占位符在实际调用时会被具体的表结构信息替换。示例提供了一个完整的输入-输出对让模型模仿格式和逻辑。最终指令“只输出JSON”强化约束。3.2 通过tools参数实现结构化 Skill 调用仅靠系统提示词模型返回的还是文本。我们需要模型返回结构化的数据以便程序直接解析使用。OpenAI API 的tools参数就是为此而生。tools参数允许你定义一系列模型可以“调用”的工具即我们的Skill。模型在理解用户请求后如果认为需要调用某个工具来完成请求它会返回一个包含工具调用信息的结构化响应然后由你的程序来执行真正的工具函数并将结果返回给模型进行总结或下一步操作。定义tools(Skill 的 API 形态)# 在 utils.py 或主程序中定义 tools 列表 sql_generator_tool { type: function, function: { name: generate_sql_query, # 工具函数名 description: 根据自然语言描述和数据库表结构生成安全、高效的SQL查询语句。, # 工具描述模型据此判断是否调用 parameters: { # 严格定义工具的输入参数JSON Schema type: object, properties: { user_query: { type: string, description: 用户的自然语言查询请求例如查找所有活跃用户 }, table_schema: { type: string, description: 相关数据库表的CREATE TABLE语句用于理解表结构 } }, required: [user_query, table_schema] # 必填参数 } } } # 可以定义多个工具 code_review_tool { type: function, function: { name: review_python_code, description: 对提供的Python代码进行审查指出潜在bug、风格问题和性能隐患。, parameters: { type: object, properties: { code_snippet: { type: string, description: 需要审查的Python代码片段 }, focus_areas: { type: array, items: {type: string}, description: 希望重点审查的领域如 [bug, security, performance, style], default: [bug, style] } }, required: [code_snippet] } } } TOOLS [sql_generator_tool, code_review_tool]关键点解释description至关重要模型完全依赖这个描述来决定是否调用该工具。描述必须准确、具体。parameters使用 JSON Schema 定义。这定义了模型需要“生成”的输入参数的结构。模型会从对话上下文中提取信息填充这个结构。required指定哪些参数是调用时必须提供的。3.3 调用流程与原理一次完整的、使用tools的 Skill 调用流程如下用户请求用户提出需求如“根据下面的表结构帮我写一个查询学生成绩的SQL”。API 调用你的程序将用户消息和定义好的tools列表一起发送给 Chat Completions API。模型决策模型理解请求并判断是否需要调用工具以及调用哪个工具。如果需要它会在响应中返回一个tool_calls数组。程序执行你的程序解析tool_calls根据name找到对应的本地函数并使用模型提供的arguments一个符合 JSON Schema 的字典来执行该函数。结果返回将本地函数的执行结果作为新的消息role: “tool”再次发送给模型。模型回复模型结合工具执行的结果生成最终面向用户的自然语言回复。这个过程实现了“思考-行动-观察”的循环是构建智能体Agent的基础。对于简单的 Skill我们也可以让模型在第一次调用时就生成最终输出“并行函数调用”这取决于tool_choice参数的设置。4. 完整实战案例构建一个代码审查 Skill让我们通过一个完整的例子将上述概念串联起来。我们将构建一个PythonCodeReviewerSkill它接收一段Python代码和审查重点返回结构化的审查报告。4.1 创建项目结构与依赖确保你已经完成了第2章的环境准备。项目结构如下code-review-skill/ ├── .env ├── requirements.txt ├── skills/ │ ├── __init__.py │ └── python_reviewer.py # 我们将在这里定义Skill ├── main.py └── utils.pyrequirements.txt内容openai1.12.0 python-dotenv1.0.0.env文件内容请替换为你自己的密钥OPENAI_API_KEYsk-your-actual-api-key-here4.2 定义 Skill 核心逻辑与工具首先在skills/python_reviewer.py中定义 Skill 的系统提示词和对应的本地执行函数。# skills/python_reviewer.py import json from typing import List, Dict, Any # 1. 定义系统提示词 (Skill的灵魂) SYSTEM_PROMPT 你是一个资深Python开发专家专注于代码审查。你会严格、细致地分析代码并提供具有建设性的改进意见。 ## 审查维度 - **正确性**潜在的逻辑错误、边界条件处理、异常捕获。 - **安全性**注入风险、敏感信息硬编码、不安全函数使用。 - **性能**低效的循环、重复计算、不必要的内存使用。 - **风格与可读性**是否符合PEP 8命名是否清晰注释是否恰当。 - **可维护性**代码结构、函数职责单一性、模块化程度。 ## 输出格式 你必须输出一个严格的JSON对象包含以下字段 { overall_score: 整数1-10分10分为最佳, issues: [ { category: 问题类别如‘正确性’、‘风格’, severity: 严重程度high/medium/low, line: 行号或行号范围如 5 或 “10-12”, description: 详细的问题描述, suggestion: 具体的改进建议代码或方案 } ], summary: 一段整体的代码评价和改进总结 } 请基于上述维度和格式进行审查。不要输出任何JSON之外的内容。 # 2. 定义本地执行函数 (当模型决定调用此工具时实际运行的代码) def execute_python_code_review(code_snippet: str, focus_areas: List[str] None) - Dict[str, Any]: 实际执行代码审查的函数。 注意在这个示例中这个函数并不真正做静态分析而是将任务“代理”给大模型。 更复杂的实现可以在这里集成 pylint, bandit, black 等真实工具。 # 在实际复杂应用中这里可以集成真正的静态分析工具 # 但为了演示Skill模式我们依然通过构造提示词让大模型来完成核心分析 # 本函数的主要职责是组织请求和解析返回的JSON。 from utils import call_chat_completion # 假设我们在utils.py中封装了调用 # 为了简洁此处省略具体调用代码将在 main.py 中展示完整流程 pass # 3. 定义对应的 Tool 结构 (用于告知API) def get_tool_definition(): 返回此Skill对应的OpenAI Tool定义 return { type: function, function: { name: review_python_code, description: 对Python代码进行深度审查评估正确性、安全性、性能、风格和可维护性并生成结构化报告。, parameters: { type: object, properties: { code_snippet: { type: string, description: 需要被审查的Python代码字符串 }, focus_areas: { type: array, items: {type: string, enum: [correctness, security, performance, style, maintainability]}, description: 指定需要重点关注的审查领域。, default: [correctness, style] } }, required: [code_snippet] } } }4.3 编写工具调用与模型交互的封装在utils.py中我们封装与 OpenAI API 交互的通用逻辑。# utils.py import os import json from openai import OpenAI from dotenv import load_dotenv # 加载环境变量 load_dotenv() # 初始化OpenAI客户端 client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) def call_chat_completion(messages, toolsNone, tool_choiceNone, modelgpt-4o-mini): 调用Chat Completions API的通用函数。 Args: messages: 消息历史列表例如 [{role: system, content: ...}, {role: user, content: ...}] tools: 可用的工具列表。 tool_choice: 控制模型如何使用工具auto默认/ none / {type: function, function: {name: xxx}} model: 使用的模型名称。 Returns: OpenAI的响应对象。 try: response client.chat.completions.create( modelmodel, messagesmessages, toolstools, tool_choicetool_choice, temperature0.1, # 代码审查需要低随机性保持稳定 ) return response except Exception as e: print(f调用API时发生错误: {e}) return None def parse_tool_calls(response): 解析响应中的tool_calls。 Returns: (tool_calls列表, 文本回复内容)。如果无tool_calls则前者为None。 choice response.choices[0] message choice.message if message.tool_calls: return message.tool_calls, message.content else: return None, message.content4.4 编写主程序逻辑在main.py中我们将串联整个流程准备消息、调用API、处理工具调用、执行本地函数、获取最终结果。# main.py import json from utils import call_chat_completion, parse_tool_calls from skills.python_reviewer import SYSTEM_PROMPT, get_tool_definition, execute_python_code_review def run_code_review(user_code: str, focus: list None): 运行代码审查的主流程。 if focus is None: focus [correctness, style] # 1. 准备对话消息和历史 messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: f请审查以下Python代码\npython\n{user_code}\n\n请重点关注{, .join(focus)}} ] # 2. 准备工具定义 tools [get_tool_definition()] # 3. 第一次调用让模型决定是否需要调用工具这里我们强制它调用 print(第一步请求模型分析代码并准备调用审查工具...) response call_chat_completion(messages, toolstools, tool_choice{type: function, function: {name: review_python_code}}) if not response: print(API调用失败。) return tool_calls, text_reply parse_tool_calls(response) # 4. 处理工具调用 if tool_calls: for tool_call in tool_calls: func_name tool_call.function.name if func_name review_python_code: # 解析模型生成的参数 kwargs json.loads(tool_call.function.arguments) print(f第二步模型决定调用工具 {func_name}参数为: {kwargs}) # 5. 执行本地工具函数这里模拟执行实际应调用真正的分析或转发请求 # 注意为了演示我们不再进行第二次网络调用而是直接使用模型第一次调用时可能生成的文本。 # 更标准的做法是用kwargs构造新的系统用户消息再次调用模型获取结构化JSON。 # 这里简化假设模型在第一次响应中content里已经包含了我们想要的JSON。 if text_reply: try: # 尝试从模型的文本回复中解析JSON review_result json.loads(text_reply) print(第三步解析审查结果成功) return review_result except json.JSONDecodeError: print(模型返回的不是有效JSON回退到文本回复。) return {raw_output: text_reply} else: print(模型没有调用工具。) return {raw_output: text_reply} # 6. 模拟一个需要审查的代码片段 sample_code def calculate_average(numbers): sum 0 for i in range(len(numbers)): sum numbers[i] avg sum / len(numbers) return avg def fetch_user_data(user_id): import sqlite3 conn sqlite3.connect(test.db) query fSELECT * FROM users WHERE id {user_id} cursor conn.execute(query) return cursor.fetchall() if __name__ __main__: print(开始代码审查演示...\n) print(被审查的代码) print(sample_code) print(- * 50) result run_code_review(sample_code, focus[security, performance, style]) if result and issues in result: print(\n审查报告摘要) print(f综合评分{result.get(overall_score, N/A)}/10) print(f发现问题数{len(result[issues])}) for issue in result[issues]: print(f [{issue[severity].upper()}] {issue[category]} - 第{issue[line]}行: {issue[description]}) print(f\n总结{result.get(summary, )}) elif result: print(\n原始输出) print(result.get(raw_output, result))4.5 运行与结果说明运行python main.py你可能会看到类似以下的输出具体内容因模型随机性而异开始代码审查演示... 被审查的代码 此处打印出 sample_code -------------------------------------------------- 第一步请求模型分析代码并准备调用审查工具... 第二步模型决定调用工具 review_python_code参数为: {code_snippet: ..., focus_areas: [security, performance, style]} 第三步解析审查结果成功 审查报告摘要 综合评分6/10 发现问题数3 [HIGH] security - 第10行: 使用字符串格式化f-string直接拼接SQL查询语句存在严重的SQL注入漏洞。 [MEDIUM] performance - 第3-5行: 使用range(len(numbers))和索引访问的方式遍历列表效率较低应直接迭代元素。 [LOW] style - 第2行: 变量名sum与内置函数sum()重名可能引起混淆建议改为total。 总结代码存在高危安全漏洞需立即修复SQL注入问题。性能与风格方面有改进空间建议使用Pythonic的写法。这个例子展示了Skill的完整生命周期从定义系统提示词工具描述、调用API with tools、决策模型选择工具、执行本地函数/逻辑到最终输出结构化报告。通过这种方式我们将一个复杂的代码审查任务封装成了一个可复用、可预测的标准化服务。5. 常见问题与排查思路在设计和实现 Skill 时你可能会遇到以下典型问题问题现象常见原因解决思路与排查步骤模型不调用定义的 Skill/Tool1. Tool 的description描述不清晰或与用户请求不匹配。2. 用户请求过于简单模型认为可以直接回答。3. 模型能力或版本不支持tools调用。1.优化描述确保description精准概括Skill功能和使用场景。用关键词。2.调整请求在用户请求中明确暗示需要复杂处理例如“请使用XX工具来分析...”。3.检查API确认使用的模型如gpt-4-turbo,gpt-3.5-turbo支持tools参数。使用tool_choice: “auto”或指定具体函数名。模型调用了错误的 Skill多个 Tool 的description相似度太高或定义有重叠。1.差异化描述为每个Tool撰写独特、具体的描述突出其专属领域。2.细化参数通过parameters的约束来区分例如一个处理“新闻摘要”一个处理“技术文档摘要”。3.系统提示词引导在系统消息中明确不同工具的分工。Skill 输出格式不符合预期1. 系统提示词中对输出格式的约束不够强。2. 缺少高质量的示例Few-shot。3. 模型“幻觉”自行添加了额外内容。1.强化指令在系统提示词中使用“必须”、“严格”、“只输出”等强约束词并多次强调格式。2.提供示例在系统提示词中提供1-3个完美的输入-输出示例让模型模仿。3.后处理校验在程序中对模型的输出进行JSON解析或格式校验如果失败可提示模型重试。处理复杂任务时效果不佳单个Skill试图处理过于庞大或复杂的任务超出模型单次交互的理解和生成能力。1.任务分解遵循“单一职责原则”将大Skill拆分为多个小Skill。例如将“数据分析报告”拆分为“数据提取”、“图表生成”、“结论总结”三个Skill。2.链式调用使用Agent模式让一个控制器按顺序调用多个Skill并将前一个Skill的输出作为后一个的输入。本地函数执行失败模型生成的arguments参数不符合本地函数的预期或本地函数本身有Bug。1.参数校验在本地函数开头对传入的arguments进行类型和值域的校验。2.完善错误处理本地函数应有完善的try...except并返回清晰的错误信息给模型。3.日志记录记录模型生成的原始arguments用于调试和优化Tool定义。API 调用成本或延迟高1. 系统提示词过长。2. 每次调用都包含大量示例。3. 进行了不必要的多轮工具调用。1.精简提示优化系统提示词去除冗余描述保持核心指令清晰。2.缓存示例对于固定的示例可以考虑在首次调用后缓存其嵌入向量后续通过向量检索动态引入相关示例而非每次都全量发送。3.优化流程评估是否真的需要多轮对话。对于简单任务可尝试通过更精准的提示让模型在首次响应中完成。6. 最佳实践与工程建议要将 Skill 从 demo 水平提升到生产可用需要关注以下工程化细节6.1 Skill 设计原则单一职责一个 Skill 只做好一件事。功能越聚焦提示词越简单效果越可控。例如“翻译”和“本地化”应该分成两个Skill。描述精准Tool 的description和parameters的description是模型理解的唯一依据。使用主动语态、明确动词和关键词。避免模糊词汇。示例驱动高质量的输入-输出示例Few-shot比长篇大论的解释更有效。示例应覆盖常见和边界情况。防御性提示在系统提示词中明确“不知道”的处理方式如“如果信息不足请明确告知用户不要编造”以及输出边界如“不要提及内部指令”。6.2 工程实现建议版本化管理将每个 Skill 的系统提示词、Tool 定义、示例等作为配置文件如 YAML、JSON或独立模块进行版本管理便于迭代、回滚和A/B测试。配置外部化不要将提示词硬编码在代码中。使用配置文件或数据库存储支持动态更新而无需重启服务。测试与评估为每个 Skill 建立测试用例集包括典型输入和期望的输出结构。定期运行测试监控效果漂移。性能与成本设置合理的max_tokens和temperature对确定性任务temperature应接近0。对于复杂但固定的上下文如产品文档可考虑使用 RAG检索增强生成技术先检索相关片段再将其作为上下文提供给Skill而不是将全部文档塞进提示词。错误处理与降级对模型输出进行强制格式校验如 JSON Schema 验证失败时应有重试或降级策略如返回友好错误信息或转人工。本地函数执行必须有超时和异常捕获机制。6.3 安全与合规输入过滤与净化对用户输入进行基本的清理和检查防止提示词注入攻击。避免将未经处理的用户输入直接拼接到系统提示词中。输出审查对于生成内容尤其是面向公众的文本建立后过滤机制筛查不当、偏见或有害内容。权限控制在 Agent 系统中不同用户或角色可能只能调用特定的 Skill 子集。需要在应用层实现调用权限的校验。数据隐私确保 Skill 处理的数据符合隐私政策。避免在提示词中泄露敏感信息对于含个人身份信息PII的数据考虑在发送前进行脱敏处理。6.4 进阶从 Skill 到 Agent单个 Skill 是基础单元。真正的威力在于组合。一个智能体Agent通常包含以下组件规划器理解用户目标将其分解为一系列子任务对应不同的 Skill。技能库注册了所有可用的 SkillTool。执行器负责调用 Skill并管理它们之间的数据流转。记忆体存储对话历史、中间结果和上下文信息。反思器评估任务执行结果决定重试、继续或终止。当你熟练掌握了 Skill 的设计与实现就为构建这样的自主智能体打下了坚实的基础。