数据表工作区

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:crates/cheng-core/src/domain/tablecrates/cheng-nodes/src/nodes/builtin/tablecrates/cheng-api/src/rest/handlers/table/mod.rschengflow-ui/src/features/table

表格是 ChengOS 中与文档相对的结构化数据形态:它是 Airtable 意义上的多维表格——有类型的列、有序的记录、可保存的视图——而不是一张由零散单元格组成的电子表格。

这个区别对工作流很关键。因为每一列都声明了类型、每条记录都由稳定 id 寻址,所以可以把一张表交给模型去改,改完得到的仍然是经过校验的数据,而不是自由文本。

领域模型

Table(聚合根)
  ├── Column
  │     └── ColumnConfig   (例如 select 的选项)
  ├── Record
  │     └── CellValue      (带标签的联合类型)
  └── View
        └── ViewConfig

列类型——textnumberselectdatecheckbox

单元格值是带标签的联合类型,而不是裸 JSON:nulltextnumbercheckboxselectdate。标签会出现在传输格式里,因此一个单元格写作 {"type": "text", "value": "hello"}。校验在写入时发生——数字拒绝 NaN 和无穷大且小数位上限为 10 位,日期必须是 YYYY-MM-DD,与所在列类型不匹配的值会被拒绝而不是被强制转换。

null 是通用的清空值:写入它可以把任何类型的单元格置空。

排序在列和记录上都使用小数型的 order_index,所以在两条记录之间插入一条新记录,不需要给整张表重新编号。

视图

视图是同一批记录之上的一份保存配置,而不是记录的副本。ViewType 声明了 gridkanbancalendargallerytimeline目前只实现了 grid——其余只是枚举里的占位,选了也拿不到任何东西。

网格视图在表头区和数据区都支持合并单元格。数据区的合并锚定在记录 id 上而非行号上,因此排序和过滤之后合并依然正确,而不会糊在恰好占据那些位置的行上。

查询

记录用过滤树来查询,而不是一串扁平条件:

FilterNode
  ├── Condition { column_id, operator, value }
  ├── And([FilterNode, ...])
  └── Or([FilterNode, ...])

操作符——equalsnot_equalscontains(仅文本)、gtgteltlte(数字与日期)、is_emptyis_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——透传给下游节点

选项:formatjsonmarkdowncsv,默认 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 总览

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容