1. 为什么要在本地项目里折腾一个数据分析智能体
Qwen Code Skills 是 Qwen Code 里一个偏实验性质的能力:它允许你把某类专业知识打包成一个可被模型发现、按需加载的“技能包”,每个技能的核心就是一个SKILL.md文件,里面写清楚这个技能是干什么的、什么时候该用、以及具体怎么执行。配合可选的脚本和模板文件,你就能让编码助手在遇到特定问题时,自动加载对应技能并调用你写好的工具脚本。
这篇要做的,是把“数据分析”这件事做成一个可复用的 Skill:在本地项目里放一个SKILL.md,再配一个执行 SQL 的 Python 脚本,让 Qwen Code 能听懂“确诊人数 Top10 的县是哪几个”这种自然语言提问,自己生成 SQL、跑脚本、拿到结果,再用人话解读给你听。适合谁?适合手头有本地数据库、想让 AI 帮忙做取数和初步分析、又不想把库连接信息到处散落的开发者。
整个链路里有两个关键点:一是SKILL.md的骨架要写对,模型才知道什么时候触发、怎么调脚本;二是模型请求要走一条稳定的 API 通道,我用 TaoToken 的统一 Key 和 API 地址来承接 Qwen Code 的模型调用,这样本地项目里只维护一份config.toml就够了。下面从环境准备一路写到跑通验证和排错。
2. TaoToken 前置:统一 Key 与 API 通道
Qwen Code 本身是命令行工具,它需要连到一个模型服务来推理。如果你每个项目、每个工具都单独配一套 Key 和地址,维护起来会很乱。TaoToken 提供统一的 API 入口,你只需要在控制台生成一个 Key,然后在 Qwen Code 的配置里指向它即可。
先到官网了解整体能力,再进控制台创建 Key:
- 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- 控制台(创建/管理 Key):https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
- API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
API 基础地址统一用https://taotoken.net/api(这个地址不加 UTM 参数,直接作为 base_url 填进配置)。拿到 Key 之后,Qwen Code 的模型请求就会走这条通道,你本地只需要关心技能包和数据库,不用在多个服务之间来回切换凭证。
注意:Key 属于敏感凭证,不要写进会提交到 Git 的
SKILL.md或脚本里。放在用户级配置或环境变量中更稳妥。
3. 可复制配置:config.toml 与 SKILL.md 骨架
3.1 Qwen Code 的 config.toml 配置片段
Qwen Code 读取用户级配置来连接模型服务。下面这段是接入 TaoToken 统一通道的写法,把base_url指向https://taotoken.net/api,api_key换成你在控制台生成的那一串:
# ~/.qwen/config.toml [model] provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" model = "qwen3-coder-plus" [model.params] temperature = 0.2 max_tokens = 8192temperature调低一点,是因为数据分析场景里我们更希望模型稳定地生成 SQL,而不是发挥创意。model字段按你实际可用的模型名填写,接入文档里有可选清单。
3.2 技能包目录结构
在项目根目录下创建技能包,个人级技能放~/.qwen/skills/,项目级放项目内的.qwen/skills/。项目级的好处是可以跟团队共享,这里用项目级:
mkdir -p .qwen/skills/db/scripts最终结构:
.qwen/skills/db/ ├── SKILL.md └── scripts/ └── run_sql.py3.3 SKILL.md 骨架
SKILL.md的 frontmatter 里name和description都不能为空,Qwen Code 会校验。description写得越贴近真实提问场景,模型越容易在合适的时候加载它。正文里把表结构、数据样例、调用脚本的指令示例都写清楚:
--- name: db-skill description: COVID-19 数据查询,包括美国 2021-01-28 各个县 county 的新冠疫情累计案例信息,含确诊病例和死亡病例 --- # 数据查询 ## 数据表结构 ```sql CREATE TABLE `us_covid19_counties` ( `date` varchar(255) DEFAULT NULL COMMENT '日期', `county` varchar(255) DEFAULT NULL COMMENT '县', `state` varchar(255) DEFAULT NULL COMMENT '州', `fips` varchar(255) DEFAULT NULL COMMENT '县编码code', `cases` int DEFAULT NULL COMMENT '累计确诊病例', `deaths` int DEFAULT NULL COMMENT '累计死亡病例' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='COVID-19 案例';数据样例
date,county,state,fips,cases,deaths 2021-01-28,Pike,Alabama,01109,2704,35 2021-01-28,Shelby,Alabama,01117,19878,141 2021-01-28,Tuscaloosa,Alabama,01125,22083,283指令
执行查询脚本,一次只传一句 SQL:
python -u scripts/run_sql.py --sql "{sql}"示例
统计每个州的累计确诊病例:
python -u scripts/run_sql.py --sql "SELECT state, SUM(cases) AS total_cases FROM us_covid19_counties GROUP BY state ORDER BY total_cases DESC"这里脚本调用我写的是 `python -u scripts/run_sql.py`,前提是你的 Python 在环境变量里。如果你用的是 conda 环境,把它换成绝对路径,比如 `E:\anaconda\conda\envs\openai\python.exe -u scripts/run_sql.py --sql "{sql}"`,避免模型执行时找不到解释器。 ### 3.4 run_sql.py 脚本 脚本负责真正连库、执行、把结果以可读形式打印出来。放在 `.qwen/skills/db/scripts/run_sql.py`: ```python import argparse import pymysql def get_conn(): return pymysql.connect( host="127.0.0.1", port=3306, database="test3", user="root", password="root", autocommit=True, charset="utf8mb4", ) def query(sql): conn = get_conn() cursor = conn.cursor() cursor.execute(sql) columns = [col[0] for col in cursor.description] rows = [dict(zip(columns, row)) for row in cursor.fetchall()] cursor.close() conn.close() return rows def run_sql(sql: str): """执行 MySQL 查询,一次仅执行一句 SQL。""" try: if not sql: print("请传递需要查询的 SQL!") return result = query(sql) print(f"数据库查询结果: \n{result}") except Exception as e: print(f"执行 SQL 错误:{str(e)},请修正后重新发起。") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Run a SQL query.") parser.add_argument("--sql", type=str, required=True, help="SQL query to execute") args = parser.parse_args() run_sql(args.sql)脚本里把异常捕获后打印成一句提示,是为了让模型看到错误信息后能自己修正 SQL 再重试,而不是直接崩掉。这一点在智能体循环里很关键。
4. 验证请求:从提问到 SQL 执行与结果校验
4.1 准备数据表
先把数据表建好并导入 CSV。建表语句和SKILL.md里保持一致:
CREATE TABLE `us_covid19_counties` ( `date` varchar(255) DEFAULT NULL COMMENT '日期', `county` varchar(255) DEFAULT NULL COMMENT '县', `state` varchar(255) DEFAULT NULL COMMENT '州', `fips` varchar(255) DEFAULT NULL COMMENT '县编码code', `cases` int DEFAULT NULL COMMENT '累计确诊病例', `deaths` int DEFAULT NULL COMMENT '累计死亡病例' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='COVID-19 案例';导入可以用LOAD DATA LOCAL INFILE,也可以先用 Python 的 pandas 读 CSV 再to_sql。导入后随手查一句确认行数:
SELECT COUNT(*) FROM us_covid19_counties;4.2 启动 Qwen Code 并加载技能
Skills 目前是实验特性,启动时要显式开启:
qwen --experimental-skills进入交互后,先确认技能被识别:
你当前有哪些 Skills如果配置正确,它会列出db-skill以及描述。这一步没看到技能,先别急着提问,回到第 5 节排查。
4.3 一次完整的提问到执行
输入一个自然语言问题:
确诊人数 Top10 的县是哪几个?预期行为是:Qwen Code 识别到这是数据查询场景,加载db-skill,根据SKILL.md里的表结构生成 SQL,然后调用脚本执行。它生成的 SQL 大致是:
SELECT county, state, cases FROM us_covid19_counties ORDER BY cases DESC LIMIT 10;脚本返回结果后,模型会把结果整理成一段解读,比如指出排名靠前的县集中在哪些州、病例量级差异等。你可以手动跑一遍同样的 SQL 做交叉校验:
python -u scripts/run_sql.py --sql "SELECT county, state, cases FROM us_covid19_counties ORDER BY cases DESC LIMIT 10"两边结果一致,说明从提问到 SQL 执行这条链路是通的。
4.4 再验证一个聚合场景
换个需要聚合的问题,检验模型是否会正确使用GROUP BY:
统计每个州的累计确诊病例,按从高到低排序对应 SQL:
SELECT state, SUM(cases) AS total_cases FROM us_covid19_counties GROUP BY state ORDER BY total_cases DESC;如果模型生成的 SQL 里把SUM写成了COUNT,结果会明显偏小,这时候脚本不会报错,但解读会不合理。所以结果校验这一步不能省:拿模型给的数字和你自己跑出来的数字对一下,尤其是聚合类查询。
5. 本篇常见错排查
5.1 技能没被加载
现象是提问后模型直接凭记忆回答,没有调用脚本。先确认启动命令带了--experimental-skills,再检查SKILL.md的 frontmatter 里name和description是否都非空。目录层级也要对:项目级必须是.qwen/skills/db/SKILL.md,多一层少一层都不会被识别。
5.2 脚本执行报“找不到 python”
SKILL.md里写的是python -u scripts/run_sql.py,但你的 Python 不在 PATH 里。解决办法是在SKILL.md的指令部分直接写解释器绝对路径,比如 conda 环境下的完整路径。改完不需要重启项目,重新提问即可。
5.3 SQL 执行报字段或表不存在
多半是模型生成的 SQL 和真实表结构对不上。检查SKILL.md里的建表语句是否和数据库里的一致,字段名、表名大小写都要对。表特别多的时候,可以在技能包里再加一个scripts/describe_tables.py,让模型先查表结构再写 SQL。
5.4 中文结果乱码
连接时没指定字符集。run_sql.py里pymysql.connect加上charset="utf8mb4",建表也用utf8mb4,两边对齐就不会乱码。
5.5 模型请求失败或超时
先确认config.toml里base_url是https://taotoken.net/api,api_key没有多余空格。如果报鉴权错误,去 API Keys 页面核对 Key 是否有效、额度是否充足。接入细节以接入文档为准。
6. 把这条链路用起来
跑通之后,这个技能包就能反复用了。我的习惯是把它当成项目里的一个“取数助手”:新来一份数据,先更新SKILL.md里的表结构和样例,脚本基本不用动;提问时尽量把口径说清楚,比如“按州聚合”“只看 2021-01-28”,模型生成的 SQL 会更准。
如果你后面要做更长期的编码和 Agent 任务,可以了解 Coding Plan,把模型调用和额度统一管理起来:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite
想直接在网页里验证模型对 SQL 的理解,可以用模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite
需要管理多个项目的 Key 时,回到控制台和 API Keys 页面操作即可。整条链路的核心就三样:一份写清楚的SKILL.md、一个稳定的执行脚本、一条统一的 API 通道。把这三样固定下来,数据分析智能体在本地项目里就能稳定跑起来。