🐦 第7章 扣子数据库实战
📄 一句话概括

平台自带一个数据库,智能体和工作流能直接读写,不用写 SQL,也不用接外部服务。 它的形状很简单:一张扁平的表,没有关联,没有 JOIN。 削掉的那部分(关联、事务、约束),正好是最容易出事的那部分。

📝 本章和前面几章的关系

前六章讲的都是「怎么让模型说话」,这一章讲「怎么让它记住事」。 知识库存的是不变的资料,数据库存的是会变的记录——访客、线索、待办、工单。 两者的差别在这里。 这一章用真 sqlite3 跑,平台那一侧的限制用「同一件事两种表结构」来对照。

一、它长什么样

平台自带一个数据库,在后台能看到「表」和「记录」两个视图。 表是扁的:一行一条记录,一列一个字段。字段类型就那么几种——文本、整数、数字、布尔、日期。 没有关联表,没有 JOIN。 一张表要把所有信息装下,包括本该分出去的那些。

演示里一张访客表七个字段:姓名、单位、电话、接待人、部门、事由、日期。三行记录,一眼能看全。

二、为什么这么设计

因为它服务的场景是对话。 用户说「登记一下张伟来访」,工作流要做的是往里插一行,不是设计一个范式化的数据模型。 在这个场景里,JOIN 是负担:要写 SQL、要考虑连接条件、要人维护。 扁平表虽然冗余,但一次查询就出结果。 所以准确的说法是:它不是简化版的数据库,是为对话场景削过的数据库。

削得有道理,但代价要认。这一章反复讲的就是这个代价。

三、同一个问题,两种查法

演示里把同一份数据建了两遍:一份扁平表,一份拆成公司、员工、来访三张表。 问「晨光文具来过哪些人」。 扁平表:一句 SELECT name FROM visit WHERE company = '晨光文具'。

三张表:要把三张连起来,visit2 连 company 连 staff,写出来两行 JOIN。 结果一样,用途不一样。 扁平表能一句话查出来,代价是公司名重复存了两份,改名要改两处。

四、扁平表的代价

「晨光文具」在扁平表里出现了两次。改公司名,得改两处——漏一处就出现两家名字不同的同一个客户。 这叫冗余。范式化(拆表)就是为了消掉它。平台数据库不做范式化,所以这个问题只能靠人守规矩。 守规矩的办法很土但有效:同一个值只让一个人录。公司名从下拉框选,不让手打; 接待人从员工列表选,不让手打。

手打是冗余失控的开始。三个人的三种写法,一张表里就有三个「晨光」。

五、它能干什么,不能干什么

能 不能
存会话里的结构化信息 多表关联查询
按条件查记录 事务(要么全成要么全不成)
增删改单条或多条 建索引、优化慢查询
给智能体当长期记忆 字段级权限
工作流节点里读写 并发写入的冲突处理

左边一列够日常用了。右边一列不出现则已,一出现就是事故——第 7.5 节逐个量。 判断标准很简单:这件事错了能不能重来? 能重来的放平台库,不能重来的(钱、库存、合同)别放。

六、动手 Demo:平台自带的表,和传统表差在哪

同一份访客数据建两遍:一遍一张扁平表,一遍拆成三张。 同一个问题各查一次,再看扁平表的冗余代价。

# 平台风格:一张扁表
CREATE TABLE visit (
  name TEXT, company TEXT, phone TEXT,
  host TEXT, dept TEXT, purpose TEXT, day TEXT)

# 传统做法:拆三张,靠 cid / sid 连
CREATE TABLE company (cid INTEGER PRIMARY KEY, name TEXT)
CREATE TABLE staff   (sid INTEGER PRIMARY KEY, name TEXT, dept TEXT, cid INTEGER)
CREATE TABLE visit2  (vid INTEGER PRIMARY KEY, name TEXT, cid INTEGER,
                      sid INTEGER, purpose TEXT, day TEXT)

实跑输出:

--------------------------------------------------------------------
平台自带的表,和传统表差在哪
--------------------------------------------------------------------


  ① 一张扁表装下所有信息
  --------------------------------------------------------------------

| 来访人 | 单位 | 接待人 | 部门 | 事由 |
|---|---|---|---|---|
| 张伟 | 晨光文具 | 李强 | 采购部 | 谈供货 |
| 王芳 | 远达物流 | 李强 | 采购部 | 送样品 |
| 陈明 | 晨光文具 | 周涛 | 技术部 | 调试设备 |

  七个字段塞在一张表里,三行记录。这就是平台数据库的样子——一张表,没有关联。

  ② 传统做法会拆成三张表
  --------------------------------------------------------------------
  公司表 2 行、员工表 2 行、来访表 3 行。三张表靠 cid、sid 连起来。

  ③ 同一个问题,两种查法
  --------------------------------------------------------------------
  问:晨光文具来过哪些人
    单表:张伟、陈明
    三表 JOIN:张伟、陈明
  结果一样,用的是同一条 SQL 语言。区别是平台数据库只支持前一种。

  ④ JOIN 在平台上没法写
  --------------------------------------------------------------------
  本沙箱的 sqlite3 支持 JOIN——所以这一条只能说明「传统库能做到」,
  不能说明平台也能。平台的多维表里没有 JOIN 这个语法,两张表要合并得在节点里手工拼。
  手工拼的代价:两表各查一次,在代码节点里按 cid 对照。
  一百行以内还行,上千行就开始慢,而且容易拼错。

  ⑤ 扁平表的代价
  --------------------------------------------------------------------
  「晨光文具」这个名字在前一种表里出现了两次。
  改公司名就得改两处——漏一处就出现两家名字不同的同一个客户。
  这是范式化要解决的问题。平台数据库不做范式化,所以这个问题得靠人守规矩:同一个值只让一个人录。
  ! 平台数据库不是「简化版的数据库」,是「为对话场景削过的数据库」。
  削掉的那部分(JOIN、事务、约束)正好是最容易出事的那部分——第 7.5 节逐个量。

①第一张表五个字段,三行记录。七个原始字段塞在一张表里——这就是平台库的样子。 ②第二段建三张表,公司两家、员工两个、来访三条。三张表靠 cid 和 sid 连起来。 ③第三段同一个问题两种查法,结果一样:张伟、陈明。差别不在结果,在平台只支持前一种。 ④第四段说清了本沙箱的局限:sqlite3 支持 JOIN,所以这里证明不了平台能不能。 平台的多维表里没有 JOIN 语法,两张表要合并得在节点里手工拼。 ⑤最后一段是扁平表的代价:「晨光文具」存了两次,改名要改两处。 平台不做范式化,只能靠「同一个值只让一个人录」这个规矩。

30 秒实验

  1. 数一下你现在那张表里,哪些字段的值是重复出现的。那些就是该拆出去的部分。
  2. 把「公司名」改成下拉选择,看还有没有人手打出三个不同的写法。
  3. 问一句:这张表里的数据错了,能重来吗?不能的话,它不该放在平台库里。

常见误区

  1. 「平台数据库就是简化版 MySQL。」它削掉了关联、事务、索引。削的部分正好最容易出事故。
  2. 「有数据库了就不用知识库。」知识库存不变资料,数据库存会变的记录。两件事。
  3. 「一张表塞全部字段最省事。」省的是设计时间,花的是维护时间。同一个值存两处,早晚不一致。
自测三问(点开对答案) 1. 平台数据库和知识库分别存什么? —— 数据库存会变的记录(访客、线索、工单),知识库存不变的资料(手册、制度、政策)。 2. 平台数据库为什么不做关联表? —— 它服务的是对话场景,一次查询出结果比范式化更要紧。代价是数据冗余,靠人守规矩。 3. 什么样的数据不该放平台库? —— 错了不能重来的。钱、库存、合同这类需要事务保障的,别放。

形状清楚了,接下来动手建。下一节讲建库、建表、定字段,还有一个容易忽略的小东西——那个自增主键。

相关笔记