TraceStudio-dev/docs/issues/workflow_results_structure_issue.md
Boshuang Zhao 5790ec164f add web v2
2026-01-10 19:08:49 +08:00

7.6 KiB
Raw Blame History

title created author labels
Workflow Execution Results Structure - Redundancy & Complexity 2026-01-10 copilot
bug
api
design
discussion

问题报告:工作流执行结果的数据结构冗余与复杂性

摘要:

  • 后端在生成并返回工作流执行报告时保留了多套数据(node_infosnode_results、并在 API 层做额外合并与序列化),导致返回体中字段重复、语义混淆、序列化逻辑分散,增加前端解析复杂度与潜在性能问题。

1) 核心矛盾

  • 后端为了兼容历史实现同时保留新的执行上下文,当前返回给前端的执行报告既包含 node_infos 又包含 node_results(并在 API 层再次合并/序列化),导致字段冗余、语义重复、序列化逻辑被多处重复实现。

2) 关键代码片段

  • 执行器在 _get_execution_report 中返回:
"node_infos": { node_id: info.to_dict() for node_id, info in self.context.node_infos.items() },
"node_results": self.context.node_results
  • API 层(endpoints_graph.execute)对 report 做两种兼容解析并合并后序列化:
if 'node_infos' in report or 'node_results' in report:
    node_infos = report.get('node_infos', {}) or {}
    node_results = report.get('node_results', {}) or {}
elif isinstance(report, dict):
    node_infos = report
    node_results = {}

for nid in all_node_ids:
    info = node_infos.get(nid)
    outputs = node_results.get(nid)
    ser_outputs = serialize(outputs)  # polars.DataFrame -> preview
    results_out[nid] = {
        'status': _get_status(info),
        'outputs': ser_outputs,
        'error': _get_error(info)
    }

3) 技术限制与风险

  • 冗余存储:NodeExecutionInfo.resultcontext.node_results[node_id]、以及 API 层 results_out 三处可含相同数据。
  • 性能与带宽DataFrame/大对象可能被多次序列化,产生额外 CPU 与内存消耗。
  • 语义不一致:前端需同时解析多套字段,提升错误率。

4) 可决策方案

方案 A推荐—— 单一标准响应canonical schema

  • 描述:强制执行一个统一的响应结构:顶层 nodes 映射,每个 node 包含 status,duration,cache_hit,error,outputs,meta
  • 优点:清晰、便于文档化与版本化、降低重复序列化。
  • 缺点:需要一次性后端变更与前端适配窗口。

方案 B渐进兼容—— 分层输出summary/results

  • 描述API 层导出 summary(轻量)与 results(可选详细),通过参数控制是否返回详细 outputs?full=true)。
  • 优点:低破坏、可渐进迁移。
  • 缺点:仍需前端适配 full 策略。

方案 C重构—— 大数据 artifact 存储与引用

  • 描述将完整的大对象DataFrame写入临时 artifact 存储,响应仅包含 previewartifact_id
  • 优点:解决大 payload 问题,支持按需拉取。
  • 缺点实现复杂存储、TTL、权限

5) 立刻可执行的短期修复

  • 把 DataFrame 序列化抽成单一函数并确保只执行一次。
  • 在响应中避免重复包含相同数据(检测并合并重复字段)。
  • 添加 api_schema_version 字段并在返回 header 中标注,便于兼容管理。

6) 建议与决策请求给 Gemini

  1. 是否接受 breaking-change方案 A并安排前端迁移窗口
  2. 是否采用渐进方案(方案 B为过渡并设定过渡期例如 2 个版本)?
  3. 是否在短期内实现 artifact 存储(方案 C 的关键部分)?

7) 建议的 canonical schema示例

{
  "status": "success",
  "execution_id": "uuid",
  "execution_time": 0.123,
  "nodes": {
    "node1": {
      "status": "success",
      "duration": 0.032,
      "cache_hit": false,
      "error": null,
      "outputs": {
        "out": {
          "__type": "DataFrame",
          "columns": ["a","b"],
          "preview": [{"a":1,"b":2}],
          "rows": 1234
        }
      },
      "meta": { "class_name": "SomeNode", "node_type": "normal" }
    }
  }
}

8) 后续动作(会议后)

  • 如果选 A制定迁移计划API version、前端适配、回退策略
  • 如果选 B实现 summary/results?full 控制,并优化序列化集中点。
  • 如果选 C实现 artifact 存储与权限管理。


title: "Refactor: Unified Execution Protocol & Compatibility Cleanup" created: 2026-01-10 author: gemini labels: [architecture, breaking-change, cleanup, protocol]

🧠 《Copilot 开发指导手册》:执行引擎标准化重构

1. 功能摘要

废除 node_infosnode_results 的冗余双轨数据流。强制推行以节点为中心的 Canonical Schema 作为唯一真相来源,彻底清理开发期遗留的兼容性逻辑(如旧版 Input/Output 节点分支),确保执行引擎职责单一化。

2. 核心矛盾

后端为了兼容历史实现当前返回的报告中字段重复且语义混淆。API 层(endpoints_graph.execute)被迫进行繁琐的合并与二次序列化,这增加了前端解析难度并导致大型对象(如 DataFrame的性能损耗。

3. 逻辑契约 (Canonical Schema)

WorkflowExecutor._get_execution_report 必须输出如下扁平化结构:

{ "success": "boolean", "execution_id": "string (uuid)", "total_duration": "float", "nodes": { "{node_id}": { "status": "success | error | pending", "metrics": { "duration": "float", "cache_hit": "boolean" }, "outputs": { "{port_name}": { "__type": "DataFrame | any", "preview": "array", "shape": "tuple" } }, "error": "string | null", "meta": { "class_name": "string", "node_type": "string" } } } }

4. 实现提示 (Implementation Hints)

  • 彻底移除兼容性方法:删除已在 workflow_executor.py 头部声明弃用的 _execute_input_node_execute_output_node 内部方法。
  • 清理 API 层冗余:重构 endpoints_graph.py,直接透传执行器结果,禁止使用 if 'node_infos' in report 等逻辑做降级解析。
  • 数据精炼原则:在执行器内部实现 _smart_serialize,针对 Polars DataFrame 等对象强制执行采样预览(如 .head(5)),贯彻“采样预览,全量生产”的核心原则。

5. 核心函数重构示例 (WorkflowExecutor)

def _get_execution_report(self) -> Dict: """ [utrace 架构决策]:废除双轨制,执行数据精炼采样。 """ if not self.context: return {"success": False, "error": "Context not initialized"}

nodes_map = {}
for nid, info in self.context.node_infos.items():
    # 合并执行上下文与结果
    raw_res = self.context.node_results.get(nid, {})
    raw_outputs = raw_res.get("outputs", {})

    nodes_map[nid] = {
        "status": info.status.value,
        "metrics": {
            "duration": round(info.duration, 4),
            "cache_hit": info.cache_hit
        },
        "outputs": self._smart_serialize(raw_outputs), 
        "error": info.error,
        "meta": {
            "class_name": self.graph.nodes[nid].get("class_name"),
            "node_type": info.node_type
        }
    }

return {
    "success": all(n["status"] == "success" for n in nodes_map.values()),
    "execution_id": self.context.execution_id,
    "nodes": nodes_map
}

utrace 架构审计批注 TraceStudio 坚持“逻辑即文档”。本次重构是消除系统熵增的关键一步。所有不符合此 Schema 的前端调用均视为待修复 Bug而非后端需要兼容的对象。