Skip to content

Graphical 协议设计规范

设计背景与目标

TFRobotServer 平台通过 @tfs_action 机制,结合 JsonSchema、JMESPath、x-namespace / x-actions / x-ref,实现了图结构(graphical)场景下节点、边等结构化数据的协议标准,支持图谱查询与节点/边/类别的增删改查联动。

本文档以当前代码实现为准,关键数据结构来自:

  • tfrobotserver/dtos/factory/base.pyBaseGraphical / Graphical
  • tfrobotserver/dtos/factory/brain/memory/graph_dto.pyNode / Edge / ClassInfo
  • tfrobotserver/robot/factory/brain/memory/graph_manage_mixin.py:图谱管理方法(get/add/update/delete)
  • tfrobotserver/robot/factory/tfs_action.py:对 category="graphical" + resource_op_type="get_graph" 的返回类型约束

基本约定

  • 所有图结构相关 Action 需通过 @tfs_action 注册。
  • 当 Action 用于“返回图结构”(即 resource_op_type="get_graph")时:
  • category 必须为 "graphical"
  • 函数返回类型必须继承 BaseGraphical(通常使用 Graphical[Node, Edge]
  • 每个 Action 应定义清晰的入参 Schema 和返回 Schema,基于 Pydantic/JsonSchema 表达。
  • 通过 x-namespace / x-actions / x-ref 实现:
  • 当前节点/边上下文变量注入x-namespace
  • 可操作动作集合x-actions
  • 动作参数与上下文变量绑定x-refx-default

图结构基础 DTO(现状对齐)

Node

  • iri: str:节点唯一标识(IRI)
  • label: str:节点展示名称
  • cls: str:节点类别 IRI
  • properties: dict[str, Any] | 显示为 null:其它属性

Edge

  • source: str:源节点 IRI
  • target: str:目标节点 IRI
  • label: str:边展示名称(通常为对象属性名)
  • property: str:对象属性 IRI(用于标识“关系类型”)
  • properties: dict[str, Any] | 显示为 null:其它属性

图响应

  • Graphical[Node, Edge]
  • nodes: list[Node]
  • edges: list[Edge]

典型接口结构

1. 主图查询Action(get_graph)

  • 对应实现参考:GraphManageMixin.aget_nodes(..., cls=None, include_edges=True) -> Graphical[Node, Edge]
  • 返回 Schema 建议在 nodes.items / edges.items 的元素结构中声明 x-namespace,为其它 Action 提供上下文变量。
  • 可在每个元素结构中声明 x-actions,表明当前节点/边可用的操作。
  • x-namespace
  • key 为变量名
  • value 为 JMESPath 路径(相对于当前元素)或 Options 结构(labels / keys / values 三个 JMESPath 字段;见下方示例)
{
  "type": "object",
  "properties": {
    "nodes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "iri": {"type": "string"},
          "label": {"type": "string"},
          "cls": {"type": "string"},
          "properties": {"type": ["object", "null"]}
        },
        "required": ["iri", "label", "cls"],
        "x-namespace": {
          "node_iri": "iri",
          "node_label": "label",
          "node_cls": "cls",
          "user_options": {
            "Options": {
              "labels": "values[*].display_name",
              "keys": "values[*].user_id",
              "values": "users"
            }
          }
        },
        "x-actions": ["update_node", "delete_node"]
      }
    },
    "edges": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "source": {"type": "string"},
          "target": {"type": "string"},
          "label": {"type": "string"},
          "property": {"type": "string"},
          "properties": {"type": ["object", "null"]}
        },
        "required": ["source", "target", "property"],
        "x-namespace": {
          "source": "source",
          "target": "target",
          "edge_property": "property"
        },
        "x-actions": ["delete_edge"]
      }
    }
  },
  "required": ["nodes", "edges"]
}

get_graph 推荐入参(可选)

当前 GraphManageMixin.aget_nodes 支持:

  • cls: str | list[str] | null:按类别过滤(类别 IRI)
  • include_edges: bool:是否包含边

2. 节点/边操作Action(如add_node、update_node、delete_edge、select_user)

  • 入参Schema字段可通过 x-ref 声明依赖,直接引用 namespace 中的变量名。
  • 可通过 x-default 声明字段默认值,值为 Jmespath 路径(相对于当前 namespace)。
  • 可通过 x-fetch 声明“补全动作”,当 x-ref/x-default 无法从当前 namespace 解析出值时,前端可触发该动作补全 namespace,然后重试解析。
{
  "type": "object",
  "properties": {
    "node_iri": {
      "type": "string",
      "x-ref": "node_iri"
    },
    "label": {"type": "string"}
  },
  "required": ["node_iri", "label"]
}

x-fetch 前端实现要求(图场景)

  • 触发条件:仅当 x-ref / x-default 均无法从当前 namespace 得到可用值时,才允许触发 x-fetch
  • 缓存/去重:前端必须按 (action_name + resolved_params) 维度对 x-fetch 做缓存/去重,避免重复触发相同补全动作。
  • 循环/风暴控制:同一次参数解析链路中,x-fetch 最大触发次数为 10;超过阈值应中止并提示。

3. 类别列表查询 Action(get_list / get_cls)

对应实现参考:GraphManageMixin.aget_cls(...) -> list[ClassInfo]

ClassInfo 字段:

  • iri: str:类别 IRI
  • name: str:展示名称
  • properties: dict | null:其它属性

此 Action 不属于 get_graph,如果你希望前端以列表形式渲染,建议注册为:

  • resource_op_type="get_list"
  • category="pageable" 并返回 PageableList[ClassInfo]CursorPageableList[ClassInfo]

如果你只是提供辅助数据,也可以不声明 resource_op_type(由业务自定义使用)。

x-namespace/x-actions/x-ref 与 List 的最佳实践

  • 推荐在 nodes/edges 的每个元素 schema 层级声明 x-namespace 和 x-actions,x-namespace 以变量名为 key,所有 Action 共享同一 namespace,便于统一引用。
  • x-ref 直接引用变量名,前端自动匹配当前上下文。
  • x-default 支持字段默认值从 namespace 动态获取。
  • x-fetch 仅填写动作名称字符串,用于在 x-ref/x-default 解析失败时补全 namespace。
  • JMESPath 用于表达相对路径,如 "iri"、"label"、"property"。

与当前 GraphManageMixin 的操作语义对齐

  • add_cls(iri, name, parent_cls?, properties?)
  • update_cls(iri, name?, properties?)
  • delete_cls(iri)
  • add_node(name, label, cls, properties?)
  • update_node(iri, label?, properties?)
  • delete_node(iri)
  • add_edge(source, target, property, properties?)
  • update_edge(source, target, property, properties?)
  • delete_edge(source, target, property)

注意:当前实现里边的增删改均为“异步方法”,通常建议在注册 @tfs_action 时标记 is_async=True(由具体 Setting 的 action alias 决定)。

FAQ

  • Q:能否只在根节点声明所有 x-namespace?
  • A:不推荐。建议在 nodes/edges 层级声明,便于每个节点/边独立注入上下文。
  • Q:如何表达 list 中“对应 index”?
  • A:前端遍历时自动注入当前项的 namespace,x-ref 直接写变量名即可。
  • Q:x-namespace 的 Options 结构是如何使用的?
  • A:Options 结构用于注入选项型变量,labels/keys/values 三个 Jmespath 字段分别对应选项的标签、键和值。

示例

  • get_graph返回:
{
  "nodes": [
    {"iri": "iri://n1", "label": "Node1", "cls": "iri://ClassA", "properties": {"name": "Node1"}},
    {"iri": "iri://n2", "label": "Node2", "cls": "iri://ClassA", "properties": {"name": "Node2"}}
  ],
  "edges": [
    {"source": "iri://n1", "target": "iri://n2", "label": "knows", "property": "iri://knows", "properties": {"name": "knows"}}
  ],
  "users": [
    {"user_id": "u1", "display_name": "User1"},
    {"user_id": "u2", "display_name": "User2"}
  ]
}
  • update_node参数:
{
  "node_iri": "iri://n1",
  "label": "Node1-updated"
}

返回类型约束(与 tfs_action 行为一致)

  • resource_op_type="get_graph"category="graphical" 时:返回类型必须继承 BaseGraphical
  • 推荐直接使用:-> Graphical[Node, Edge]

否则在 @tfs_action 注册阶段会抛出 TypeError(即“返回类型不符合 graphical 协议”)。