基于百炼平台构建贷款业务 Text-to-SQL 智能问答 Agent
Jun 16, 2025
About
11
min read
(
108
Words
)
Reads
背景 在贷款业务中,运营团队每天需要高频查询各类数据指标:放款量、逾期率、客群分布、渠道转化……传统模式下,每一次数据需求都要经历”提需求 → BI → 手写 SQL → 导出结果”的漫长流程,单次耗时 5~10 分钟,遇到复杂多表关联甚至更久, 效率特别低下。
因为本身我有后端背景,而且自己也感兴趣,并且最后需要在 xxx Editor 上构建一个”智能问答”组件,所以这个任务就被我接下来了。
整体架构 1 2 3 4 5 6 7 8 9 10 11 用户(钉钉 / xxx 页面) │ ▼ 百炼 Agent API │ ┌─────┼──────┐ 意图识别 SQL生成 图表渲染 │ │ │ 澄清追问 安全校验 卡片/组件推送 │ 业务数据库
核心链路:意图识别 → SQL 生成 → 安全校验 → 查询执行 → 可视化反馈
入口不止一处——既可以通过钉钉 Robot 对话触发,也可以在 xxx Editor 搭建的运营页面中直接嵌入”智能问答”组件,拖拽即用,零门槛接入,特别好使,哈哈。
一、Agent 工作流设计
利用百炼平台的 Workflow 能力,将整个问答过程拆解为以下节点:
节点
职责
意图识别
判断用户是在查放款数据、逾期数据还是客群分析
参数补全
对模糊时间范围(”上个月”、”最近一周”)自动澄清
SQL 生成
调用 LLM 结合 Schema 上下文生成 SQL
安全校验
语法规则 + 字段白名单双重拦截
查询执行
沙箱环境执行,返回结构化结果集
图表渲染
动态生成图表,输出至钉钉卡片或 Quark 组件
多轮对话机制让模糊意图变得可处理,而不是直接报错:
1 2 3 4 5 6 用户:查一下最近的放款情况 Agent:请问您想查哪个时间段?(今天 / 本周 / 本月) 用户:本月 Agent:需要按渠道拆分吗? 用户:是的,按渠道 Agent:[生成图表] 本月各渠道放款金额如下 ↓
二、Text-to-SQL 工程优化 Schema 上下文构建 贷款业务涉及 30+ 张核心表,字段命名风格不一、指标口径各异,直接交给模型效果很差。我们为每张表构建了结构化的 Schema 描述,明确字段含义与业务口径:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 SCHEMA_CONTEXT = """ 表名: table_A(贷款订单表) 字段说明: - loan_id: 贷款订单ID - user_id: 借款人ID - apply_amount: 申请金额(元) - approve_amount: 审批金额(元) - loan_date: 放款日期 - channel: 获客渠道(APP/H5/线下) - product_code: 产品编码 - status: 订单状态(1-放款中 2-还款中 3-已结清 4-逾期) 指标口径: - 放款量 = COUNT(loan_id) WHERE status IN (2,3,4) - 放款金额 = SUM(approve_amount) WHERE status IN (2,3,4) - 逾期率 = COUNT(status=4) / COUNT(status IN (2,3,4)) """
Few-Shot 示例库 针对贷款业务高频查询场景,沉淀典型 NL→SQL 样本,引导模型对齐业务语义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 FEW_SHOT_EXAMPLES = [ { "question" : "查本月各渠道放款金额" , "sql" : """ SELECT channel, SUM(approve_amount) AS loan_amount FROM table_A WHERE DATE_FORMAT(loan_date, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') AND status IN (2, 3, 4) GROUP BY channel ORDER BY loan_amount DESC """ }, { "question" : "最近7天逾期率趋势" , "sql" : """ SELECT loan_date, ROUND(SUM(status=4) / COUNT(*) * 100, 2) AS overdue_rate FROM table_A WHERE loan_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY loan_date ORDER BY loan_date """ } ]
经过 Schema 上下文 + Few-Shot 双重优化,复杂查询的 SQL 生成准确率从 **60% 提升至 90%**左右。
三、安全校验机制 贷款数据高度敏感,SQL 执行前设置两道防线:字段黑名单拦截身份证、手机号等 PII 字段,关键字过滤阻断一切写操作。
1 2 3 4 5 6 7 8 9 10 11 12 FIELD_BLACKLIST = {"id_card" , "mobile" , "bank_card" , "password" } DANGEROUS_KEYWORDS = {"DROP" , "DELETE" , "UPDATE" , "INSERT" , "TRUNCATE" , "ALTER" } def validate_sql (sql: str ) -> tuple [bool , str ]: sql_upper = sql.upper() for kw in DANGEROUS_KEYWORDS: if kw in sql_upper: return False , f"包含禁止操作: {kw} " for field in FIELD_BLACKLIST: if field in sql.lower(): return False , f"包含敏感字段: {field} " return True , "ok"
四、工具执行与图表渲染 在独立沙箱中执行 SQL,并将结果集动态渲染为图表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import pandas as pdimport matplotlib.pyplot as pltfrom sqlalchemy import create_engine, textdef execute_query_and_render (sql: str , chart_type: str = "bar" ) -> dict : df = pd.read_sql(text(sql), create_engine(DB_URL)) fig, ax = plt.subplots(figsize=(8 , 4 )) df.plot(kind=chart_type, ax=ax, marker="o" if chart_type == "line" else None ) img_path = f"/tmp/chart_{uuid4().hex [:8 ]} .png" plt.tight_layout() plt.savefig(img_path) plt.close() return {"data" : df.to_dict(orient="records" ), "chart" : img_path}
钉钉交互卡片 将图表和数据表格封装为钉钉 ActionCard,运营同学在手机端即可直接查看,并支持一键导出:
1 2 3 4 5 6 7 8 9 10 11 { "msgtype" : "action_card" , "action_card" : { "title" : "📊 放款数据报表" , "text" : "\n\n${data_table}" , "btns" : [ { "title" : "导出 Excel" , "actionURL" : "${export_url}" } , { "title" : "继续追问" , "actionURL" : "${bot_url}" } ] } }
xxx Editor 组件化接入 除钉钉 Robot 外,我们将 Text-to-SQL Agent 进一步封装为 xxx Editor 低代码组件 。运营人员在搭建数据看板或活动页面时,直接从组件库拖拽”智能问答”模块到任意位置,无需任何开发介入,即可在页面内嵌入完整的自然语言查数能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 export default defineComponent ({ name : "TextToSQLWidget" , props : { placeholder : { type : String , default : "请输入您想查询的数据,例如:本月各渠道放款金额" }, defaultChartType : { type : String , default : "bar" }, }, setup (props ) { const query = ref ("" ); const result = ref (null ); async function handleQuery ( ) { result.value = await callBailianAgent ({ query : query.value }); } return () => ( <div class ="text-to-sql-widget" > <Input v-model ={query.value} placeholder ={props.placeholder} onPressEnter ={handleQuery} /> {result.value && <ChartRenderer data ={result.value} chartType ={props.defaultChartType} /> } </div > ); }, });
组件对外暴露 placeholder 和 defaultChartType 两个配置项,编辑器内可视化调整,真正做到拖拽即用、配置即得 。
五、系统集成与稳定性 完整调用链路: 钉钉 Robot / 业务页面 → 百炼 API → 业务数据库
关键保障措施:
SSO 鉴权 :通过钉钉 userId 映射企业内部权限,不同角色只能查看授权范围内的数据
异常熔断 :SQL 执行超时(>10s)自动降级,返回友好提示并触发告警
查询限流 :单用户每分钟最多 10 次,防止数据库压力过大
1 2 3 4 5 6 7 8 @circuit_breaker(timeout=10 , fallback="查询超时,请稍后重试或联系数据团队" ) @rate_limit(max_calls=10 , period=60 ) def safe_execute (sql: str , user_id: str ) -> dict : check_permission(user_id, sql) valid, msg = validate_sql(sql) if not valid: raise ValueError(msg) return execute_query_and_render(sql)
效果对比
指标
上线前
上线后
单次查询耗时
5~10 分钟
平均 30 秒
SQL生成准确率
60%
90%
运营查数占比
~10%
~85%
BI介入次数
每日 20+ 次
每日 2~3 次
总结 这套方案的核心价值在于打通了从自然语言到数据洞察 的完整链路,并通过多端接入降低了使用门槛。 虽然本身难度不高,因为很多组件和能力百炼平台都已经提供了,但在实际落地过程中,还是给了我不少启发:
Schema 上下文 + Few-Shot :解决业务语义理解难题,是准确率跃升的关键
多轮对话澄清 :让模糊意图变得可处理,交互体验更接近真人分析师
安全校验前置 :在贷款敏感数据场景下守住安全底线
组件化 :将 AI 能力下沉为低代码积木,运营团队拖拽即用,彻底消除技术门槛
后续计划引入查询语义缓存 (相同意图直接命中缓存)和定时报表订阅 (关键指标自动推送),不过这都是后话了。