news 2026/8/22 4:14:02

Agent Workflow 学习向:最小工作流,创建图、同步跑通、按 run_id 查事件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent Workflow 学习向:最小工作流,创建图、同步跑通、按 run_id 查事件

上一篇:对照 Dify 搭学习仓:先让后端能跑起来

示例仓库:flow-forge
本篇讲:在已能探活、能连库的后端上,第一次把Workflow跑通——用一份图描述步骤、触发一次运行、事后按run_id回看逐步事件。


先说结论

上一篇只证明「壳子活着」。本篇补的是工作流产品里最短的一条可演示路径:

能力你能感知到什么
持久化图定义POST /workflows交一份「步骤图」,之后能按 id 读回来
同步执行POST .../runs发一次请求,服务在同一次请求里跑完 Start → Template → End
运行可回看拿回一个run_id,再查终态和逐步事件,不必开推送长连接

一句话:本篇证明「一张最小图能被创建、跑完、事后复盘」;不做画布、LLM 节点,也不做 SSE/队列。

跟跑时你主要和两种「文字格式」打交道,都不是 Python 专属:

格式像什么本篇哪里会用到
JSON花括号包起来的键值对,前后端传数据的常用包装创建图、启动运行时的请求体;接口返回
HTTP对某个网址发 GET/POST,拿回状态码和正文curl跟跑全程

Python 只是本仓用来实现这些能力的语言;你先会调接口,再回头看源码,进度更顺。


1. 上一篇停在哪,本篇补哪块?

壳子阶段有探活、分层空位、SQLite 连通,但还没有:

  • 什么叫「一份工作流定义」
  • 怎么触发「跑一次」
  • 跑的过程如何留下可查询记录

对照 Dify 一类产品,用户真正关心的正是这三块。本仓用 OpenSpec changegraph-runner切出最小可跑切片:节点只留三种,执行在请求内做完,事件先写入数据库,给以后「反复查询进度」留句柄。

术语是什么本仓怎么用
图(graph)工作流定义:有哪些步骤、谁连谁一份 JSON,存进数据库
节点(node)图上的一步本阶段只有:开始、模板拼接、结束
边(edge)谁连到谁每条边写清「从哪来 / 到哪去」
Run某一次执行有稳定编号run_id;结果是成功或失败
Event这一次运行里的逐步记录例如某节点开始了、成功了、失败了
Runner(执行器)负责按图把节点跑完的程序入口今天在 HTTP 请求里直接跑;以后可改成排队,事件结构尽量不动

2. 功能一:用图描述「开始 → 模板 → 结束」

2.1 图长什么样(先看 JSON,不看 Python)

一份合法图至少有两块:

字段含义
nodes步骤列表;每个步骤有自己的id,类型写在data.type
edges连线列表;用source/target指向节点的id

三种节点:

data.type干什么
start入口;你这次运行提交的输入从这里进入
template把输入填进一句模板(例如Hello, {name}!),得到一段文字
end收尾,把最终文字当作本次运行的输出

非法图(未知节点类型、边指到不存在的节点、两个节点共用同一个 id 等)在创建时就会被拒绝,不会变成「跑到一半才炸」。

最小示例(逻辑上就是:开始 → 拼一句 Hello → 结束):

{"nodes":[{"id":"start_1","data":{"type":"start"}},{"id":"tpl_1","data":{"type":"template","template":"Hello, {name}!"}},{"id":"end_1","data":{"type":"end"}}],"edges":[{"id":"e1","source":"start_1","target":"tpl_1"},{"id":"e2","source":"tpl_1","target":"end_1"}]}

{name}不是神秘语法:它表示「这里以后要换成名叫name的输入」。你启动运行时若传"name": "Forge",拼出来就是Hello, Forge!

2.2 接口怎么用

动作方法与路径你交出什么你拿到什么
创建工作流POST /workflows{ "graph": <上面那份图> }工作流id
读取工作流GET /workflows/<id>无 body同一份图

2.3 仓库里校验在干什么(可选读)

实现上,校验写在graph.py。你暂时只需知道它在做三件事:

  1. 检查每个节点有没有合法的type
  2. 检查节点id是否重复
  3. 检查每条边的两端是不是都指向已有节点

顺带认一个 Python 词:源码里常见的class ...可以先当成「一份带规则的表格模板」——声明图里允许出现哪些字段、不合法时抛出错误。不必先背class语法;看到「校验图」就定位到这个文件即可。


3. 功能二:触发一次运行,同请求内跑完整张图

创建只是「把菜谱存起来」。真正干活是启动一次Run

动作方法与路径请求体示例
启动运行POST /workflows/<workflow_id>/runs{ "inputs": { "name": "Forge" } }

inputs就是一张「名字 → 值」的对照表。传给模板里的{name}用。

服务端按固定顺序做事(你无需读代码也能跟):

  1. 新建一条 Run,状态进入「执行中」
  2. 从唯一的start出发,沿边走到下一个节点(本阶段不支持同时走两条岔路)
  3. 遇到template:用inputs(以及上游写出的变量)填模板;缺了需要的名字 → 这次 Run 记为失败,但服务进程继续活着
  4. 遇到end:收齐最终输出
  5. 这次 HTTP 请求返回时,运行已经结束;正文里带id(即run_id)、status,成功时还有outputs

成功时outputs大致是:

{"text":"Hello, Forge!"}

执行器在仓库里怎么走(白话版)

对应文件:runner.py。逻辑可以记成一张流程图:

拿到图和 inputs → 记下「当前节点 = start」 → 循环: 写事件:节点开始 按类型做事(start 几乎空转 / template 填空 / end 收输出) 成功则写「节点成功」;失败则写「节点失败」并结束本次 Run 沿唯一一条出边走到下一节点;没有出边就停 → 把 Run 标成 succeeded 或 failed,写回数据库

顺带认两个 Python 词(仍不必会写):

你在源码里可能瞥到先怎么理解
dict/{ "name": "Forge" }「键值对照表」,和 JSON 对象很像;运行时的变量就放在这类结构里
while ...「条件还成立就重复做」;这里用来沿着边一节点一节点往下走

今天 HTTP 层直接调用执行器;以后若改成「先入队、后台再跑」,优先换的是谁去调用,而不是推倒事件该怎么记——这是本切片故意留下的升级缝。


4. 功能三:用run_id查终态与逐步事件

启动接口返回时你往往已经看到结果了,但产品约定仍是:run_id为稳定编号,以后查询都认它。

接口用途
GET /runs/<run_id>看终态:成功还是失败、最终输出、错误信息
GET /runs/<run_id>/events看逐步事件列表(按顺序):每个节点何时开始、成败如何

为什么要先落库、而不是「必须开一条推送流才能看见过程」?
因为先有可查询的事件记录,以后无论是「隔几秒再问一次」(轮询)还是推送,读的都是同一套数据。

数据库里对应三张表(名字即职责):

表名存什么
workflows图定义(菜谱)
workflow_runs某一次运行的输入、输出、状态
workflow_run_events该次运行的逐步事件(带序号sequence

表结构入口:models.py——可以先当「三张表的说明书」,不必先学 ORM。


5. 跟跑:create → run → events

  1. 按上一篇在api/启动服务。
  2. api/README.md的 curl 示例走三步(Windows / macOS 续行符不同,README 里有说明):
做什么你要记下来的
1POST /workflows提交最小图返回的工作流id
2POST /workflows/<id>/runs,带上inputs返回的 runid,以及outputs
3GET /runs/<run_id>.../events终态与逐步事件是否对得上

想确认仓库自测也绿:

cdapi uv run pytest

pytest是自动跑测试的工具:它替你扮演客户端,把「创建 → 运行 → 查事件」走一遍。你暂时只需知道「全绿 ≈ 这条主路径没坏」。


6. 分层空位怎么被填上?

上一篇留下的职责地图,本篇开始有实活:

本篇长出的内容若你想点开文件
controllers对外的 HTTP 门口:创建图、启动 run、查询workflows.pyruns.py
services创建/读取时的编排(先校验再存盘)workflow_service.py
core/workflow图校验规则、真正按边执行、写事件graph.pyrunner.py
models / db三张表;启动时建好models.py

读仓库的建议顺序(仍然可以几乎不读语法):

  1. 先会用 curl 打通三步(本节第 5 节)
  2. 再打开controllers,对照「哪个网址对应哪段门口代码」
  3. 最后才进core/workflow,对照「填模板 / 写事件」发生在哪

不要在路由文件里找「模板字符串怎么替换」——那是core的事。


本篇顺带认识的 Python(一张表就够)

先怎么记和本篇功能的关系
.py文件Python 源码文件业务都在api/src/flow_forge/
class「一类带行为的数据结构」的声明图校验、执行器都是 class
dict键值对照表inputs、运行中的变量
while条件成立就重复执行器沿着边往下走
抛错 /raise主动报告「这里不行了」非法图、缺变量时失败并记入 Run

系列后续仍按「功能先、语法附注」写:每出现绕不开的词,就地用一句话钉住,不单独开语法长课。


和前作怎么接

补哪一段
01 后端壳子依赖、探活、分层空位、库能连
本篇最小图、同步执行、run/event 可查询

下一篇更可能落在「谁来消费这些 API」(例如最小 Web 联调),而不是先把节点类型堆满。


你可以从这里带走什么?

  1. 工作流最短演示路径是:存图 → 跑一次 → 用 run_id 回看,不是先做画布。
  2. 是定义,Run是某一次执行,Event是那一次里的时间线;三者分开,以后才好做轮询或异步。
  3. 跟跑优先认JSON + HTTP;Python 是实现语言,可以后看。
  4. Template 本阶段只做「填空成句」,不执行任意代码。
  5. 读源码按门口 → 编排 → 核心规则的顺序,比从上到下背语法更快建立地图。

仓库与相关文档

  • GitHub:https://github.com/jimchou-h/flow-forge
  • 跟跑与 curl:api/README.md
  • 图校验:core/workflow/graph.py
  • 执行器:core/workflow/runner.py
  • HTTP(工作流):controllers/workflows.py
  • HTTP(运行):controllers/runs.py

欢迎 Star、Issue 和 PR。


本文基于 Flow Forgegraph-runner:覆盖最小图定义、同步执行与按 run_id 查询事件;假定读者不必先会 Python。不包含画布、LLM 节点与 SSE/队列。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 4:13:58

小宇宙播客App竞品分析:垂直社区如何通过产品设计构建护城河

1. 项目概述&#xff1a;一次播客产品经理的深度“体检”最近和几个做内容产品的朋友聊天&#xff0c;话题总绕不开“播客”这个赛道。大家普遍的感觉是&#xff0c;这阵风刮了好几年&#xff0c;但真正能让人“用起来”且“留下来”的独立播客应用&#xff0c;掰着手指头数&am…

作者头像 李华
网站建设 2026/8/22 4:10:29

C++11右值引用与移动语义:从原理到实战的性能优化指南

1. 从拷贝到移动&#xff1a;C11性能革命的基石干了这么多年C&#xff0c;从C98/03一路走到C17/20&#xff0c;要说哪个特性对日常编码性能和代码简洁度的提升最立竿见影&#xff0c;我首推C11引入的右值引用和移动语义。这玩意儿刚出来的时候&#xff0c;很多老C程序员&#x…

作者头像 李华
网站建设 2026/8/22 4:10:06

SAP ABAP查表数据接口设计:从RFC到OData的实战指南

1. 项目概述&#xff1a;从“查表”到“接口”的ABAP实战在SAP ABAP开发领域&#xff0c;“查表数据接口”这个标题听起来简单&#xff0c;甚至有些基础&#xff0c;但它背后所涵盖的技术深度和业务广度&#xff0c;却远超字面意思。这不仅仅是写一段SELECT * FROM MARA的代码&…

作者头像 李华
网站建设 2026/8/22 4:09:31

LLM智能体开发入门:从Context、Tool到Agent Loop的核心概念解析

1. 从“聊天机器人”到“智能体”&#xff1a;为什么我们需要重新理解AI&#xff1f;如果你最近关注AI领域&#xff0c;大概率已经被“智能体”这个词刷屏了。从能自动写代码的Devin&#xff0c;到能帮你规划旅行的AI助手&#xff0c;再到企业内部的各种自动化流程&#xff0c;…

作者头像 李华