装了个 AI Skill 却查不了数据,这种事儿我最近真没少碰。上个月给 AI Agent 配了个销售数据查询类 Skill,装完之后信心满满,上来就让 AI"查一下本月华东区销售额",结果它愣是给我回了一句"我无法直接访问数据库,请提供数据文件"。当时我第一反应是模型不行,后来排查了一下午才发现,问题根本不在模型,而在这个 Skill 背后调用接口的方式没选对。AI Skill 本身只是一份"技能说明书",真正让它拿到数据,靠的是 scripts、CLI、MCP 这三条通道。这篇文章不聊虚的,就聊我实际调试时踩过的坑——为什么装好的 Skill 查不了数据,三种调用接口到底怎么选、怎么配、怎么排查。
1. 先搞懂 AI Skill 的构成,再谈查数据
1.1 SKILL.md 决定 AI 会不会用你的 Skill
很多人对 Skill 有一个误解,以为 Skill 是一个"能跑的程序"。其实在 Claude Code、Codex 这类 Agent 工具里,Skill 更像一个"岗位说明书 + 工具包"的目录结构。最典型的布局是:
- SKILL.md:核心描述文件,告诉 Agent 这个技能是干嘛的、什么时候该用、具体怎么操作。里面通常会写明"执行 scripts/xxx.py 获取数据""运行某条 CLI 命令查询状态"这类操作指引。
- scripts/:放辅助脚本,Python、Shell、Node 都行。
- references/:放参考文档、SQL 模板、常见问题说明。
- assets/:偶尔放静态文件。
关键点在于,SKILL.md 就是 AI 决定"怎么调用"的依据。如果 SKILL.md 只写了"本技能用于查询销售数据",却没写明具体命令、运行前提、输出格式,那 AI 就只能靠猜,然后给你一句"我无法获取数据"。这不怪模型,怪技能没写好。我习惯把 SKILL.md 当成"给 AI 的入职培训手册"——它的质量直接决定 AI 是替你干活还是当场摆烂。后面我会给出可以直接抄的模板片段。
1.2 查不了数据,病根通常在三条通道之一
我把自己和身边人踩过的坑梳理了一遍,"Skill 查不了数据"基本逃不出三种情况:
- 通道一(scripts 路径):AI 应该去执行脚本,但脚本跑不起来。原因集中在执行权限、依赖缺失、路径写错、输出格式 AI 解析不了。
- 通道二(CLI 路径):AI 应该调用系统命令行工具,但命令本身不可用,或者输出是交互式界面、表格文本,AI 拿不到完整结果或读不懂。
- 通道三(MCP 路径):Skill 想通过 MCP 调用数据工具,但 MCP server 没启动、工具没注册、配置里的 transport 不对,AI 根本找不到这个工具。
这个分类特别重要。因为排查的时候,如果你能先判断"我这套 Skill 走的是哪条通道",再往对应方向去看日志、看配置、手动复现,效率会成倍提升。下面三种方式逐个讲透。
2. scripts 方式:让 AI 执行脚本拉数据
2.1 原理与适用场景
scripts 方式是三种方式里最"朴素"的:把数据读取逻辑写成一个脚本,AI 通过 SKILL.md 里的指令找到并执行它,然后从脚本输出(通常是 stdout)里读取数据。可以这么理解:Skill 里的 scripts 就像后厨的菜谱,AI 是照着菜谱做菜的厨师,它把 scripts/query_sales.py 这道菜端上来,其中真正有用的"菜品"就是脚本打印出来的数据。
这种方式的优势是门槛低:脚本文件放对位置,给足读取权限,把运行命令写清楚,AI 一般就能跑。缺点是脆弱:脚本一旦报错,AI 靠自己纠错的能力很有限;输出格式稍微混乱一点,AI 就可能解析出完全错误的结论。适用场景也很明确:
- 数据源是本地文件(CSV、JSON、Excel),需要做汇总、过滤、计算。
- 一次性任务,比如从日志里提取某段时间的错误率。
- 没有现成 API,也不值得为它单独搭一个服务。
我自己最早的 Skill 就是 scripts 方式,因为它是三种方式里最快能跑通的。
2.2 脚本本身要能被 AI "盲操作"
写 Skill 附带脚本和写普通脚本是两回事。普通脚本是给人跑的,Skill 脚本是给 AI 这个"不太可靠的执行者"跑的。我总结了几条硬性要求。
第一,脚本必须有明确入口。Python 脚本要保证if __name__ == "__main__":干净清晰,AI 大概率会用python scripts/xxx.py这种姿势执行,入口不清晰容易出幺蛾子。
第二,输出必须机器可读。AI 是解析 stdout 的,不是看人类友好的表格。建议脚本把结果print成 JSON、CSV 这类结构化格式,并且不要在 stdout 里夹带"正在处理中…"这种日志。日志走 stderr,数据走 stdout,这个习惯救了我很多次。
我举个例子,一个把 CSV 销售额按区域汇总的脚本:
#!/usr/bin/env python3 import csv import json import sys def main(csv_path: str): summary = {} with open(csv_path, newline="", encoding="utf-8") as f: for row in csv.DictReader(f): region = row.get("region", "未知") amount = float(row.get("amount", 0)) summary[region] = summary.get(region, 0) + amount print(json.dumps(summary, ensure_ascii=False)) if __name__ == "__main__": main(sys.argv[1])这段脚本输出的是{"华东": 123456.0, "华南": 88000.0}这种结构,AI 一眼就能看懂。注意,如果脚本里多写一句print(f"读取了 {len(rows)} 行数据"),AI 会把这句话也当数据解析,污染结果。所以"数据归 stdout,噪音归 stderr"是铁律。
第三,依赖别太多。Skill 脚本最好只依赖标准库,或者用 requirements.txt 把依赖写清楚。我踩过最狠的一个坑是:Skill 里用 pandas 处理 Excel,结果 Agent 环境里根本没有 pandas,AI 尝试pip install又没有网络权限,整个查询流程直接凉了。
2.3 权限和路径:两个最容易翻车的地方
scripts 方式最常见的报错有三个。
第一个是 Permission denied。脚本没有执行权限,或者 Agent 以受限用户身份执行。如果你的 SKILL.md 里写的是直接./scripts/xxx.py,那就必须提前执行chmod +x scripts/query_sales.py;如果 AI 是用python scripts/xxx.py执行,文件本身可读即可,不用加执行位,但目录权限得保证。
第二个是 File not found。SKILL.md 里写的是相对路径,但 Agent 的工作目录不一定在 Skill 所在目录。最稳的做法是:脚本内部基于自己的所在目录定位数据文件,或者 SKILL.md 里明确写出从 Skill 根目录运行的命令,比如cd /绝对路径/skill-name && python scripts/query_sales.py data/sales.csv。
第三个是依赖导入失败。这个上面提过,能标准库就标准库;实在有第三方依赖,必须把安装命令原原本本写进 SKILL.md。还有一个细节:AI 执行失败后,有时候会擅自修改你的脚本,比如改路径、改编码。这倒不全是坏事,但改多了容易越改越乱。我的习惯是把脚本里的关键参数设计成命令行参数,让 AI 想调整时只换参数,而不是动源码逻辑。
3. CLI 方式:把命令行变成数据源
3.1 CLI 调用的机制
如果说 scripts 是"给 AI 定制脚本",那 CLI 就是"让 AI 直接用现成的命令行工具"。SKILL.md 里写清楚命令和参数,AI 在终端里执行,然后从标准输出解析结果。和 scripts 的本质区别在于:scripts 的执行主体是脚本,CLI 的执行主体是系统里已有的工具,比如 sqlite3、psql、docker、git、curl。
一个很常见的场景:Skill 要查数据库,但没有 API 服务,只有 sqlite3 命令。那 SKILL.md 里可以这么写:
## 查询数据 使用 sqlite3 查询数据库文件 data.db: sqlite3 -header -csv data.db "select region, sum(amount) from sales group by region;" AI 拿到 CSV 输出后再整理成结论。CLI 方式的适用场景我总结为三类:
- 已经有成熟 CLI 工具,比如查 Git 历史、查 Docker 容器状态。
- 数据库有官方命令行客户端,不想为 AI 再包一层服务。
- 系统运维类技能,AI 需要通过
ip、ps、df这类命令了解机器状态。
这套方式的好处是零开发成本,坏处是"零开发"的同时也意味着"零控制"——你能控制的只有命令参数,没法重写工具的输出格式。
3.2 输出解析:CLI 输出的坑比脚本还多
CLI 方式最常翻车的地方,就是输出格式。真实世界的 CLI 输出是给人看的,不是给 AI 解析的,问题集中在三处。
第一,交互式命令。很多 CLI 不带参数运行时会进入交互模式,比如 sqlite3 不带 SQL 参数会进入.help提示符,psql 不带-c也会停在等待输入状态。AI 执行这类命令,要么卡住,要么返回一堆提示文案。解决办法是给所有命令加"非交互"参数:sqlite3 加-batch,psql 用-c "SQL",mysql 用-e "SQL",python 用-c 参数。
第二,表格格式。默认情况下 sqlite3 输出的是一堆|分隔的表格,AI 解析时容易错位。我现在强制改成 CSV 或 JSON 输出:
- sqlite3 加
-header -csv。 - psql 用
\pset format unaligned或\copy ... TO STDOUT CSV。 - 支持
--json的命令直接--json。
第三,输出量太大。AI 执行命令通常有时长限制,输出 token 也有限制。如果查询结果有 10 万行,AI 根本读不完。SKILL.md 里必须限制返回行数:SQL 加LIMIT 100,日志查询加tail -50,批处理任务预聚合。
我再给一个可以直接抄进 SKILL.md 的 CLI 查询小节:
## 查询数据库 1. 先看表结构: sqlite3 -header -csv data.db ".tables" 2. 查询时始终使用 -header -csv,控制结果在 100 行内: sqlite3 -header -csv data.db "select * from sales order by amount desc limit 100;" 3. 结果以 CSV 形式解读,不要臆造表头。3.3 命令可用性与授权问题
CLI 方式有个特别隐蔽的问题:AI 环境里可能根本找不到某些命令。比如你本机装了 sqlite3,但 Agent 跑在隔离沙箱里,未必有这个二进制。所以装好 Skill 后,第一步应该手动验证:在 Agent 能访问的终端环境里,原样运行一次 SKILL.md 里的命令,确认输出正常,再让 AI 接管。
至于授权,很多 Agent 工具对命令有执行白名单/黑名单机制。默认情况下,敏感命令(比如rm、访问外部地址的curl、修改系统配置的命令)可能被直接拦截。如果你的 Skill 就是要curl一个内部接口,得提前在 Agent 配置里放行。
我给两条原则:查询类命令尽量走白名单,避免 AI 自由发挥出危险命令;任何写操作命令,SKILL.md 里要明确"仅在用户明确要求时执行",别让 AI 养成乱改的习惯。CLI 方式对 AI 的"听话程度"要求最高,因为输出格式完全依赖工具,AI 只能被动解析,它改变不了工具本身。所以尽量选择输出格式好控制的工具,实在不行就在命令外层包一个脚本做格式化——这就是 scripts 和 CLI 的结合。
4. MCP 方式:把数据查询变成标准工具调用
4.1 MCP 到底是什么
MCP(Model Context Protocol)是专门为解决"AI 要调用外部数据/工具却各自为政"而生的协议。打个比方,MCP 就是 AI 世界的 USB-C 接口:过去每个设备都要自己的充电线,各家 AI 有各自的插件、API 格式;MCP 统一了接口标准,让 AI 客户端通过同一套协议去发现并调用工具。
和 scripts、CLI 最大的区别是:scripts/CLI 是"AI 去理解脚本或命令行",本质靠文本解析;MCP 则是"工具主动提供结构化调用入口"。MCP server 会声明自己有哪些 tool,每个 tool 带 inputSchema(参数定义),AI 按 schema 传参,MCP server 返回结构化 JSON。整个过程没有"解析表格文本"的环节,数据基本不会因为格式问题而丢失。
MCP 里三个核心概念,我理一下:
- tools:AI 可以直接调用的函数,比如
query_sales(region, month)。 - resources:AI 可以读取的数据资源,类似文件但走统一 URI。
- prompts:预设的提示模板,帮 AI 知道怎么完成任务。
4.2 写一个最小 MCP Server
如果不想引入太重的框架,用 Python 官方 SDK 里的 FastMCP 模块写最小 server 非常快。给一个能跑的例子:
from mcp.server.fastmcp import FastMCP mcp = FastMCP("sales-data") @mcp.tool() def query_sales(region: str, month: str) -> dict: """按区域和月份查询销售额,返回 JSON 格式结果。""" # 这里替换成真实数据源查询 data = { "region": region, "month": month, "total_amount": 123456.0, "unit": "元" } return data if __name__ == "__main__": mcp.run()这个 server 暴露了一个query_sales工具,AI 客户端通过 MCP 协议自动发现它。真正集成时只需要做两件事:启动 MCP server(stdio 方式通常由客户端拉起,SSE/HTTP 方式需要自己启动服务);在 AI 客户端的 MCP 配置里注册这个 server。
4.3 注册配置与调试
以普遍做法为例,在 MCP 配置文件里写明 server 名称、command、args。比如用 stdio 方式启动上面的 Python server,配置大概是这样:
{ "mcpServers": { "sales-data": { "command": "python", "args": ["/path/to/sales_mcp_server.py"] } } }配好之后,AI 就能在会话里看到 sales-data 这个 server 提供的工具了。
MCP 方式的高频坑,我列几个最典型的:
- server 没启动:stdio 方式的 server 由客户端负责拉起,如果你的 Python 环境不对、依赖缺失,server 会静默失败。排查顺序是先在命令行手动运行 server,确认不报错,再交给客户端。
- transport 对不上:客户端配了 SSE 地址,但你 server 是 stdio 模式,两边永远握手失败。先确认 server 跑的是哪种 transport。
- 工具没有权限:部分客户端会要求你批准某个工具后才能调用。Skill 依赖的 tool 若是首次出现,记得先去"同意工具使用"。
- 日志看不到:建议给 server 加文件日志,输出到 stderr。很多 server 挂掉的根本原因是路径、环境变量问题,有日志才好定位。
现在的 MCP 生态里已经有很多现成 server:数据库类、浏览器类、设计工具类、办公软件类。比如浏览器自动化方向,browser-use-mcp 和 playwright-mcp 的区别就很有代表性:browser-use-mcp 偏"让 AI 通过浏览器自主操作拿数据",playwright-mcp 偏"按固定脚本做自动化测试"。选哪个取决于你的 Skill 是要"AI 动态探索网页"还是"跑固定 E2E 流程"。用现成 server 的最大好处是:不用自己写脚本,在配置里注册,SKILL.md 里写上"使用 xxx MCP 工具的 xxx 能力"就行。
5. 三种方式对比、选型与排查清单
5.1 一张表看清三者的定位
| 对比维度 | scripts | CLI | MCP |
|---|---|---|---|
| 核心机制 | AI 执行脚本并解析 stdout | AI 执行系统命令并解析输出 | AI 调用标准定义的工具,返回结构化 JSON |
| 开发成本 | 低,写脚本即可 | 最低,用现成命令 | 中高,需要写或配 server |
| 稳定性 | 中,输出格式依赖脚本质量 | 低,CLI 输出多为人类设计 | 高,结构化协议保证 |
| 适用数据源 | 本地文件、一次性计算 | 系统状态、有 CLI 的数据库/工具 | 数据库 API、长期复用的工具 |
| 典型坑 | 权限、依赖、路径 | 交互模式、表格解析、命令缺失 | server 未启动、transport 不匹配 |
5.2 排查清单:从"查不了"到"查得到"
遇到 Skill 查不了数据,千万别慌,按下面的步骤一步步走。这是我在无数次翻车之后总结出来的排查路径。
第一步,确认 Skill 真的被加载。直接在对话里问 AI"你能使用哪些技能?",或者看 Agent 日志确认 SKILL.md 是否被读取。有时候配置了但没生效,AI 根本没机会执行你的脚本。
第二步,在终端手动复现 Skill 里的指令。如果是 scripts,手动跑一下python scripts/xxx.py;如果是 CLI,手动敲一遍命令。这一步能过滤掉 80% 的问题——如果手动都跑不通,那问题压根不在 AI,而在技能本身。
第三步,确认数据源权限。文件能否读取?数据库账号有没有权限?外部接口通不通?AI 环境可能是隔离的,本机能访问不代表 AI 能访问。
第四步,看 AI 报错的原文。AI 的错误信息其实很有价值:"permission denied"指向权限,"command not found"指向命令或 PATH,"no such file"指向路径。不要只盯着它最后那句"我无法完成",要看上下文。
第五步,按通道定向排查:
- scripts:检查脚本 stdout 是否干净、依赖是否齐全、路径是否绝对。
- CLI:检查命令是否可执行、是否交互、输出格式是不是 CSV/JSON。
- MCP:检查 server 是否存活、transport 是否匹配、工具是否被批准。
5.3 高频问题速查表
| 现象 | 可能原因 | 解决手段 |
|---|---|---|
| 脚本 Permission denied | 无执行权限或受限用户 | chmod +x,或改由 python 执行 |
| 找不到数据文件 | 相对路径错、工作目录不对 | 用绝对路径,或脚本内基于自身目录定位 |
| AI 说命令不存在 | PATH 缺失、沙箱没有该 CLI | SKILL.md 写绝对路径,或提前安装工具 |
| sqlite3 输出卡住 | 进入了交互模式 | 加 -batch、-cmd,或把 SQL 直接写在命令行 |
| 查询结果解析混乱 | 表格或自由文本输出 | 强制 CSV/JSON 输出,并限制返回行数 |
| MCP 工具找不到 | server 没启动或没注册 | 手动启动 server 验证,再检查配置 |
| MCP server 连不上 | transport 不一致或端口被占 | 确认 stdio 或 SSE/HTTP 与客户端一致 |
5.4 我的选型思路
最后聊点实际的选型经验。装 Skill 之前,先问自己三个问题:这个数据是一次性查还是长期要查?数据源有没有现成 CLI 或 API?Skill 的使用者会不会经常换环境?
一般情况下我的原则是:一次性的、本地文件类的查询,用 scripts 最省事;系统状态、已有成熟 CLI 的,用 CLI,但一定把输出格式固化下来;要稳定长期复用、被多个 Skill 共享的数据源,直接上 MCP,成本虽高但回报最大。
我个人实际更偏爱 MCP 一点,因为它的结构化反馈对 AI 太友好了。scripts 和 CLI 更像是应急方案:不依赖额外服务,轻量,但每次调试都像开盲盒。如果你只有一个下午的时间想把数据查通,我建议先走 scripts,它能覆盖掉绝大多数"本地查数据"的场景;如果发现 scripts 一直因为环境问题翻车,再考虑往 MCP 迁移。
踩过几次坑之后,我现在养成了一个习惯:所有 Skill 里的脚本,一律用相对路径加参数传路径;凡是第三方依赖,必须在 SKILL.md 里写清楚安装命令;MCP server 一定要写一个自检命令,跑通了再收工。还有一个独门小技巧——我会在 SKILL.md 里专门加一段"故障排查"小节,告诉 AI 如果脚本执行失败应该往哪些日志看、该运行哪些诊断命令。这个看似多余的段落,实际上能救回很多次"查不了数据"的尴尬局面。