从零构建AI Agent技能:基于MCP协议实现数据库查询实战
1. 项目概述从“经验”到“可调用能力”的范式转变最近在折腾AI Agent开发的朋友估计没少被“Skill”这个词刷屏。无论是GitHub上那些标着“xx-skill”的火热项目还是各种AI工具里突然冒出来的“技能市场”都在传递一个信号我们正在从“给AI下指令”的时代快速进入“为AI装配技能”的时代。这篇东西我想抛开那些花里胡哨的概念包装就从一个一线开发者的角度聊聊到底什么是Agent Skills以及我们怎么把自己或团队里那些宝贵的“经验”——比如怎么高效查数据库、怎么调用某个生僻的API、怎么写一份标准的项目周报——变成AI能理解、能稳定调用的“能力”。简单来说Agent Skill就是一个封装好的、可复用的功能模块。它让AI Agent不再只是一个“聊天机器人”而是一个能真正“动手做事”的智能体。想象一下你教会了一个新同事一套处理客服工单的标准流程先查用户历史、再根据问题类型匹配知识库、最后生成回复模板以后他就能独立处理这类问题。Agent Skill干的也是这个事只不过“同事”换成了AI。当前最主流的实现框架比如MCPModel Context Protocol本质上就是为AI模型如Claude、GPT提供了一套标准化的“外挂”接口协议让模型能安全、可控地调用外部工具、数据源或服务而这些“外挂”的具体实现就是一个一个的Skill。为什么这件事现在这么火因为痛点太明显了。以前我们让AI干活要么靠长篇大论的Prompt描述效果不稳定容易遗忘要么靠写死代码调用不灵活非开发者玩不转。Skill的出现相当于把中间层标准化了。它通过一个结构化的定义文件通常是skill.md或agents.md明确告诉AI我这个技能叫什么、能干什么、需要什么输入、会返回什么输出。AI模型根据这个“说明书”来决定何时调用、如何调用。对于开发者这意味着能力的沉淀和复用对于使用者这意味着AI变得更强大、更可控。2. 核心概念拆解Skill、MCP与生态文件要玩转Agent Skills得先理清几个核心概念和它们之间的关系不然很容易在各类文档和项目中迷失方向。2.1 Skill的本质结构化指令集与能力契约一个Skill远不止是一个函数或一段脚本。它是一个完整的、自描述的能力单元。我们可以从三个层面来理解它接口层Interface这是AI模型能“看见”的部分。通常由一个Markdown文件如skill.md定义里面用自然语言和结构化格式描述了技能的名称、描述、输入参数名称、类型、描述、是否必需、输出格式等。这就像一份给AI看的API文档。逻辑层Logic这是技能具体“怎么做”的部分。它可以是任何可执行的代码Python、JavaScript、Shell脚本、一个API调用封装、一个数据库查询模板甚至是一套复杂的决策流程。这部分对AI模型是“黑盒”模型只关心输入和输出。配置层Configuration技能运行可能需要密钥、端点URL、数据库连接串等配置信息。这些信息通常通过环境变量或配置文件管理确保安全性和灵活性。一个典型的skill.md文件可能长这样# Skill: 查询用户订单状态 **描述**: 根据用户ID或订单号从内部数据库查询订单的当前状态、物流信息及历史记录。 **输入参数**: - user_id (string, 可选): 用户的唯一标识符。与order_number至少提供一个。 - order_number (string, 可选): 订单号。 - include_history (boolean, 可选默认false): 是否包含订单状态变更历史。 **输出**: 返回一个JSON对象包含订单基本信息、当前状态、物流跟踪号如有以及可选的变更历史列表。 **调用示例**: “查询订单号为 ‘ORD-20240815-001’ 的详细信息包括历史记录。”这份“契约”让AI知道当用户问“我的订单到哪里了”时它可以主动请求order_number参数然后调用这个Skill去获取真实数据。注意Skill的描述质量直接决定AI的调用准确率。描述要清晰、无歧义并尽可能枚举常见的用户问法在描述或示例中体现这能极大地提升意图匹配的精度。2.2 MCP协议Skill的“通用插座”如果说Skill是各种电器功能那么MCPModel Context Protocol就是墙上的“通用插座”和“供电协议”。它是由Anthropic等公司推动的一个开放协议旨在为AI模型提供一个标准化、安全的方式来访问外部资源和功能。MCP的核心思想是解耦和标准化解耦将AI模型大脑和具体能力手脚分开。模型不需要知道Skill是用Python还是Go写的它只需要按照MCP协议发送请求和接收响应。标准化定义了一套统一的通信方式通常是基于JSON-RPC over stdio/HTTP/SSE、资源Resources如数据库表、文件列表和工具Tools即Skill的发现与调用机制。一个MCP服务器MCP Server就是一个实现了该协议的后台服务它对外暴露一个或多个Skill。主流的AI应用或平台如Claude Desktop、Cursor、Windsurf通过集成MCP客户端MCP Client就能自动发现并加载这些服务器提供的Skill从而扩展自身能力。MCP与Skill的关系MCP是“道”定义了能力交互的规则Skill是“术”是在此规则下实现的具体能力。你编写的Skill需要通过一个MCP Server包装起来才能被支持MCP的AI应用所使用。2.3 生态文件agents.md与claude.md的角色在具体项目中你可能会看到agents.md、claude.md之类的文件。它们通常是特定AI应用或框架的配置文件用于集中声明和管理本项目或本对话中可用的Skill。agents.md常见于一些Agent开发框架或平台。它是一个全局或项目级的技能清单可能包含更丰富的元数据如技能的分类、图标、依赖关系、适用场景等用于帮助AI或开发者更好地组织和发现技能。claude.md可能是为Claude系列模型优化的特定配置文件其格式和字段可能更贴合Claude模型的提示工程特点。它们和skill.md的区别在于skill.md是单个技能的完整自述文件。agents.md/claude.md是技能集合的目录或索引文件可能会引用多个skill.md并附加一些全局配置。在实际操作中很多简单场景下一个清晰的skill.md就足够了。复杂项目才需要agents.md来管理技能间的协作和优先级。3. 实战从零构建一个可用的Agent Skill光说不练假把式。我们以构建一个“查询本地SQLite数据库中的项目信息”的Skill为例完整走一遍流程。这个场景非常实用比如你可以用它来让AI查询你的个人笔记库、项目任务清单等。3.1 技能设计与规划首先明确技能的目标允许AI通过自然语言查询我们指定的SQLite数据库。输入用户用自然语言描述的查询意图例如“找出所有状态为‘进行中’的项目”、“显示张三上个月创建的任务”。处理我们需要将自然语言转换为安全的SQL查询语句并执行它。输出以清晰、友好的格式如表格返回查询结果。安全必须严格防范SQL注入并且限制查询范围不能允许任意SQL执行。基于MCP的实现我们需要编写Skill的逻辑一个Python脚本。用MCP Server包装这个逻辑。创建skill.md描述文件。在AI客户端中配置并测试。3.2 开发环境与MCP Server搭建我们选择Python来开发因为它有丰富的库和相对简单的MCP生态支持。步骤1初始化项目与环境mkdir project-query-skill cd project-query-skill python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install mcp sqlite-utils这里安装了mcp库一个Python的MCP服务器开发框架和sqlite-utils一个方便操作SQLite的库。步骤2创建数据库与示例数据我们先创建一个简单的数据库projects.db包含一张projects表。# create_db.py import sqlite3 conn sqlite3.connect(projects.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, owner TEXT, status TEXT, created_at TEXT ) ) # 插入一些示例数据 sample_data [ (网站改版, 张三, 进行中, 2024-07-01), (数据分析报告, 李四, 已完成, 2024-06-15), (移动端App, 王五, 规划中, 2024-08-01), (API接口开发, 张三, 进行中, 2024-07-20), ] cursor.executemany(INSERT INTO projects (name, owner, status, created_at) VALUES (?,?,?,?), sample_data) conn.commit() conn.close() print(数据库和示例数据创建完成。)运行python create_db.py完成初始化。3.3 编写Skill核心逻辑与MCP Server接下来是核心部分编写MCP Server并在其中定义我们的查询Skill。# mcp_server.py import sqlite3 import json from typing import Any from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions import mcp.server.stdio import mcp.shared.exceptions # 初始化MCP服务器 server Server(project-query-server) # 定义工具即我们的Skill server.list_tools() async def handle_list_tools(): # 返回我们提供的工具列表 return [ { name: query_projects, description: 根据项目名称、负责人或状态查询项目信息。可以接受自然语言描述但为了准确请尽量提供明确的筛选条件。例如‘找张三负责的项目’ 或 ‘状态是进行中的项目’。, inputSchema: { type: object, properties: { query_description: { type: string, description: 用自然语言描述你想查询的项目信息。 }, owner_filter: { type: string, description: 按负责人精确筛选可选。 }, status_filter: { type: string, description: 按状态精确筛选如 ‘进行中’、‘已完成’、‘规划中’可选。 } }, required: [query_description] } } ] # 处理工具调用 server.call_tool() async def handle_call_tool(name: str, arguments: dict[str, Any]) - list[dict]: if name query_projects: return await query_projects_in_db(arguments) raise mcp.shared.exceptions.McpError(f未知工具: {name}) async def query_projects_in_db(arguments: dict) - list[dict]: 执行数据库查询 query_desc arguments.get(query_description, ) owner_filter arguments.get(owner_filter) status_filter arguments.get(status_filter) # 连接数据库 conn sqlite3.connect(projects.db) conn.row_factory sqlite3.Row # 以字典形式返回行 cursor conn.cursor() # 构建安全的SQL查询 sql SELECT * FROM projects WHERE 11 params [] # 根据提供的过滤器添加条件 if owner_filter: sql AND owner ? params.append(owner_filter) if status_filter: sql AND status ? params.append(status_filter) # 执行查询 cursor.execute(sql, params) rows cursor.fetchall() conn.close() # 格式化结果 results [] for row in rows: results.append(dict(row)) # 返回给MCP客户端AI模型的结果需要遵循特定格式 return [{ type: text, text: f根据您的查询‘{query_desc}’找到 {len(results)} 条记录\n\n json.dumps(results, ensure_asciiFalse, indent2) }] async def main(): # 使用标准输入输出运行服务器这是MCP最常见的通信方式 async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await server.run(read_stream, write_stream, InitializationOptions()) if __name__ __main__: import asyncio asyncio.run(main())代码解读与注意事项安全第一我们使用了参数化查询?占位符来拼接SQL这是防止SQL注入的黄金法则。绝对不要用字符串拼接的方式将用户输入直接放入SQL语句。输入设计我们设计了query_description作为主要输入让AI可以传递用户的自然语言。同时也提供了owner_filter和status_filter这两个明确字段AI在能明确识别时可以直接使用使查询更精准。这是一种“模糊精确”的混合策略。结果格式化返回给AI的结果必须是模型易于理解的格式。这里我们返回了纯文本并将数据以格式化的JSON嵌入其中。更复杂的Skill可以返回结构化程度更高的内容如type: “object”。错误处理示例中省略了详细的错误处理如数据库连接失败在生产环境中必须补全并返回友好的错误信息给AI。3.4 创建技能描述文件skill.md为了让其他开发者或AI平台更好地理解我们的技能我们需要创建一份描述文件。这个文件可以独立于代码存在。# Skill: 项目信息查询器 **标识符**: project_database_query **描述**: 这是一个用于查询本地项目数据库的技能。它能够根据项目负责人、项目状态等条件从预定义的SQLite数据库中检索项目信息。适用于需要快速了解项目概况、筛选特定类型项目的场景。 **核心能力**: - 按负责人(owner)筛选项目。 - 按状态(status)筛选项目进行中、已完成、规划中。 - 支持通过自然语言描述查询意图技能会尝试解析并应用最相关的过滤器。 **输入参数说明**: 1. query_description (字符串必需): 用户查询意图的自然语言描述。例如“帮我找找张三负责的都在做哪些项目” 或 “列出所有还没完成的项目”。 2. owner_filter (字符串可选): 项目负责人的精确姓名。若提供将优先使用此条件。 3. status_filter (字符串可选): 项目状态的精确值。已知状态包括‘进行中’、‘已完成’、‘规划中’。 **输出格式**: 技能返回一个文本段落首先复述查询意图然后以JSON数组的形式列出所有匹配的项目记录。每条记录包含id, name, owner, status, created_at字段。 **使用示例**: - **用户提问**: “王五手上有哪些项目” - **AI调用**: query_projects(query_description: “王五手上有哪些项目”, owner_filter: “王五”) - **用户提问**: “现在有哪些项目还在进行中” - **AI调用**: query_projects(query_description: “现在有哪些项目还在进行中”, status_filter: “进行中”) **配置要求**: - 需要确保projects.db数据库文件位于MCP服务器的工作目录下。 - 数据库表结构需符合预期拥有name, owner, status, created_at字段。 **安全与限制**: - 本技能仅支持只读查询无法修改、删除数据。 - 查询范围被限定在projects表内无法访问其他表或执行任意SQL。这个skill.md文件是技能的“名片”和“说明书”对于技能的传播、理解和集成至关重要。3.5 在AI客户端中集成与测试以Claude Desktop为例展示如何集成我们刚开发的MCP Server。配置Claude Desktop 在Claude Desktop中MCP Server的配置通常放在一个特定的配置文件中。对于macOS位置可能在~/Library/Application Support/Claude/claude_desktop_config.json。编辑配置文件 在该JSON文件中找到或添加mcpServers配置项。将我们的Python脚本配置进去。{ mcpServers: { project-query: { command: /path/to/your/venv/bin/python, args: [/absolute/path/to/your/mcp_server.py], env: { PYTHONPATH: /absolute/path/to/your/project } } // ... 可以配置其他MCP Server } }command: 是你Python虚拟环境中python解释器的绝对路径。args: 是你的mcp_server.py脚本的绝对路径。env: 如果需要可以设置环境变量。踩坑提醒路径一定要用绝对路径这是最常见的问题之一。相对路径在Claude Desktop的运行时环境中很可能失效。重启与测试 保存配置文件并重启Claude Desktop。在聊天窗口中你现在可以直接问“帮我查一下所有状态是‘进行中’的项目”。Claude应该会识别出这个请求需要调用query_projects技能并在后台通过MCP协议与你的服务器通信最终将数据库查询结果返回给你。实测心得 第一次成功看到AI调用本地技能返回数据库结果时体验是非常奇妙的。它意味着你赋予了AI直接“感知”和“操作”你私有数据的能力而无需经过复杂的复制粘贴或手动查询。关键在于MCP配置要准确尤其是路径问题。建议在命令行先单独测试你的mcp_server.py是否能正常运行通常会等待标准输入这能排除代码本身的错误。4. 高级技巧与生态集成掌握了基础开发流程后我们可以看看如何提升Skill的实用性并融入更广阔的生态。4.1 技能设计的进阶模式动态参数与上下文感知 上面的例子参数是固定的。更高级的Skill可以根据运行时上下文动态生成参数列表。例如一个“文件操作”Skill可以先通过list_files工具让AI浏览目录再根据用户选定的文件调用read_file工具。这需要在list_tools的响应中提供更灵活的模式定义。复杂技能链Skill Chaining 一个复杂任务可能需要多个Skill协作完成。例如“生成季度报告”可能链式调用query_sales_data-analyze_trend-generate_chart-write_doc。这依赖于AI模型自身的规划和推理能力。我们在设计Skill时应保持其功能的单一性和接口的清晰性以方便组合。技能与提示词Prompt的协同 Skill负责“执行”而复杂的逻辑判断和流程控制有时更适合写在系统提示词System Prompt里。例如在系统提示中告诉AI“当用户询问数据时优先考虑使用‘项目查询’技能如果需要总结则使用‘分析’技能”。Skill和Prompt是互补的关系。4.2 连接外部服务以搜索类MCP Server为例除了操作本地资源Skill更强大的能力在于连接外部服务。社区已经有很多优秀的MCP Server实现例如tavily-mcp连接Tavily搜索API、brave-search-mcp连接Brave搜索。将它们集成到你的AI工作流中非常简单。以在Cursor或Claude Code中集成tavily-mcp为例安装MCP Server通常这些项目都提供了NPM包或Python包。# 假设是NPM包 npm install -g modelcontextprotocol/server-tavily配置AI客户端和之前配置自定义Server类似在客户端的配置文件中添加这个Server。// 例如在Cursor的settings.json中 { mcpServers: { tavily-search: { command: npx, args: [-y, modelcontextprotocol/server-tavily], env: { TAVILY_API_KEY: your_tavily_api_key_here } } } }使用配置完成后重启客户端。当你问AI“今天AI领域有什么最新新闻”时AI就可以调用集成的搜索技能获取实时信息来回答你而不是依赖于它可能过时的训练数据。重要提示使用外部API时务必妥善管理API密钥通过环境变量传入切勿硬编码在配置文件中或上传到公开仓库。4.3 技能调试与问题排查实录开发Skill时难免会遇到AI不调用、调用出错等问题。以下是一些常见问题及排查思路问题现象可能原因排查步骤AI完全“无视”技能从不调用。1. MCP Server配置错误未成功加载。2. Skill描述(description)不够清晰AI无法匹配用户意图。3. 客户端不支持或未启用MCP。1. 检查客户端配置文件的语法和路径查看客户端日志是否有加载错误。2. 优化skill.md中的描述加入更具体、更贴近用户常见问法的例子。3. 确认你使用的AI客户端版本是否支持MCP。AI尝试调用但失败提示“工具未找到”或调用错误。1. MCP Server进程启动失败或崩溃。2. 工具Skill名称在代码和描述中不一致。3. 输入参数格式不符合inputSchema定义。1. 在命令行手动运行MCP Server脚本看是否有报错如缺少依赖库。2. 核对server.list_tools返回的name和AI调用的name是否完全一致大小写敏感。3. 使用简单的参数进行最小化测试确保Server能正确处理请求。技能被调用但返回结果不符合预期或为空。1. 技能内部逻辑错误如SQL查询条件错误。2. 权限或资源问题如数据库文件无法读取。3. 结果格式化错误AI无法解析。1. 在Skill代码中添加详细的日志打印接收到的参数和中间结果。2. 检查文件路径、API密钥、网络连接等。3. 确保返回给MCP协议的数据格式正确通常是包含type和content的列表。技能响应缓慢。1. 依赖的外部API或数据库查询慢。2. MCP Server启动或初始化耗时过长。1. 优化Skill内部逻辑考虑增加缓存、使用更高效的查询。2. 对于复杂初始化考虑使用Server的initialization钩子或实现资源懒加载。调试心法始终记住MCP是一个客户端-服务器协议。当出现问题时要隔离判断是客户端AI的问题、服务器你的Skill的问题还是两者之间的通信问题。最有效的方法就是单独测试MCP Server很多开发框架都提供了测试工具或者你可以自己写一个简单的测试客户端来发送模拟请求。5. 技能生态展望与个人实践建议Agent Skills和MCP协议正在快速演化社区生态日益活跃。从Git上那些标星数飙升的Skill仓库就能感受到这股热潮。未来的方向可能会围绕以下几个方面技能市场与发现可能会出现更中心化的Skill商店让开发者可以发布技能用户一键安装。mcp市场、skill推荐等热词反映了这种需求。技能组合与编排当前技能链依赖AI的自主规划未来可能出现可视化的技能编排工具让非开发者也能通过拖拽搭建复杂的工作流。技能评估与排名像ai skills最新排行榜这类概念会变得重要。如何评估一个Skill的准确性、易用性、安全性会催生新的标准和工具。垂直领域深化针对特定行业如数学建模skill、skill语言学习或专业工具如Figma mcp、obsidian的mcp的Skill会越来越丰富和专业化。对于想要入局或已经在实践的开发者我的建议是从解决自己的一个具体问题开始最好的Skill往往源于个人或团队的真实痛点。比如自动整理每日Git提交记录、监控服务器状态并生成摘要、从设计稿中提取颜色规范等。实用性是第一驱动力。遵循“单一职责”与“良好描述”原则一个Skill只做好一件事并用清晰、无歧义的自然语言在skill.md中描述它。这是确保AI能正确理解和调用的关键。安全与隐私是底线尤其是处理敏感数据或操作关键系统的Skill必须内置严格的权限控制和输入验证。不要信任来自AI的任意输入始终假设它可能是恶意的。积极参与社区多看看GitHub上热门的MCP Server和Skill项目如搜索类、数据库操作类学习别人的设计模式和代码实现。很多共性问题社区已有解决方案。我个人在将团队内部的项目管理经验封装成Skill后最深的体会是它带来的不仅是效率提升更是工作流的固化与知识沉淀。以前需要口口相传或者写在Confluence里等人查阅的“经验”现在变成了AI可以随时调用的“能力”。新同事 onboarding 时AI就能直接指导他按照最佳实践来操作。这个过程本质上是在构建一个属于你自己或团队的、可进化的“数字肢体”让AI这个“大脑”真正落地去解决那些具体而微的现实问题。