1. 引言
在证券业务场景中,自然语言交互式查询(也就是让业务人员直接用普通话问"合肥齐云山路营业部的存量有效户覆盖率在全公司排名第几"这种问题)一直是智能数据分析的圣杯命题。传统做法无非两条路:一是写死几十个 SQL 模板然后做意图映射,二是在通用大模型上狂塞 Prompt 规则。前者每新增一个指标口径就要改代码上线,后者随着规则越来越多 Token 成本爆炸不说,模型还容易上下文漂移。
本文将深度拆解一个在生产环境运行超过数月的证券数据智能问答系统 agent_ds_plus_v3.py。它不是简单的「大模型 + 数据库」调用,而是一个精心设计的5-Agent 流水线 + 多级并行优化 + 私有知识库检索增强系统,把「意图识别 → SQL 生成 → SQL 审查 → 回答生成 → 忠实度审计」这五个环节各自解耦为独立 Agent,并引入了私有知识库混合规则召回和推测执行等优化手段。
2. 整体架构:五段式流水线
先上图,让读者快速建立全局认知:
#mermaid-svg-WY59gtmlG4RFaio7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WY59gtmlG4RFaio7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WY59gtmlG4RFaio7 .error-icon{fill:#552222;}#mermaid-svg-WY59gtmlG4RFaio7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WY59gtmlG4RFaio7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WY59gtmlG4RFaio7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WY59gtmlG4RFaio7 .marker.cross{stroke:#333333;}#mermaid-svg-WY59gtmlG4RFaio7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WY59gtmlG4RFaio7 p{margin:0;}#mermaid-svg-WY59gtmlG4RFaio7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-WY59gtmlG4RFaio7 .cluster-label text{fill:#333;}#mermaid-svg-WY59gtmlG4RFaio7 .cluster-label span{color:#333;}#mermaid-svg-WY59gtmlG4RFaio7 .cluster-label span p{background-color:transparent;}#mermaid-svg-WY59gtmlG4RFaio7 .label text,#mermaid-svg-WY59gtmlG4RFaio7 span{fill:#333;color:#333;}#mermaid-svg-WY59gtmlG4RFaio7 .node rect,#mermaid-svg-WY59gtmlG4RFaio7 .node circle,#mermaid-svg-WY59gtmlG4RFaio7 .node ellipse,#mermaid-svg-WY59gtmlG4RFaio7 .node polygon,#mermaid-svg-WY59gtmlG4RFaio7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WY59gtmlG4RFaio7 .rough-node .label text,#mermaid-svg-WY59gtmlG4RFaio7 .node .label text,#mermaid-svg-WY59gtmlG4RFaio7 .image-shape .label,#mermaid-svg-WY59gtmlG4RFaio7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-WY59gtmlG4RFaio7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WY59gtmlG4RFaio7 .rough-node .label,#mermaid-svg-WY59gtmlG4RFaio7 .node .label,#mermaid-svg-WY59gtmlG4RFaio7 .image-shape .label,#mermaid-svg-WY59gtmlG4RFaio7 .icon-shape .label{text-align:center;}#mermaid-svg-WY59gtmlG4RFaio7 .node.clickable{cursor:pointer;}#mermaid-svg-WY59gtmlG4RFaio7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WY59gtmlG4RFaio7 .arrowheadPath{fill:#333333;}#mermaid-svg-WY59gtmlG4RFaio7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WY59gtmlG4RFaio7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WY59gtmlG4RFaio7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WY59gtmlG4RFaio7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WY59gtmlG4RFaio7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WY59gtmlG4RFaio7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WY59gtmlG4RFaio7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WY59gtmlG4RFaio7 .cluster text{fill:#333;}#mermaid-svg-WY59gtmlG4RFaio7 .cluster span{color:#333;}#mermaid-svg-WY59gtmlG4RFaio7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WY59gtmlG4RFaio7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WY59gtmlG4RFaio7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-WY59gtmlG4RFaio7 .icon-shape,#mermaid-svg-WY59gtmlG4RFaio7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WY59gtmlG4RFaio7 .icon-shape p,#mermaid-svg-WY59gtmlG4RFaio7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WY59gtmlG4RFaio7 .icon-shape .label rect,#mermaid-svg-WY59gtmlG4RFaio7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WY59gtmlG4RFaio7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WY59gtmlG4RFaio7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WY59gtmlG4RFaio7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
OUT_OF_SCOPE
DIRECT_QUERY / ANALYTICAL_QUERY
HARD_FAIL
PASS
FAIL 回到Agent4
PASS
用户问题
阶段0:并行前置处理Agent1意图+结构化解析 ‖ RAG Schema召回 ‖ PrivateKB业务背景召回
Agent1 意图判断
拒绝回答并引导
阶段1:Agent2 Text-to-SQL生成DeepSeek reasoning 模式
阶段2:Agent3 SQL审查Fast Rule Guard (毫秒级) → LLM深审 (仅复杂题)
阶段3:Agent4 业务回答生成不喂SQL,仅基于数据表格写自然语言
Agent5 忠实度审计(仅排名/分位/中位数题触发)
业务清洗兜底 → 最终输出
注意亮点:Agent2/3/4 之间存在两层并行机制——Eager Agent4 在 SQL 执行成功后立即后台预跑,与 Agent3 的 fast_rule 和深审完全并行;对复杂题还有 Speculative Agent4 与 LLM 深审并肩作战。简单来说,能并行的地方绝不串行。
那么,混合规则召回相比传统全量 Prompt 注入到底带来了多少实际收益?下面这张表把差异量化出来:
| Token 消耗(单次 Agent2 调用) | 200+ 行 System Prompt,约 2,500–3,500 Token | 按需召回,System Prompt 缩减至约 1,000–1,800 Token | 单次调用 Token 节省 30%–60% |
| 平均响应延迟 | 90%+ 的 SQL 审查走 LLM 深审,延迟集中在 2–5 秒 | 约 70% 的查询仅走 Fast Rule Guard(毫秒级),长尾复杂题才触发 LLM 深审 | 简单查询延迟降至 1 秒以内,P50 延迟降低 50%–70% |
| 规则维护成本 | 每次新增/修改规则需改几百行 Prompt,牵一发动全身,规则越多维护越困难 | 规则以独立 .md 文件存储,新增一条规则只需新增一个文件,索引启动时自动重建 | 单条规则维护成本从「改几百行 Prompt」降至 「新增一个文件」,且规则可跨 Schema 复用 |
这张表揭示了一个关键点:随着业务规则从 5 条增长到 50 条甚至 100 条,传统方案的 Token 消耗和维护成本会线性甚至超线性恶化,而混合召回方案的成本几乎不随规则总量增长——因为每次只按需加载与当前问题相关的少数规则。
如果再拉远一点看系统上线前后的整体效果,下面这张对比图更直观:
#mermaid-svg-9qs6OIuAGQzNnEG2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9qs6OIuAGQzNnEG2 .error-icon{fill:#552222;}#mermaid-svg-9qs6OIuAGQzNnEG2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9qs6OIuAGQzNnEG2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9qs6OIuAGQzNnEG2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9qs6OIuAGQzNnEG2 .marker.cross{stroke:#333333;}#mermaid-svg-9qs6OIuAGQzNnEG2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9qs6OIuAGQzNnEG2 p{margin:0;}#mermaid-svg-9qs6OIuAGQzNnEG2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}P50 / P90 延迟对比传统全量 Prompt混合规则召回5000450040003500300025002000150010005000延迟 (ms)
- 蓝色柱(P50 延迟):传统方案约 2,850 ms(几乎每次调用都需 LLM 深审)→ 本方案降至约 620 ms(70% 查询经 Fast Rule Guard 毫秒级放行)
- 橙色柱(P90 延迟):传统方案约 5,100 ms → 本方案约 1,450 ms(长尾复杂题才触发 LLM 深审,并行推测执行进一步压缩长尾)
#mermaid-svg-cDoQMp6CrU6F29fW{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cDoQMp6CrU6F29fW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cDoQMp6CrU6F29fW .error-icon{fill:#552222;}#mermaid-svg-cDoQMp6CrU6F29fW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cDoQMp6CrU6F29fW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cDoQMp6CrU6F29fW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cDoQMp6CrU6F29fW .marker.cross{stroke:#333333;}#mermaid-svg-cDoQMp6CrU6F29fW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cDoQMp6CrU6F29fW p{margin:0;}#mermaid-svg-cDoQMp6CrU6F29fW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}SQL 生成准确率 & 规则维护耗时对比传统全量 Prompt混合规则召回1009080706050403020100百分比 / 分钟
- 蓝色柱(SQL 生成准确率):传统方案约 76%(规则互相干扰造成上下文漂移)→ 本方案约 93%(按需召回强相关规则,聚焦生成)
- 绿色折线(单条规则维护耗时):传统方案约 30 分钟/次(翻阅数百行 Prompt 改一处)→ 本方案约 2 分钟/次(新增一个 .md 文件,索引自动重建)
注:以上数据来自该系统在证券问数场景下 1,200+ 条真实查询的离线回放评测,传统方案和混合召回方案使用相同的测试集与业务规则集。
这就是「让规则回归检索」所带来的可扩展性红利。
3. 核心设计理念:不只是写 SQL,而是管规则
领域 Text-to-SQL 的真正难点从来不是 SQL 语法——现在的大模型写 SELECT … FROM … WHERE … 已经炉火纯青。真正的难点是那些藏在业务人员脑子里、文档里、Excel 公式里的隐性知识:
- 「覆盖率」到底取哪个表的哪个字段?是 BASE_EFFECTIVE_KH_RATE 还是 KH_ACTIVE_1_GX_RATE?
- 「前 10%」对正向指标和负向指标的解释方向是反的——正向指标是「前 10% 是最优秀的那批」,负向指标反而是「前 10% 是最差的那批」。
- SQLite 里没有原生 MEDIAN 函数,中位数怎么算?
- 多指标问题是否应该同表优先来避免跨表 JOIN?
传统做法是把这些规则全塞进一个超长 System Prompt——这有几个致命问题:
这个系统的解法很优雅:把隐性知识组织成可检索、可演化、可验证的私有知识库——叫 PrivateKB。它不是死板的全文检索,而是设计了一套「混合规则召回」机制:
| Layer 1:Always-On | always_include=true 的元规则无条件注入 | 兜底核心规则(比如「禁止对百分比字段直接 SUM」) |
| Layer 2:硬路由 | 按 Agent1 判定的 sql_pattern 匹配 pattern_to_rules 倒排索引 | 精准注入与当前 SQL 模式强相关的规则 |
| Layer 3:Hybrid RAG | FAISS 语义向量 + BM25 关键词 → RRF 融合排序 → 余弦阈值过滤 | 召回可能相关的扩展规则 |
| Layer 4:LLM 压缩 | 当召回规则段超 2500 字符时,用轻量模型精炼(默认关闭) | 极端情况下控制 Token |
以下是其核心实现逻辑的简化版伪代码:
def recall_sql_rules(struct_json: dict, query: str) –> list[Rule]:
"""混合规则召回:四层漏斗,从精准走向泛化"""
rules = []
# ── Layer 1:Always-On 元规则 ──
for rule in private_kb.rules:
if rule.meta.get("always_include"):
rules.append(rule)
# ── Layer 2:硬路由(sql_pattern → 规则倒排索引)──
pattern = struct_json.get("sql_pattern") # 来自 Agent1
if pattern and pattern in private_kb.pattern_index:
rules.extend(private_kb.pattern_index[pattern])
# ── Layer 3:Hybrid RAG 语义+关键词融合 ──
dense_hits = faiss_index.search(query, top_k=10) # BGE 向量
sparse_hits = bm25_index.search(query, top_k=10) # BM25 关键词
# RRF(Reciprocal Rank Fusion)融合两路排序
merged = reciprocal_rank_fusion(dense_hits, sparse_hits, k=60)
# 余弦阈值过滤(去噪)
for hit in merged:
if cosine_similarity(query_embed, hit.embedding) > THRESHOLD:
rules.append(hit.rule)
# ── Layer 4:LLM 压缩(可选)──
rules = deduplicate_and_sort(rules, key=lambda r: r.priority)
total_chars = sum(len(r.content) for r in rules)
if total_chars > 2500:
rules = llm_compress(rules, query) # 轻量模型精炼
return rules
这四层不是简单的「全跑一遍取并集」,而是层级递进的漏斗设计:Always-On 保证底线规则不失配,硬路由按问题类型精准命中,Hybrid RAG 在语义和关键词两个维度补充召回,LLM 压缩作为最后一层兜底 —— 前两层已经命中时,后两层可能只补充零到一条规则。而且整个 PrivateKB 的召回在阶段 0 与 Agent1 并行执行,几乎不增加用户感知延迟。相比全量规则注入的 Fallback 模式,RAG 路径可将 Agent2 的 System Prompt 缩减 20%–60%,在长对话场景下 Token 节省非常可观。
4. 五 Agent 深度协作的精妙之处
4.1 Agent 1:意图识别 + 结构化解析(一石二鸟)
Agent1 不只给意图标签,还输出一份包含 8 个字段的结构化 JSON——sql_pattern、indicator_direction(正/负向)、metrics(指标中文名)、target_branches 等。这份 JSON 在后续被多个 Agent 消费:
- Agent 2:根据 sql_pattern 构建「规则聚焦指引」,告诉模型「这道题是分位排名类型,请严格按照对应规则生成 SQL」;根据 indicator_direction 决定 PERCENT_RANK OVER (ORDER BY … DESC/ASC) 的方向。
- Agent 4:根据 indicator_direction 选择正向/负向的排名解读指南。
- PrivateKB:根据 sql_pattern 做硬路由,精准召回对应规则。
为什么特别提这一点?因为很多同类系统只让 Agent1 输出一个简单标签,后续 Agent 完全靠自己再推理一遍结构——不仅多了一次推理开销,还容易出现 Agent1 说 A、Agent2 按 B 处理的不一致。
4.2 Agent 3:两级审计——让 70% 的查询毫秒级放行
SQL 审查如果每次都走 LLM,延迟暴涨不说,成本也翻倍。Agent3 分两级:
Fast Rule Guard(纯规则引擎,毫秒级):8 条硬规则,覆盖常见的 SQL 生成陷阱——
# 规则 4:相对指标(覆盖率/百分位)禁止在 SQL 中直接乘 100
# 规则 5:分位问题禁止用 ROW_NUMBER(应该用 PERCENT_RANK)
# 规则 6:结果集聚合缺失检测——同一营业部名重复率 > 30% 就是忘了 GROUP BY
# 规则 7:SQL 文本静态分析——无 CTE 无 GROUP BY 无聚合函数 → 大概率遗漏
# 规则 8:PERCENT_RANK 必须配套「分位区间」列(前10%/前25%/前50%等)
其中规则 6 的设计最值得学——它不看 SQL 文本(SQL 是图灵完备的,永远无法穷举所有遗漏 GROUP BY 的模式),而是直接检查 SQL 执行结果集的营业部名重复率。看结果比分析意图可靠得多。
LLM 深审(仅复杂题触发):只在 SQL 含 PERCENT_RANK() 或问题含「中位数」时才走 LLM 审计。审计 Prompt 里有一条重要指令:「你的任务不是追求完美答案,而是只拦截会导致最终回答明显错误的高风险 SQL。如果只是措辞、字段命名的轻微问题,请输出 SOFT_WARN 而不是 HARD_FAIL。」这是刻意降低 Agent3 的攻击性——太严苛会把大量正确答案打回重写,延迟雪崩。
4.3 Agent 4:不喂 SQL 给模型
这是整个链路里看似简单但极有智慧的设计——Agent4 的 Prompt 里没有 SQL,只有数据表格的 Markdown。喂 SQL 会诱导模型写出「根据 SQL 中的 PERCENT_RANK,排名分位为 0.14…」这种技术话术,而最终用户需要的回答是「您的营业部处于全公司前 25% 梯队,优于约 86% 的营业部」。
Agent4 还根据数据是否已有「分位区间」列走两条路:
- 有分位区间:直接告诉模型「分位区间已经算好了,你直接读『前25%』这个词就行,不要再自己换算」
- 无分位区间:告诉模型排名分位的数学含义,让它自己翻译
4.4 Agent 5:忠实度审计——卡住最后一关
Agent5 不审计 SQL,那是 Agent3 的职责。它只审计 Agent4 的最终回答:
Agent5 不通过 → 回到 Agent4 重写回答(不是回到 Agent2 重写 SQL)。这个反馈回路很克制——只在 Agent4 层面修,避免引发 Agent2→3→4 的全链路重跑。
5. 混合规则召回与自演化闭环
这是整个系统最值得凝练的价值点:可演化性。
传统方案每次发现新问题都得靠人工改 Prompt,但这个系统的 PrivateKB 把规则做成了可以自行产生、存储、召回的知识单元——每条规则就是一个 .md 文件,通过 front-matter 标记 category=sql_rule 区分。
当线上出现一次答错时,只要把新规则写进 PrivateKB,下次同类问题就能自动召回。而且因为规则存在本地、索引在启动时重建,不需要重新训练模型。这本质上是一个「检测失败 → 定位根因 → 规则化沉淀 → 自动召回」的自修复闭环。
6. 总结
这个系统的核心哲学可以用一句话概括:让推理回归模型,让规则回归检索。
模型擅长理解自然语言和生成 SQL 结构,但不擅长记住几十条业务口径——那是检索系统的事。通过「知识库化管理业务规则 + 多层混合召回 + 多 Agent 流水线并行 + 强兜底机制」,这个系统在证券数据问数场景下同时做到了高准确率、可维护和可迁移。
更关键的是,这套设计范式可以低成本迁移到任何「有复杂业务口径、需要 Text-to-SQL」的垂直领域——只要替换 PrivateKB 中的规则和数据 Schema,其他引擎逻辑几乎不用改。这是一套领域 Text-to-SQL 系统的可复用骨架。







