ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FastGPT 工作流变量统一管理:WorkflowVariableState 的存储态与运行态分离设计解析

FastGPT 工作流变量统一管理:WorkflowVariableState 的存储态与运行态分离设计解析 FastGPT 工作流变量统一管理WorkflowVariableState 的存储态与运行态分离设计解析【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT本篇文章围绕 FastGPT 开源仓库中.agents/design/core/workflow/file-variable-runtime-store-split.md的设计方案结合packages/service/core/workflow/dispatch/utils/variables.ts的落地实现深入讲解工作流全局变量尤其是文件变量如何在可持久化的存储态与节点可直接消费的运行态之间安全转换以及如何用一个统一入口WorkflowVariableState替代原来散落在调度链路上的variables storeVariables fileMetaMap三套数据。引言为什么要把文件变量的运行形态和存储形态分开FastGPT 工作流中的全局变量存在两种天然形态节点运行时需要的是字符串数组string[]也就是一组可以直接访问的文件 URL而存储和回显时需要的是稳定对象例如{ key, name, type }或{ url, name, type }。前者是运行缓存后者是唯一可持久化的事实源。在旧设计中variables、storeVariables、fileMetaMap三套数据会一起在调用链上传递child/parent 应用之间、变量更新节点、最终保存之间需要反复同步。尤其当 file 变量从 store object 转成 runtime URL、再在保存时反推 object极易出现三类问题name/type元数据丢失落库后文件类型无法还原临时签发的 preview URL 被错误写入数据库data:协议的 base64 数据被混入数据库既膨胀体积又不可追踪。本方案的最终答案是工作流内部只保留一个全局变量管理入口由WorkflowVariableState统一负责变量读取、写入、运行态转换和存储态输出。variables.ts这个文件名并不代表只处理userId/appId/chatId等系统字段它管理的是整个工作流的variables。一、方案目标与边界目标清单runWorkflow内部只传variableState不再传variables、storeVariables、fileMetaMap变量读取统一返回 runtime value节点仍然按现有方式消费 file URL变量更新统一写入 store value并同步刷新 runtime value工作流最终返回和变量更新 SSE 都只输出 store recordfile/password 特殊逻辑集中在utils/variables.ts前端FileSelector对外只输出可存储字段不输出内部渲染字段。非目标刻意不做的事不修改 MongoDB schema不做历史数据迁移不改变普通 ChatBox 消息文件的历史结构不要求所有 workflow 节点都改成直接消费文件对象运行时仍然消费 URL。这套边界保证了改动聚焦在调度层与保存层不波及数据模型与存量数据风险可控。二、核心类型设计2.1 文件存储值ChatFileStoreValuetype ChatFileStoreValue | { key: string; name: string; type: ChatFileTypeEnum; } | { url: string; name: string; type: ChatFileTypeEnum; };落库规则与仓库中 fileStoreValue.ts 的normalizeChatFileStoreValue实现一一对应key和url二选一key文件不保存 previewurl——preview 是临时签发的只用于前端预览不回写数据库url文件保存外链原始 URLname/type必须进入数据库id/rawFile/icon/status/process/error等前端渲染字段不进入数据库data:URL 不允许落库normalizeChatFileStoreValue 中会显式丢弃以data:开头的 url未知外部 URL 的type推断失败时兜底为ChatFileTypeEnum.file。值得留意的是key与url并存时的优先级normalizeChatFileStoreValue中如果同时存在 key 和 url优先保存 key因为key能保持私有桶权限、TTL 和回收语义这是 FastGPT 私有文件安全的根基。2.2 文件运行值FileRuntimeValuetype FileRuntimeValue string[];{ key, name, type }在运行前通过 fileStoreValuesToRuntimeUrls 签发 preview URL调用createChatFilePreviewUrlGetter(){ url, name, type }在运行时直接使用url运行时 URL 到 store file 的映射保存在WorkflowVariableState内部即构造函数中的fileMetaMap: Mapstring, ChatFileStoreValue。2.3 变量状态项WorkflowVariableStateItemtype WorkflowVariableStateItem { key: string; config?: VariableItemType; storeValue: unknown; runtimeValue: unknown; runtimeOnly?: boolean; };storeValue是唯一可持久化事实源runtimeValue是运行缓存runtimeOnly变量可读取但不会进入toStoreRecord()。三、WorkflowVariableState统一管理入口的设计3.1 类结构仓库 variables.ts 中的实现与设计文档完全一致class WorkflowVariableState { static create(props: WorkflowVariableStateCreateProps): PromiseWorkflowVariableState; get(key: string): unknown; set(key: string, value: unknown): Promiseunknown; getStoreValue(key: string): unknown; toRuntimeRecord(): Recordstring, unknown; toStoreRecord(): Recordstring, unknown; }构造函数是private的外部必须通过create()初始化——这样可以保证 file 预签名、password 解密、系统变量注入都在创建阶段完成。除了上述方法实际实现还多了两个能力getFileStoreValueByRuntimeUrl(url)根据运行时 URL 找回文件 store metadata并且会向sourceVariableState递归查询这是 child 应用恢复 parent 文件元数据的关键clone()克隆当前状态用于子运行/分支运行时隔离运行态修改。3.2 创建参数type WorkflowVariableStateCreateProps { timezone: string; runningAppInfo: ChatDispatchProps[runningAppInfo]; uid: ChatDispatchProps[uid]; chatId: ChatDispatchProps[chatId]; responseChatItemId?: ChatDispatchProps[responseChatItemId]; histories?: ChatDispatchProps[histories]; variablesConfig?: VariableItemType[]; inputVariables?: Recordstring, unknown; externalVariables?: Recordstring, unknown; runtimeOnlyVariables?: Recordstring, unknown; sourceVariableState?: WorkflowVariableStateLike; };create()在创建阶段依次完成以下工作见 variables.ts#L169-L223根据变量配置读取inputVariables和默认值——读取时兼容 API 的label入参与前端的key入参getVariableInputValue初始化 file/password/普通变量的 store/runtime 值根据timezone/runningAppInfo/uid/chatId/responseChatItemId/histories内部生成系统 runtime-only 变量例如userId/appId/chatId/histories/cTime其中appId仅在runningAppInfo.sourceType app时注入注入externalVariables作为 runtime-only 变量但会跳过那些配置为internal类型的 key允许 parent 输入覆盖内部变量默认值注入额外runtimeOnlyVariables用于少量调用点补充特殊运行时变量维护 runtime URL 到ChatFileStoreValue的内部映射提供 child app 从 parent runtime URL 恢复 store file 的能力。3.3 变量优先级变量配置默认值 inputVariables externalVariables runtimeOnlyVariablesexternalVariables和runtimeOnlyVariables不进入toStoreRecord()。这一优先级在 variables.test.ts 中有直接覆盖测试should allow parent input to override internal variable defaults验证 parent 输入可覆盖 internal 默认值而should let the external provider override an input external dynamic variable验证外部 provider 可覆盖 input 变量且最终不出现在toStoreRecord()中。四、转换规则file / password / 普通变量4.1 file 变量的双向转换初始化或更新 store object存储态 → 运行态ChatFileStoreValue[] - normalize - storeValue - sign/return url - runtimeValue实现即initConfiguredVariable的 file 分支与fileStoreValuesToRuntimeUrlsstore 数组经 normalize 后写入storeValue再为每个{ key }文件签发 preview URL、为{ url }文件直接复用 URL同时把url - file写进内部fileMetaMap。更新 runtime URL运行态 → 存储态string[] - current metadata - source metadata - infer external url - storeValue对应 variables.ts#L255-L282 中set()的 file 分支恢复优先级为优先从当前 state 的fileMetaMap恢复{ key, name, type }当前 state 没有时从sourceVariableState递归恢复child 恢复 parent 文件的关键路径都没有时按未知外链保存{ url, name, type }调用normalizeChatFileStoreValue({ url })推断 name/typedata:URL 过滤非数组 file 更新值直接抛错File variable value must be an array且仅接受绝对 HTTP(S) URLisAbsoluteHttpUrl校验否则抛File variable updates only accept absolute HTTP(S) URLs。此外file 更新还会按config.maxFiles截断数量maxFiles由请求级文件上下文getWorkflowFileContext与变量配置共同决定默认上限为 5DEFAULT_VARIABLE_FILE_INPUT_MAX_FILES。4.2 password 变量的加解密初始化storeValue: { value: , secret } - runtimeValue: plain string更新plain string - storeValue: { value: , secret } runtimeValue: plain string实现上初始化时用anyValueDecrypt(value)解出明文作为 runtimeValue再用encryptSecret重新加密写入 storeValue即使传入的本来就是加密对象也能保证不二次加密——测试should not double encrypt password when initialized from stored JSON string专门覆盖了这一场景更新时set()对 string 类型做同样的存密文、读明文处理。数据库中永远看不到明文节点运行中永远能读到明文。4.3 普通变量普通变量按valueType格式化valueTypeFormat且storeValue runtimeValue即普通变量不区分双态读写一致格式化逻辑在 runtime/utils.ts 的valueTypeFormat中。五、工作流链路改造从三套数据到单一 variableState5.1 root workflow 的创建dispatchWorkFlow创建 rootWorkflowVariableStateconst variableState await WorkflowVariableState.create({ timezone, runningAppInfo, uid: data.uid, chatId, responseChatItemId: data.responseChatItemId, histories, variablesConfig: data.chatConfig?.variables, inputVariables: data.variables, externalVariables: externalProvider.externalWorkflowVariables });runWorkflow只接收variableState一个参数。ChatDispatchProps与RunWorkflowProps中原来的variables、storeVariables、fileMetaMap字段全部移除改为统一的variableState: WorkflowVariableStateLike类型定义见 runtime.ts。5.2 变量读取统一走variableState.get(key)全工作流的全局变量读取只保留一个入口各读取点只接收 runtime variables record 而不直接依赖完整的WorkflowVariableStategetReferenceVariableValue、replaceEditorVariable只接收 runtime variables recordWorkflowQueue节点参数解析见 runtime.ts (utils) 的getWorkflowNodeRunParams它在每个节点执行前按需构造runtimeVariables variableState.toRuntimeRecord()惰性求值无变量节点不复制整张变量表runIfElse条件判断前const runtimeVariables variableState.toRuntimeRecord()然后交给getReferenceVariableValue解析左右值见 runIfElse.ts#L112-L133http468HTTP 变量替换使用 runtime variables recordloopRun/service.ts循环变量引用使用variableState.toRuntimeRecord()其他所有工作流运行路径上的variables[key]全部清理。5.3 变量更新统一走await variableState.set(varKey, value)runUpdateVarrunUpdateVar.ts不再手动维护variables[varKey] value; storeVariables[varKey] value; fileMetaMap.set(url, file);而是区分两种目标全局变量varNodeId VARIABLE_NODE_ID读旧值用variableState.get(varKey)写新值用await variableState.set(varKey, value)非全局变量仍然写对应 node 的 output。值得注意的实现细节runUpdateVar中的 number operator - * /除零保护、boolean modetrue/false/negate、array 的append/clear等运算逻辑被完整保留只是把读写层替换为variableState变量更新节点执行完成后若当前不是 child app还会通过workflowStreamResponse?.(workflowSseEvent.updateVariables(variableState.toStoreRecord()))推送 SSE——注意这里推送的是store record前端拿到的永远是可持久化的形态。5.4 child / plugin / agent 子应用child workflow 创建自己的WorkflowVariableState并通过sourceVariableState关联 parentconst childVariableState await WorkflowVariableState.create({ timezone: props.timezone, runningAppInfo: childRunningAppInfo, uid: props.uid, chatId: props.chatId, responseChatItemId: props.responseChatItemId, histories: childHistories, variablesConfig: childChatConfig.variables, inputVariables: childInputVariables, externalVariables, sourceVariableState: parentVariableState });如果 parent 传给 child 的 file 输入是 runtime URLchild 通过sourceVariableState.getFileStoreValueByRuntimeUrl(url)递归恢复原始 store file从而避免把临时 preview URL 误存为外链。四个子应用创建点分别是child/runApp.tschild workflowabandoned/runApp.ts废弃 runApp 节点仍需创建独立 child state不能复用 parent state保证运行态隔离plugin/run.ts工具工作流ai/agent/sub/app/index.tsagent 子应用。同时 child 返回值不会混入 parent 的 runtime record——因为 parent 与 child 各自持有独立的Map状态。5.5 工作流返回与 SSE工作流返回统一使用variableState.toStoreRecord()变量更新 SSE 同样使用variableState.toStoreRecord()。DispatchNodeResultType内部不再透传newVariables避免nodeResponse或子运行结果里携带一份过期变量快照——变量更新后的事实源始终是同一个variableState从根上消除了多份副本不同步的问题。六、前端与回显FileSelector 的输出协议6.1 内部渲染字段与对外值的分离FileSelectorprojects/app/src/components/core/app/FileSelector/index.tsx内部可以保留渲染字段id rawFile icon status process error url // key 文件的临时 preview url这些字段用于上传进度展示、图标渲染、图片预览等 UI 逻辑但onChange对外只输出{ key, name, type } // 或 { url, name, type }即全局变量、PluginRunBox、工具输入里的fileSelect使用同一套输出协议杜绝id/rawFile/icon/status/process/error、临时 preview URL、base64 混入存储层。6.2 DB 读取返回前端时补 preview url数据库中的{ key, name, type }在响应阶段由服务端补一个临时的 previewurl见 chat/utils.ts 中的formatFileValueList逻辑if (!file.key) return { ...file }有 key 的文件调用getPreviewUrl(file.key, file.name)补 url{ url, name, type }则原样返回。补出来的 previewurl只用于前端预览不回写数据库。这样刷新历史对话后图片图标也能正常渲染且不会污染存储数据。七、改动文件全景设计文档第 9 节给出了完整的改动清单落地后仓库现状与之对应文件修改点variables.ts新增WorkflowVariableState统一管理工作流全局变量内聚 file normalize、store/runtime 转换、URL 推断工具dispatch/utils/index.ts删除getWorkflowVariableState和旧变量转换逻辑runtime.tsglobal 类型增加WorkflowVariableStateLike移除 dispatch props 中的旧变量字段runtime.tsglobal utilsgetReferenceVariableValue、replaceEditorVariable改为通过 runtime variables record 读取dispatch/index.tsroot 初始化WorkflowVariableStaterunWorkflow入参和返回改造dispatch/type.tsRunWorkflowProps改为接收variableStaterunUpdateVar.ts全局变量更新改成variableState.get/setrunIfElse.ts条件变量读取使用variableState.toRuntimeRecord()http468.tsHTTP 变量替换使用 runtime variables recordloopRun/service.ts循环变量引用使用variableState.toRuntimeRecord()child/runApp.tschild workflow 创建自己的WorkflowVariableStateabandoned/runApp.ts废弃 runApp 节点创建独立 child stateplugin/run.ts工具工作流创建自己的WorkflowVariableStateai/agent/sub/app/index.tsagent 子应用创建自己的WorkflowVariableStateFileSelector对外值只输出 store valuechat/utils.ts返回前端时给{ key }文件补 previewurlfileStoreValue.ts统一文件存储值清洗复用到工作流变量和交互表单保存saveChat.tsTTL 续期从 store file object 提取 key交互表单fileSelect保存前清洗为 store value此外清理阶段还移除了runtimeSystemVar2StoreType、formatStoreVariables、getWorkflowVariableState、getStoreVariableValue、updateFileVariableValue等冗余函数重复逻辑统一收敛到utils/variables.ts。八、测试计划与验证8.1 单元测试覆盖测试文件覆盖点variables.test.ts初始化普通变量、password、file、runtime-only、external runtime-onlyfilekey - runtime URL、runtime URL 还原 key、未知外链推断、base64 过滤、非数组更新抛错通过sourceVariableState恢复 parent fileruntime/utils.test.tsgetReferenceVariableValue、replaceEditorVariable通过 runtime variables record 获取全局变量runUpdateVar.test.ts变量更新节点只写variableState.set()file/password/数组操作结果正确FileSelector/utils.test.tsFileSelector 清洗输出不含内部渲染字段chat/utils.test.tsDB file{ key }返回前端时补 preview urlsaveChat.test.tsTTL 从 store file object 提取 key交互表单fileSelect保存前过滤多余字段和data:URL8.2 回归命令cd packages/service pnpm test test/core/workflow/dispatch/variables.test.ts cd packages/service pnpm test test/core/workflow/dispatch/tools/runUpdateVar.test.ts cd packages/service pnpm test test/core/workflow/dispatch/loopRun/runLoopRun.test.ts cd projects/app pnpm test test/components/core/app/FileSelector/utils.test.ts设计文档中的本轮验证记录显示packages/global、packages/service、projects/app下的相关测试均已通过且packages/service的tsc --noEmit -p tsconfig.json已清除本次变量改造相关的类型错误并确认旧函数runtimeSystemVar2StoreType、formatStoreVariables、getWorkflowVariableState、getStoreVariableValue、updateFileVariableValue已从仓库中彻底移除。总结WorkflowVariableState是 FastGPT 工作流全局变量的一次收敛式重构从三套并行数据variables、storeVariables、fileMetaMap收敛为一个带内部状态机的单一对象用storeValue守住持久化事实源、用runtimeValue服务节点运行、用runtimeOnly隔离系统与外部注入变量并把 file/password 的所有特殊转换逻辑内聚到 utils/variables.ts。对上层而言节点读取统一get()、更新统一set()、返回统一toStoreRecord()对存储而言{ key, name, type }与{ url, name, type }二选一、data:URL 与临时 preview URL 永不落库从根本上解决了文件变量同步错位、元数据丢失与脏数据入库的三大顽疾。对于想要深入理解 FastGPT 工作流调度内核或打算在其上扩展自定义变量类型的开发者这份设计文档与 variables.test.ts 中的测试用例是绝佳的阅读起点。【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表