上一篇讲了为什么要把数据团队从写 SQL 转向训练 Agent,提到在建一个六层 Context Stack。这篇把每一层拆开,讲怎么落地的。

先放全景

                信号密度 ↑  获取成本 ↑

        ┌─────────────────────────┐
    L6  │  Runtime Exploration    │  Agent 实时探查
        ├─────────────────────────┤
    L5  │    Agent Memory         │  交互纠错 → 自进化
        ├─────────────────────────┤
    L4  │  Domain Knowledge       │  组织知识 → 分析框架
        ├─────────────────────────┤
    L3  │ Code-Level Semantics    │  ETL 代码 → 真实语义
        ├─────────────────────────┤
    L2  │  Curated Semantics      │  专家标注 → 指标/关系
        ├─────────────────────────┤
    L1  │   Schema & Usage        │  元数据 + 查询模式
        └─────────────────────────┘

从下往上,信号密度越来越高,获取成本也越来越高。下面逐层讲我们做了什么、为什么这么选。

L1:不靠 RAG,先靠目录

一开始我们把所有表结构和查询历史全部进 embedding,查询时 RAG 召回。表数过千、指标过百之后,纯语义检索的召回准确率不够稳。

后来加了一层叫 Manifest 的轻量结构。每个 manifest 文件只存条目的 id、name、aliases 和 ref,所有 manifest 加起来在 context 里也就几千 token。Agent 启动时 manifest 直接进 context,相当于一张「目录页」。命中之后再按 ref 拉详情。

先精确路由,再按需补详情。RAG 留作兜底,不是主路径。

L2:指标必须结构化,自然语言描述不够

有「DAU」、「eDAU」、「播放 DAU」这种指标群,同一个名字底下,不同业务有不同口径。如果只靠人工写一段自然语言描述,Agent 会把它们混着用。

我们把指标、维度、表的 JOIN 图谱都写成结构化的 yaml。每个指标明确写出 source_tables、filter、cross_day_rule、related_metrics。结构化之后 Agent 在相似指标之间不会乱选。

这一层的工作量是 stack 里最大的。但回头看,它撑起了 80% 的日常查询。

L3:表的含义在 ETL 代码里

Schema 告诉你一张表有哪些列,但它不会告诉你这张表只包含 App 端流量、不含车载和第三方渠道。这些信息在生产这张表的 ETL 代码里。

我们有自己的任务调度系统,存着每张产出表的完整 SQL、调度配置、上下游依赖。基于这个做了一层工具封装,让 Agent 在 L2 不够用的时候,可以拉一张表的产出 SQL,反推它的真实含义和血缘。

ETL 本来就在描述「这张表是怎么算出来的」。

接上这一层之后,Agent 区分两张名字相似但含义不同的表的能力有了明显提升。

L4:领域知识要独立于 skill

早期走的是「为每个业务域写一个 skill」的路子。推送分析一个 skill,会员分析一个 skill,每个 skill 里塞着路由、领域知识、SQL 模板、执行逻辑。写了几个之后就乱了,同一个指标在两个 skill 里定义不一样,领域知识没法穷举,skill 里既有”知识”又有”执行流程”,一改就要动一整块。

解法是把领域知识从 skill 里抽出来,单独作为 yaml 和 md 维护。

抽离之后,专题分析 skill 退化为”轻量路由 + 特殊流程编排”。大部分场景,通用分析 Agent 读领域知识就能处理,不再需要为每个业务域单独写一个 skill。

L5:记忆要会过期

纠错的复用是基本功,用户改了一次,下次别再错。但 memory 不能只进不出,否则半年下来就是一堆噪音。

给每条记忆加了 hotness_score,用 sigmoid 归一化命中频率、乘以半衰期 7 天的时间衰减。30 天前的 memory 权重大约 5%,不靠人工清理,让记忆自然过期。

同时高 hotness 的记忆进 review 队列,确认有价值的会被沉淀回 L2 或 L4 的源文件,变成”固化知识”。L5 的本质是个临时缓冲区,它的价值不在于自己长期保存,而在于把高频纠错及时往下沉淀。

L6:探查的结果要沉淀

兜底层。L1 到 L5 都没有答案时,Agent 现场查仓库,验证 schema、采样数据、追溯血缘。

多做了一步,每次 L6 探查产生的有价值发现,都会被建议沉淀回低层。表结构 → L1,字段含义 → L2 的 glossary,业务理解 → L5 memory。Agent 临时探查的成本不算低,能沉淀就别让它再探一次。

整个 stack 是一个反馈回路,上层的临时知识往下沉淀,下次直接用得上。

为什么不用本体

银行和金融机构做数据问答,很多走的是本体(ontology)路线,把概念、关系、规则全部形式化,让 AI 在确定性的知识图谱上推理。

本体的吸引力是确定性。概念有明确定义,关系有明确边界,推理路径可审计。金融领域用它有道理,「净资产收益率」的定义十年不变,监管要求可解释性,出错成本极高。花两年建一套形式化的知识体系是值得的。

但本体有两个隐含假设:领域知识可以穷举式形式化,维护成本可持续。

对业务快速迭代的场景,两条都不成立。今天多一个「沉浸 DAU」,明天多一个实验指标,后天某张表的口径悄悄改了。本体跟不上变化的速度。

其实 L2 本质上就是一个轻量本体,指标有 id、定义、source_tables、related_metrics,维度有值域和 sql_snippet,表有 JOIN 图谱。只是不追求穷举,结构化到够用就好,管不了的交给上层兜底。

纯本体方案试图把所有东西压进一层。分层的好处是,不需要一开始就把所有知识形式化,先把高频的 80% 结构化到 L2,剩下的让 L3 到 L6 逐步覆盖和沉淀。

还在 Phase 1

我得诚实地说,这套 Stack 目前只做完了 Phase 1,L1 + L2 的 manifest 路由 + 详情读取。L3 接了一部分,L4 在抽离中,L5 和 L6 都还没真正激活。

Phase 1 跑下来,比 ChatBI 时代纯靠 RAG 召回稳定多了。错误的”形状”也变了,从前是 Agent 自信地选了一张错的表,现在是 Agent 知道自己不确定,会去问详情或兜底语义检索。

有一次内部分享,一位高管问了个问题:“公司有多少个 AB 实验?有哪些运行 6 个月以上、不符合公司实验全局最优 spec、且 xx 指标为负的实验?列出来。”

这种问题我们从来没测过。我当时很忐忑,觉得跑不出来。Agent 跑了 40 分钟,给出了结果。用户很满意。

过程中,Agent 表现出了强大的目标驱动,想尽任何办法去完成、不甩锅的执行力。我们的方向是对的。

几条体会

第一,Context 必须分层,而且层与层之间要有明确的协作关系。把所有东西塞进一个大 prompt、一个大 RAG 库、一个大 skill 里,效果都不会好。

第二,分层不是拍脑袋。六层之所以站得住,是因为每一层有清晰的边界。L1 是物理事实,L2 是人加工过的语义,L3 是代码反推的真相,L4 是组织共识,L5 是 Agent 自己的学习,L6 是临时探查。边界清晰,才能独立迭代。

第三,分层的最大回报是让 skill 变轻。原本臃肿的 topic skill 退化为轻量路由,大部分领域知识进了 L4。skill 写得少了,质量反而高了。

最后,这条路远没走完。L5 的沉淀机制、L6 的探查协议、跨层 ID 体系,都还在迭代。下一步真正要验的是,Stack 跑稳之后,Agent 能不能稳定的做到可审计、可纠偏,驱动更多 insight 和对应策略的产生。