2026/9/18 1:24:46

从 Postgres 默认计划到 Qwen 4B 智能体,TaoToken Key 怎么切

从 Postgres 默认计划到 Qwen 4B 智能体,TaoToken Key 怎么切 1. 从 EXPLAIN 里的 Nested Loop 说起DBA 为什么要引入 Qwen 4B 查询优化智能体如果你经常看 Postgres 的EXPLAIN (ANALYZE, BUFFERS)大概率遇到过这种场景SQL 本身不复杂但默认计划选了多个 Nested Loop或者把大表放在驱动侧执行时间从几十毫秒变成几秒。问题不一定在数据库配置而在于 planner 看到的统计信息、选择率估计、成本参数和相关性假设。DBA 的传统做法是更新统计信息、加扩展统计、调整work_mem、改写 SQL、使用pg_hint_plan或者临时关闭某类 join。现在多了一条新路径把默认计划、表结构、索引和统计信息交给一个 Qwen 4B 查询优化智能体让它生成候选计划再由 DBA 在本地测试库验证。公开技术分享里出现过一条训练路线用 SFT 和智能体强化学习把 Qwen 4B 模型训练成查询优化智能体并在 Join Order Benchmark 这类 join 密集查询集上评估候选计划相对 Postgres 默认计划有加速表现。本文不讨论训练过程和具体倍数而是站在 Postgres DBA 视角把它落地成可复现的接入链路先到 TaoToken 官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba 获取 Key然后把调用 Base URL 固定为https://taotoken.net/api再让 Qwen 4B 智能体在生成替代计划时消耗 Token最后把 Token 消耗记录落到本地审计表。需要先明确边界查询优化智能体不是数据库内核插件也不是 Planner 的替代品。它更适合做候选计划生成器。你给它默认计划、schema、索引、统计信息和原始 SQL它返回 join order、访问路径、hint 建议、SQL 改写建议和风险提示。真正执行 SQL 的仍然是 Postgres而且所有命令都应由你在本地测试库执行不要授权智能体直连生产库。本文的可复现产出有三件计划切换步骤、Postgres 默认计划、Token 消耗记录。在开始前建议准备一个本地 Postgres 测试库版本建议 14 及以上便于使用EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)。可选安装pg_hint_plan用于验证智能体给出的 join 顺序和 join 方式建议。一个 TaoToken API Key占位符写作YOUR_API_KEY。一个模型 ID去模型对话页面确认实际可用模型不要凭记忆硬编码。一张本地审计表用于记录每次智能体调用产生的 Token 用量。2. 在 TaoToken 创建 Key并把 Base URL 固定为 https://taotoken.net/api切换模型调用供应商时最容易出错的不是模型能力而是 Key、Base URL 和模型 ID 三个值。DBA 平时写 SQL 很严谨但配置 AI 工具链时经常把 URL 拼错或者在 Codex 里套用 Claude Code 的环境变量。这里按最小步骤走一遍。第一步打开 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba完成注册或登录后进入控制台创建 API Key。创建 Key 的页面是https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba创建后先复制到安全位置后续配置里用YOUR_API_KEY占位。不要把这个 Key 写进 SQL 文件、Git 仓库或聊天记录。如果只是临时测试可以放在本机环境变量里。第二步确认模型 ID。不同账号、不同计划下可用模型可能不同所以不要直接把示例里的模型名当成固定值。打开模型对话页面查看可用模型https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba找到你要用于查询优化智能体的 Qwen 系列模型把模型 ID 记录下来。本文后续脚本用TAOTOKEN_MODEL_ID环境变量接收它。第三步固定 Base URL。工具配置里的 Base URL 是https://taotoken.net/api注意Base URL 不要附加 UTM 参数。UTM 用于官网页面来源追踪不用于 API 请求。如果你的工具要求填写 OpenAI 兼容地址通常是在 Base URL 后拼接/v1/chat/completions最终请求地址为https://taotoken.net/api/v1/chat/completions。具体以你所用工具和文档为准但供应商根地址保持https://taotoken.net/api。第四步在本地 shell 里设置环境变量export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL_ID你的模型ID第五步做一次最小连通性验证curl -sS ${TAOTOKEN_BASE_URL}/v1/chat/completions \ -H Authorization: Bearer ${TAOTOKEN_API_KEY} \ -H Content-Type: application/json \ -d { model: ${TAOTOKEN_MODEL_ID}, messages: [ {role: user, content: 只回复 ok} ], max_tokens: 8, temperature: 0 }返回结构里应包含choices并且通常包含usage。如果返回 401优先检查 Key 是否复制完整、是否在Authorization里用了Bearer。如果返回 404优先检查 Base URL 是否误写成带 UTM 的官网地址或者模型 ID 是否可用。这个阶段不要急着跑复杂查询计划先让最小请求成功。3. 捕获 Postgres 默认计划把 EXPLAIN JSON 变成智能体输入查询优化智能体要生成替代计划必须先看到默认计划。DBA 最熟悉的入口就是EXPLAIN。为了便于程序解析建议使用 JSON 格式并开启ANALYZE和BUFFERS。下面用一个本地测试查询举例表名和字段可以按你的测试库调整。不要在生产库上直接运行高开销查询。\timing on EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT t.id, t.title, n.name, mc.company_id FROM title AS t JOIN cast_info AS ci ON ci.movie_id t.id JOIN name AS n ON n.id ci.person_id JOIN movie_companies AS mc ON mc.movie_id t.id WHERE t.production_year BETWEEN 2000 AND 2010 AND n.gender m ORDER BY t.production_year DESC, t.id LIMIT 50;把默认计划保存为 JSON 文件psql host127.0.0.1 port5432 dbnamejob_test userpostgres \ -Atc EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT t.id, t.title, n.name, mc.company_id FROM title AS t JOIN cast_info AS ci ON ci.movie_id t.id JOIN name AS n ON n.id ci.person_id JOIN movie_companies AS mc ON mc.movie_id t.id WHERE t.production_year BETWEEN 2000 AND 2010 AND n.gender m ORDER BY t.production_year DESC, t.id LIMIT 50; default_plan.json除了执行计划还应该给智能体提供必要的统计信息。否则它只能根据 SQL 文本猜测候选计划质量会下降。可以导出表级和列级统计信息SELECT schemaname, relname, n_live_tup, n_dead_tup, last_analyze, last_autoanalyze FROM pg_stat_user_tables WHERE relname IN (title, cast_info, name, movie_companies) ORDER BY relname;SELECT schemaname, tablename, attname, n_distinct, correlation, most_common_vals, most_common_freqs FROM pg_stats WHERE tablename IN (title, cast_info, name, movie_companies) ORDER BY tablename, attname;再把索引定义也导出SELECT tablename, indexname, indexdef FROM pg_indexes WHERE tablename IN (title, cast_info, name, movie_companies) ORDER BY tablename, indexname;把这些信息整理成agent_input.json结构可以如下{ query_id: job_like_001, sql: SELECT ..., default_plan: {}, tables: [ { name: title, rows: 2528312, columns: [ {name: id, n_distinct: -1, correlation: 1.0}, {name: production_year, n_distinct: 100, correlation: 0.3} ] } ], indexes: [ CREATE INDEX ... ], constraints: [ PRIMARY KEY ..., FOREIGN KEY ... ] }注意不要把生产库连接串、账号密码、业务敏感字段放进智能体输入。计划优化只需要 schema、统计信息、索引和 SQL 结构。所有 SQL 仍在本地执行。4. 调用 Qwen 4B 查询优化智能体生成替代计划并记录 Token 消耗这一节是核心Qwen 4B 智能体在生成替代计划时消耗 Token。我们要让调用结果可解析、可审计、可回放。建议要求模型输出 JSON字段包括 join order、访问路径、hint 建议、SQL 改写和风险提示。下面是一个 Python 示例使用requests调用 TaoToken 的 OpenAI 兼容接口并把usage写入本地 Postgres 审计表。import json import os from datetime import datetime, timezone import psycopg import requests BASE_URL os.environ[TAOTOKEN_BASE_URL] API_KEY os.environ[TAOTOKEN_API_KEY] MODEL_ID os.environ[TAOTOKEN_MODEL_ID] DB_DSN os.environ.get( PG_DSN, host127.0.0.1 port5432 dbnamejob_test userpostgres ) with open(agent_input.json, r, encodingutf-8) as f: agent_input json.load(f) system_prompt 你是一个 Postgres 查询优化智能体。你只生成候选计划不执行 SQL。 请基于给定的 SQL、默认计划、表统计信息、索引和约束输出 JSON。 不要编造不存在的表、列、索引。不要要求连接生产库。 输出字段 - query_id: 字符串 - join_order: 数组元素为表别名 - access_paths: 对象键为表别名值为 index/seq scan 建议 - join_methods: 对象描述每两个表之间的 join 方式 - hints: 字符串数组pg_hint_plan 风格 - rewritten_sql: 字符串可为空 - risks: 字符串数组 - explanation: 字符串 user_prompt json.dumps(agent_input, ensure_asciiFalse) payload { model: MODEL_ID, messages: [ {role: system, content: system_prompt}, {role: user, content: user_prompt} ], temperature: 0, response_format: {type: json_object} } resp requests.post( f{BASE_URL}/v1/chat/completions, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json }, jsonpayload, timeout180 ) resp.raise_for_status() data resp.json() content data[choices][0][message][content] usage data.get(usage, {}) prompt_tokens usage.get(prompt_tokens, 0) completion_tokens usage.get(completion_tokens, 0) total_tokens usage.get(total_tokens, prompt_tokens completion_tokens) print(content) with psycopg.connect(DB_DSN) as conn: with conn.cursor() as cur: cur.execute( INSERT INTO llm_token_audit ( query_id, model_id, created_at, prompt_tokens, completion_tokens, total_tokens, request_digest ) VALUES (%s, %s, %s, %s, %s, %s, %s) , ( agent_input.get(query_id, unknown), MODEL_ID, datetime.now(timezone.utc), prompt_tokens, completion_tokens, total_tokens, json.dumps(payload, ensure_asciiFalse)[:500] ) ) conn.commit()本地审计表可以这样建CREATE TABLE IF NOT EXISTS llm_token_audit ( id BIGSERIAL PRIMARY KEY, query_id TEXT NOT NULL, model_id TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), prompt_tokens INTEGER NOT NULL DEFAULT 0, completion_tokens INTEGER NOT NULL DEFAULT 0, total_tokens INTEGER NOT NULL DEFAULT 0, request_digest TEXT ); CREATE INDEX IF NOT EXISTS idx_llm_token_audit_query_id ON llm_token_audit (query_id, created_at DESC);智能体返回的 JSON 可以保存到agent_plan.json后续用于对比。这里要注意Token 消耗主要来自两部分一是你传入的默认计划和统计信息二是模型生成的候选计划与解释。输入越大prompt_tokens越高要求输出越详细completion_tokens越高。对于复杂 join 查询建议先裁剪统计信息只保留与查询相关的列避免把整张pg_stats塞进去。5. 计划切换步骤从默认计划到候选计划再到本地验证拿到智能体候选计划后不要直接相信也不要直接改写生产 SQL。正确流程是本地验证、对比、记录、灰度。下面给出可复现的计划切换步骤。步骤 1保存默认计划。使用EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)记录总执行时间、各节点 actual time、rows、loops、buffers。步骤 2保存默认计划摘要。把 JSON 中关键节点提取成表格例如SELECT query_id, plan_node, actual_total_time_ms, plan_rows, actual_rows, buffers_hit, buffers_read FROM plan_node_audit WHERE query_id job_like_001 ORDER BY actual_total_time_ms DESC;步骤 3调用 Qwen 4B 智能体生成候选计划。输入包括 SQL、默认计划 JSON、相关统计信息、索引定义。输出保存为agent_plan.json同时把usage写入llm_token_audit。步骤 4人工审查候选计划。重点看它是否引用了不存在的索引、是否违反约束、是否建议了不合理的 Cartesian Join、是否要求关闭关键 join 方式。任何涉及数据正确性的改写都必须放弃。步骤 5在本地测试库验证 hint。如果安装了pg_hint_plan可以把智能体建议转成 hint。例如/* Leading((t ci n mc)) HashJoin(t ci) NestLoop(ci n) HashJoin(mc t) */ EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT t.id, t.title, n.name, mc.company_id FROM title AS t JOIN cast_info AS ci ON ci.movie_id t.id JOIN name AS n ON n.id ci.person_id JOIN movie_companies AS mc ON mc.movie_id t.id WHERE t.production_year BETWEEN 2000 AND 2010 AND n.gender m ORDER BY t.production_year DESC, t.id LIMIT 50;如果暂时不想安装扩展也可以用会话级参数模拟部分计划SET LOCAL enable_nestloop off; SET LOCAL enable_hashjoin on; SET LOCAL enable_mergejoin on; EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT t.id, t.title, n.name, mc.company_id FROM title AS t JOIN cast_info AS ci ON ci.movie_id t.id JOIN name AS n ON n.id ci.person_id JOIN movie_companies AS mc ON mc.movie_id t.id WHERE t.production_year BETWEEN 2000 AND 2010 AND n.gender m ORDER BY t.production_year DESC, t.id LIMIT 50;步骤 6记录候选计划执行结果。建一张对比表CREATE TABLE IF NOT EXISTS plan_compare ( id BIGSERIAL PRIMARY KEY, query_id TEXT NOT NULL, plan_source TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), execution_ms NUMERIC, total_cost NUMERIC, plan_json JSONB, notes TEXT );插入默认计划与候选计划INSERT INTO plan_compare ( query_id, plan_source, execution_ms, total_cost, plan_json, notes ) VALUES (job_like_001, postgres_default, 1832.4, 920000.0, {}::jsonb, 默认计划), (job_like_001, qwen4b_agent, 744.8, 310000.0, {}::jsonb, 本地验证后的候选计划);步骤 7重复运行。查询计划受缓存、参数、并发和数据分布影响。至少运行 5 次取中位数不要只看一次最快结果。步骤 8形成结论。如果候选计划稳定更快再考虑通过 SQL 改写、索引调整或受控 hint 落地。不要把智能体生成的 hint 直接全量推到生产。落地前要经过代码审查、回滚方案和灰度验证。6. 把同一套 TaoToken Key 接入 Claude Code、Codex、CC SwitchDBA 的日常工作不只在数据库客户端里。你可能还会用 Claude Code 查文档、写迁移脚本、整理 EXPLAIN 输出或者用 Codex 在终端里辅助排障。这些工具都可以走同一套 TaoToken Key但配置字段不同不能混用。Claude Code 使用settings.json和ANTHROPIC_*环境变量。示例{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: 你的模型ID } }如果使用 shell 环境变量也可以这样export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODEL你的模型IDClaude Code 的文档入口在文末 CTA 中给出。注意 Base URL 仍然是https://taotoken.net/api不要带 UTM。Codex 使用config.toml不要把ANTHROPIC_*套到 Codex 上。示例model 你的模型ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat然后在 shell 里设置export TAOTOKEN_API_KEYYOUR_API_KEYCC Switch 可以理解成供应商切换器。不管界面怎么变本质就是三件套Base URL、API Key、模型 ID。示例配置{ provider: taotoken, baseUrl: https://taotoken.net/api, apiKey: YOUR_API_KEY, model: 你的模型ID }三件套里最容易错的是把 Base URL 写成官网带 UTM 的链接。请记住官网链接用于注册、控制台和文档API 调用只使用https://taotoken.net/api。模型 ID 不要写死去模型对话页面复制当前可用的 ID。7. 可复现实验记录模板计划切换步骤、默认计划、Token 消耗为了让实验可回放建议把每次查询优化都记录成三类数据默认计划、候选计划、Token 消耗。可以设计如下表结构CREATE TABLE IF NOT EXISTS query_plan_experiment ( id BIGSERIAL PRIMARY KEY, query_id TEXT NOT NULL, sql_text TEXT NOT NULL, default_plan_json JSONB, agent_plan_json JSONB, default_execution_ms NUMERIC, agent_execution_ms NUMERIC, default_total_cost NUMERIC, agent_total_cost NUMERIC, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), status TEXT NOT NULL DEFAULT draft ); CREATE TABLE IF NOT EXISTS llm_token_audit ( id BIGSERIAL PRIMARY KEY, query_id TEXT NOT NULL, model_id TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), prompt_tokens INTEGER NOT NULL DEFAULT 0, completion_tokens INTEGER NOT NULL DEFAULT 0, total_tokens INTEGER NOT NULL DEFAULT 0, request_digest TEXT );一次完整记录可以这样写入INSERT INTO query_plan_experiment ( query_id, sql_text, default_plan_json, agent_plan_json, default_execution_ms, agent_execution_ms, default_total_cost, agent_total_cost, status ) VALUES ( job_like_001, SELECT ..., {}::jsonb, {}::jsonb, 1832.4, 744.8, 920000.0, 310000.0, verified );Token 消耗汇总SELECT query_id, model_id, SUM(prompt_tokens) AS prompt_tokens, SUM(completion_tokens) AS completion_tokens, SUM(total_tokens) AS total_tokens, COUNT(*) AS call_count FROM llm_token_audit GROUP BY query_id, model_id ORDER BY total_tokens DESC;计划对比汇总SELECT query_id, default_execution_ms, agent_execution_ms, ROUND( (default_execution_ms - agent_execution_ms) / NULLIF(default_execution_ms, 0) * 100, 2 ) AS improvement_pct, default_total_cost, agent_total_cost, status FROM query_plan_experiment WHERE query_id job_like_001;建议每次实验都记录以下字段query_id查询编号方便关联默认计划、候选计划和 Token 消耗。default_plan_jsonPostgres 默认计划使用 JSON 格式保存。agent_plan_jsonQwen 4B 智能体生成的候选计划。default_execution_ms默认计划中位数执行时间。agent_execution_ms候选计划中位数执行时间。prompt_tokens输入默认计划和统计信息消耗的 Token。completion_tokens生成候选计划消耗的 Token。total_tokens总 Token用于评估优化成本。statusdraft、verified、rejected、rolled_back。这样即使后续换模型、换 Key、换 Base URL你也能回答两个问题计划是否真的更快Token 是否值得花。8. 常见排障401、404、模型 ID、Token 统计和计划不生效问题一请求返回 401。检查Authorization是否为Bearer YOUR_API_KEYKey 是否复制完整。如果 Key 是在控制台创建的去 API Keys 页面重新确认。不要用官网 UTM 链接当 API 地址。问题二请求返回 404。检查 Base URL 是否为https://taotoken.net/api不要带 UTM 参数。检查模型 ID 是否从模型对话页面复制而不是手写。检查路径是否与工具要求一致OpenAI 兼容接口通常需要拼/v1/chat/completions。问题三Claude Code 能通Codex 不通。Claude Code 用ANTHROPIC_*Codex 用config.toml里的model_provider、base_url、env_key。不要把 Claude Code 的环境变量直接复制到 Codex。问题四Token 统计为 0。不同接口返回的usage字段可能略有差异。先打印完整响应确认是否有usage。如果网关或工具不返回用量可以在本地按请求体大小和响应文本长度做粗略估算但审计仍应以接口返回为准。问题五智能体输出不是 JSON。降低temperature明确要求只输出 JSON并在代码里做容错解析。如果模型不支持response_format可以要求把 JSON 放在代码块里然后本地提取。问题六候选计划更快但 SQL 不能改。优先考虑索引、统计信息和会话参数。pg_hint_plan适合验证思路不适合长期把 hint 写满所有 SQL。根因如果是对某些列的相关性估计不准应该补扩展统计。问题七计划在测试库很快到生产变慢。检查数据分布、参数化查询、缓存、并发和 autovacuum 状态。测试库的统计信息不代表生产库。任何计划优化都应在生产等价数据集上验证。问题八智能体建议直连数据库执行。不要接受。SQL 和命令由读者本地执行智能体只生成候选计划和解释。生产库凭证不要交给任何外部工具。9. 文末 CTA按顺序完成模型对话、Coding Plan、创建 Key、Claude Code 文档如果你准备把 Qwen 4B 查询优化智能体接入自己的 Postgres 排障流程可以按下面路径走一遍先到模型对话页面确认可用模型和模型 IDhttps://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba查看 Coding Plan确认调用额度和使用方式https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba在控制台创建 API Key保存为YOUR_API_KEYhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba如果你还要配置 Claude Code参考 Claude Code 文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentpostgres_qwen4b_dba最后再强调一次配置要点官网入口用于注册、查看模型和创建 KeyAPI 调用 Base URL 固定为https://taotoken.net/apiKey 占位符是YOUR_API_KEYQwen 4B 智能体在生成替代计划时消耗 Token所有 Token 消耗都应写入本地审计表。先把默认计划保存下来再让智能体生成候选计划最后在本地测试库用EXPLAIN (ANALYZE, BUFFERS)验证。这样你得到的不是一段不可信的模型输出而是一条可回放、可对比、可审计的 Postgres 查询优化链路。