Skip to content
Cooing's Blog
Go back

从一句话到一张可信图表:NL2BI Agent 的工程架构与生产实践

TL;DR

NL2BI1 的核心目标并非“替代用户手写 SQL2”,旨在“让用户通过自然语言安全、准确、可追溯地检索业务数据”。进入生产环境后,难点不再是 SQL 语法,而是业务语义、权限边界、工具契约、数据新鲜度、跨域关联、上下文续接、结果证据和评测闭环。一句“帮我看看各矿山目前分别有多少条报价,用柱状图展示”会依次经过权限门禁、语义策略、只读工具、受治理查询、证据绑定、结构化图表校验和真实链路评测。模型负责理解语言、选择能力和组织表达;代码负责租户、权限、业务口径、数据新鲜度、参数合法性与事实来源。本文以这条请求链为主线,复盘一套 NL2BI Agent3 从入口、语义路由、Tool Calling4、MCP5、受控 Query Layer6,到 Evidence Binding7、Context Engineering8 与 Eval9 的工程设计,从原型走向生产时真正需要解决的问题

业务链路可以概括为:

flowchart LR
    U[用户自然语言] --> A[身份与租户边界]
    A --> C[上下文构建]
    C --> P[语义策略]
    P --> L[LLM Tool Calling]
    L --> M[MCP Tool Registry]
    M --> Q[受治理 Query Layer]
    Q --> E[结果与证据绑定]
    E --> R[文本或可信图表]
    R --> V[审计与真实 Eval]

一个“会聊天的数据页面”是它的C端形态,一条将非确定性模型嵌入确定性业务系统的工程链路是承载其能力的核心

一、真实的业务提问远不止拼接几句 SQL

某天,采购人员往系统里语音输入了一句话:

帮我比较几个收货地的当前平均运费,用柱状图展示。

从他的视角看,这只是一条再普通不过的聊天框查询,系统似乎只要理解“比较”“平均运费”“按收货地分组”和“柱状图”几个关键词,查出数据,再画一张图就结束了

但真正进入生产环境后,对于业务人员而言,这句话背后至少隐藏着一连串必须被认真回答的问题:当前登录用户有没有资格使用 AI 查询能力?他只能看到哪个租户的数据?“运费”指当前有效运费还是历史版本?“平均”应该对哪一个字段做什么聚合?多个地点是否需要消歧?发货地与收货地的方向能不能被模型弄反?查询结果是否完整、是否被限制了行数?图表中的每一个数值,究竟来自本轮哪一次真实查询?当数据过期、结果被截断、工具参数错误或模型输出了一段看似正确的 JSON10 时,系统应该怎样失败?数据如何在各个服务器客户端间流转?如何实现流式传输与打字机特效的前端效果?

如果这些问题的任意一环缺失确定答案,那么系统即使生成了一张看起来很专业的图,也可能只是把错误的数据包装得更漂亮

这也是我在实现 NL2BI Agent 时最重要的一次认知转变

NL2BI 的核心并非让大模型代替用户写 SQL,而是把自然语言转换成一条受权限、业务语义、数据范围与证据约束的数据查询流水线
模型负责处理语言的不确定性,确定性系统负责守住业务的确定性

1.1 Text2SQL11 只解决了表达转换

Text2SQL 的核心问题是:

自然语言问题 -> 数据库 Schema 与上下文 -> SQL -> 查询结果

它关心生成的 SQL 是否符合语法,表和字段是否选择正确,连接、过滤、聚合是否能得到目标结果。

这项能力当然重要,但它并不足以构成企业级 NL2BI。

NL2BI 面对的是另一组问题:

  • 用户口中的“报价”是产品出厂价、运费价格,还是到岸成本?
  • “当前”是数据库里更新时间最新的一行,还是已经审批生效并进入当前快照的数据?
  • “平均运费”按路线、目的地、吨位档还是供应商聚合?
  • “南京”是收货地、发货地,还是一个需要消歧的主数据候选?
  • 用户是否只能查询当前租户,还是拥有跨租户治理权限?
  • 返回的二十行代表全量结果,还是一个受限页面?
  • 图表是模型根据查询结果生成的,还是根据上下文中另一组数字“补”出来的?

SQL 可以表达一项计算,但不能自行决定这些业务约束

1.2 企业数据绝非简单的裸表组合

平台的业务事实远比“报价表 + 运费表”复杂。系统内同时交织着:

  • 矿山、规格、物资分类、发货地、收货地和吨位档等主数据;
  • 矿山产品、报价版本、航线与运费版本等业务数据;
  • 审批、生效时间、供应状态和租户可见性等治理状态;
  • 面向当前查询的物化视图和受控聚合接口;
  • 矿山与发货地之间的业务映射关系。

因此,“查询某矿山的价格”和“计算某产品运到某地的到岸成本”是两类完全不同的任务。

前者通常属于单域明细:找到目标矿山和规格,再读取当前有效报价

后者则是一个跨域工作流:

矿山与规格 -> 当前有效报价 -> 矿山—发货地映射 -> 同方向、同目的地、同吨位档运费 -> 报价 + 运费 = 到岸成本

数学上只是加法,业务上却要求每个数字都来自可以连接的同一条证据链。缺少映射时,系统不能选择“附近港口”;缺少精确吨位档时,不能拿相邻档位估算;报价与运费方向不一致时,即使两个数字都真实,也不能相加

1.3 语义层无需做成独立产品

自然语言分析的质量兼取于语义模型、数据取值与 Agent 配置,切不可孤立押宝模型自身能力;指标、维度、字段描述、同义词和可查询范围需要先被业务系统定义,模型才能稳定使用它们12

在本平台中,这个“语义层”没有被压缩成一份巨大的 Schema13或一段万能 Prompt14,分散在多个确定性组件中:

语义问题主要承载位置
什么是矿山、规格、发货地和吨位档主数据与 Reference Tool15
哪些报价和运费属于当前有效事实Go Query Layer 与 Current View16
哪些维度、指标和聚合组合允许执行受治理分析 Tool 的白名单
哪类问题必须先查映射SemanticPolicy17 前置条件
用户能查哪些租户和数据行身份、租户与权限边界
图表中的数据来自哪次查询Evidence Binding

带来一个重要结论:

NL2BI 的业务语义无法单靠模型“凭空理解”,需由模型与业务系统共同定义与收口

模型负责把自然语言映射到已有语义;业务系统负责定义哪些语义成立、哪些数据可用、哪些组合允许计算18

1.4 Text2SQL 翻译 != NL2BI 解释

SQL 即使语法正确,也可能查询了错误的版本、错误的方向、错误的状态或错误的租户。把所有表结构和关联关系交给模型,只是把业务风险从应用代码转移到了一个更难验证的生成过程里

因此,本项目明确走“自然语言 → 受治理业务能力”路线,放弃让模型直连数据库生成“任意 SQL”。模型看到一组业务 Tool,而非数据库表。Tool 背后仍然复用已有的查询服务、权限上下文和当前态数据模型

这种设计牺牲了一部分完全开放的查询自由,但也换来了更清晰的能力边界、更稳定的业务口径和更容易验证的执行轨迹。对于一个业务范围有限、数据权限严格、需要审计的企业查询助手,我个人认为,这个取舍比追求“什么问题都能临时写 SQL”更重要

二、业务系统先于 Agent:AI 不能成为第二套事实链

这个 NL2BI Agent 所在的项目,本身是一个面向供应链数据协作的多租户平台。系统包含主数据字典、业务数据填报、审批流转、版本管理、规则治理、当前态查询、外部标准接口和审计能力。

在 AI 助手出现之前,平台已经有一套明确的数据生命周期:业务数据经过录入、审批、规则校验和版本生效后,形成可对外读取的当前态。报价和运费也是具有状态与生效语义的版本化事实。

这意味着 AI 查询助手不能为了开发方便,重新从底表拼出一套自己的业务答案。它必须服从既有事实链:写操作统一经过应用服务和审批治理;对外与 AI 查询只读取受控的当前态视图,确保后台页面、外部 API19、AI 助手和评测 Oracle20 看到的是同一种业务事实,某条记录因为审批状态变化而不再有效时,AI 不需要额外维护一套同步逻辑;当前视图不可用或过期时,查询层统一失败关闭,严禁继续使用旧结果生成一份“看起来合理”的回答。

如果你也打算在传统的 CRUD/RBAC 平台中接入 Agent,优先考量的绝非选什么模型,而是确认系统内部是否拥有充当基石的可靠事实层

三、技术选型:让不同语言承担不同确定性

对一个 NL2BI Agent 来说,我更关心的是五件事情是否显式:本轮允许哪些能力、模型实际看到了什么、调用了哪些 Tool、每个 Tool 返回了什么受控事实、最终答案依赖哪一份结果,只要这些东西还能被直接追踪,Runtime21 反而可以保持朴素

Agent 架构更适合优先采用简单、可组合的模式,而不是先堆叠复杂框架;框架能快速封装模型调用、工具定义和链式执行,但也可能隐藏底层 Prompt 和 Response22,使调试变困难,并诱导开发者在问题尚未需要时引入额外复杂度;对定义清楚的任务,固定 Workflow23 通常比完全自主的 Agent 更可预测24

本平台因此采用了相对直接的实现,手搓Agent runtime:消息和 Tool 使用明确的类型;应用自己维护 Tool Call 与 Tool Result 的对话循环;SemanticPolicy 在模型前后约束可用工具和前置条件;MCP 结果以统一结构写回;SSE25 事件由应用自己定义并记录;评测可以看到完整轨迹,当前场景的核心复杂度来自业务治理,并非编排 API 的数量,自建轻量 Runtime 感觉已经足够,不太需要引入额外依赖

层级技术选择在 NL2BI 中的职责
页面与交互TypeScript、Next.js、React会话页面、请求入口、SSE 消费、Markdown 与图表承载
UI 与可视化Tailwind、shadcn/ui、ECharts26中文业务界面、图表渲染、响应式布局与局部降级
Agent RuntimeNext.js 服务端 TypeScript上下文组装、语义策略、Tool Loop、流式响应、审计事件
模型接口OpenAI-compatible27 Chat Completions28Function Calling、流式内容与推理事件、主备 Provider29 适配
工具层独立 TypeScript MCP Tool Server、Zod30Tool Registry、参数校验、调用归一化、证据元数据
业务查询层Go、GoFrame31、模块化单体当前态查询、聚合白名单、租户过滤、错误语义
数据层PostgreSQL、物化 Current View主数据、业务事实、治理配置、当前态读模型
网关与运行Nginx、Docker Compose统一入口、容器边界、请求标识与部署一致性
评测Node.js 脚本、Vitest、Go Test、Playwright真实链路回归、Tool Contract32、Oracle、Judge33 与浏览器验收

四、模型不应该直接碰数据库

不让模型直连数据库,原因是数据库没有天然理解以下边界:

  • 当前登录用户属于哪个租户
  • 哪些业务行对该角色可见
  • 哪些价格版本已经生效
  • 哪张物化视图代表“当前”
  • 当前快照是否过期或刷新失败
  • 哪些字段允许出现在外部响应
  • 哪些聚合组合符合业务口径
  • 哪些数据不得进入模型上下文

把数据库凭证和任意 SQL 能力交给模型,相当于把全部确定性边界重新交给非确定性恶意攻击警告

4.1 第一层边界:在调用模型之前拒绝

Chat API 在读取模型配置、拉取 Tool Registry 或发起任何 LLM34/MCP 调用之前,会先验证:

  • JWT35 是否有效
  • 当前租户是否启用了 AI 查询能力
  • 当前账号是否拥有查询服务读取权限
  • 请求中的租户上下文是否属于允许范围

当权限依赖不可用或无法判定时,系统选择 fail closed36:返回明确的用户态错误;这项设计不仅是安全措施,也是一项成本措施。无权请求不会产生模型 Token37,也不会触发 MCP 和后端查询

4.2 第二层边界:模型只能看到受治理 Tool

MCP 将 Host、Client 和 Server 分成明确的参与方:MCP Server 对 AI 应用提供工具、资源或 Prompt,但协议本身并不决定应用如何使用模型和上下文38

独立 Tool Server 承担了这个隔离层的职责,生产 Registry39 是一组有限的只读业务能力

  • 主数据查询:矿山、规格、发货地、收货地、吨位档
  • 关系查询:矿山与发货地映射
  • 当前明细查询:当前报价、当前运费
  • 受治理分析:当前报价或运费的白名单聚合

模型不会看到“执行任意 SQL”“读取任意表”“自行指定租户”之类的能力,我可不给

Agent Tool 可以理解为非确定性 Agent 与确定性系统之间的契约;工具名称、描述、参数和返回内容都会直接影响 Agent 的成功率40

因此,一个生产 Tool 至少要回答四个问题:

  1. 它解决哪个明确业务任务?
  2. 哪些参数允许出现,哪些组合非法?
  3. 返回什么事实和元数据?
  4. 空结果、歧义、权限拒绝和系统失败如何区分?

4.3 第三层边界:Tool Call 仍是不可信输入

即使模型只看到白名单 Tool,它生成的参数仍然不能直接执行,在本Agent中,Registry 使用严格 Schema 校验:

  • 未声明字段不能悄悄混入
  • 页码、数量和列表长度有边界
  • 数据集、维度、指标与聚合使用枚举
  • 报价字段不能被拿去查询运费数据集
  • 精确过滤字段必须属于当前数据集
  • 互斥或成对参数需要同时满足
  • tenant_id 不能由模型传入,只能继承认证上下文

此外,运行时会把原始参数规范化为稳定形式,生成 Registry Version 和 Schema Hash。这样同一次 Tool Call 在审计、缓存和评测中都有一致身份,模型输出的 JSON 只是待验证输入41

4.4 第四层边界:Current View 是唯一当前态来源

Go Query Layer 不允许 AI 绕过当前态读模型,直接从版本底表拼接“最新数据”。

Current View 的职责是把审批、生效窗口、供应状态和业务版本等复杂规则,收口成可查询的当前事实。它还有独立的新鲜度门禁:当视图未刷新、刷新失败、过期或校验异常时,查询链硬拒绝

更进一步,为避免一个经典竞态:刚验证完“数据是新的”,刷新任务立刻开始更新,而查询又在旧/新快照切换中读取,读链会在新鲜度校验和数据查询期间持有共享数据库锁,刷新链使用互斥机制,确保“验证”和“读取”面对的是同一套当前态条件

这个设计看起来和 AI 没什么关系,—反而要触发八股了,但却决定了 AI 是否可信,Agent 的 Grounding42 并不是“调用过 Tool”就结束了,Grounding 还意味着:Tool 后面的数据本身有清晰的状态语义

当系统不能证明数据是当前的,宁愿不回答,也不把旧数据包装成实时结论,这种失败方式可能不如“永远有答案”讨喜,但它比语言流畅更接近 BI 系统对可信度的要求

4.5 第五层边界:用户输出也需要边界

安全并不止发生在输入和 Tool 层。模型最终生成的内容也需要过滤和本地化:

  • SQL 代码块不能进入普通回答;
  • 内部 Tool 名映射为中文业务动作;
  • 英文字段和内部状态不直接展示;
  • 完整性、总量和刷新时间只在用户明确询问时披露;
  • ChartSpec43 中禁止出现租户、JWT、密钥、SQL 等敏感键。

“最小披露”原则:系统内部可以保留完整审计,用户界面只展示完成任务所需的信息。

对于 MCP Tool,最小权限、授权边界、凭证保护,以及避免把敏感内容记录到不必要的上下文中,同样属于安全边界的一部分44

本平台中使用 SSE 把一次业务回合拆成多种事件:

事件用户侧作用
Tool 状态展示“正在查询报价”“正在分析当前数据”等中文活动
文本增量逐步显示最终回答
最终消息以服务端定稿替换流式草稿
Audit记录 Token 和上下文信息,不直接暴露给普通用户
Trajectory保存本地轨迹、证据和压缩事件,不作为正文展示
Done / Error明确结束状态并恢复输入

这里有一个AI很容易忽略的设计问题:用户需要看到业务动作,但不需要看到内部 Tool 名、推理文本、JWT、Trace ID45 或原始 JSON 求你了,GPT,别把英文字段和JSON全给用户看,我改的好辛苦

可观测性不等于把内部实现全部展示给用户,前端呈现的是业务投影,完整轨迹则进入本地证据和评测系统

sequenceDiagram
    participant U as 用户
    participant W as Next.js Chat API
    participant B as 权限边界
    participant A as Agent Runtime
    participant M as MCP Server
    participant G as Go Query Layer
    participant D as Current View

    U->>W: 自然语言问题
    W->>B: 校验登录、租户与 AI 权限
    B-->>W: 允许 / 拒绝
    W->>M: 获取 Tool Registry 快照
    M-->>W: Tools + Version + Schema Hash
    W->>A: 消息、上下文、Tools
    A->>A: SemanticPolicy 决策
    A->>M: Tool Call
    M->>M: 鉴权、Schema 与参数校验
    M->>G: 调用受治理查询接口
    G->>D: 读取租户当前态
    D-->>G: 受控事实
    G-->>M: 数据 + 完整性元信息
    M-->>A: Tool Result + 证据
    A-->>W: SSE 消息与轨迹
    W-->>U: 文本或可信图表

五、如何理解用户真正想查什么

5.1 意图识别不等于万能分类器

不少的 Agent 教程会在最前面放一个 Intent Classifier:把问题路由成报价、运费、分析或闲聊,然后进入不同分支。

真实业务中,意图通常具有多层结构:

用户要查什么领域? -> 要明细、单值、比较、聚合还是图表? -> 是否包含明确实体? -> 实体是否唯一? -> 是否跨越多个业务域? -> 是否需要前置映射或当前快照证据? -> 系统是否支持这个时间范围和口径?

举例:“各矿山目前分别有多少条报价”同时包含:

  • 报价数据集;
  • 当前快照;
  • 按矿山分组;
  • 计数指标;
  • 聚合分析;
  • 柱状图输出;
  • 最大返回范围。

例如,如果只分类为“报价查询”,模型很可能调用明细 Tool,读取一页结果后自行统计,在小样本,本地小数据量测试里也许看起来正确,但生产中无法证明覆盖了全部当前报价

5.2 给高价值语义加确定性护栏

本平台使用版本化 SemanticPolicy 作为同步和流式 Runtime 共用的语义门,它当前重点识别几类会改变工具路径的高价值意图:

  • 普通查询;
  • 历史趋势查询;
  • 到岸成本等需要映射前置条件的跨域问题;
  • 聚合、比较、排名和图表等受治理分析。

Policy 不只输出一个标签,还维护:

  • 当前状态;
  • 允许与禁止的 Tool
  • 必须先完成的前置条件;
  • 最终必须出现的证据;
  • 前置条件是否已经满足。

例如,命中“到岸成本”后,矿山—发货地映射会成为前置条件;在映射完成前,分析 Tool 会被阻止,命中明确聚合或图表意图后,系统要求受治理分析 Tool 提供终止证据,不能把明细页交给模型自行聚合

随着 Tool Call 完成,Policy 会推进自己的状态。例如到岸成本在矿山—发货地映射尚未完成时,不允许直接进入最终分析;要求聚合结果的任务,在受治理分析工具没有成功完成之前,不能把某次明细查询当作最终证明

这些规则数量有限、业务影响大、错误代价明确,适合由代码稳定执行,模型可以帮助理解更开放的表达,但关键路径不应该依赖或相信他它每次都能“自觉”选择正确工具

注意:Tool列表修改,可能导致缓存前缀无法命中问题,在强基模场景下,该路由可能也会随时间而略显笨重,需要设计对比实验权衡,考虑如何简化/废弃了46

5.3 实体解析:名称相同不代表业务对象相同

用户输入的是业务语言而不是UUID或英文数据库字段,例如

  • 某个矿山的简称
  • 一个规格的口语写法
  • 某个港口或城市
  • “大船”“小吨位”等业务习惯词、

Reference Tool 负责把自然语言候选映射到受治理主数据。
一个稳健的实体解析流程通常是:47

  1. 从用户问题中抽取最强业务关键词;
  2. 查询对应主数据;
  3. 没有候选时停止事实查询;
  4. 只有一个明确候选时继续;
  5. 多个候选且用户未唯一指定时,列出业务名称并追问;
  6. 将规范实体写入后续 Tool 参数,而不是继续使用模糊文本。

这可以避免两个常见问题:

  • 宽泛搜索伪造完整性:用多个猜测关键词拼出一份“看似完整”的列表;
  • 模型自行消歧:多个同名地点时,根据语言习惯随便选一个。

5.4 业务语义应该下沉并分层

System Prompt 适合描述高层角色、业务边界、跨工具编排原则以及输出风格,例如“跨域计算必须有完整证据”“不要把旧快照当趋势”“只回答用户实际询问的字段”。

Tool Schema 适合描述这个工具做什么、参数是什么、哪些字段允许组合、输入的结构边界是什么。模型应该通过工具本身理解怎样正确调用它。

SemanticPolicy、Validator48、权限系统和 Query Layer 则负责所有能够被确定性强制的规则,历史数据当前不支持,就直接禁止历史路径;某类分析必须经过指定能力,就由执行层阻断绕过;租户 ID 不能来自模型,就在 Registry 中拒绝。

”应该放在语义模型里的硬规则”和“只针对特定 Agent/User 的定性上下文”不一样,不要把本应成为系统事实的规则重复塞进 Prompt,然后寄希望于模型每次都记住

当前的 System Prompt 仍然承担了较多业务说明,从长期演进看,规则应按性质分层:

规则类型更适合的位置
Tool 用途、参数含义、返回字段Tool Description / Schema
数据集允许的维度、指标和聚合Query Layer / Validator
跨 Tool 的编排原则简短 System Policy
租户、权限、状态和新鲜度确定性代码
典型语言表达与意图SemanticPolicy / 少量示例
呈现风格输出契约与前端 Renderer49

长期稳定的业务定义更适合进入语义模型,特定 Agent/User 的交互规则则更适合留在 Agent 指令中;当一个规则可以通过 Schema、枚举或服务端校验强制执行时,不应继续要求模型阅读一段更长的文字然后自觉遵守,Prompt 不能变成“无法写成代码的所有规则的垃圾桶”

六、不同问题走不同查询路径

上述章节中,我们解释完了意图和实体后,该进入真正让Agent跑起来的编排环节了,这里有两个极端:一端是固定 Pipeline50,每个问题都按照相同步骤执行;另一端是自由 Agent loop,让模型一直循环,直到它觉得任务完成;本文将 Workflow 定义为由代码按预设路径编排模型和工具,将 Agent 定义为模型动态决定过程和工具使用

本平台当前位于中间:权限、历史能力拒绝、分析白名单和映射前置条件偏 Workflow;自然语言解析、部分 Tool 选择和最终生成偏 Agent;Tool 参数和业务事实由确定性系统校验;图表格式由模型生成,但必须通过协议和证据绑定,保留 Agent 的语言理解与工具选择能力,不把可确定的业务部分收敛成Pipeline,充分发挥强大的基模实力

6.1 单域明细:先找到事实,再组织回答

用户明确问:查询某矿山某规格的当前报价

→ 确认矿山实体
→ 查询当前报价
→ 返回用户关心的字段,组织简洁文本或表格

这类简单问题不需要模型自由规划十几个步骤,明细 Tool 返回当前有界记录,模型负责选择相关字段并转为中文表达即可

需要注意的是,部分有界明细页不能被描述成“全量”,如果用户要求“一条不漏”,系统必须根据完整性元数据决定是否能作出全局断言,必要时要求缩小条件,禁止不断翻页直到上下文被撑爆

6.2 受治理聚合:模型不在上下文里做 BI

用户问:各矿山目前分别有多少条报价,用柱状图展示,这类问题必须进入受治理分析路径:

识别为当前聚合 + 图表 -> 选择报价数据集 -> [dimension = 矿山, metric = 报价数量, aggregation = 计数] -> Go Query Layer 执行白名单聚合 -> 返回分组行与完整性证据 -> 模型生成绑定证据的 ChartSpec

模型无法知道这一页是否覆盖全部记录,也无法稳定处理分页、过滤和去重,聚合属于数据库和 Query Layer 擅长的确定性计算,模型只需要选择合法参数并解释结果 我已经三秒钟没有看到模型偷懒和幻觉笑话了

analyze_current_data 的设计正是如此:本阶段没有做sandbox,只做了两个当前数据集上的受控聚合接口。数据集、维度、指标、聚合、过滤、排序和限制都有严格白名单。51

6.3 跨域查询:到岸成本是一条有前置条件的 Workflow

用户问:某矿山某规格运到南京的当前到岸成本是多少?这条路径需要:

→ 矿山—发货地映射
→ 产品当前报价
→ 发货地到目的地的当前运费
→ 条件全部一致后计算

模型除了需要“规划”外,系统还需要知道:

  • 映射是报价与运费之间的连接条件;
  • 方向不能反转;
  • 目的地和吨位档必须精确匹配;
  • 缺少任一事实时应该报告缺口,而不是估算;
  • 到岸成本的两个数字必须保留来源。

这类任务理论上来讲更适合受控 Agent + Workflow,模型可以解析用户表达、选择候选、组织最终说明,前置条件、合法连接和停止条件由代码维护,核心是维护可验证的关联关系在 FSM52 中,Agent 的可靠性,很大程度上取决于状态机能否表达“什么时候允许进入下一步”

6.4 多轮追问:重新利用语义,不盲目复用事实

用户可能先问:比较几个目的地的平均运费,下一轮继续说:只保留吨位最大的两档。

多轮能力不简单等于“把全部历史消息重新塞给模型”,与一般的chatbot53不同,系统需要区分:

  • 哪些是仍然有效的用户约束;
  • 哪些是历史 Tool Result;
  • 哪些结果可能已经过期;
  • 哪些证据可以引用,但需要重新查询当前事实;
  • 哪些工具链必须成对重放,不能只保留一半。

当前本平台的策略是:保留对话语义和证据引用,但跨轮业务追问默认重新执行受治理查询,不把恢复的旧结果自动当作仍然有效的当前数据(业务视图/数据源可能已经更新)

七、怎样保证回答有证据

到这里,Agent 已经能够查询真实数据,但“查到了真实数据”仍然不等于“最终回答可信”,模型可能正确调用 Tool,却在回答中写错一个数字;把上一轮结果当成本轮结果;把两个不同查询的结果混在同一张图;生成合法 JSON,却伪造一个不存在的类目;用真实结果推导一个当前证据不支持的“最高、最低、趋势”结论

下一步应当做的,就是把执行事实展示结果绑定起来

7.1 一张合法 JSON 图表 != 一张可信图表

Structured Outputs54 可以通过严格 JSON Schema 保证 Function Calling 参数满足定义的结构

它解决的是:输出长什么样,但没有自动解决:输出里的值是不是来自正确的数据。

这段结构完全可能合法:

chart_type: bar
dimension: mine
metric: average_price
data: [...]

data 里的数字仍然可能是模型自己写的,因此,本平台没有把 ChartSpec 只设计成“图表配置”,而是把它设计成“数据 + 查询证据”的组合。

7.2 从 Tool Call 到图表的 Provenance55

每次成功 Tool 调用会形成一组身份信息:

字段作用
Call ID标识本轮具体 Tool Call
Normalized Arguments记录真正执行的规范参数
Query Fingerprint对规范查询生成稳定指纹
Result Hash标识受控结果内容
Registry Version / Schema Hash说明使用了哪版工具契约
Snapshot Version说明事实来自哪个当前快照
Complete / Truncated说明结果完整性
Row / Group Count说明底层事实和返回分组规模

模型在生成 ChartSpec 时把关键证据复制到图表的 evidence 中,前端收到结果后,除解析 JSON外,还要用本轮已完成 Tool Call 的绑定信息验证:

ChartSpec.call_id
    == 本轮已完成 Tool Call.call_id
ChartSpec.query_fingerprint
    == 本轮 Tool Call.query_fingerprint
ChartSpec.result_hash(若存在)
    == 本轮 Tool Result.result_hash

绑定失败时,这个图表块不能被当作可信图表渲染

同时,ChartSpec 只表达业务层最小信息:图表类型、标题、维度、指标、单位与展示精度、数据行、查询证据等,模型无法输出任意 ECharts option 或夹带 JavaScript、SQL、租户、JWT、密钥等字段,LLM只决定“展示什么”,不决定复杂前端实现。由Renderer 统一处理主题、移动端、密集标签和 PNG 导出,协议方便被严格校验和长期版本化

在一条回答可能包含多张图时,每个 ChartSpec 独立绑定自己的 Tool Call

解析器会逐块完成:JSON 与字段校验、敏感字段扫描、图表类型和点数边界、完整性证据一致性、Call ID、Fingerprint 和 Result Hash 绑定、重复图表检测、单条消息图表数量限制。某一块失败时,只降级该块,其他合法图表和正文继续展示

7.3 证据绑定也得Eval

只在前端检查还不够,数据集中也设定了真实 Eval ,重新解析 ChartSpec,并把它绑定到实际完成的 Tool Call,然后比较图表数据行是否来自受控结果、维度和指标是否符合任务预期、Evidence 中的完整性元数据是否与 Tool Result 一致、图表是否选择了正确的数据分析能力;是否出现跨租户参数等

综上所述,图表至少包含五层正确性:结构、Tool 选择、查询参数、数据行、证据绑定。可信与可用同样重要

八、长对话如何保持上下文

受控治理介绍完了,但当前有一个致命问题还未解决:Agent 的上下文会自然膨胀

每一轮用户消息、模型 Tool Call、Tool Result、最终回答、必要的修复和审计信息,都可能成为下一轮的候选上下文。如果简单地把所有历史完整重放,Token 会随着轮数增长,延迟和成本也一起增长;更麻烦的是,工具原始结果可能非常大,而很多旧数据已经不应该继续作为当前事实使用

Context Engineering 管理的是整个推理时上下文,而不仅仅是写 System Prompt。System 指令、Tools、MCP 数据、外部结果和消息历史都在竞争有限的注意力预算,这比“把 Prompt 写好”更适合描述多轮 Agent 的真实问题

在本Agent中,上下文管理也是按这个思路设计的:稳定前缀、可见消息、浏览器轨迹和旧工具证据不是完全等价的内容,需要分层处理

第一轮,用户问:“九江到南京现在的运费是多少?”

系统查询 Current View,得到一组当前运费数据,并给出回答。

紧接着用户继续问:“那换成另一个吨位档呢?”

对于chatbot,这似乎只是一个上下文续写问题:把上一轮聊天记录重新发给模型,让它知道“另一个吨位档”指的是什么即可,但对于 NL2BI,这里混在一起的其实是两件完全不同的事情:

第一件事是对话记忆:模型需要记得上一轮讨论的是九江、南京和哪个吨位档。

第二件事是业务事实:上一轮查到的运费,到这一轮是否仍然有资格被称为“当前运费”?

前者应该继承,后者却不能无条件继承

历史上下文负责帮助模型理解“我们刚才在聊什么”,受治理查询负责重新确认“现在真实的数据是什么”。

8.1 会话历史不仅是一个 messages[]

接入 Tool Calling 后,一轮真实 Agent 过程远比最终聊天界面复杂:User/Reasoning/Tool Call/Tool Result/Thinking/Assistant Final等等,其中用户真正需要长期看到的,只是问题、答案以及简化后的“正在查询报价”“分析当前数据”等活动状态;而系统为了恢复 Agent 过程,还需要知道 Tool 名称、规范参数、结果、查询指纹、结果 Hash、Registry 版本、Snapshot 版本、Reasoning、错误和压缩事件。

我选择在浏览器端维护了两层状态,且相关数据持久化全部在浏览器本地进行:

第一层是 localStorage56 中的可见会话。它保存 Session 标题、用户消息、Assistant 最终回答,以及经过简化的中文 Tool 活动。持久化时,Tool 的参数、callIdqueryFingerprintresultHash 等内部字段都会被移除,因此它更像 UI History,而不是完整 Agent Memory57

第二层是 IndexedDB58 中的原始执行轨迹。这里记录 request、user、assistant、reasoning、tool call、tool result、retry、error、checkpoint、compaction、telemetry59 等事件,同时还可以单独保存大型 Tool Result Artifact60

所以更准确的结构是:Visible Conversation 负责“用户看到什么”;Raw Trajectory 负责“Agent 实际做过什么”;Artifact 负责“太大的结果放在哪里”,这三者的生命周期和用途并不相同

8.2 存下来,下一轮模型会看到吗?

浏览器保存完整轨迹,并不意味着模型每次都会重新加载全部历史,当前没有一个供模型主动调用的 search_historyread_artifact 工具,而采用了 Push Context61

浏览器选取历史
→ 主动发送给服务端
→ 服务端重建 Working Context[^note_working_context]
→ 模型被动看到

用户发送新消息时,客户端同时提交两类数据,一类是可见 messages,另一类是从 IndexedDB 构造出的 context Envelope62,因此“浏览器存了什么”和“模型这一轮看到什么”,实际上是两个完全不同的问题

8.3 从 Archive Memory63 到 Working Memory

可见聊天消息在服务端只保留最后 12 条 user/assistant Message,Raw Trajectory 在浏览器侧也不会无限发送。构造 Context Envelope 时,会取最近 256 个 Raw Event;如果最新 Checkpoint64 已经掉出这个窗口,则额外把它补回来,同时,只自动召回最近两个被引用的大型 Artifact

服务端收到 Envelope 后还有第二道限制:最多接受 576 个 Event,总载荷不超过 2 MB,并尽量按照 turnId 保留完整 Turn,不从 Tool Call 和 Tool Result 中间截断,到这里,IndexedDB 里的全部历史已经缩成了一个“近期候选集”,真正给模型构造 Prompt 时,还会进一步变成一个更小的 Working Set65

buildLayeredContext() 会将浏览器轨迹重新还原为 Atomic Turn66

User
Assistant
  reasoning
  tool_calls
Tool Result
Assistant Final

历史 Tool Call 只有在对应 Tool Result 也存在时才会重新构建,避免产生孤立的 Tool Chain;如果一个需要 Replay67 的 Tool Chain 缺少必要 reasoning,它还会被认为不可安全重放并从上下文中移除。

随后系统默认只保留最近 8 个 Atomic Turn。这个限制与 Token 是否已经接近上下文窗口无关,就是一个平平无奇的 Sliding Working Set,这个设计过于死板了,作为对比基线和反映对话chatbot“进化”的一部分,这个方法将在后续被废弃

也因此,浏览器里仍然能被看到存在的历史,不一定属于当前模型的 Working Memory。

IndexedDB
    = Archive Memory
最近 8 Turn
    = Working Memory

8.4 上一轮 Tool Result 到底还能不能看到?

假设上一轮 Tool 查询出了:A 矿 20mm:54 元;A 矿 25mm:57 元

如果这个 Turn 仍在 Working Context 中,下一轮重建时不仅会恢复上一轮 User 和 Assistant 最终回答,还会恢复 Assistant Tool Call,以及对应的 Tool Result Content

所以第二轮模型并不是只记得:“上一轮回答过 A 矿”,它很可能还能直接看到上一轮查询出的明细,但这份历史 Tool Result 与当前轮新查询得到的 Tool Result 并不完全相同,当前 Tool 执行结束后,Runtime 会把结果包装成:

content
complete / truncated
row counts
query
last_refresh_at
call_id
query_fingerprint
result_hash
...

再交给模型,而历史重放主要恢复的是旧 result.content;更完整的 Provenance 则保存在 Raw Trajectory 和 Evidence Reference 中,也就是说:当前 Tool Result 是“当前事实 + 证据包”,历史 Tool Result 更接近“对话理解材料”,这种差异为跨轮 Fresh Query 留出了空间。

8.5 大结果不应当无限占据 Context

Tool Result 很容易成为上下文最大的部分,一条聚合结果可能只有十几行,而一次明细查询可能返回几千几万条结构化记录,本平台因此采用了 Payload Externalization68

当 Tool Result 超过一定大小时,不再把完整内容内嵌进 Raw Event,而是把内容单独存入 IndexedDB 的 artifacts Store;Trajectory 中只保留这样的引用,下一轮构造 Context 时,系统只自动召回最近两个相关 Artifact

artifact_id
tool_call_id
hash

因此大型历史结果存在三种状态:

  • Hot:近期 Tool Result,直接进入 Working Context
  • Warm:Checkpoint 中只剩事实和 Evidence Reference
  • Cold:完整 Artifact 仍在 IndexedDB,但这一轮未加载

当前模型并没有主动从 Cold Storage 读取 Artifact 的能力。只能审计历史轨迹,这意味着 Evidence Reference 目前首先是一个可验证句柄,而不是一个已经实现的 Agent Retrieval API,后期这也是个可优化的点

8.6 超过上下文预算后,Fold 成 Checkpoint

在本Agent中,默认使用 128K Context Window69,并在估算输入达到窗口约 75% 时主动进行 Compaction70,同时为模型后续生成预留额外空间。71

触发压缩后会保留最近若干 Turn,把更老的部分折叠成一个结构化 Checkpoint。

Checkpoint 预留了以下结构,然后它以 System Message 的形式重新进入模型:

facts
decisions
constraints
unresolved
evidenceRefs
snapshotVersions
registryVersions
schemaHashes
summary

一段很长的历史:Turn 1、Turn 2…Turn 20,最终可能被压缩成:System Prompt Checkpoint(Turn 1~12)、Turn 13 … Turn 20

这其实更接近数据库中的 Snapshot + Recent WAL72 而并非“聊天摘要”,当前自动 Folding 真正主动从旧 Turn 生成的,主要还是两类信息:一类是历史 Assistant 最终回答截取出的 Facts;另一类是历史 Tool Result 的 Evidence Reference;Snapshot、Registry 和 Schema 版本再从 Evidence 中聚合出来;decisionsconstraintsunresolved 目前更多是承接已有 Checkpoint,并非每次都从自然语言历史重新结构化抽取。

在此基础上,系统还会调用一个隔离的 Checkpoint Summarizer:不提供任何 Tool,并关闭 Thinking73,只允许概括已有事实、决策、约束、未决项和证据引用。

当前的 Compact 可以理解为确定性 Folding + 受限 LLM Summary 的组合

8.7 多轮 Context 治理:无需追求“全知全能”

盲目堆叠“更长的上下文”毫无意义,工程核心在于切中关键:

当前这一轮决策,究竟需要什么?

为了理解模糊意图和消除歧义,最大化用户流畅体验,模型需要最近实体和用户约束:

“那第二个呢?”

为了重新回答:

“现在运费是多少?”

它需要重新执行 Current View Tool

为了恢复旧图表,它需要 Evidence Binding,为了审计历史过程,它需要 Raw Trajectory,为了减少 Prompt,它需要 Checkpoint

这些需求不应该共享同一种 Memory,因此在本Agent中,现在逐渐形成的是五层不同的上下文:

  • Visible Memory 用户看到的聊天
  • Trajectory Memory Agent 实际做过什么
  • Working Memory 模型本轮真正看到什么
  • Evidence Memory 历史事实从哪里来
  • Current Business Truth 本轮重新查询得到的受治理事实

多轮 NL2BI 的根本目标是在保留语义连续性的同时,阻止历史数据冒充当前事实

九、如何评价一个 NL2BI Agent?

Agent 能跑通 Demo 以后,最困难的问题就变成了优化问题:我改了 Prompt、Tool 或 Runtime,它到底变好了还是变坏了?

9.1 最终答案正确 != Agent 正确

Agent 的评测比普通问答难,一个很大的原因是“正确答案”不再足以描述一次运行。

假设用户问某条航线的平均运费,模型最后给出了正确数字。它可能是真的调用了正确的当前数据分析,也可能查了反向航线后碰巧得到相同数字;可能使用了完整数据,也可能只看了一页明细后恰好平均值相同;甚至可能没有调用工具,只是根据上一次对话记住了这个数字

如果评测只把最终回答交给另一个 LLM 判断“看起来对不对”,OK啊,这些路径差异全部消失了,trace不了一点,而且LLM还会往测试集/prompt里给自己灌水

多轮、会调用工具并根据中间结果改变行为的 Agent,需要同时评价过程与结果,不能沿用单轮文本生成的评测思路。对 NL2BI 来说,这几乎是必须的,因为事实正确性往往依赖工具路径本身74

9.2 第一层:Tool Contract 有没有走对路

评测首先检查工具轨迹,测试场景可以声明必须出现的 Tool、禁止出现的 Tool、允许的 Capability Path75,以及调用顺序。例如到岸成本场景要求先取得矿山—发货地映射,再进入依赖该映射的分析;聚合型任务如果规定必须使用受治理分析,就不能只靠某次原始明细查询来交差76

评测开始前还会对这些 Contract 做 Preflight:场景里要求的 Tool 是否仍然存在于当前 Registry,Oracle 要绑定的能力是否和场景允许路径冲突,多个同名 Tool Call 是否会造成无法唯一绑定的歧义,Preflight 的意义主要在于,防止一次“测试配置本身已经失效”的运行,被统计成“模型选错了工具”。

9.3 第二层:Oracle 事实到底对不对

Tool 路径合法以后,下一层才是事实判断。

Oracle 不依赖模型自己解释结果,而是通过规范业务查询或直接绑定本次 Agent 的受控 Tool Result,检查预期行、数值、Meta77 和聚合结果。对于需要证明“模型确实基于自己这次查询作答”的场景,Oracle 会根据 Tool Name、规范参数、call_idquery_fingerprint 绑定到唯一一次真实调用,再读取那次调用的 controlled result。

如果绑定缺失或歧义,评测应该报告“证据绑定失败”,而不是继续猜某个 Tool Result 是模型当时用的那一个,测试系统也必须有自己的事实契约

9.4 第三层:Presentation 与 Evidence 图画对了吗

ChartSpec 是否能解析只是最低要求,系统还会检查它是否绑定到正确的分析调用、结果 Hash 是否一致、维度和指标是否符合题面、数据行是否来自受控结果、Evidence 中的完整性信息是否与 Oracle Meta 对得上,主要防止一个普通 LLM Judge 难发现的问题:一张图视觉上非常合理、语言也很自然,但其中某个类目的数字是模型复制/幻觉错的

9.5 第四层:LLM Judge 确定性检查覆盖不了的部分

所有东西不是都适合写成 assert expected == actual,回答有没有真正解决用户问题、过程说明是否合理、表达是否让普通业务人员看得懂、缺数据时有没有给出合适的下一步,这些更适合让 LLM Judge 评价。

Judge 在当前体系里是补充层,事实、工具路径、权限和结构协议能确定性判断的,优先用确定性检查,Judge 主要负责语义质量和用户体验,如果 Judge Provider 自身不可用,系统可以把它标记成评测基础设施问题

9.6 第五层:Runtime Metrics 答对了,代价是多少

当前评测会同时收集 Tool Call 数、Prompt/Completion Token、缓存 Token、总时延、首个 Tool 时间、首个回答时间,以及 LLM、MCP、Oracle、Judge 各层的时间线,SSE 运行事件会被重建成一组 Span78,区分“模型慢”“工具慢”和“评测慢”。

模型调用也应该像 HTTP、数据库一样拥有稳定的 operation、provider、model、token usage 和 trace 语义,使 Agent 的一次运行全生命周期与指标都可以被展开成时间线追踪,这也是 OpenTelemetry GenAI Semantic Conventions79 所推动的统一语义方向

十、好处说完了,坏处呢?

到这里,一个NL2BI Agent 已经能够把自然语言接到真实数据,限制模型能力,绑定查询证据,并通过真实链路评测,但“能被治理”并不等于“运行得足够高效”,当前实现也暴露出一系列典型 Agent 工程问题:

问题原因解决方法
LLM 成本和端到端延迟过高一个业务回合可能经历首次生成、Tool 续写、nudge 重试、最终合成和修复等多次完整 LLM 请求,上下文与 Tool Schema 被反复发送优先减少 LLM 往返次数,将一次任务的请求次数作为核心预算,再优化 Prompt Cache80、输入 Token 和输出 Token
Tool 路由不稳定并产生额外模型调用Tool 选择主要依赖模型自行试探,失败后再通过 nudge81、tool_choice 调整或重复规划修正,导致调用链放大在首次调用前由 SemanticPolicy 根据业务意图确定 Capability Bundle82、Tool 策略和 required/auto 状态,尽量一次完成正确路由
Agent Loop 容易进入高成本错误路径当前主要依赖统一最大步数限制,缺少针对不同意图的 LLM、Tool、重试和修复预算,错误路径可能持续消耗剩余步骤将 Agent Loop 收敛为有限状态机,按任务类型设置最大 LLM 次数、Tool 次数、重试次数和阶段预算,确认下一步确有必要后再继续调用
Prompt Cache 命中率和上下文复用不稳定Tool 列表、动态指令、nudge、Context 拼接顺序和序列化结果会在不同 operation 间变化,使稳定前缀频繁失效固定 Capability Bundle82 和 Tool 顺序,稳定 System Prompt、Schema 与消息序列化,并显式区分稳定前缀和动态上下文
上下文预算、裁剪和摘要行为缺乏统一模型上下文构造、Token 估算、Provider Window、输出预留、Tool Schema、历史裁剪和 Checkpoint 分散在多个阶段处理,容易出现预算重复计算或执行前后不一致建立不可变的 ResolvedWorkingContext,在每次 LLM operation 前一次性确定消息、Tool、Token Budget、保留历史、Checkpoint 和 Provider Profile,并以该快照作为真实请求依据
多轮会话、刷新与长期上下文存在状态一致性风险浏览器历史、Context Event、Artifact、服务端 Turn 和模型上下文分别维护,Turn 中途失败、页面刷新或局部写入时可能产生残缺状态;长会话中重要 Artifact 也可能被简单裁剪使用 Session Ledger 和 Atomic Turn 管理提交边界,将消息、Context Event、Artifact 与 Turn 状态统一版本化;长期内容采用 Checkpoint + Token-aware Artifact Recall,而不是仅按最近消息数量截断
Tool / MCP 调用链耗时高且存在重复工作多个互不依赖的 MCP 调用可能串行执行,相同查询可能在一次任务中重复触发,重试、排队和下游 Go API 耗时目前也难以区分对无依赖 Tool Call 并发执行,对相同参数请求进行 Turn 内去重,并分别记录 queue、MCP、下游 API、retry 和 cache 时间,同时保持 Tool Result 写回顺序确定
简单任务的 Thinking 和最终生成成本过高默认推理等级、最大输出长度和最终回答策略没有充分按照任务复杂度区分,简单查询也可能进行长推理、长解释或额外 presentation repair根据意图和复杂度动态设置 Thinking 等级,并为查询、分析、解释、图表等任务设置 Completion Budget83;最终合成阶段优先复用已有事实,避免为格式修复重新进行完整推理
Prompt、Tool Schema 与业务规则重复且难维护System Prompt、Tool 描述、SemanticPolicy、Validator 中可能重复表达相同约束,既增加 Token,也容易出现规则版本不一致Tool 能力和参数约束放入 Schema,确定性业务规则下沉 SemanticPolicy 和 Validator,System Prompt 只保留模型必须理解的行为原则;Prompt 精简作为结构治理结果而不是主要性能手段
Token、Cache 和 Provider 指标缺乏统一语义不同 Provider 对 input/output、cached tokens、cache creation、reasoning tokens、TTFT 和错误信息的字段定义不同,当前回合级汇总无法稳定比较模型与版本建立 Provider-specific Usage Normalization84,在 Provider Adapter 边界统一 Token、Cache、Reasoning、Latency、Finish Reason 和 Error,再向上层暴露统一指标
难以定位单次 LLM、Tool 和跨服务性能瓶颈当前主要通过 SSE 和最终 Audit 汇总重建回合时间线,Context Hash、Token Usage 和耗时无法可靠绑定到每一次真实 Provider operation,也难以关联 MCP、Go API 和最终修复阶段以每次真实 LLM operation 为观测单位记录 model、role、context hash、tool bundle、cache、token、TTFT、duration 和 error,并采用 OpenTelemetry GenAI + 自定义业务属性建立 Agent → LLM → Tool → MCP → API 的统一 Trace
优化和质量回归缺乏统一验证闭环性能、事实正确性、Tool 路径、安全、上下文和最终体验由不同指标判断,多项优化同时进行时难以确认收益来源,现有测试也无法完全覆盖真实浏览器持久化链路采用 Eval Driven85:保留真实 Tool/Oracle 的确定性校验和独立 Judge,将质量、Token、Cache、调用次数、TTFT、Tool 耗时纳入统一 baseline;PR 只做稳定快速回归,周期性运行完整真实链路与浏览器级 Context 测试

十一、小结

让我们回到文章开头:

帮我看看各矿山目前分别有多少条报价,用柱状图展示,最多展示二十个矿山。

在一个 Demo 中,这句话可能只是:

模型写 SQL → 数据库执行 → 前端画图

在本Agent中,它实际代表:

验证用户和租户权限 -> 识别“当前 + 聚合 + 图表”意图 -> 选择受治理分析能力 -> 用严格 Schema 生成规范参数 -> 由 Go Query Layer 查询可用 Current View -> 返回分组数据、完整性与快照证据 -> 模型生成最小 ChartSpec -> 前端验证结构、敏感字段和 Evidence Binding -> ECharts 渲染并按设备自适应 -> 真实 Eval 验证 Tool、Oracle、图表和运行成本

这条链路看起来比“让模型写 SQL”复杂得多,但每一层都在解决一个生产环境无法回避的问题

NL2BI 的价值不只是降低查询门槛。它真正改变的是业务用户与数据系统的接口:用户不再需要记住表、字段、接口和筛选器,而系统也不必为了自然语言体验放弃原有权限、事实口径和审计能力。

模型在其中最适合承担的角色,是处理自然语言中的模糊与变化;它不应该成为新的数据库、权限中心或业务事实源。

一张能显示出来的图只是可视化;一张能够说明“查了什么、为什么这样查、数据来自哪里、是否完整”的图,才是 NL2BI Agent 的意义所在

Footnotes

  1. NL2BI(Natural Language to Business Intelligence,自然语言转商业智能):本文用它指“自然语言 → 受治理业务查询/分析 → 可解释结果”的完整 BI 交互链路,意在让自然语言直接接入既有的语义、权限、事实与评测体系,而非单纯生成 SQL

  2. SQL(Structured Query Language,结构化查询语言):用于定义、查询和操作关系型数据库数据的声明式语言,本文只把它视为底层查询表达之一,而不是 NL2BI 的产品边界

  3. Agent(智能体):能够围绕目标进行一定程度的自主决策、调用工具并根据中间结果继续行动的大模型应用,本文中的 Agent 主要负责语言理解、工具选择和结果表达

  4. Tool Calling(工具调用):模型只负责提出“调用哪个能力、参数是什么”,真正执行、鉴权、校验与回传结果仍由应用负责;工程上应把 Tool Call 当作不可信结构化输入。OpenAI 的 Function Calling 文档还提供 tool_choiceauto / required / 指定函数 / allowed_tools)与 parallel_tool_calls 等控制项,但这些属于具体 Provider 能力,OpenAI-compatible 接口不能默认全部支持。原文:“By default the model will determine when and how many tools to use” OpenAI Function Calling

  5. MCP(Model Context Protocol,模型上下文协议):一种 client-server 协议,用统一方式让 AI 应用连接 Tool、Resource、Prompt 等外部上下文能力;官方架构说明其重点是上下文交换协议,而不是规定应用如何使用 LLM,原文摘录:“MCP focuses solely on the protocol for context exchange” MCP Architecture Overview

  6. Query Layer(查询层):本文指位于 Agent/Tool 与数据库之间的确定性业务读取层,负责租户过滤、当前态语义、聚合白名单、错误语义和数据完整性等约束

  7. Evidence Binding(证据绑定):本文指把最终文本或 ChartSpec 与本轮真实 Tool Call、规范参数、查询指纹、结果 Hash、快照版本等执行证据关联起来,避免“结构合法但数据来源不明”

  8. Context Engineering(上下文工程):每次推理都从“可能有用的全部历史”中挑出最小、高信号的上下文集合,而不是只优化 System Prompt 文案;System、Tools、MCP、外部数据、消息历史和 Tool Result 都争夺同一有限注意力预算。Anthropic 建议以“smallest possible set of high-signal tokens”为目标,并把 Compaction、结构化笔记与 Tool Result Clearing 作为长期任务的重要手段 Effective Context Engineering for AI Agents

  9. Eval(Evaluation,评测):Agent Eval 不只是“Prompt → Response → 一个分数”,更完整的抽象是 Task(输入与成功标准)→ Trial(一次随机运行)→ Transcript/Trajectory(完整工具与中间过程)→ Outcome(环境最终状态)→ Grader(一个或多个评分器)→ Evaluation Harness(批量执行与汇总基础设施)。Anthropic 建议组合确定性、模型和人工评分器,并把生产监控、用户反馈与 Transcript Review 作为自动 Eval 的补充 Demystifying Evals for AI Agents

  10. JSON(JavaScript Object Notation):一种轻量级结构化数据交换格式,Agent 工程中常用于 Tool 参数、Tool Result、结构化输出和前后端协议

  11. Text2SQL:把自然语言问题转换为可执行 SQL 的任务范式,主要解决自然语言到数据库查询表达的映射,不自动覆盖权限、业务版本、事实新鲜度、跨域工作流和结果证据等问题。Google Cloud 的生产实践把可迁移的难点归纳为业务上下文、用户意图和生成约束,并使用语义层、实体解析、消歧、检索/上下文学习以及确定性验证来补足模型;这些范式同样适用于“自然语言 → 受治理 Tool”的 NL2BI,只是最终执行目标从 SQL 换成了业务能力。原文:“Understanding schema, data and business concepts” Getting AI to write good SQL: Text-to-SQL techniques explained

  12. Google Cloud 的 Looker Conversational Analytics 实践把 LookML 语义模型、数据值和 Data Agent 配置共同作为自然语言分析的事实基础,并建议把长期字段定义、同义词和计算集中在语义模型中,Agent Instructions 保持简洁、避免重复;原文摘录:“using your Looker semantic model (LookML), data values, and data agent configurations as its source of truth” Best practices for configuring Conversational Analytics in Looker

  13. Schema(模式/结构定义):描述一组数据允许有哪些字段、类型、约束和组合关系;本文同时涉及数据库 Schema、Tool 的输入 Schema 与 ChartSpec 的结构 Schema

  14. Prompt(提示词/提示上下文):发送给模型的指令与输入内容;System Prompt 是其中用于声明角色、边界和高层行为规则的系统级部分

  15. Reference Tool(主数据/参考数据工具):本文自定义的一类只读 Tool,用于把用户的自然语言实体候选映射到受治理主数据,例如矿山、规格、发货地、收货地和吨位档

  16. Current View(当前态视图):本文自定义的业务概念,把审批、生效时间、供应状态和版本等规则收口为只暴露当前有效事实的只读数据模型,隔离 AI 查询与底层版本表

  17. SemanticPolicy(语义策略):本文自定义的架构组件,在模型调用前后维护高价值业务意图、可用 Tool、前置条件和终止证据等确定性约束

  18. 范式:把业务上下文当作数据产品的一部分,而不是临时 Prompt 补丁。Google Cloud 将生产级自然语言查询所需上下文拆成显式 Schema/字段/样例和隐式业务语义,并用语义层、业务示例、数据值关联和查询历史补足模型;对 NL2BI 更适合把长期口径沉到主数据、Query Layer、Validator 和语义模型,只把本轮必要上下文交给 Agent。原文:“Business knowledge and semantics are often not well documented” Getting AI to write good SQL: Text-to-SQL techniques explained

  19. API(Application Programming Interface,应用程序编程接口):不同软件组件之间约定好的调用入口与数据协议

  20. Oracle(评测基准/事实裁判):本文 Eval 体系中的确定性事实来源,通过规范业务查询或绑定真实 Tool Result 判断预期行、数值、聚合和元数据是否正确

  21. Runtime(运行时):真正执行 Agent 循环的应用层代码,负责上下文组装、模型调用、Tool Call/Result 写回、状态推进、错误处理、流式事件和审计

  22. Response(响应):一次模型/API 调用返回的数据;这里特指框架封装后可能不容易直接观察的底层模型响应

  23. Workflow(工作流):由代码通过预定义路径编排 LLM 和 Tool 的 agentic system;与 Agent 的主要区别是关键执行路径由代码而不是模型动态决定

  24. Anthropic 的《Building effective agents》建议优先采用简单、可组合的模式,只在确有收益时增加 Agent 复杂度;文中同时区分 Workflow 与 Agent,并提醒框架可能遮蔽底层 Prompt/Response、增加调试成本,原文摘录:“Workflows are systems where LLMs and tools are orchestrated through predefined code paths” Building Effective Agents

  25. SSE(Server-Sent Events,服务端推送事件):基于 HTTP 的服务端到浏览器单向事件流,本文用于持续推送 Tool 状态、文本增量、审计轨迹和结束/错误事件

  26. ECharts:Apache ECharts 的图表可视化库,本文由前端 Renderer 将受控 ChartSpec 转换为可交互图表,而不是让模型直接生成任意 ECharts option

  27. OpenAI-compatible:接口形状兼容 OpenAI Chat Completions/Tool Calling 风格,不代表参数和协议细节完全等价,Runtime 应做 Provider Capability/Profile 适配。例如 DeepSeek 思考模式通过 reasoning_content 返回推理内容;一旦某个 Assistant 轮次发生 Tool Call,后续请求必须完整回传该轮 reasoning_content,否则 API 会返回 400,而没有 Tool Call 的中间推理则无需继续拼接。原文:“the reasoning_content must be fully passed back” DeepSeek Thinking Mode

  28. Chat Completions:以角色化 messages 为主要输入、返回 Assistant 内容和 Tool Call 等结果的对话模型 API 形态

  29. Provider(模型服务提供方):向 Runtime 提供模型 API 的厂商或部署端;本文需要在统一接口之上适配不同 Provider 的模型能力、缓存、Usage 与故障语义

  30. Zod:TypeScript 的 Schema 声明与运行时校验库,本文用于验证模型产生的 Tool 参数和结构化对象,使 JSON 仍需通过确定性代码校验后才能执行

  31. GoFrame:Go 语言 Web/工程框架,本文用于业务查询层与现有后端模块,不承担模型推理本身

  32. Tool Contract(工具契约):本文 Eval 中对 Tool 使用的可验证要求,例如必须/禁止调用哪些 Tool、参数边界、前置依赖和允许的能力路径;契约应约束业务不变量,而不是把 Agent 的每一步都写死。像“到岸成本必须先得到矿山—发货地映射”属于事实成立的前置条件,可以确定性检查;若多条路径都能得到同样可信 Outcome,则不应仅因 Tool 顺序不同判错 Demystifying Evals for AI Agents

  33. Judge(评测裁判):Eval 中负责给输出或轨迹评分的组件;LLM Judge 更适合语言质量、交互体验、解释充分性等难以写成精确断言的维度,事实、权限、Schema、数值和环境 Outcome 则应优先用确定性 grader。Anthropic 还建议把 Judge Rubric 拆成清晰维度,并用专家人工评分校准模型裁判,信息不足时允许返回“未知”而不是逼迫判断 Demystifying Evals for AI Agents

  34. LLM(Large Language Model,大语言模型):根据上下文 Token 预测并生成文本或结构化输出的模型,本文把它视为语义理解与决策组件,而不是业务事实源

  35. JWT(JSON Web Token):RFC 7519 定义的紧凑 Token 格式,用于在各方之间携带可验证 Claims;本文把身份与租户上下文从认证链路传入,而不是允许模型自行声明

  36. fail closed(失败关闭):当权限、安全状态或依赖无法确认时默认拒绝访问,而不是为了“可用性”继续放行

  37. Token(词元):模型处理文本时使用的离散计量单位,输入、输出和推理 Token 共同影响上下文容量、费用和延迟

  38. MCP 采用 Host–Client–Server 架构:Host 管理一个或多个 MCP Client,每个 Client 与对应 Server 建立连接,Server 提供上下文能力;同时 MCP 只定义上下文交换协议,不规定 AI 应用如何管理模型和上下文,原文摘录:“MCP follows a client-server architecture” MCP Architecture Overview

  39. Registry(注册表):本文指 Tool 的受控目录,集中保存工具名称、描述、输入 Schema、处理器和版本信息,并生成稳定 Version/Schema Hash 供 Runtime、审计与 Eval 使用

  40. Tool 设计重在为 Agent 打造边界清晰、重叠度低、高 Token 效能的定制化能力,切忌无脑将现有 API 原样暴露给模型;Schema 负责结构合法性,名称/描述负责可发现性,Validator 负责不可妥协的硬约束,Schema 难以表达的格式约定、可选参数相关性和相似 Tool 边界则适合补少量真实示例。Anthropic 的 Advanced Tool Use 建议示例使用真实数据,覆盖最小/部分/完整参数形态,通常每个 Tool 保持 1–5 个且只针对歧义处添加,避免把所有边缘情况重新塞回 Prompt Writing effective tools for AI agents / Advanced Tool Use

  41. 范式:让非法状态尽量在 Tool Contract 中“不可表示”。OpenAI 的 Function Calling 最佳实践包括:函数名/参数/返回值写清楚;用 enum 和对象结构减少无效组合;已经由程序知道的参数不要再让模型填写;总是固定串联的动作可以考虑合并;工具面大小需要通过 Eval 调整。若使用 OpenAI strict mode,还需按其 Schema 规则设置 additionalProperties: false 等约束。对应本文:tenant_id 从认证上下文注入,dataset/dimension/metric/aggregation 采用枚举,互斥和成对条件由 Zod/Validator 表达。原文:“Use enums and object structure to make invalid states unrepresentable” OpenAI Function Calling

  42. Grounding(事实锚定):让模型回答受外部事实或受控上下文约束,而不是只依赖模型参数记忆;本文进一步要求事实自身具备明确的当前态、租户和完整性语义

  43. ChartSpec(图表协议):本文自定义的最小结构化图表描述,包含图表类型、维度、指标、数据行和 Evidence 等业务字段,由前端 Renderer 统一转换为 ECharts 配置

  44. MCP 安全文档建议对远程 Server 使用明确授权、验证 Token、保护凭证并执行最小权限 Scope;同时禁止把 Session ID 当作认证依据并提醒避免记录敏感凭证,原文摘录:“Least-privilege scopes. Don’t use catch-all scopes” Understanding Authorization in MCP / Security Best Practices

  45. Trace ID(追踪标识):分布式追踪中用于把同一次请求跨模型、MCP、后端服务和评测组件的多个 Span 关联到一起的标识

  46. 范式:工具少时保持直接,工具面变大后再引入按需发现,不要为了“先进”提前增加编排层。OpenAI 建议初始可用函数保持较少,并把“少于 20”作为软性参考;Anthropic 的 Advanced Tool Use 则建议在 10+ Tools、多 MCP Server、Tool Schema 已明显挤占上下文或误选率升高时评估 Tool Search/Deferred Loading,而小于 10 个且高频使用的紧凑工具集收益可能有限。Anthropic 的 defer_loading 会把延迟工具排除在初始上下文之外;OpenAI 的 allowed_tools 能在不改变全量 tools 定义的情况下限制本轮可调用子集、利于缓存,但二者都属于具体 Provider 能力,不能作为 OpenAI-compatible 的统一契约。原文:“Less beneficial when: Small tool library (<10 tools)” Anthropic Advanced Tool Use / OpenAI Function Calling

  47. 范式:先判断“现有事实是否足以回答”,再决定执行还是追问。自然语言问题常同时存在 Ambiguous(多种解释)和 Underspecified(缺关键条件)两类不确定性;Google Cloud 的做法是先判断当前 Schema/数据是否足够,不能回答时生成澄清问题,并把 Entity Resolution 单独处理。对应本文:0 候选停止、1 个唯一候选继续、多候选且缺唯一条件就追问,禁止模型凭语言习惯替用户选实体。原文:“generate the necessary follow-up questions to clarify the user’s intent” Getting AI to write good SQL: Text-to-SQL techniques explained

  48. Validator(校验器):在执行前后以确定性代码检查结构、枚举、参数组合、证据或输出契约是否满足规则的组件

  49. Renderer(渲染器):把模型产出的最小业务协议转换为具体 UI/图表实现的确定性前端组件,使模型不需要直接控制 ECharts option、主题、响应式布局等细节

  50. Pipeline(流水线):按固定顺序执行的一组处理阶段;本文用它表示比 Workflow 更固定、更少依赖模型动态决策的执行方式

  51. 范式:聚合、过滤、循环和大结果处理中,尽量让数据库/代码做确定性计算,模型消费高信号结果。Anthropic 将大量中间 Tool Result 不断回灌模型称为 Context Pollution,并用 Programmatic Tool Calling 让代码执行循环、条件、变换与并行,只把最终结果送入模型上下文;本文的 analyze_current_data 没有采用同一产品能力,但采用了更确定性的相同思想:数据库完成白名单聚合,模型只负责选择受控参数和解释结果。原文:“only the results of the code are returned to Claude” Anthropic Advanced Tool Use

  52. FSM(Finite State Machine,有限状态机):由有限状态和状态转移规则组成的计算模型,本文用于表达跨域业务中的前置条件、可执行阶段和停止条件

  53. chatbot(聊天机器人):以对话问答为主要交互形式的应用;本文用它和需要重新查询当前业务事实的 NL2BI Agent 做区分

  54. Structured Outputs(结构化输出):用 JSON Schema 等约束模型输出“形状”,不能自动保证值来自正确业务事实。以 OpenAI strict mode 为例,官方建议开启 strict: true,并要求对象设置 additionalProperties: false、Schema 中字段按其规则标记为 required;这类机制适合降低参数格式错误,但业务枚举、权限、当前态、Evidence 仍必须由 Validator/Query Layer 校验。原文:“ensure function calls reliably adhere to the function schema” OpenAI Function Calling — Strict mode

  55. Provenance(数据来源/血缘):描述一个结果从哪次查询、哪组参数、哪个数据快照和哪个工具版本产生的可追溯链路

  56. localStorage:浏览器提供的同步键值存储,适合保存体积较小、结构简单的本地状态;本文只用它保存用户可见会话,而非完整执行证据

  57. Agent Memory(智能体记忆):Agent 为后续轮次保留和重新利用的信息集合,不等同于聊天记录;本文进一步拆成 Visible、Trajectory、Working、Evidence 与 Current Business Truth 等不同层

  58. IndexedDB:浏览器内置的异步结构化数据库 API,适合保存较大的对象和索引化数据;本文用于持久化完整 Raw Trajectory 和大型 Tool Result Artifact

  59. Telemetry(遥测):运行过程中自动采集的结构化观测数据,例如 Token、延迟、缓存、Tool 次数、错误和时间点,用于定位性能与可靠性问题

  60. Artifact(产物/大对象):本文指从主轨迹中外置保存的大型 Tool Result 内容,Trajectory 中只保留可验证引用,需要时再按策略召回

  61. Push Context(推送式上下文):本文自定义的上下文装载方式,由客户端/服务端主动挑选历史并随请求发送给模型,而不是由模型通过检索工具主动拉取历史

  62. Context Envelope(上下文信封):本文客户端提交给服务端的结构化历史载荷,包含候选轨迹、Checkpoint 和 Artifact 引用等内容,服务端会继续做大小与完整性校验

  63. Archive Memory(归档记忆):长期保存在本地轨迹/Artifact 中的历史全集,与模型当前调用实际可见的 Working Memory 相区分

  64. Checkpoint(检查点):把更早的多轮历史折叠成结构化状态的中间表示,保留 Facts、Constraints、Unresolved、Evidence Reference 和版本信息,减少后续 Prompt 重放量

  65. Working Set(工作集):当前构造 Prompt 时真正保留的一小部分近期 Turn 与必要证据,是 Archive 中被挑选出来的活动子集

  66. Atomic Turn(原子回合):本文用于恢复历史时的最小完整回合单元,要求 User、Assistant Tool Call、Tool Result 与 Final 等关联部分尽量成组保留,避免截断工具链

  67. Replay(重放):把历史 Tool Call/Result 等消息重新构造成模型可消费的对话协议;只有依赖信息完整时才适合安全重放

  68. Payload Externalization(载荷外置):把过大的 Tool Result 从主轨迹/Prompt 中拆出,单独保存为 Artifact,只在上下文里留下 ID、Hash 和引用

  69. Context Window(上下文窗口):一次模型调用能够处理的最大 Token 范围,包含 System、历史消息、Tool 定义、Tool Result 和本轮输入等所有送入模型的内容

  70. Compaction(压缩/紧凑化):当上下文接近预算时折叠或外置旧 Turn,其关键在于保留后续决策所需的高信号状态,盲目追求简短反而容易丢失关键信息。Anthropic 的实践建议先最大化压缩阶段的 recall,确保关键架构决策、未解决问题和实现细节不丢失,再逐步去除冗余 Tool Result;这也是 Checkpoint 应优先结构化保存 Facts / Constraints / Unresolved / Evidence,而不是只生成一段自然语言摘要的原因 Effective Context Engineering for AI Agents

  71. 范式:长上下文管理优先保留“未来还会改变决策的信息”,而不是追求保留全部历史。Anthropic 把 Compaction、结构化笔记与 Tool Result Clearing 作为长期 Agent 的常见手段,并建议压缩策略先最大化 Recall,再逐步去冗余;对于 NL2BI 还需要额外区分“语义连续性”和“当前业务事实”,因此旧 Tool Result 可以留下 Evidence/实体/约束用于理解,但带“当前”语义的数据更适合重新执行受治理查询。原文:“first maximize recall” Effective Context Engineering for AI Agents

  72. WAL(Write-Ahead Log,预写日志):数据库常用的增量日志机制;这里借用 “Snapshot + Recent WAL” 作类比,表示“较早历史被折叠成快照,最近回合继续以增量形式保留”

  73. Thinking(推理模式):部分模型/API 暴露的额外推理能力或推理强度配置,通常会消耗额外推理 Token;本文在 Checkpoint Summarizer 中关闭它以减少不必要推理。Provider 适配还必须尊重消息协议:DeepSeek 当前文档要求,思考模式下若某轮发生 Tool Call,该轮 reasoning_content 必须在后续请求完整回传;未发生 Tool Call 的历史推理则可不拼接,这类差异说明“统一 ChatCompletions 类型”不能代替 Provider-specific transcript 规则 DeepSeek Thinking Mode

  74. 范式:Agent Eval 要拆开过程证据与环境结果。Anthropic 用 task / trial / grader / transcript / outcome / evaluation harness / agent harness / suite 描述 Agent 评测:Transcript 说明模型如何行动,Outcome 说明环境最终到底发生了什么;用户侧 Agent 还需要同时评价任务完成和交互质量。映射到本文就是 Tool Contract/Trajectory 看“怎么查”,Oracle/Evidence 看“查到的事实是否成立”,Judge 看“是否真正解决用户问题”,Runtime Metrics 看“以什么代价完成” Demystifying Evals for AI Agents

  75. Capability Path(能力路径):Eval 场景允许 Agent 采用的一组业务能力/Tool 路径,用于约束关键业务前置条件,而不是机械规定每一步都必须完全相同

  76. 范式:只把“业务不变量”写成强路径断言,其他地方优先评价 Outcome。Anthropic 提醒,过度要求 Agent 严格遵循某个预设 Tool 顺序会让 Eval 变脆,也会误伤模型找到的其他有效解法;因此本文的 Tool Contract 更适合约束租户边界、映射前置条件、必须使用完整聚合能力等“换条路径就可能改变事实”的条件,而不应把所有普通 Tool 顺序都固定成脚本。原文:“better to evaluate what the agent produced rather than the path it took” Demystifying Evals for AI Agents

  77. Meta(元数据):描述结果自身而不是业务值本身的信息,例如完整性、行数、刷新时间、快照版本和查询范围

  78. Span(追踪片段):分布式 Trace 中表示一个有开始、结束和属性的操作单元,例如一次 LLM 调用、一次 MCP Tool Call 或一次 Oracle 查询

  79. OpenTelemetry GenAI Semantic Conventions(生成式 AI 语义约定):为 Trace、Metric、Event 等观测数据提供统一命名和属性语义,使不同代码库和平台上的模型调用更容易关联分析;OpenTelemetry 对语义约定的定义是为采集、生产和消费遥测数据提供共同属性,原文摘录:“define a common set of (semantic) attributes” OpenTelemetry Semantic Conventions

  80. Prompt Cache(提示前缀缓存):Provider 对重复输入前缀的复用机制,可降低重复前缀的计算/计费成本,但不会消除额外 LLM 往返,也不等同于 MCP/业务数据缓存。DeepSeek 当前硬盘缓存要求后续请求完整匹配已落盘的缓存前缀单元,通过 prompt_cache_hit_tokens / prompt_cache_miss_tokens 暴露命中量,并说明缓存构建是秒级且 best effort;因此 System、Tool 定义、序列化顺序等前部内容的稳定性会直接影响复用,缓存实验也应区分冷请求与已落盘后的暖请求 DeepSeek Context Caching

  81. nudge(纠偏提示):模型没有按预期调用 Tool 后,Runtime 追加的一段动态提示,用于强制或提醒模型改走指定路径;它会额外增加一次模型往返和动态上下文

  82. Capability Bundle(能力包):本文后续优化方向中的概念,把同类意图固定到一组稳定、版本化、顺序一致的 Tool/Schema 配置,兼顾工具选择边界和 Prompt Cache 前缀稳定性。若未来 Tool Surface 扩展到多 MCP Server/数十工具,还可进一步采用按需发现:Anthropic 的 Tool Search 用 defer_loading 让不常用工具不进入初始上下文;OpenAI Function Calling 也建议在工具很多时减少初始暴露或使用 Tool Search,但这属于 Provider-specific 能力,需要 Eval 后决定是否值得增加一次搜索步骤 Anthropic Advanced Tool Use / OpenAI Function Calling 2

  83. Completion Budget(输出预算):按任务类型限制模型最大输出 Token 或目标答案长度,避免单值/简单明细任务扩展成不必要的长报告

  84. Provider-specific Usage Normalization(Provider 用量归一化):把不同模型厂商返回的 input/output/reasoning/cache 等 Usage 字段转换成统一内部指标,避免跨 Provider 比较时误判

  85. Eval Driven(评测驱动):先把成功标准写成 Eval,再改 Prompt、Tool、Runtime 或模型,并用固定数据集验证单一变量收益。Anthropic 建议早期不用等待“几百条数据”,从真实故障和手工验收中整理约 20–50 个任务即可起步;Capability Eval 用难题衡量能力上限,Regression Eval 应接近满分以防回退;同时覆盖“应该触发”和“不应该触发”两侧案例并定期阅读 Transcript。需要重复采样时,pass@k 衡量 k 次中至少一次成功,pass^k 衡量 k 次全部成功,面向用户且强调稳定性的 Agent 更应关注后一类一致性指标 Demystifying Evals for AI Agents

Copyright & License

Author: Cooing

Original Link: https://blog.cooingcode.space/en/blog/nl2bi-agent/

License: Feel free to share or quote, but please attribute properly.