可视化编排 + AI Copilot + 私有知识库:Sim如何打造下一代AI智能体开发平台?

simstudioai/sim:一个用于快速构建和部署AI智能体工作流的平台

1. 整体介绍

概要说明

项目地址https://github.com/simstudioai/sim
项目简介:Sim 是一个基于有向无环图(DAG)的、声明式的低代码/无代码 AI 智能体工作流编排与执行引擎。它并非一个简单的"可视化工具",其核心是一个高性能、支持复杂流程控制(如循环、并行、暂停恢复)的运行时系统。代码展示了其核心执行引擎、DAG 构建器和智能体(Agent)处理器。

面临问题、场景与人群

  • 问题
    1. 编排复杂性:构建多步骤、具备条件判断、循环、并行及人工介入的 AI 应用逻辑复杂,传统代码编写工作量大,易出错。
    2. 状态管理困难:AI 工作流执行时间长、可能中断(如等待人工审批),需要可靠的持久化与恢复机制。
    3. 工具集成繁琐:需要将多种工具(API、函数、MCP 协议工具、自定义代码)统一封装,供 AI 智能体安全、便捷地调用。
    4. 开发效率瓶颈:从原型到生产部署周期长,需要兼顾可视化设计和底层执行性能。
  • 目标人群:AI 应用开发者、产品经理、业务分析师,以及需要在业务流程中集成 AI 能力的企业团队。
  • 核心场景:自动化客服、内容生成流水线、数据分析与报告、复杂的决策支持系统等需要串联多个 AI 步骤和人工审核的流程。

解决方法与演进

  • 传统方式:开发人员使用脚本(如 Python)硬编码流程逻辑,耦合度高,状态管理需自行实现,可视化与执行引擎分离。
  • Sim 新方式
    • 声明式建模:将工作流抽象为 DAG,节点(Block)声明其功能,边(Edge)声明依赖关系。执行引擎负责解析 DAG 并调度。
    • 可视化即代码 :画布操作直接生成可序列化的 DAG 描述(SerializedWorkflow),设计与执行使用同一套数据模型。
    • 内置复杂控制流 :通过 LoopConstructor, ParallelConstructor 等将高级语义(循环、并行)编译为底层 DAG 结构。
    • 执行快照与恢复ExecutionEngine 支持将运行时状态(如 pendingBlocks, remainingEdges)序列化,实现"暂停/恢复"。

商业价值预估

  • 代码成本估算:构建一个具备类似 DAG 执行引擎、状态恢复、多工具集成能力的系统,需要一个 5-8 人的资深前端与后端团队约 9-12 个月,粗略人力成本在数百万人民币级别。
  • 覆盖问题空间效益 :Sim 通过提供标准化平台,将上述成本分摊给大量用户。它解决了 AI 应用开发中的 "编排"、"集成"、"部署" 三大核心痛点,覆盖了从原型验证到生产部署的全链路。其商业价值在于显著降低企业引入 AI 自动化的技术门槛和时间成本,潜在市场涵盖金融、教育、电商、客服等多个行业。其开源+托管模式,有助于快速建立生态,获取早期用户与场景反馈。

2. 详细功能拆解(产品+技术视角)

  1. 可视化 DAG 编辑器(产品层)

    • 产品视角:用户通过拖拽 Block、连线来设计工作流,直观表达"先做什么,后做什么,在什么条件下分支"。
    • 技术支撑 :前端组件生成 SerializedWorkflow 数据。核心是后端的 DAGBuilder.build() 方法,它将用户友好的序列化数据,编译(Construction)为内部可执行的 DAG 图结构,处理了循环展开、并行路径识别等复杂转换。
  2. 高性能 DAG 执行引擎(技术层)

    • 产品视角:用户点击"运行",工作流自动执行,支持实时日志、中途暂停。
    • 技术支撑ExecutionEngine 类是核心。它管理一个就绪队列(readyQueue)和正在执行的 Promise 集合(executing)。其 run() 方法循环检查"是否有工作"(hasWork()),从队列中取出就绪节点,通过 NodeExecutionOrchestrator 异步执行,并根据边条件(EdgeManager)将下游节点加入队列,实现并行推进。
  3. 状态持久化与恢复(技术层)

    • 产品视角:工作流可以暂停(如等待人工审批),之后从断点继续执行,状态不丢失。
    • 技术支撑 :这是关键技术。ExecutionEngineinitializeQueue() 时检查是否有来自快照的 pendingBlocksremainingEdges,并据此恢复队列和节点依赖状态。AgentBlockHandler 中的 _pauseMetadatabuildPausedResult() 方法共同实现了暂停点的创建与快照生成。
  4. 多工具集成与智能体调度(技术层)

    • 产品视角:在 Agent Block 中可配置 LLM 模型、提示词,并勾选各种工具(数据库查询、API、自定义函数)供其调用。
    • 技术支撑AgentBlockHandler 是复杂度最高的处理器之一。它负责:
      a. 工具格式化 :将配置的工具(ToolInput)统一格式化为 LLM 可识别的工具调用模式(formatTools)。
      b. MCP 工具处理 :支持 Model Context Protocol 工具,包含缓存 Schema、服务发现、批量处理等优化(processMcpToolsBatched)。
      c. 上下文构建 :整合系统提示、用户输入、记忆(Memory)服务,构建完整的对话历史(buildMessages)。
      d. 供应商抽象 :将请求统一发送给 executeProviderRequest,屏蔽不同 AI 供应商(OpenAI, Anthropic, 本地 Ollama 等)的 API 差异。

3. 技术难点挖掘

  1. 动态 DAG 的构建与执行:如何将用户定义的循环、并行等高级结构,正确无误地编译为静态 DAG,并确保执行时的语义正确性(如循环的迭代、并发的扇入扇出)。
  2. 有状态工作流的暂停与精确恢复:如何在任意节点(特别是并行分支中)暂停,并序列化所有中间状态(变量、未完成的边、待执行节点队列),保证恢复后执行结果的一致性。
  3. 工具调用的安全性与性能:如何安全地执行用户自定义代码(Function Block)、动态发现和调用外部工具(MCP),同时管理连接、超时和错误,并避免对 LLM 请求造成性能瓶颈。
  4. 混合执行模式的支持:需要同时支持非流式响应、流式响应(Streaming),并在浏览器和服务器两种环境下都能稳定工作,对网络通信和错误处理要求高。

4. 详细设计图

4.1 核心架构图 (Component Diagram)

4.2 核心链路序列图:工作流执行 (Sequence Diagram)

AI Provider AgentBlockHandler NodeOrchestrator DAG ExecutionEngine 用户 AI Provider AgentBlockHandler NodeOrchestrator DAG ExecutionEngine 用户 alt [需要调用工具] loop [处理就绪队列] run(workflowId) initializeQueue() get ready nodes [node1] executeNode(node) execute(ctx, block, inputs) formatTools(), buildMessages() 携带工具的LLM请求 响应(可能含工具调用) executeFunction(toolCall) 工具执行结果 发送工具结果 最终响应 BlockOutput handleNodeCompletion(output) edgeManager.processOutgoingEdges() 获取新的就绪节点 [node2, node3...] ExecutionResult

4.3 核心类图 (Class Diagram)

构建
持有
持有
委托执行
路由到
DAGBuilder
-pathConstructor
-loopConstructor
-nodeConstructor
-edgeConstructor
+build(SerializedWorkflow, triggerBlockId) : DAG
DAG
+nodes: Map<string, DAGNode>
+loopConfigs: Map<string, SerializedLoop>
DAGNode
+id: string
+block: SerializedBlock
+incomingEdges: Set<string>
+outgoingEdges: Map<string, DAGEdge>
+metadata: NodeMetadata
ExecutionEngine
-readyQueue: string[]
-executing: Set<Promise>
-context: ExecutionContext
-dag: DAG
+run(triggerBlockId) : Promise<ExecutionResult>
-processQueue()
-executeNodeAsync(nodeId)
-handleNodeCompletion()
-buildPausedResult()
ExecutionContext
+executedBlocks: Set<string>
+blockLogs: any[]
+metadata: ExecutionMetadata
+workflowVariables: Map
+pendingDynamicNodes: string[]
NodeExecutionOrchestrator
+executeNode(ctx, nodeId) : Promise<NodeResult>
AgentBlockHandler
+canHandle(block) : boolean
+execute(ctx, block, inputs) : Promise<BlockOutput>
-formatTools(tools)
-buildMessages(inputs)
-executeProviderRequest()

5. 核心函数解析

5.1 DAGBuilder.build() - DAG 编译核心

这是将用户定义的静态工作流编译成可执行 DAG 的核心过程,体现了"构造"而非"解释"的设计思想。

typescript 复制代码
// apps/sim/executor/dag/builder.ts - 简化伪代码
export class DAGBuilder {
  build(workflow: SerializedWorkflow, triggerBlockId?: string, savedIncomingEdges?: Record<string, string[]>): DAG {
    const dag: DAG = { nodes: new Map(), loopConfigs: new Map(), parallelConfigs: new Map() };

    // 阶段1:初始化配置
    this.initializeConfigs(workflow, dag); // 提取循环、并行配置

    // 阶段2:路径分析
    const reachableBlocks = this.pathConstructor.execute(workflow, triggerBlockId);
    // 关键:基于触发点或起点,计算工作流中实际可达的所有区块,实现"按需执行"。

    // 阶段3:循环结构展开
    this.loopConstructor.execute(dag, reachableBlocks);
    // 关键:将逻辑上的循环节点,在DAG中展开为实际的节点和边结构,为执行引擎创造明确的路径。

    // 阶段4:节点实例化与元数据标记
    const { blocksInLoops, blocksInParallels, pauseTriggerMapping } = this.nodeConstructor.execute(workflow, dag, reachableBlocks);
    // 关键:为每个SerializedBlock创建对应的DAGNode,并标记其元数据(如是否在循环内、是否为恢复触发点等)。

    // 阶段5:边构建
    this.edgeConstructor.execute(workflow, dag, blocksInParallels, blocksInLoops, reachableBlocks, pauseTriggerMapping);
    // 关键:根据区块间的连接关系,创建DAGEdge,并可能根据循环/并行上下文调整边的源和目标。

    // 阶段6:状态恢复(如从快照恢复)
    if (savedIncomingEdges) {
      for (const [nodeId, incomingEdgeArray] of Object.entries(savedIncomingEdges)) {
        const node = dag.nodes.get(nodeId);
        if (node) {
          node.incomingEdges = new Set(incomingEdgeArray); // 恢复节点的依赖边状态
        }
      }
    }
    return dag;
  }
}

技术要点 :这是一个多阶段的编译器前端。pathConstructor 确保 DAG 只包含必要节点;loopConstructoredgeConstructor 协同工作,将高级控制流语义"降低"为基本 DAG 结构;最后对 savedIncomingEdges 的处理,是支持状态恢复的关键,它直接修改了 DAG 节点的运行时依赖状态。

5.2 ExecutionEngine.run() - 执行调度核心

这是运行时的心脏,一个基于队列的异步调度器,负责驱动整个 DAG 的执行。

typescript 复制代码
// apps/sim/executor/execution/engine.ts - 简化伪代码
export class ExecutionEngine {
  async run(triggerBlockId?: string): Promise<ExecutionResult> {
    const startTime = Date.now();
    try {
      // 1. 队列初始化:可能从快照恢复,或从触发点/起点开始
      this.initializeQueue(triggerBlockId);

      // 2. 主调度循环:只要有待处理工作就继续
      while (this.hasWork()) { // 检查 readyQueue 或 executing
        await this.processQueue(); // 处理就绪节点,并等待任意一个执行完成
      }
      // 3. 收尾:等待所有异步执行彻底结束
      await this.waitForAllExecutions();

      // 4. 构建成功结果
      return { success: true, output: this.finalOutput, ... };
    } catch (error) {
      // 5. 错误处理:包装错误信息,保留执行上下文
      const executionResult: ExecutionResult = { success: false, ... };
      if (error && typeof error === 'object') {
        (error as any).executionResult = executionResult; // 将结果附加到原始错误上,便于调试
      }
      throw error; // 重新抛出,由上层捕获
    }
  }

  private async processQueue(): Promise<void> {
    // 批量执行所有当前就绪的节点(实现隐式并行)
    while (this.readyQueue.length > 0) {
      const nodeId = this.dequeue();
      const promise = this.executeNodeAsync(nodeId); // 异步执行,不等待
      this.trackExecution(promise); // 加入执行跟踪集合
    }
    // 关键:等待至少一个正在执行的任务完成,以释放资源并可能产生新的就绪节点
    if (this.executing.size > 0) {
      await this.waitForAnyExecution(); // 使用 Promise.race
    }
  }

  private async executeNodeAsync(nodeId: string): Promise<void> {
    const wasAlreadyExecuted = this.context.executedBlocks.has(nodeId);
    const result = await this.nodeOrchestrator.executeNode(this.context, nodeId);

    if (!wasAlreadyExecuted) {
      // 关键:节点完成后的回调,需要加锁(queueLock)确保状态更新原子性
      await this.withQueueLock(async () => {
        await this.handleNodeCompletion(nodeId, result.output, result.isFinalOutput);
      });
    }
  }

  private async handleNodeCompletion(nodeId: string, output: NormalizedBlockOutput, isFinalOutput: boolean): Promise<void> {
    const node = this.dag.nodes.get(nodeId);
    // 关键点1: 检查暂停
    if (output._pauseMetadata) {
      this.pausedBlocks.set(pauseMetadata.contextId, output._pauseMetadata);
      this.context.metadata.status = 'paused';
      return; // 不再处理下游,工作流进入暂停状态
    }
    // 关键点2: 处理节点输出(如变量赋值)
    await this.nodeOrchestrator.handleNodeCompletion(this.context, nodeId, output);
    // 关键点3: 边处理,激活下游
    const readyNodes = this.edgeManager.processOutgoingEdges(node, output, false);
    this.addMultipleToQueue(readyNodes); // 下游节点进入就绪队列
    // 关键点4: 处理动态节点(如并行展开新增的节点)
    if (this.context.pendingDynamicNodes?.length > 0) {
      this.addMultipleToQueue(this.context.pendingDynamicNodes);
      this.context.pendingDynamicNodes = [];
    }
  }
}

技术要点

  • 队列驱动 :核心模型是持续消耗 readyQueue,并通过 edgeManager 的生产者-消费者模型。
  • 并发控制 :通过 executing Set 和 Promise.race/Promise.all 管理并发度,实现自然的并行执行。
  • 原子性更新withQueueLock 确保在处理节点完成、更新边状态、向队列添加新节点这一系列操作时,不会产生竞态条件。
  • 暂停机制 :在 handleNodeCompletion 中优先检查 _pauseMetadata,一旦发现即停止推进,并构建暂停结果。这是一种"协作式"的中断。

5.3 AgentBlockHandler 中的工具格式化

展示了 Sim 如何将多样化的工具抽象为 LLM 可用的统一接口,这是其扩展性的关键。

typescript 复制代码
// apps/sim/executor/handlers/agent/agent-handler.ts - 节选
private async formatTools(ctx: ExecutionContext, inputTools: ToolInput[]): Promise<any[]> {
    // ... 过滤 usageControl 等 ...
    const mcpTools: ToolInput[] = [];
    const otherTools: ToolInput[] = [];

    // 1. 分类工具
    for (const tool of filtered) {
      if (tool.type === 'mcp') { mcpTools.push(tool); }
      else { otherTools.push(tool); }
    }
    // 2. 并行处理非MCP工具(自定义工具、内置工具转换)
    const otherResults = await Promise.all(otherTools.map(async (tool) => {
      if (tool.type === 'custom-tool') {
        return await this.createCustomTool(ctx, tool); // 创建可执行函数
      }
      return this.transformBlockTool(ctx, tool); // 转换内置工具(如API块)
    }));
    // 3. 批量处理MCP工具(性能优化)
    const mcpResults = await this.processMcpToolsBatched(ctx, mcpTools);
    // 4. 合并并返回
    return [...otherResults, ...mcpResults].filter(tool => tool != null);
}

private async createCustomTool(ctx: ExecutionContext, tool: ToolInput): Promise<any> {
    // ... 获取schema和code ...
    const toolId = `${AGENT.CUSTOM_TOOL_PREFIX}${title}`;
    return {
      id: toolId,
      name: schema.function.name,
      description: schema.function.description || '',
      parameters: filteredSchema, // 过滤掉已由params提供的参数
      params: userProvidedParams, // 预设参数
      usageControl: tool.usageControl || 'auto',
      // 关键:定义执行函数,在LLM调用时被执行
      executeFunction: async (callParams: Record<string, any>) => {
        const mergedParams = mergeToolParameters(userProvidedParams, callParams);
        const result = await executeTool('function_execute', {
          code, // 用户定义的函数代码
          ...mergedParams,
          timeout: tool.timeout,
          envVars: ctx.environmentVariables || {},
          workflowVariables: ctx.workflowVariables || {},
        }, false, false, ctx);
        if (!result.success) { throw new Error(result.error); }
        return result.output;
      }
    };
}

技术要点

  • 统一包装 :无论底层是代码片段、HTTP API 还是 MCP 协议,最终都包装成具有 executeFunction 方法的对象。
  • 参数合并 :支持在工具配置时提供预设参数(params),并与 LLM 调用时产生的参数(callParams)安全合并。
  • 上下文注入 :执行时自动注入工作流变量、环境变量等上下文(ctx),使工具能感知工作流状态。
  • MCP 优化processMcpToolsBatched 体现了对性能的考虑,通过按服务器分组发现工具,减少连接开销。

总结

Sim 项目的技术核心在于一个精心设计的 DAG 编译与执行引擎。它成功地将可视化编程的易用性与底层执行引擎的严谨性(状态管理、并发控制、错误处理)结合起来。其架构清晰,通过构造器模式编译工作流,通过队列和 Promise 管理异步执行,通过统一的处理器接口集成多样化功能。代码中体现的"快照恢复"、"工具抽象"和"混合执行模式支持"是应对实际生产环境复杂性的关键设计,使其超越了简单的原型工具范畴,具备了支撑企业级 AI 应用的能力。

同类方案中,相较于 LangChain 等代码库,Sim 提供了更低门槛的可视化界面和开箱即用的状态管理;相较于 n8n 等通用自动化工具,它在 AI 智能体、工具集成和提示工程方面做了更深度的原生集成。其主要的考量点可能在于应对超大规模、高并发工作流时的性能优化,以及更复杂嵌套控制流的场景支持。

相关推荐
冬奇Lab26 分钟前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab27 分钟前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP4 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年4 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼4 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS5 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区6 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈6 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang6 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk18 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能