本文记录我在 Picuro一个 AI 图像处理产品中搭建 AI 工作流编排层的设计思路为什么需要编排、核心概念怎么分层、一次修图任务如何端到端跑通以及在可靠性和演进上踩过哪些坑。一、为什么需要编排Picuro 表面上只是一个上传图片、输入需求、拿到结果的产品但背后的 AI 场景其实不少智能修图、图片生成、多图融合、风格转换、卡通创作、AI 选片。每一个场景都不是一次模型调用能完成的而是由理解需求 → 分析原图 → 生成/编辑 → 质检 → 回写结果这样一串步骤组成。早期实现里这些步骤是散落在业务 Service 代码里的修图逻辑靠一串方法调用和if状态判断拼起来哪个场景走哪几步、用哪个模型、Prompt 是什么全都硬编码在 Go 代码里。随着场景变多问题开始集中暴露业务与模型耦合业务 Service 直接认识具体的 Provider 客户端和模型参数换一个模型要改业务代码。流程隐含在代码里没法直观地查看、发布、追踪一个场景到底由哪些步骤组成。接口语义不统一文本流式、视觉分析、同步图片、异步图片任务各自的输入输出和错误格式都不一样。日志链路不完整只能看到单次模型调用日志无法从一次业务任务展开到全部节点、模型、成本和结果。路由逻辑固化场景与能力的匹配靠switch写死无法平滑配置主模型、降级模型和参数覆盖。进度体验有限用户端只能看到任务级的stage/progress分析阶段多为同步等待节点进度和可恢复性都不足。一句话概括当一个场景 一个模型调用变成一个场景 一条有依赖关系的处理流水线时就需要一个专门的编排层来承接复杂度。二、先划清边界业务是业务编排是编排设计编排层最容易犯的错误是把它做成又一个业务系统——既管 AI 流程又顺手去改工程状态、扣积分、决定任务是否完成。这样只会让耦合更深。所以第一步是明确一个铁律AI 编排层不直接修改任何业务表业务层通过应用服务与它交互依赖方向严格单向。Picuro 前端Photo Studio APIPhotoStudioServicephoto_studio_*业务数据Studio Execution CoordinatorAI 编排层Workflow / Skill / Capabilityai_execution*两侧各司其职业务层photo_studio_*工程、素材、对话、方案、执行批次、处理项和结果用户归属、权限、额度、幂等和业务状态机负责把业务对象转换成 AI 输入再把 AI 输出转换成业务事实。AI 编排层ai_*根据入口解析启用的 Workflow校验输入按依赖关系执行调用 Skill/Capability/Model/Provider处理重试、降级、超时和输出校验保存执行快照、节点状态和最终输出。AI 层不负责改工程标题、创建业务结果、扣退款用户额度、决定工程是否完成、覆盖用户手动编辑的内容。这些只能由业务层在消费 AI 输出后自己完成。这个边界带来两个直接好处一是业务与模型解耦二是让编排层可以独立演进、独立测试。三、一层一层的抽象编排层的核心是把一次 AI 任务拆成一组职责清晰、各归其位的概念。它们的关系如下Agent 业务入口Workflow 运行定义Node 编排位置Skill 可执行配置Capability 稳定契约Prompt 配置Implementation RouterCapability ImplementationModelProvider 连接与认证注册的代码实现每一层回答一个不同的问题概念回答的问题是否可复用示例Agent业务是谁、以什么身份进入 AI否photo_editorWorkflow一条任务由哪些步骤、什么依赖组成是智能修图工作流Node某个步骤在流程中的位置、依赖谁、数据怎么映射否analyze_source_imageSkill用哪套能力配置、Prompt、实现路由和默认参数是photo_image_analyzerCapability输入输出的稳定协议是什么是image.analyzeCapability Implementation哪套代码把标准协议适配到具体 Provider API是jimeng_seedream_46_editModelProvider 下可调用的具体模型或请求键是qwen-vl-maxProvider服务商账号与连接配置是阿里云百炼、火山即梦有两个容易混淆的点值得单独说清楚1. Node 与 Skill 不重复。Node 只负责编排——在哪个位置、依赖谁、输入输出如何映射它自己不实现任何模型调用只引用一个skill_id。Skill 才负责执行配置——绑定 Capability、Prompt、实现候选策略和默认参数。这样同一个image.analyzeSkill 可以被修图、选片、商品图多个 Workflow 复用而不同 Node 可以给它不同的输入映射和超时。2. Capability 与 Implementation 要同时存在。Capability 是平台内部的标准契约JSON Schema、同步/异步模式、标准错误业务和 Workflow 只认识它Implementation 是第三方接口的适配层一份接口文档对应一套代码实现负责把标准输入翻译成 Provider 请求再把 Provider 响应翻译回标准输出。这样换模型、换服务商只动 Implementation 这一层Workflow 和业务代码完全无感。一个能落地的纪律别再增加 Tool、Action、Component 这类同义概念。概念每多一层理解和维护成本就翻一倍。四、Workflow 是怎么执行的Workflow 是可发布、可复用的执行定义。我们刻意把它做成了一个受约束的 DAG而不是图灵完备的脚本引擎——不做循环、不做动态建节点、不做任意脚本只保留图像处理真正需要的那些能力节点依赖依赖必须存在禁止自依赖和环执行前先做环检测。拓扑分层并发同一层无依赖的节点并发执行受max_concurrency限制。输入/输出映射用简化的$.a.b路径把 Context 里的值喂给节点再把节点输出写回 Context。条件分支支持、!、不满足时节点标记为skipped。修图首版只需要需求不明确时提前结束这一个分支。超时与重试Workflow 有总超时每个节点有独立超时、最大尝试次数和退避时间。失败策略on_failurefail终止执行on_failureskip继续往下走。一个 Skill 节点被真正执行时顺序是这样的校验 Capability 的 Input Schema合并 Skill 默认参数、节点参数和运行参数渲染 Prompt并合并 Prompt 里的模型参数按实现策略的候选列表加载启用的 Implementation过滤掉能力不匹配、或超出模型限制的实现调用代码里注册的 Provider Adapter校验 Capability 的 Output Schema 和 Skill 的输出校验若错误码落在fallback_on里则切换到下一个候选实现继续尝试。这套先校验、再路由、再降级的顺序把用什么模型、失败换谁从硬编码变成了可配置的策略。五、一次智能修图是怎么跑完的把上面这些概念串起来一次智能修图的完整链路大致是这样事件存储AI 编排层业务层用户端事件存储AI 编排层业务层用户端创建工程需求 素材ProjectNo, analyzing后台 SubmitAgentExecutionExecutionNo, queued记录 current_execution_no建立 SSE(cursor)ListExecutionEvents(sequence)补发历史事件SubscribeExecutionEvents(afterSequence)节点变化时写入 ai_event发布新增通知AgentEventSSE 事件消费统一输出更新业务结果几个关键设计点提交是同步返回、异步执行。SubmitAgentExecution立即返回execution_no状态通常是queuedAI 路由和执行在后台进行。业务层拿到执行编号后就把它关联到自己的工程记录上然后通过事件流等待结果而不是傻等 HTTP 响应。Agent 是稳定入口路由由配置驱动。业务层用namespace code定位 AgentAgent 再根据execution_role在候选 Workflow 里做路由——只有一个候选就直接选多个候选就调用 Planner Skill 来决定。选中的 Workflow、选中的原图/历史结果都会写进执行快照供后续重试、审计和解释使用。事件落库是事实来源。每个节点状态变化都会持久化成一条ai_event进程内 Channel / Redis Pub/Sub 只负责唤醒订阅者。SSE 用递增的Sequence作为id客户端断线重连时带上Last-Event-ID服务端先补发数据库里漏掉的历史事件再订阅新事件。这样用户刷新页面或断网重连都不会丢进度。回写是业务层的事。AI 完成后业务层消费统一输出协议把结果落成photo_studio_result再更新 Plan/Run/Project 的状态最后追加工程事件通知前端。AI 层从头到尾没有碰过任何业务表。六、可靠性的几个抓手异步长任务最怕跑了半天结果丢了或者点重试结果重复扣费。这里有几个针对性的设计幂等唯一键是agent_id idempotency_key。同一次业务动作重复提交直接返回原来的 Execution不会重复调度。快照Execution 保存 Agent、Workflow、Node、Skill、Prompt 和路由策略的完整快照。配置发布后修改不影响已经在跑的任务——历史执行永远可解释、可重放。状态机Execution 走queued → running → succeeded/failed/canceled重试会新建一条 Execution 并记录parent_execution_id。断点恢复服务启动时以及每 5 秒扫描恢复queued/running的执行succeeded/skipped的节点直接从数据库恢复不重复调用 Provider异步 Provider 的waiting_provider节点根据 Implementation ID 和 Provider Job ID 继续轮询。错误分类与降级限流、超时、Provider 不可用、输出无效这类错误自动重试invalid_input、content_policy、canceled永不重试也不降级避免拿错误输入反复烧钱。连续修图与增量执行用户继续、再、基于刚才的增量修改通过变更分类和依赖失效计算尽量复用上一轮的图片分析、需求理解和方案只重跑真正受影响的部分。七、当前边界与下一步这套设计已经落地但它有意保持克制也留下了一些明确的没做一次 Execution 目前只选择一个 Workflow多 Workflow 的数据结构已就绪执行尚未接入尚不支持从指定节点重试只能整体重试取消能停掉本地 Executor但远端任务的真正取消还没完全打通SSE 目前用 500ms 数据库轮询、异步 Provider 用固定 5 秒轮询后续可换成更高效的通知机制JSON Schema 校验和 Condition 表达式都是轻量实现够用但不算完备。这些边界是刻意的。编排层第一阶段的目标不是做一个 Dify 式的低代码平台而是把业务与模型解耦、流程可配置、执行可追踪、失败可恢复这几件最实在的事做扎实。八、写在最后回头看做 AI 工作流编排最值钱的不是那些花哨的 DAG 或插件机制而是两件朴素的事把流程从代码里显式地拎出来以及把业务和模型之间画一条清晰的边界。前者让一个场景能看、能配、能追踪后者让模型、服务商的任何变化都停留在 Implementation 那一层不再向上污染业务。这套编排目前已经在 Picuro 里实际跑起来了。如果你想直观感受一个会编排、可追踪、可恢复的 AI 图像处理流程长什么样可以到 picuro.fengwenyi.com 上手试试——从上传一张图、写一句需求开始看它是如何一步步分析、生成、质检再把结果回写到你眼前的。