工业级Text2SQL实战:半导体晶圆厂Agent系统
GitHubhttps://github.com/BumbleBee-ZDS/FAB_Text2sql一、背景为什么晶圆厂的Text2SQL这么难半导体晶圆厂Fab是Text2SQL最地狱的应用场景之一没有之一。想象一下这个画面你走进一座12英寸晶圆厂的数据中心面前是上千张表每张表动辄上百个字段命名风格百花齐放——LOT_TRACKING批次追踪还算正常F001_WAFER_PARAM_RAW某测试参数表缩写F001TBL_EQP_CHMBR_LOG_2024设备腔体日志年份硬编码中英文混杂产品型号、PRODUCT、PRD_CD三个字段可能指同一件事字段值千奇百怪STEP_NAMEPHOTO_01A、STATUSRUNNINGvsIN_PROCESS语义重复更致命的是业务知识极度隐性用户说查一下良率——是YIELD_PERCENT还是(GOOD_DIE/TOTAL_DIE)*100不同产品类型公式不同用户说看看SPC有没有异常——是看MEAS_VALUEUCL还是MEAS_VALUELCL还是连续7点同侧用户说WIP堆积——哪个工序算堆积1000片还是超过该工序平均WIP的2倍学术界BenchmarkSpider/BIRD上的SOTA模型丢到这种环境里准确率直接腰斩。这不是模型不够大而是问题本身就不是纯生成问题——它是一个领域知识 确定性模板 灵活生成的混合问题。二、核心创意能用确定性的就不麻烦LLM这是整个项目最重要的设计哲学一句话概括「80%的查询是可枚举的固定模式把它们写成参数化模板关键词命中即用零LLM调用、零幻觉、零延迟。」这听起来很朴素甚至有点不AI。但正是这个朴素的思想让我们在195条领域测试用例上达到了100%准确率——而同期直接用GPT-4o生成SQL的方案在同样的数据上准确率只有60%~70%。为什么不直接上LLM维度纯LLM方案Skill模板优先方案准确率60%~70%幻觉/方言错误模板命中≈100%延迟2~5秒/次API调用模板命中10ms成本每次消耗token模板命中零成本可解释性黑盒生成模板可读、可审计安全性可能生成DML/DDL模板白名单天然安全维护成本Prompt越写越长新增场景新增模板文件LLM不是万能锤子Text2SQL也不是钉子。把它用在真正需要理解创造的地方Skill未命中时的兜底才是正确姿势。三、系统架构四层九Agent┌──────────────────────────────────────────────────────────────────────┐ │ 用户查询 (自然语言) │ │ 查询产品A3最近三个月的良率趋势并按工序分组 │ └──────────────────────────────┬───────────────────────────────────────┘ │ ┌──────────────▼──────────────┐ │ Text2SQLAgent │ │ (think → act → observe) │ │ ReAct 循环 ≤10步 │ └──────────────┬──────────────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ SkillMatch │ │ ToolRegistry │ │ LLM Fallback │ │ (优先, 免调用) │ │ (9 个工具) │ │ (Skill未命中时) │ │ 239个模板 │ │ execute/fix/ │ │ GPT-4o-mini │ │ 加权关键词匹配 │ │ search/safety │ │ 兜底生成 │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ ▼ ▼ │ ┌─────────────────┐ ┌─────────────────┐ │ │ semantic_layer │ │ oracle_mock │ │ │ 术语词典 │ │ (DuckDB 内存) │◀──────────┘ │ Skill模板 │ │ 方言翻译层 │ │ 参数提取 │ │ Oracle→DuckDB │ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ │ 结果 / SQL │ │ 可视化图表 │ └─────────────────┘四层职责拆解层级职责关键模块用户交互层Streamlit Web UI CLI REST APIapp.py,cli.py,main.pyAgent编排层LangGraph状态图 ReAct循环 Human-in-the-loopworkflow.py,agent.py语义理解层Skill匹配 术语词典 参数提取 LLM兜底semantic_layer.py,llm_client.py数据执行层DuckDB内存引擎 Oracle方言翻译 安全拦截oracle_mock.py,tools.py四、Skill模板机制项目的灵魂4.1 一个Skill长什么样{name:yield_trend,description:产品良率趋势分析按月聚合,keywords:[良率趋势,yield trend,良率变化,yield by month],extract_params:_extract_params_yield_trend,# 提取产品名、时间范围template: SELECT TRUNC(START_TIME, MM) AS MONTH, PRODUCT, AVG(YIELD_PERCENT) AS AVG_YIELD FROM LOT_TRACKING WHERE PRODUCT {product} AND START_TIME ADD_MONTHS(SYSDATE, -{months}) GROUP BY TRUNC(START_TIME, MM), PRODUCT ORDER BY MONTH }用户输入最近三个月A3产品的良率趋势 → 匹配到yield_trend→ 提取productA3, months3→ 渲染模板 → 得到可执行SQL。全程零LLM调用耗时10ms。4.2 加权关键词匹配算法这是Skill命中率的关键。不是简单的包含即匹配而是多因子加权评分score匹配关键词数量 ×2000# 命中越多越优先Σ(匹配关键词长度)×10# 越长越具体良率趋势良率Σ(位置奖励)×100# 关键词列表越靠前权重越高描述命中数 ×500# 描述中也出现 强信号为什么不用向量相似度在239个模板的规模下关键词匹配的精确率远高于向量检索——良率趋势和良率分布语义接近但SQL完全不同向量检索容易混淆关键词则精确可控。4.3 分代注入从20到239的演进模板不是一次性写完的而是跟着测试用例一起生长代模板数覆盖场景设计思路Gen320基础查询良率/SPC/设备/批次先跑通核心链路Gen425多表JOIN/聚合/子查询覆盖跨表关联Gen550边界/压力NULL/闰年/ROLLUP/CUBE极端case驱动Gen630多步业务流程/统计建模/数据血缘复杂分析场景每一代都是测试失败→分析根因→补模板→再测试的迭代产物。这本质上是一种测试驱动的Skill工程。4.4 业务术语词典TERM_DICTIONARY{良率:{field:YIELD_PERCENT,table:LOT_TRACKING},在制品:{table:LOT_TRACKING,condition:STATUSRUNNING},WIP:{table:LOT_TRACKING,condition:STATUSRUNNING},SPC异常:{table:SPC_RESULTS,condition:MEAS_VALUEUCL OR MEAS_VALUELCL},低库存:{table:MATERIAL_INVENTORY,condition:QTY_ON_HAND100},OEE:{field:DURATION_MIN,table:EQUIPMENT_LOG,calc:running_time/total_time},}LLM在生成SQL前先把自然语言中的模糊词替换为确定性映射相当于给模型配了一本领域字典。五、Agent执行循环ReAct范式当Skill未命中时系统进入Agentic模式whilestepmax_stepsandnottask_completed:# Think决定下一步用什么工具thought_heuristic_decision(user_input,current_sql,observation)# Act执行工具ifthoughtmatch_skill:resultSkillMatchTool.invoke(user_input)elifthoughtgenerate_sql:resultLLMFallbackSQLTool.invoke(user_input,schema_context)elifthoughtexecute_sql:resultExecuteSQLTool.invoke(current_sql)elifthoughtfix_sql:resultFixSQLTool.invoke(current_sql,error_msg)# Observe观察结果更新状态observationresult.outputifresult.success:task_completedTrue这是一个标准的ReActReasoning Acting循环最多10步。每一步的thought/action/observation都被记录可以在Streamlit界面上实时展示——用户能看到Agent在想什么、“在做什么”。六、LangGraph工作流状态图编排fromlanggraph.graphimportStateGraph,START,ENDfromlanggraph.checkpoint.memoryimportMemorySaver# 定义状态classGraphState(TypedDict):user_input:strschema_context:listlogical_plan:strgenerated_sql:strcritic_feedback:strhuman_edited_sql:strapproved:booliteration_count:interror_type:Optional[str]# 构建图builderStateGraph(GraphState)builder.add_node(planner,planner_agent)# 意图理解逻辑计划builder.add_node(generator,sql_generator)# SQL生成builder.add_node(critic,sql_critic)# 执行校验builder.add_node(safety,safety_auditor)# 安全审计builder.add_edge(START,planner)builder.add_edge(planner,generator)builder.add_edge(generator,critic)builder.add_conditional_edges(critic,decide_next)# 错误→回generator成功→safetybuilder.add_edge(safety,END)# 编译带内存检查点支持对话上下文graphbuilder.compile(checkpointerMemorySaver())为什么用LangGraph而不是直接调函数三个理由状态持久化MemorySaver自动保存每步状态多轮对话不丢上下文条件分支Critic校验失败自动回退到Generator无需手写if-else可观测性每个节点的输入输出天然可追踪方便调试和可视化七、Oracle → DuckDB 方言适配离线可跑的秘诀真实晶圆厂用的是Oracle但开发和测试不可能连生产库。解决方案用DuckDB做内存模拟加一层方言翻译。def_translate_oracle_sql(sql:str)-str:把Oracle语法翻译成DuckDB兼容语法sqlre.sub(rTRUNC\s*\(([^,]),\s*[\]MM[\]\),rDATE_TRUNC(\month\, \1),sql,flagsre.I)sqlre.sub(rADD_MONTHS\s*\(([^,]),\s*([^)])\),r(\1 INTERVAL \2 MONTH),sql,flagsre.I)sqlre.sub(rSYSDATE,CURRENT_TIMESTAMP,sql,flagsre.I)sqlre.sub(rNVL\s*\(,COALESCE(,sql,flagsre.I)sqlre.sub(rTO_DATE\s*\(,CAST(,sql,flagsre.I)# ... 更多规则returnsql这意味着所有Skill模板可以用Oracle原生语法编写贴合真实场景运行时自动翻译为DuckDB执行。开发和CI/CD完全离线不依赖任何外部数据库。八、Human-in-the-loop让人和AI协作而非对抗Streamlit界面设计遵循一个原则AI生成人类拍板。┌──────────────────────────┬──────────────────────────┐ │ 聊天区 (60%) │ Agent状态面板 (40%) │ ├──────────────────────────┼──────────────────────────┤ │ 用户查A3良率趋势 │ 当前节点: generator │ │ Agent已匹配Skill │ 逻辑计划: 按月聚合... │ │ │ Schema: LOT_TRACKING │ │ ┌──────────────────────┐ │ 反馈: 无 │ │ │ SELECT ... │ │ 迭代: 1/3 │ │ │ (可编辑的SQL框) │ │ │ │ └──────────────────────┘ │ ┌──────────────────────┐ │ │ │ │ ▶ 执行 ❌ 不满意 │ │ │ ┌──────────────────────┐ │ └──────────────────────┘ │ │ │ 执行结果表格 │ │ │ │ │ MONTH | AVG_YIELD │ │ │ │ │ 2025-01| 92.3% │ │ │ │ │ 2025-02| 94.1% │ │ │ │ └──────────────────────┘ │ │ └──────────────────────────┴──────────────────────────┘编辑框AI生成的SQL不是终点用户可以修改后执行不满意按钮点击后iteration_countAgent带着上次错误信息重新生成状态面板实时展示Agent在想什么、用什么工具、当前进度九、双层安全拦截工业场景容不得半点闪失——绝不能让AI生成一条DELETE语句。# 第一层ExecuteSQLTool 正则拦截DANGER_PATTERNS[r\bDROP\s(TABLE|INDEX|VIEW|SCHEMA|DATABASE),r\bDELETE\sFROM\b,r\bINSERT\sINTO\b,r\bUPDATE\s\w\sSET\b,r\bALTER\s(TABLE|INDEX|VIEW),r\bCREATE\s(TABLE|INDEX|VIEW|DATABASE),r\bTRUNCATE\sTABLE\b,r;\s*(DROP|DELETE|INSERT|UPDATE|ALTER),# 多语句注入]# 第二层SafetyCheckTool 独立审计节点# 在LangGraph工作流中critic之后再次校验两层防御的哲理第一层是防呆简单粗暴但有效第二层是审计独立节点防止第一层被绕过。即使LLM产生了危险SQL也绝无执行可能。十、评测体系195条用例100%通过 总览: 总用例: 195 通过: 195 失败: 0 总准确率: 100.0% Skills 总数: 239代用例数覆盖维度设计意图Gen320基础单表查询验证核心链路可用Gen425多表JOIN/聚合/子查询覆盖跨表关联Gen5115NULL/闰年/ROLLUP/CUBE/安全拦截/中英混合压力测试115条是主力Gen630多步业务流/统计建模(CORR/STDDEV/PERCENTILE)/数据血缘复杂分析场景Gen5的115条是用心之作——它包含了你能想到的几乎所有边界case闰年2月29日的日期计算全NULL列的聚合处理空结果集的友好提示超长SQL100行的渲染中英混合输入“查A3 yield trend”SQL注入尝试的拦截十一、关键数据12张表、6.5万行表行数说明LOT_TRACKING5,000批次追踪核心表WAFER_TEST10,000晶圆测试参数EQUIPMENT_LOG8,000设备运行日志SPC_RESULTS20,000SPC统计过程控制PRODUCTION_LOG15,000生产日志DEFECT_ANALYSIS3,000缺陷分析PROCESS_STEP50工艺步骤定义CUSTOMER_ORDER1,500客户订单4张主数据表—产品/设备/员工/物料6.5万行数据全部在内存中运行DuckDB的OLAP性能绰绰有余单条查询毫秒级返回。十二、项目结构fab_text2sql/ ├── eval_dashboard.py # 评测入口一键跑195条用例 ├── requirements.txt ├── .env # LLM API Key ├── README.md │ ├── src/ │ ├── core/ # 13个核心模块 │ │ ├── semantic_layer.py # ★ Skill模板库 术语词典 │ │ ├── tools.py # ★ 9个工具 ToolRegistry │ │ ├── agent.py # ★ Text2SQLAgent (ReAct) │ │ ├── agents.py # LangGraph节点Agent │ │ ├── workflow.py # LangGraph状态图 │ │ ├── oracle_mock.py # DuckDB 方言翻译 │ │ ├── schema_knowledge.py # 12张表Schema │ │ ├── data_generator.py # Mock数据生成 │ │ ├── vector_index.py # TF-IDF向量检索 │ │ ├── llm_client.py # LLM客户端封装 │ │ ├── config.py # 全局配置 │ │ └── utils.py # 通用工具 │ │ │ └── skills/ # 分代Skill注入 │ ├── install_gen3_skills.py │ ├── install_gen4_skills.py │ ├── install_gen5_skills.py │ └── install_gen6_skills.py │ ├── tests/ # 195条评测用例 │ ├── test_gen3.py │ ├── test_gen4.py │ ├── test_gen5_edge.py │ └── test_gen6.py │ ├── scripts/ # 应用入口 │ ├── main.py # CLI / API │ ├── cli.py # rich交互式命令行 │ └── app.py # ★ Streamlit Web界面 │ └── archive/ # 历史调试脚本十三、运行方式# 1. 安装依赖pipinstallduckdb langgraph rich streamlit scikit-learn pandas openai# 2. 评测验证195/195python eval_dashboard.py# 生成 eval_dashboard.html浏览器打开查看可视化报告# 3. CLI交互模式python scripts/main.py# 4. Streamlit Web界面streamlit run scripts/app.py界面展示十四、经验总结做对了的5件事✅ 1. Skill优先LLM兜底不是所有问题都需要大模型。确定性模板在领域场景的准确率和效率碾压LLM。✅ 2. 测试驱动Skill工程先写会失败的测试用例再写模板让它通过。239个模板不是拍脑袋写的是195条用例逼出来的。✅ 3. 方言翻译层Oracle→DuckDB的翻译让项目完全离线可跑CI/CD无依赖开发体验极佳。✅ 4. 双层安全正则拦截 独立审计节点即使LLM叛变也执行不了危险操作。✅ 5. Human-in-the-loopAI不是取代人是辅助人。编辑框 不满意按钮 状态可视化让人在回路中始终掌控。十五、局限与展望当前局限Skill模板需要人工编写虽然分代注入降低了门槛但仍是人力成本239个模板覆盖的是已知场景长尾查询仍依赖LLM兜底准确率约70%DuckDB模拟无法100%还原Oracle的优化器行为未来方向自动Skill挖掘从SQL日志中自动提取高频模式半自动生成模板RAG增强将表注释、字段描述、历史SQL存入向量库提升LLM兜底质量多轮对话支持把刚才的查询按周聚合这种上下文依赖的交互真实Oracle适配替换DuckDB为Oracle连接方言翻译层反向工作ChatBI产品化从Text2SQL进化为完整的对话式BI支持图表自动推荐写在最后Text2SQL不是一个新课题但工业级落地和学术Benchmark刷分是两回事。Spider 2.0上GPT-4o只有10%成功率不是因为模型不够强而是因为真实世界的数据库太脏、太复杂、太有领域特性。解法不是更大的模型而是更好的架构。Skill模板、术语词典、方言适配、安全拦截、Human-in-the-loop——这些工程化的东西才是让Text2SQL从Demo惊艳走向生产可用的关键。代码不一定要优雅但一定要对业务有用。参考资源Spider 2.0: The Second Edition of the Spider Dataset — 企业级Text2SQL BenchmarkBIRD: Large-Scale Dataset for Text-to-SQL — 真实数据库脏数据LangGraph官方文档 — Agent编排框架DuckDB官方文档 — 内存OLAP引擎ReAct: Synergizing Reasoning and Acting — ReAct范式原论文LinkedIn’s SQL Bot — 工业级Text2SQL实践参考如果这篇文章对你有帮助欢迎点赞、收藏、转发三连 有任何问题评论区见