适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:
crates/cheng-core/src/domain/table、crates/cheng-nodes/src/nodes/builtin/table、crates/cheng-api/src/rest/handlers/table/mod.rs、chengflow-ui/src/features/table
表格是 ChengOS 中与文档相对的结构化数据形态:它是 Airtable 意义上的多维表格——有类型的列、有序的记录、可保存的视图——而不是一张由零散单元格组成的电子表格。
这个区别对工作流很关键。因为每一列都声明了类型、每条记录都由稳定 id 寻址,所以可以把一张表交给模型去改,改完得到的仍然是经过校验的数据,而不是自由文本。
领域模型
Table(聚合根)
├── Column
│ └── ColumnConfig (例如 select 的选项)
├── Record
│ └── CellValue (带标签的联合类型)
└── View
└── ViewConfig
列类型——text、number、select、date、checkbox。
单元格值是带标签的联合类型,而不是裸 JSON:null、text、number、checkbox、select、date。标签会出现在传输格式里,因此一个单元格写作 {"type": "text", "value": "hello"}。校验在写入时发生——数字拒绝 NaN 和无穷大且小数位上限为 10 位,日期必须是 YYYY-MM-DD,与所在列类型不匹配的值会被拒绝而不是被强制转换。
null 是通用的清空值:写入它可以把任何类型的单元格置空。
排序在列和记录上都使用小数型的 order_index,所以在两条记录之间插入一条新记录,不需要给整张表重新编号。
视图
视图是同一批记录之上的一份保存配置,而不是记录的副本。ViewType 声明了 grid、kanban、calendar、gallery、timeline;目前只实现了 grid——其余只是枚举里的占位,选了也拿不到任何东西。
网格视图在表头区和数据区都支持合并单元格。数据区的合并锚定在记录 id 上而非行号上,因此排序和过滤之后合并依然正确,而不会糊在恰好占据那些位置的行上。
查询
记录用过滤树来查询,而不是一串扁平条件:
FilterNode
├── Condition { column_id, operator, value }
├── And([FilterNode, ...])
└── Or([FilterNode, ...])
操作符——equals、not_equals、contains(仅文本)、gt、gte、lt、lte(数字与日期)、is_empty、is_not_empty。后两个不需要值。
排序是一个 SortSpec:列加上 asc/desc。
表格节点
三个节点,各司其职。
| 节点 | 作用 |
|---|---|
table/query |
分页读取列与记录 |
table/apply_record_ops |
批量应用插入 / 更新 / 删除操作 |
table/build_llm_context |
把表格加上一条指令打包成模型可直接消费的提示词 |
table/query 在返回记录的同时返回列定义(include_schema,默认为真),因为下游模型不知道每个列 id 的含义就无法解读 data。分页默认每页 100 条、硬上限 1000;也可以传 record_ids 精确取一组记录。workspace_id 由环境执行上下文注入,用来校验这张表确实属于调用者的工作区。
写入记录
table/apply_record_ops 接受一个有序的 operations 数组,每项用 op 标记:
{
"table_id": "...",
"operations": [
{ "op": "insert", "data": { "col_1": { "type": "text", "value": "Acme" } } },
{ "op": "insert", "data": { "col_1": { "type": "text", "value": "Globex" } },
"position": { "after": { "reference_id": "<record-id>" } } },
{ "op": "update", "id": "<record-id>", "data": { "col_2": { "type": "number", "value": 42 } } },
{ "op": "delete", "id": "<record-id>" }
],
"summary": "import from CRM"
}
有两个细节会改变你的用法:
update是 PATCH 而不是 PUT。 只有出现在data里的列会被改动,未提及的列保持原值。你永远不需要为了改一个字段而先把整条记录读出来。insert默认追加到末尾。 传position并给出after的参考记录 id,才能精确放置。
输出会给出每个操作的结果,以及 records_inserted / records_updated / records_deleted / records_affected,因此一个部分成功的批次无需重读整张表就能诊断。
summary 是可选的,用于日志与审计——调用方是智能体时尤其该填。
把表格交给智能体
table/build_llm_context 承担了本来会散落在工作流各处的提示词工程。给它一个 table_id 和一条 instruction,它产出:
system_prompt——编辑这张表的规则user_prompt——指令加上渲染后的表格tools_json——记录操作的函数调用工具定义table_id——透传给下游节点
选项:format(json、markdown 或 csv,默认 json)、max_records(默认 100,0 表示不限),以及 selected_record_ids 把上下文限定在子集上——当用户在网格里选中了若干行时,这是自然的选择。
标准形态是一条三节点链:
table/build_llm_context ──► 智能体或 ai/llm ──► table/apply_record_ops
模型返回操作,应用节点负责校验并执行。因为单元格值是有类型的、记录是按 id 寻址的,一次格式错误的修改会在边界处失败,而不会把表弄脏。
REST 访问
表格在 API 上是完全可寻址的:/tables 做 CRUD,/tables/:id/columns(含 /reorder),/tables/:id/records(含 /query、/reorder、/batch-delete),以及 /tables/:id/views。参见 REST API 总览。
下一步
- 文档工作区——非结构化的对应物。
- 知识库与工作区上下文——工作区数据如何抵达智能体。
- ReAct 智能体指南——消费这些节点所构建上下文的智能体。

暂无评论内容