使用AnalyticDB PostgreSQL Query Skill进行数据库查询分析
概述
AnalyticDB PostgreSQL Query Skill 是一款面向 AI Agent 的数据库查询 Skill,能让任何具备 Shell 执行能力的 AI Agent(如 Claude、Qoder、ClawHub 等)通过 psql 连接阿里云云原生数据仓库AnalyticDB PostgreSQL版,执行只读查询并可选将结果导出为本地 CSV 文件。
用户只需使用自然语言描述数据需求,Agent 即可自动:
查询 DBA 配置的语义模型(_agent_meta schema),理解可用表、字段及关联关系
在语义模型范围内生成合规 SQL
经用户确认后执行查询,返回结果或导出 CSV 供下游分析
适用场景:数据提取、即席报表、探索性分析、多表关联查询、 CSV导出分析。
发布平台
平台 | 链接 |
GitHub | https://github.com/aliyun/alibabacloud-aiops-skills/tree/alibabacloud-analyticdb-postgresql-query-0.0.1-beta.1/skills/database/gpdb/alibabacloud-analyticdb-postgresql-query |
Skills.sh | https://www.skills.sh/aliyun/alibabacloud-aiops-skills/alibabacloud-analyticdb-postgresql-query |
ClawHub | https://clawhub.ai/sdk-team/skills/alibabacloud-analyticdb-postgresql-query |
安装方式
方式一:通过 npx 安装(推荐)
在支持 npx 的 Agent 环境(如 Claude Desktop、Qoder 等)中,可直接运行:
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills/tree/alibabacloud-analyticdb-postgresql-query-0.0.1-beta.1 --skill alibabacloud-analyticdb-postgresql-query方式二:复制链接给 Agent 安装
将以下任一链接直接发送给支持 Skill 安装的 Agent,Agent 会自动识别并完成安装:
GitHub 链接:
https://github.com/aliyun/alibabacloud-aiops-skills/tree/alibabacloud-analyticdb-postgresql-query-0.0.1-beta.1/skills/database/gpdb/alibabacloud-analyticdb-postgresql-querySkills.sh 链接:
https://www.skills.sh/aliyun/alibabacloud-aiops-skills/alibabacloud-analyticdb-postgresql-queryClawHub 链接:
https://clawhub.ai/sdk-team/skills/alibabacloud-analyticdb-postgresql-query
前置条件
条件 | 说明 |
| Agent 运行环境需安装 psql(推荐使用 AnalyticDB PostgreSQL版 官方客户端包psql) |
数据库连接信息 | 通过环境变量或 |
AnalyticDB PostgreSQL 实例 | 阿里云已开通的 AnalyticDB PostgreSQL版 实例(存储弹性模式7.0以上) |
安全注意事项
本 Skill 涉及数据库直连操作,安全配置是使用前的首要工作。请务必完整阅读以下内容。
1. 必须使用只读账号
禁止使用超级管理员(superuser)账号连接。 请让 DBA 创建专用只读账号:
-- DBA 执行
CREATE ROLE readonly_agent LOGIN PASSWORD '<强密码>';
GRANT CONNECT ON DATABASE mydb TO readonly_agent;
GRANT USAGE ON SCHEMA public TO readonly_agent;
-- 仅授权业务所需的表(禁止 GRANT SELECT ON ALL TABLES)
GRANT SELECT ON public.orders, public.customers, public.products TO readonly_agent;2. 强制只读连接
Skill 在每次连接时会强制启用只读模式(default_transaction_read_only=on),确保即使 SQL 中包含写操作也会被数据库拒绝。推荐通过 ~/.pg_service.conf 配置:
[adbpg]
host=gp-xxx.gpdb.rds.aliyuncs.com
port=5432
dbname=mydb
user=readonly_agent
options=-c default_transaction_read_only=on3. 凭据安全 — 绝对禁止明文暴露
禁止行为 | 说明 |
在对话中输入密码 | Agent 不会主动索要密码,也禁止在对话中接收密码 |
硬编码密码到脚本 | 密码必须通过 |
echo/cat 密码文件 | Agent 被严格禁止执行任何可能暴露密码的命令 |
提交密码到版本控制 |
|
正确做法:使用 ~/.pgpass 文件管理密码,并设置文件权限为 chmod 600。
4. 配置专用资源组
为避免 Agent 查询与业务负载争抢资源,必须在 AnalyticDB PostgreSQL版 中为只读账号配置专用资源组(Resource Group):
CREATE RESOURCE GROUP agent_readonly_rg WITH (concurrency=5, cpu_max_percent=10, memory_limit=10);
ALTER ROLE readonly_agent RESOURCE GROUP agent_readonly_rg;若未配置专用资源组,Agent 会在会话启动时发出警告并暂停等待用户确认,更细致的资源组配置请参见使用Resource Group(资源组)进行负载管理。
5. 语义模型作为访问边界
Agent 无法绕过语义模型直接查询任意表。_agent_meta schema 充当硬性访问边界:
只有在语义模型中注册的表和字段才可被查询
未注册的表/字段请求会被拒绝并提示联系 DBA
任何绕过尝试(如「紧急需要」「只这一次」)均被明确禁止
6. 人工确认机制(HITL)
Agent 在执行任何查询前 必须暂停并等待用户明确确认:
生成 SQL 后展示给用户,等待用户回复「确认」或「执行」后才会实际执行
超过 10 万行的结果需二次确认
检测到安全风险时立即暂停,不会「先执行再汇报」
7. 网络访问控制
确保 AnalyticDB PostgreSQL版 实例的白名单中已添加 Agent 运行环境的出口 IP:
登录阿里云 AnalyticDB PostgreSQL版 控制台
进入实例 → 数据安全性 → 白名单设置
添加 Agent 所在机器的公网/VPC IP
快速开始
步骤一:安装 Skill
选择上述任一安装方式完成 Skill 安装。
步骤二:配置数据库连接
推荐方式(macOS/Linux):使用 pg_service.conf + pgpass
# 1. 创建 ~/.pg_service.conf
cat > ~/.pg_service.conf << 'EOF'
[adbpg]
host=gp-xxx.gpdb.rds.aliyuncs.com
port=5432
dbname=mydb
user=readonly_agent
options=-c default_transaction_read_only=on
EOF
# 2. 创建 ~/.pgpass
echo "gp-xxx.gpdb.rds.aliyuncs.com:5432:mydb:readonly_agent:<你的密码>" > ~/.pgpass
chmod 600 ~/.pgpass
# 3. 验证连接
psql "service=adbpg" -c "SELECT 1;"步骤三:配置语义模型
联系 DBA 创建 _agent_meta schema 并注册可查询的表和字段。Skill 内置了语义模型初始化引导,Agent 会协助生成建表 SQL 交由 DBA 执行。
步骤四:开始查询
在对话中用自然语言描述查询需求即可:
用户:帮我查一下最近 7 天的订单总金额,按天分组
Agent:[生成 SQL 并展示] → 等待确认 → 执行 → 返回结果功能特性
功能 | 说明 |
自然语言查询 | 用户用自然语言描述需求,Agent 自动生成 SQL |
语义模型驱动 | 基于 |
指标/过滤器 | 支持预定义业务指标(metric_meta)和过滤条件(filters) |
CSV 导出 | 查询结果可导出为带时间戳的本地 CSV 文件 |
多表关联 | 支持基于语义模型的跨表 JOIN 查询 |
查询保护 | 自动附加 LIMIT(默认 50000)、语句超时(60s) |
会话安全检查 | 每次会话自动检测权限、资源组、语义模型状态 |
兼容的 Agent 平台
Claude(Anthropic)— 通过 MCP 或 Skill 安装
Qoder / Qoder Worker — 原生支持
ClawHub — 通过 ClawHub 平台启用
任何具备 Shell 执行能力的 AI Agent
约束与限制
约束项 | 限制值 |
单轮最大 psql 调用次数 | 5 次 |
失败重试次数 | 1 次 |
默认结果行数限制 | 50,000 行 |
语句超时 | 60 秒 |
允许的 SQL 类型 | 仅 SELECT / WITH...SELECT / EXPLAIN |
写操作 | 全面禁止(数据库级别强制只读) |
完整使用流程(电商场景实操)
以下以一个典型电商数据分析场景为例,手把手带你走完从环境准备到实际查询的完整流程。即使你从未使用过该 Skill,也可以按步骤逐一完成。
步骤一:确认环境准备
在开始之前,请确认以下条件已满足:
检查项 | 如何确认 |
AnalyticDB PostgreSQL版 实例已开通 | 登录 AnalyticDB PostgreSQL版 控制台可看到实例 |
psql 已安装 | 终端执行 |
Skill 已安装 | 参考上方「安装方式」章节完成安装 |
如果 psql 尚未安装:
推荐:使用 AnalyticDB PostgreSQL版 官方客户端包,确保与 AnalyticDB PostgreSQL版 服务器版本兼容。详细安装步骤参见 psql文档。
步骤二:创建只读账号(DBA 操作)
为什么需要单独账号? 禁止使用超级管理员账号连接。专用只读账号可确保 Agent 只能读取数据、无法修改或删除。
请 DBA 在 AnalyticDB PostgreSQL版 实例上执行以下 SQL:
-- 1. 创建只读账号
CREATE ROLE readonly_agent LOGIN PASSWORD '<YOUR_PASSWORD_HERE>';
-- 2. 授予连接权限
GRANT CONNECT ON DATABASE mydb TO readonly_agent;
GRANT USAGE ON SCHEMA public TO readonly_agent;
-- 3. 创建专用资源组(防止 Agent 查询挤占业务资源)
CREATE RESOURCE GROUP agent_readonly_rg WITH (concurrency=5, cpu_max_percent=10, memory_limit=10);
ALTER ROLE readonly_agent RESOURCE GROUP agent_readonly_rg;此时暂不需要授权具体表的 SELECT 权限,后续步骤会一起处理。更细致的资源组配置请参见使用Resource Group(资源组)进行负载管理。
步骤三:配置本地连接信息
在你的本地机器(即 Agent 运行的环境)上配置连接信息。
macOS / Linux
创建 ~/.pg_service.conf(连接配置,不含密码):
cat > ~/.pg_service.conf << 'EOF'
[adbpg]
host=gp-xxxxxxxxx.gpdb.rds.aliyuncs.com
port=5432
dbname=mydb
user=readonly_agent
options=-c default_transaction_read_only=on
EOF创建 ~/.pgpass(密码文件,权限必须为 600):
echo "gp-xxxxxxxxx.gpdb.rds.aliyuncs.com:5432:mydb:readonly_agent:<YOUR_PASSWORD_HERE>" > ~/.pgpass
chmod 600 ~/.pgpassWindows
创建 %APPDATA%\postgresql\pg_service.conf:
New-Item -ItemType Directory -Force -Path "$env:APPDATA\postgresql"
@"
[adbpg]
host=gp-xxxxxxxxx.gpdb.rds.aliyuncs.com
port=5432
dbname=mydb
user=readonly_agent
options=-c default_transaction_read_only=on
"@ | Out-File -Encoding UTF8 "$env:APPDATA\postgresql\pg_service.conf"创建 %APPDATA%\postgresql\pgpass.conf:
"gp-xxxxxxxxx.gpdb.rds.aliyuncs.com:5432:mydb:readonly_agent:<YOUR_PASSWORD_HERE>" | Out-File -Encoding UTF8 "$env:APPDATA\postgresql\pgpass.conf"Windows 下 pgpass.conf 无需设置文件权限,PostgreSQL 会自动识别。
验证连接:
psql "service=adbpg" -c "SELECT 1;"如果返回结果为 1,说明连接配置正确。
步骤四:创建业务表(DBA 操作)
实际使用中,你的业务表已经存在于数据库中,可跳过此步骤。本步骤仅用于体验完整流程。
以下步骤四、五、六的所有 SQL 均由 DBA 使用管理员账号执行,不能使用 service=adbpg(该连接已配置为只读模式)。
以电商场景为例,创建 5 张核心业务表:
4.1 客户主表
CREATE TABLE IF NOT EXISTS customers (
customer_id BIGINT NOT NULL,
customer_name VARCHAR(100),
gender VARCHAR(10),
age_group VARCHAR(20),
city VARCHAR(50),
province VARCHAR(50),
channel VARCHAR(30),
register_date DATE,
is_member BOOLEAN,
member_level VARCHAR(20),
total_orders INT,
total_amount NUMERIC(12,2),
last_order_date DATE
) DISTRIBUTED BY (customer_id);4.2 商品主表
CREATE TABLE IF NOT EXISTS products (
product_id BIGINT NOT NULL,
product_name VARCHAR(200),
category_l1 VARCHAR(50),
category_l2 VARCHAR(50),
brand VARCHAR(100),
unit_price NUMERIC(10,2),
cost_price NUMERIC(10,2),
stock_qty INT,
status VARCHAR(20),
launch_date DATE,
supplier VARCHAR(100)
) DISTRIBUTED BY (product_id);4.3 订单主表
CREATE TABLE IF NOT EXISTS orders (
order_id BIGINT NOT NULL,
customer_id BIGINT,
order_date DATE,
order_time TIMESTAMP,
order_status VARCHAR(20),
payment_method VARCHAR(30),
channel VARCHAR(30),
city VARCHAR(50),
province VARCHAR(50),
total_amount NUMERIC(12,2),
discount_amount NUMERIC(10,2),
actual_amount NUMERIC(12,2),
shipping_fee NUMERIC(8,2),
is_refunded BOOLEAN,
refund_amount NUMERIC(12,2)
) DISTRIBUTED BY (order_id);4.4 订单明细表
CREATE TABLE IF NOT EXISTS order_items (
item_id BIGINT NOT NULL,
order_id BIGINT,
product_id BIGINT,
qty INT,
unit_price NUMERIC(10,2),
discount_rate NUMERIC(5,4),
subtotal NUMERIC(12,2)
) DISTRIBUTED BY (item_id);4.5 营销活动表
CREATE TABLE IF NOT EXISTS campaigns (
campaign_id BIGINT NOT NULL,
campaign_name VARCHAR(100),
channel VARCHAR(30),
start_date DATE,
end_date DATE,
budget NUMERIC(12,2),
actual_cost NUMERIC(12,2),
impressions INT,
clicks INT,
conversions INT
) DISTRIBUTED BY (campaign_id);以上 5 张表是本例中将向 Agent 开放查询的表。在真实场景中,数据库通常还有许多其他业务表,这些敏感表不会注册到语义模型,Agent 无法感知其存在。
4.6 插入模拟数据
建表完成后,执行以下 SQL 生成模拟数据(使用 PostgreSQL 的 generate_series 批量生成):
-- 客户数据 200 条
INSERT INTO customers
SELECT i, '客户_' || i,
CASE WHEN i % 2 = 0 THEN '男' ELSE '女' END,
CASE (i % 4) WHEN 0 THEN '18-25' WHEN 1 THEN '26-35' WHEN 2 THEN '36-45' ELSE '46+' END,
(ARRAY['北京','上海','广州','深圳','杭州','成都','武汉','南京'])[1 + (i % 8)],
(ARRAY['北京市','上海市','广东省','广东省','浙江省','四川省','湖北省','江苏省'])[1 + (i % 8)],
(ARRAY['自然流量','付费广告','老客转介绍','活动拉新'])[1 + (i % 4)],
CURRENT_DATE - (i * 3 || ' days')::INTERVAL,
(i % 3 != 0),
CASE (i % 4) WHEN 0 THEN '普通' WHEN 1 THEN '银卡' WHEN 2 THEN '金卡' ELSE '钻石' END,
5 + (i % 20), (1000 + i * 37.5)::NUMERIC(12,2),
CURRENT_DATE - (i % 30 || ' days')::INTERVAL
FROM generate_series(1, 200) i;
-- 商品数据 50 条
INSERT INTO products
SELECT i,
(ARRAY['无线耳机','智能手表','笔记本电脑','平板电脑','手机壳','充电宝','蓝牙音箱','机械键盘','鼠标','显示器','路由器','摄像头','台灯','空气净化器','咖啡机','跑步机','瑜伽垫','运动手环'])[1 + (i % 18)] || '_' || i,
(ARRAY['数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','数码电子','家居生活','家居生活','家居生活','运动健康','运动健康','运动健康'])[1 + (i % 18)],
(ARRAY['音频','穿戴','电脑','电脑','配件','配件','音频','外设','外设','显示','网络','安防','照明','净化','厨电','健身','健身','穿戴'])[1 + (i % 18)],
(ARRAY['品牌A','品牌B','品牌C','品牌D','品牌E'])[1 + (i % 5)],
(99 + i * 23.5)::NUMERIC(10,2), (60 + i * 14.2)::NUMERIC(10,2),
100 + (i * 7 % 500),
CASE WHEN i % 10 = 0 THEN '下架' WHEN i % 15 = 0 THEN '预售' ELSE '在售' END,
CURRENT_DATE - (i * 10 || ' days')::INTERVAL, '供应商_' || (1 + i % 10)
FROM generate_series(1, 50) i;
-- 订单数据 1000 条
INSERT INTO orders
SELECT i, 1 + (i % 200),
CURRENT_DATE - (i % 365 || ' days')::INTERVAL,
NOW() - (i % 365 || ' days')::INTERVAL,
(ARRAY['已支付','已支付','已完成','已完成','已完成','已退款','已发货','待支付'])[1 + (i % 8)],
(ARRAY['支付宝','微信支付','微信支付','银行卡','花呗'])[1 + (i % 5)],
(ARRAY['APP','APP','小程序','PC','H5'])[1 + (i % 5)],
(ARRAY['北京','上海','广州','深圳','杭州','成都','武汉','南京'])[1 + (i % 8)],
(ARRAY['北京市','上海市','广东省','广东省','浙江省','四川省','湖北省','江苏省'])[1 + (i % 8)],
(200 + i * 13.7)::NUMERIC(12,2), (10 + i % 50)::NUMERIC(10,2),
(190 + i * 13.2)::NUMERIC(12,2),
CASE WHEN i % 5 = 0 THEN 0 ELSE 10 END::NUMERIC(8,2),
(i % 8 = 5),
CASE WHEN i % 8 = 5 THEN (190 + i * 13.2)::NUMERIC(12,2) ELSE 0 END
FROM generate_series(1, 1000) i;
-- 订单明细 2000 条
INSERT INTO order_items
SELECT i, 1 + (i % 1000), 1 + (i % 50), 1 + (i % 3),
(99 + (i % 50) * 23.5)::NUMERIC(10,2),
CASE WHEN i % 5 = 0 THEN 0.9 ELSE 1.0 END::NUMERIC(5,4),
((1 + i % 3) * (99 + (i % 50) * 23.5) * CASE WHEN i % 5 = 0 THEN 0.9 ELSE 1.0 END)::NUMERIC(12,2)
FROM generate_series(1, 2000) i;
-- 营销活动 15 条
INSERT INTO campaigns
SELECT i,
(ARRAY['春节大促','3.8节','618大促','双11','双12','年货节','开学季','暑期促销','中秋活动','国庆促销','会员日','品牌日','清仓特卖','新品首发','周年庆'])[i],
(ARRAY['APP','小程序','PC','APP','全渠道','APP','小程序','APP','全渠道','PC','APP','小程序','APP','全渠道','APP'])[i],
CURRENT_DATE - ((200 - i * 13) || ' days')::INTERVAL,
CURRENT_DATE - ((190 - i * 13) || ' days')::INTERVAL,
(50000 + i * 10000)::NUMERIC(12,2), (45000 + i * 9500)::NUMERIC(12,2),
100000 + i * 20000, 5000 + i * 1000, 200 + i * 50
FROM generate_series(1, 15) i;4.7 创建未注册到语义模型的业务表(12 张)
以下 12 张表真实存在但不会注册到语义模型,Agent 无法感知。用于验证语义层的安全边界。
CREATE TABLE IF NOT EXISTS payments (
payment_id BIGINT NOT NULL, order_id BIGINT, payment_method VARCHAR(30),
amount NUMERIC(12,2), payment_time TIMESTAMP, status VARCHAR(20), transaction_no VARCHAR(64)
) DISTRIBUTED BY (payment_id);
CREATE TABLE IF NOT EXISTS coupons (
coupon_id BIGINT NOT NULL, coupon_name VARCHAR(100), coupon_type VARCHAR(30),
discount_value NUMERIC(10,2), min_amount NUMERIC(10,2), valid_from DATE, valid_to DATE,
usage_limit INT, used_count INT
) DISTRIBUTED BY (coupon_id);
CREATE TABLE IF NOT EXISTS warehouses (
warehouse_id BIGINT NOT NULL, warehouse_name VARCHAR(100), city VARCHAR(50),
province VARCHAR(50), is_active BOOLEAN, capacity INT
) DISTRIBUTED BY (warehouse_id);
CREATE TABLE IF NOT EXISTS inventory (
inventory_id BIGINT NOT NULL, product_id BIGINT, warehouse_id BIGINT,
stock_qty INT, safety_stock INT, last_restock DATE
) DISTRIBUTED BY (inventory_id);
CREATE TABLE IF NOT EXISTS shipments (
shipment_id BIGINT NOT NULL, order_id BIGINT, carrier VARCHAR(50),
tracking_no VARCHAR(64), ship_time TIMESTAMP, deliver_time TIMESTAMP,
status VARCHAR(20), receiver_city VARCHAR(50)
) DISTRIBUTED BY (shipment_id);
CREATE TABLE IF NOT EXISTS returns (
return_id BIGINT NOT NULL, order_id BIGINT, product_id BIGINT, qty INT,
reason VARCHAR(200), return_status VARCHAR(20), refund_amount NUMERIC(12,2),
apply_date DATE, process_date DATE
) DISTRIBUTED BY (return_id);
CREATE TABLE IF NOT EXISTS reviews (
review_id BIGINT NOT NULL, product_id BIGINT, customer_id BIGINT,
rating INT, content VARCHAR(500), review_date DATE, is_verified BOOLEAN
) DISTRIBUTED BY (review_id);
CREATE TABLE IF NOT EXISTS employee_salary (
emp_id BIGINT NOT NULL, emp_name VARCHAR(100), department VARCHAR(50),
position_level VARCHAR(30), base_salary NUMERIC(12,2), bonus NUMERIC(12,2),
bank_account VARCHAR(30), id_card VARCHAR(20)
) DISTRIBUTED BY (emp_id);
CREATE TABLE IF NOT EXISTS supplier_contracts (
contract_id BIGINT NOT NULL, supplier_name VARCHAR(100), product_line VARCHAR(50),
contract_amount NUMERIC(14,2), discount_rate NUMERIC(5,4), payment_terms VARCHAR(100),
start_date DATE, end_date DATE
) DISTRIBUTED BY (contract_id);
CREATE TABLE IF NOT EXISTS pricing_strategy (
strategy_id BIGINT NOT NULL, category_l1 VARCHAR(50), cost_markup NUMERIC(5,4),
min_price NUMERIC(10,2), promo_floor NUMERIC(10,2), effective_date DATE
) DISTRIBUTED BY (strategy_id);
CREATE TABLE IF NOT EXISTS employee_performance (
perf_id BIGINT NOT NULL, emp_id BIGINT, review_period VARCHAR(20),
rating VARCHAR(10), kpi_score NUMERIC(5,2), salary_adj_pct NUMERIC(5,4),
manager_comment TEXT
) DISTRIBUTED BY (perf_id);
CREATE TABLE IF NOT EXISTS chat_history (
chat_id BIGINT NOT NULL, customer_id BIGINT, agent_name VARCHAR(50),
message TEXT, chat_time TIMESTAMP, tags VARCHAR(100)
) DISTRIBUTED BY (chat_id);安全边界验证:以上 12 张表包含员工薪资、银行卡号、身份证、客服聊天记录等高度敏感信息。因为它们未注册到语义模型,Agent 无法发现并查询它们。
4.8 插入未注册到语义模型的业务表模拟数据
INSERT INTO payments
SELECT i, 1 + (i % 1000),
(ARRAY['支付宝','微信支付','微信支付','银行卡','花呗'])[1 + (i % 5)],
(190 + i * 8.3)::NUMERIC(12,2),
NOW() - (i % 365 || ' days')::INTERVAL,
(ARRAY['成功','成功','成功','成功','失败','退款'])[1 + (i % 6)],
'TXN' || md5(i::text)
FROM generate_series(1, 800) i;
-- 优惠券 20 条
INSERT INTO coupons
SELECT i,
(ARRAY['新人礼包','满200减30','品类券-数码','品类券-家居','会员专享9折',
'双11满减','年货节优惠','免运费券','限时秒杀券','老客召回券',
'满500减80','品牌联合券','生日特权','拼团优惠','新品尝鲜券',
'满1000减150','PLUS会员券','直播专属','签到7天券','评价返券'])[i],
(ARRAY['满减','满减','品类','品类','折扣','满减','满减','运费','直减','直减',
'满减','品类','折扣','直减','直减','满减','折扣','直减','直减','直减'])[i],
(ARRAY[30,30,50,30,0,50,40,0,20,15,80,30,0,10,5,150,0,8,5,3])[i]::NUMERIC(10,2),
(ARRAY[0,200,100,50,0,300,200,0,0,0,500,100,0,0,0,1000,0,0,0,0])[i]::NUMERIC(10,2),
CURRENT_DATE - (i * 5 || ' days')::INTERVAL,
CURRENT_DATE + ((30 - i) || ' days')::INTERVAL,
500 + i * 100,
(i * 23) % (500 + i * 100)
FROM generate_series(1, 20) i;
-- 仓库 6 条
INSERT INTO warehouses
SELECT i,
(ARRAY['华东仓-上海','华北仓-北京','华南仓-广州','华中仓-武汉','西南仓-成都','东北仓-沈阳'])[i],
(ARRAY['上海','北京','广州','武汉','成都','沈阳'])[i],
(ARRAY['上海市','北京市','广东省','湖北省','四川省','辽宁省'])[i],
TRUE, 50000 + i * 10000
FROM generate_series(1, 6) i;
-- 库存 300 条
INSERT INTO inventory
SELECT i, 1 + (i % 50), 1 + (i % 6),
50 + (i * 7 % 500), 20 + (i % 30),
CURRENT_DATE - (i % 60 || ' days')::INTERVAL
FROM generate_series(1, 300) i;
-- 物流单 600 条
INSERT INTO shipments
SELECT i, 1 + (i % 1000),
(ARRAY['顺丰速运','中通快递','圆通速递','韵达快递','极兔速递'])[1 + (i % 5)],
'SF' || (1000000000 + i),
NOW() - (i % 30 || ' days')::INTERVAL,
CASE WHEN i % 4 != 0 THEN NOW() - ((i % 30) - 2 || ' days')::INTERVAL ELSE NULL END,
(ARRAY['已签收','已签收','已签收','运输中','已揽收','异常'])[1 + (i % 6)],
(ARRAY['北京','上海','广州','深圳','杭州','成都','武汉','南京'])[1 + (i % 8)]
FROM generate_series(1, 600) i;
-- 退货单 100 条
INSERT INTO returns
SELECT i, 1 + (i % 1000), 1 + (i % 50), 1 + (i % 2),
(ARRAY['质量问题','尺寸不符','不喜欢','发错货','其他'])[1 + (i % 5)],
(ARRAY['已退款','处理中','已拒绝','已退款','已退款'])[1 + (i % 5)],
(50 + i * 3.7)::NUMERIC(12,2),
CURRENT_DATE - (i % 90 || ' days')::INTERVAL,
CASE WHEN i % 5 != 2 THEN CURRENT_DATE - ((i % 90) - 3 || ' days')::INTERVAL ELSE NULL END
FROM generate_series(1, 100) i;
-- 商品评价 500 条
INSERT INTO reviews
SELECT i, 1 + (i % 50), 1 + (i % 200),
CASE WHEN i % 5 = 0 THEN 1 WHEN i % 5 = 1 THEN 3 ELSE 5 END,
CASE WHEN i % 3 = 0 THEN '好评,物超所值!' WHEN i % 3 = 1 THEN '一般般' ELSE '非常满意,推荐购买' END,
CURRENT_DATE - (i % 180 || ' days')::INTERVAL,
(i % 4 != 0)
FROM generate_series(1, 500) i;
-- 员工薪资 20 条
INSERT INTO employee_salary
SELECT i, '员工_' || i,
(ARRAY['研发部','财务部','运营部','市场部'])[1 + (i % 4)],
(ARRAY['P5','P6','P7','P8'])[1 + (i % 4)],
(15000 + i * 2000)::NUMERIC(12,2),
(3000 + i * 500)::NUMERIC(12,2),
'6222****' || (1000 + i),
'110***' || (19700000 + i * 10000)
FROM generate_series(1, 20) i;
-- 供应商合同 15 条
INSERT INTO supplier_contracts
SELECT i, '供应商_' || i,
(ARRAY['数码电子','家居生活','运动健康','数码电子','家居生活'])[1 + (i % 5)],
(500000 + i * 200000)::NUMERIC(14,2),
(0.85 + (i % 5) * 0.02)::NUMERIC(5,4),
(ARRAY['Net30','Net60','Net90','预付30%','月结'])[1 + (i % 5)],
CURRENT_DATE - (i * 30 || ' days')::INTERVAL,
CURRENT_DATE + ((365 - i * 20) || ' days')::INTERVAL
FROM generate_series(1, 15) i;
-- 定价策略 10 条
INSERT INTO pricing_strategy
SELECT i,
(ARRAY['数码电子','家居生活','运动健康','数码电子','家居生活',
'运动健康','数码电子','家居生活','运动健康','数码电子'])[i],
(1.2 + (i % 5) * 0.1)::NUMERIC(5,4),
(50 + i * 20)::NUMERIC(10,2),
(30 + i * 10)::NUMERIC(10,2),
CURRENT_DATE - (i * 60 || ' days')::INTERVAL
FROM generate_series(1, 10) i;
-- 员工绩效 20 条
INSERT INTO employee_performance
SELECT i, i,
(ARRAY['2024-H1','2024-H2','2025-H1'])[1 + (i % 3)],
(ARRAY['S','A','B','C'])[1 + (i % 4)],
(3.0 + (i % 5) * 0.5)::NUMERIC(5,2),
(0.0 + (i % 5) * 0.05)::NUMERIC(5,4),
CASE WHEN i % 4 = 0 THEN '表现优异,建议晋升' WHEN i % 4 = 1 THEN '符合预期'
WHEN i % 4 = 2 THEN '需要改进' ELSE '新员工,观察期' END
FROM generate_series(1, 20) i;
-- 客服聊天 100 条
INSERT INTO chat_history
SELECT i, 1 + (i % 200),
(ARRAY['客服小王','客服小李','客服小张'])[1 + (i % 3)],
CASE WHEN i % 3 = 0 THEN '我的收货地址是北京市朝阳区XX小区' || i
WHEN i % 3 = 1 THEN '我的手机号是138****' || (1000 + i)
ELSE '退款到我的银行卡 6222****' || (2000 + i) END,
NOW() - (i % 60 || ' days')::INTERVAL,
(ARRAY['退款','物流','商品咨询','投诉'])[1 + (i % 4)]
FROM generate_series(1, 100) i;步骤五:创建语义模型(DBA 操作)
什么是语义模型? 语义模型是 Agent 的「数据字典」。只有在语义模型中注册的表和字段,Agent 才能查询。未注册的表对 Agent 完全不可见——这是核心安全机制。
5.1 创建元数据 Schema
CREATE SCHEMA IF NOT EXISTS _agent_meta;5.2 创建五张元数据表
元数据表 | 作用 | 是否必须 |
| 注册哪些表可以被 Agent 查询 | 必须 |
| 定义字段含义、同义词、聚合方式 | 必须 |
| 声明表间关联关系 | 推荐 |
| 预定义复合业务指标 | 可选 |
| 预定义常用过滤条件 | 可选 |
CREATE TABLE IF NOT EXISTS _agent_meta.tables (
table_schema VARCHAR(64) NOT NULL, table_name VARCHAR(128) NOT NULL,
display_name VARCHAR(128), description TEXT, business_domain VARCHAR(64),
PRIMARY KEY (table_schema, table_name)
);
CREATE TABLE IF NOT EXISTS _agent_meta.columns (
table_schema VARCHAR(64) NOT NULL, table_name VARCHAR(128) NOT NULL,
column_name VARCHAR(128) NOT NULL, display_name VARCHAR(128),
column_role VARCHAR(16), data_type VARCHAR(64), column_description TEXT,
synonyms TEXT[], agg_default VARCHAR(16), example_values TEXT[],
PRIMARY KEY (table_schema, table_name, column_name)
);
CREATE TABLE IF NOT EXISTS _agent_meta.joins (
join_id SERIAL, left_schema VARCHAR(64), left_table VARCHAR(128),
left_column VARCHAR(128), right_schema VARCHAR(64), right_table VARCHAR(128),
right_column VARCHAR(128), join_type VARCHAR(10) DEFAULT 'LEFT',
relationship_type VARCHAR(20) DEFAULT 'many_to_one', description TEXT
);
CREATE TABLE IF NOT EXISTS _agent_meta.metric_meta (
metric_name VARCHAR(64) PRIMARY KEY, business_desc TEXT NOT NULL,
metric_type VARCHAR(16) NOT NULL DEFAULT 'derived', sql_expression TEXT NOT NULL,
numerator TEXT, denominator TEXT, dimensions TEXT[], filters TEXT[],
owner VARCHAR(64), ai_hints TEXT
);
CREATE TABLE IF NOT EXISTS _agent_meta.filters (
filter_name VARCHAR(64) PRIMARY KEY, description TEXT NOT NULL,
sql_fragment TEXT NOT NULL, filter_scope VARCHAR(16) NOT NULL DEFAULT 'where',
owner VARCHAR(64)
);5.3 注册可查询的表
INSERT INTO _agent_meta.tables VALUES
('public','customers', '客户主表', '注册客户基础信息,含等级、渠道、消费汇总', '销售'),
('public','products', '商品主表', '在售商品信息,含类目、价格、库存', '商品'),
('public','orders', '订单主表', '订单头信息,含金额、状态、渠道、地区', '销售'),
('public','order_items', '订单明细表', '订单中每个商品的购买数量和金额', '销售'),
('public','campaigns', '营销活动表', '营销活动定义,含渠道、预算、效果指标', '营销');5.4 注册字段语义(核心步骤)
为每张表的字段定义角色和同义词,让 Agent 能理解自然语言:
-- orders 表字段
INSERT INTO _agent_meta.columns (table_schema, table_name, column_name, display_name, column_role, data_type, column_description, synonyms, agg_default, example_values) VALUES
('public','orders','order_id', '订单ID', 'identifier','BIGINT', '订单唯一标识', NULL, NULL, NULL),
('public','orders','customer_id', '客户ID', 'identifier','BIGINT', '关联客户主表', NULL, NULL, NULL),
('public','orders','order_date', '下单日期', 'dimension', 'DATE', '订单创建日期', ARRAY['日期','购买日期','交易日期','成交日期'], NULL, NULL),
('public','orders','order_status', '订单状态', 'dimension', 'VARCHAR', '订单当前状态', ARRAY['状态','交易状态'], NULL, ARRAY['已支付','已完成','已退款','已发货','待支付']),
('public','orders','payment_method','支付方式', 'dimension', 'VARCHAR', '客户使用的支付渠道', ARRAY['支付','付款方式'], NULL, ARRAY['支付宝','微信支付','银行卡','花呗']),
('public','orders','channel', '下单渠道', 'dimension', 'VARCHAR', '客户下单使用的终端', ARRAY['渠道','终端','平台'], NULL, ARRAY['APP','小程序','PC','H5']),
('public','orders','province', '省份', 'dimension', 'VARCHAR', '订单收货省份', ARRAY['地区','省','收货地'], NULL, NULL),
('public','orders','actual_amount', '实付金额', 'measure', 'NUMERIC', '扣除优惠后实际支付金额(元)',ARRAY['实付','支付金额','成交金额','GMV','流水'], 'SUM', NULL),
('public','orders','total_amount', '订单原价', 'measure', 'NUMERIC', '未扣除优惠的原始订单金额(元)',ARRAY['原价','订单金额','下单金额'], 'SUM', NULL),
('public','orders','discount_amount','优惠金额', 'measure', 'NUMERIC', '本单折扣/优惠金额(元)', ARRAY['折扣','优惠','减免金额'], 'SUM', NULL),
('public','orders','is_refunded', '是否退款', 'dimension', 'BOOLEAN', '订单是否已退款', ARRAY['退款','是否退货'], NULL, ARRAY['true','false'])
ON CONFLICT DO NOTHING;
-- order_items 关键字段
INSERT INTO _agent_meta.columns (table_schema, table_name, column_name, display_name, column_role, data_type, column_description, synonyms, agg_default, example_values) VALUES
('public','order_items','item_id', '明细ID', 'identifier','BIGINT', '明细行唯一标识', NULL, NULL, NULL),
('public','order_items','order_id', '订单ID', 'identifier','BIGINT', '关联订单主表', NULL, NULL, NULL),
('public','order_items','product_id', '商品ID', 'identifier','BIGINT', '关联商品主表', NULL, NULL, NULL),
('public','order_items','qty', '购买数量', 'measure', 'INT', '本次购买数量', ARRAY['数量','购买量','件数','销量'], 'SUM', NULL),
('public','order_items','subtotal', '小计金额', 'measure', 'NUMERIC', '本行商品合计金额(元)', ARRAY['金额','小计','明细金额','销售额'], 'SUM', NULL)
ON CONFLICT DO NOTHING;
-- customers 关键字段
INSERT INTO _agent_meta.columns (table_schema, table_name, column_name, display_name, column_role, data_type, column_description, synonyms, agg_default, example_values) VALUES
('public','customers','customer_id', '客户ID', 'identifier','BIGINT', '客户唯一标识', NULL, NULL, NULL),
('public','customers','customer_name', '客户名称', 'dimension', 'VARCHAR', '客户姓名', ARRAY['客户','买家','用户名'], NULL, NULL),
('public','customers','city', '城市', 'dimension', 'VARCHAR', '客户所在城市', ARRAY['城','所在城市'], NULL, ARRAY['北京','上海','广州']),
('public','customers','channel', '获客渠道', 'dimension', 'VARCHAR', '客户注册来源渠道', ARRAY['渠道','来源','获客方式'], NULL, ARRAY['自然流量','付费广告','老客转介绍','活动拉新']),
('public','customers','is_member', '是否会员', 'dimension', 'BOOLEAN', '是否为会员用户', ARRAY['会员','会员状态'], NULL, ARRAY['true','false']),
('public','customers','member_level', '会员等级', 'dimension', 'VARCHAR', '会员等级分类', ARRAY['等级','会员级别'], NULL, ARRAY['普通','银卡','金卡','钻石']),
('public','customers','total_orders', '历史订单数', 'measure', 'INT', '客户历史总订单数', ARRAY['下单次数','购买次数','订单量'], 'SUM', NULL),
('public','customers','total_amount', '历史消费额', 'measure', 'NUMERIC', '客户历史累计消费金额(元)', ARRAY['消费总额','历史金额','累计消费'], 'SUM', NULL)
ON CONFLICT DO NOTHING;
-- products 关键字段
INSERT INTO _agent_meta.columns (table_schema, table_name, column_name, display_name, column_role, data_type, column_description, synonyms, agg_default, example_values) VALUES
('public','products','product_id', '商品ID', 'identifier','BIGINT', '商品唯一标识', NULL, NULL, NULL),
('public','products','product_name','商品名称', 'dimension', 'VARCHAR', '商品名称', ARRAY['商品','产品','货品','SKU'], NULL, NULL),
('public','products','category_l1', '一级类目', 'dimension', 'VARCHAR', '商品一级分类', ARRAY['大类','主类目','品类'], NULL, ARRAY['数码电子','家居生活','运动健康']),
('public','products','brand', '品牌', 'dimension', 'VARCHAR', '商品品牌', ARRAY['牌子','厂牌'], NULL, NULL),
('public','products','unit_price', '售价', 'measure', 'NUMERIC', '商品零售价(元)', ARRAY['价格','定价','销售价','原价'], 'AVG', NULL),
('public','products','stock_qty', '库存量', 'measure', 'INT', '当前可售库存数量', ARRAY['库存','剩余库存','现货量'], 'SUM', NULL),
('public','products','status', '商品状态', 'dimension', 'VARCHAR', '商品当前上架状态', ARRAY['状态','是否在售'], NULL, ARRAY['在售','下架','预售'])
ON CONFLICT DO NOTHING;
-- campaigns 关键字段
INSERT INTO _agent_meta.columns (table_schema, table_name, column_name, display_name, column_role, data_type, column_description, synonyms, agg_default, example_values) VALUES
('public','campaigns','campaign_id', '活动ID', 'identifier','BIGINT', '营销活动唯一标识', NULL, NULL, NULL),
('public','campaigns','campaign_name','活动名称', 'dimension', 'VARCHAR', '活动名称', ARRAY['活动','营销活动','促销'], NULL, NULL),
('public','campaigns','channel', '投放渠道', 'dimension', 'VARCHAR', '活动投放渠道', ARRAY['渠道','投放平台'], NULL, ARRAY['APP','小程序','PC','全渠道']),
('public','campaigns','budget', '活动预算', 'measure', 'NUMERIC', '活动总预算(元)', ARRAY['预算','投放预算'], 'SUM', NULL),
('public','campaigns','actual_cost', '实际花费', 'measure', 'NUMERIC', '活动实际花费(元)', ARRAY['花费','消耗','实际投放额'], 'SUM', NULL),
('public','campaigns','impressions', '曝光量', 'measure', 'INT', '活动总曝光次数', ARRAY['曝光','展示量','展现量'], 'SUM', NULL),
('public','campaigns','clicks', '点击量', 'measure', 'INT', '活动总点击次数', ARRAY['点击','点击数'], 'SUM', NULL),
('public','campaigns','conversions', '转化量', 'measure', 'INT', '活动带来的下单转化次数', ARRAY['转化','转化数','成交数'], 'SUM', NULL)
ON CONFLICT DO NOTHING;核心价值:synonyms 字段让 Agent 能理解自然语言。当用户说「查一下 GMV」「看看流水」「商品销量」时,Agent 都能正确匹配到对应字段。
5.5 定义表间关联
INSERT INTO _agent_meta.joins
(left_schema, left_table, left_column, right_schema, right_table, right_column, join_type, relationship_type, description)
VALUES
('public','order_items','order_id', 'public','orders', 'order_id', 'LEFT','many_to_one','订单明细关联订单头'),
('public','order_items','product_id', 'public','products', 'product_id', 'LEFT','many_to_one','订单明细关联商品'),
('public','orders', 'customer_id','public','customers','customer_id','LEFT','many_to_one','订单关联客户');5.6(可选)定义业务指标
INSERT INTO _agent_meta.metric_meta VALUES
('net_revenue', '净收入 = 实付金额 - 优惠金额', 'derived',
'SUM(actual_amount) - SUM(discount_amount)', NULL, NULL,
'{public.orders.province, public.orders.channel, public.orders.order_date}',
'{paid_or_completed, not_refunded}', 'finance_team', '必须按省份或渠道聚合'),
('refund_rate', '退款率 = 退款订单数 / 总订单数', 'ratio',
'SUM(CASE WHEN is_refunded THEN 1 ELSE 0 END)::numeric / COUNT(*)',
'SUM(CASE WHEN is_refunded THEN 1 ELSE 0 END)', 'COUNT(*)',
'{public.orders.province, public.orders.channel}', NULL, 'ops_team', NULL),
('cumulative_gmv', '按日期累计 GMV(实付金额)', 'cumulative',
'SUM(SUM(actual_amount)) OVER (ORDER BY order_date ROWS UNBOUNDED PRECEDING)',
NULL, NULL, '{public.orders.order_date}', '{not_refunded}', 'finance_team', NULL),
('completed_gmv', '已完成订单的 GMV', 'simple',
'SUM(actual_amount) FILTER (WHERE order_status = ''已完成'')',
NULL, NULL, '{public.orders.province, public.orders.channel}',
'{completed_measure}', 'ops_team', NULL),
('campaign_roi', '营销活动 ROI = 转化量 / 实际花费', 'ratio',
'SUM(conversions)::numeric / NULLIF(SUM(actual_cost), 0)',
'SUM(conversions)', 'NULLIF(SUM(actual_cost), 0)',
'{public.campaigns.channel}', NULL, 'marketing_team', '仅适用于 campaigns 表');5.7(可选)定义过滤器
INSERT INTO _agent_meta.filters VALUES
('paid_or_completed', '已支付或已完成的订单',
'order_status IN (''已支付'',''已完成'')', 'where', 'ops_team'),
('not_refunded', '排除退款订单',
'is_refunded = false', 'where', 'finance_team'),
('completed_measure', '已完成订单的度量过滤',
'order_status = ''已完成''', 'measure', 'ops_team');步骤六:为只读账号授权(DBA 操作)
语义模型创建完成后,授予只读账号对应表的 SELECT 权限:
-- 仅授权语义模型中注册的 5 张表
GRANT SELECT ON public.customers, public.products, public.orders,
public.order_items, public.campaigns TO readonly_agent;
-- 授权语义模型元数据表的读取权限
GRANT USAGE ON SCHEMA _agent_meta TO readonly_agent;
GRANT SELECT ON ALL TABLES IN SCHEMA _agent_meta TO readonly_agent;安全原则:仅授权语义模型中注册的表。即使 Agent 因某种原因构造了对其他表的查询,数据库权限层也会拒绝。
步骤七:验证一切就绪
回到本地终端,用只读账号验证整体配置:
# 1. 验证连接
psql "service=adbpg" -c "SELECT current_user;"
# 预期输出:readonly_agent
# 2. 验证只读模式
psql "service=adbpg" -c "CREATE TABLE test_write (id int);"
# 预期报错:ERROR: cannot execute CREATE TABLE in a read-only transaction
# 3. 验证语义模型可读
psql "service=adbpg" -c "SELECT COUNT(*) FROM _agent_meta.tables;"
# 预期输出:5
# 4. 验证未注册表不可访问(权限拒绝)
psql "service=adbpg" -c "SELECT * FROM employee_salary LIMIT 1;"
# 预期报错:ERROR: permission denied for table employee_salary全部通过后,环境配置完成。
步骤八:开始与 Agent 对话
现在打开 Agent(Claude / Qoder / ClawHub 等),直接用自然语言提问即可。
Agent 首次启动时会自动执行安全检查:
验证连接 → 2. 检查账号权限 → 3. 检查资源组 → 4. 检测语义模型
通过后即可开始查询。
实际对话示例
示例 1:简单聚合查询
你:帮我查一下最近 30 天各省份的实付金额排名
Agent:根据语义模型,为您生成以下查询:
SELECT province, SUM(actual_amount) AS total_amount
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY province
ORDER BY total_amount DESC
LIMIT 50000;
是否确认执行?
你:确认
Agent:查询结果如下:
| province | total_amount |
|----------|--------------|
| 广东省 | 128,350.00 |
| 浙江省 | 96,720.00 |示例 2:使用预定义业务指标
你:查一下各渠道的净收入
Agent:匹配到预定义指标「净收入」(net_revenue),自动应用标准过滤条件:
- 仅统计已支付/已完成订单
- 排除退款订单
SELECT channel,
SUM(actual_amount) - SUM(discount_amount) AS net_revenue
FROM orders
WHERE order_status IN ('已支付','已完成')
AND is_refunded = false
GROUP BY channel
ORDER BY net_revenue DESC;
是否确认执行?示例 3:跨表关联查询
你:帮我看一下品牌A各商品的销量排名
Agent:需要关联 products 和 order_items,已根据语义模型的 JOIN 定义生成:
SELECT p.product_name, SUM(oi.qty) AS total_qty
FROM order_items oi
LEFT JOIN products p ON oi.product_id = p.product_id
WHERE p.brand = '品牌A'
GROUP BY p.product_name
ORDER BY total_qty DESC
LIMIT 50000;
是否确认执行?示例 4:查询被安全边界拒绝
你:帮我查一下员工的薪资情况
Agent:无法执行此查询。
employee_salary 表未在语义模型中注册,不在可查询范围内。
如需访问此数据,请联系 DBA 将该表添加到 _agent_meta 语义模型中。示例 5:导出 CSV
你:把上个月的订单数据导出成 CSV
Agent:将为您导出上月订单数据,预计约 80 条。
\copy (SELECT order_id, customer_id, order_date, actual_amount, channel
FROM orders WHERE order_date >= '2026-05-01' AND order_date < '2026-06-01')
TO '/tmp/orders_20260623_143022.csv' WITH CSV HEADER;
导出文件可能包含业务数据,请按组织数据安全策略处理。
是否确认执行?流程总结
完整流程一共 8 步,按角色分工如下:
DBA 负责(步骤二/四/五/六):
创建只读账号 → 准备业务表 → 配置语义模型 → 授权
用户负责(步骤一/三/七/八):
环境准备 → 配置本地连接 → 验证 → 开始对话查询步骤 | 执行者 | 操作 | 一次性/每次 |
步骤一 | 用户 | 确认环境、安装 psql 和 Skill | 一次性 |
步骤二 | DBA | 创建只读账号和资源组 | 一次性 |
步骤三 | 用户 | 配置本地 pg_service.conf / pgpass | 一次性 |
步骤四 | DBA | 准备业务表(实际场景通常已存在) | 一次性 |
步骤五 | DBA | 创建语义模型 | 一次性(后续增量维护) |
步骤六 | DBA | 授权只读账号 | 一次性(新增表时追加) |
步骤七 | 用户 | 验证配置 | 一次性 |
步骤八 | 用户 | 与 Agent 对话查询 | 每次使用 |
安全防护总结
防护层 | 机制 | 效果 |
语义模型边界 | 仅注册指定表 | 未注册的敏感表对 Agent 完全不可见 |
数据库权限 | 仅 GRANT 语义模型内的表 | 即使绕过语义模型,数据库也会拒绝访问 |
只读连接 |
| 任何写操作都被数据库拒绝 |
人工确认 | HITL 机制 | 每条 SQL 执行前必须用户明确批准 |
资源隔离 | 专用 Resource Group | Agent 查询不影响业务负载 |
反绕过条款 | 硬编码禁止规则 | Agent 不会因「紧急」等理由绕过安全边界 |
常见问题
Q:Agent 提示「语义模型不存在」怎么办?
A: 需要 DBA 在数据库中创建 _agent_meta schema 并注册表元数据。Agent 可以协助生成建表 SQL,但不会自行执行 DDL。
Q: 可以直接查询未注册的表吗?
A: 不可以。语义模型是硬性访问边界,Agent 会拒绝查询未注册的表并提示联系 DBA 扩展语义模型。
Q: 导出的 CSV 文件有安全风险吗?
A: CSV 文件可能包含敏感业务数据,请按照组织的数据安全策略处理导出文件(如加密存储、定期清理、限制访问权限)。
Q: 支持哪些 AnalyticDB PostgreSQL版本?
A: 支持存储弹性模式 AnalyticDB PostgreSQL版 7.0 及以上版本。