Activepieces 架构决策解读:Agent 是项目作用域的数据行,Flow 步骤对其进行实时引用
【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces
本篇文章解读 Activepieces 仓库内的一份架构决策记录 000027-an-agent-is-a-project-scoped-row-that-flow-steps-reference-live.md,深入剖析"Agent(智能体)以项目作用域的数据行形式存在、Flow 步骤通过agentId实时引用其配置"这一核心设计。通过阅读本文,你将理解 Agent 行的字段结构、Run Agent 步骤如何做到"改一处配置、所有引用它的流程下次运行自动生效且无需重新发布"、Detach & customise快照机制的工作方式,以及该决策在无人值守授权、跨项目迁移、审计与运行来源(AgentRunSource)等方面带来的影响与权衡。
决策概述:Live Reference,而非快照
决策文档给出的核心结论非常简洁而关键:
一个
agent行持有 instructions(指令)、tools(工具)、model(模型)、max steps(最大步数)和 structured output(结构化输出)。Run Agent 步骤只存储agentId,服务端在运行开始时解析配置——因此编辑一个 Agent 会改变所有引用它的流程的下一次运行结果,且无需重新发布。对于需要差异化的步骤,Detach & customise(分离并定制)会将配置内联复制一份并清除agentId。
也就是说,Agent 与流程之间是"实时引用(live reference)"关系,而非"发布时快照(snapshot)"关系。这是 Activepieces 中 Agent 功能与常见"下拉框预填步骤配置"方案的根本分水岭。
数据模型:agent 行的字段与索引
决策文档声明 Agent 是"项目作用域的行"(project-scoped row)。这一表述在源码中有完整的实体映射,见 agent-entity.ts,实体表名为agent,核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
projectId | ApId(非空) | 项目作用域的直接体现,所有 Agent 行隶属于某个项目,并带有级联删除外键(onDelete: 'CASCADE') |
ownerId | ApId(非空) | 创建者用户,多对一关联user表 |
externalId | String(非空) | 供跨环境/项目状态同步(upsert)使用的稳定外部标识,是"从第一天起就有"的字段 |
displayName | String(非空) | Agent 显示名称 |
description | String(可空) | Agent 描述 |
icon/color | String(非空) | 界面展示用图标与颜色 |
visibility | String(非空) | 可见性(如私有/共享) |
sharedWithUserIds | String 数组(默认'{}') | 显式共享给的用户 ID 列表 |
draft | jsonb(非空) | 草稿配置(instructions、tools、model、max steps、structured output 等) |
published | jsonb(可空) | 已发布配置,可空说明允许存在"有草稿未发布"的状态 |
实体上定义了两个索引,恰好对应决策文档强调的两个关键点:
idx_agent_project_created_id:按(projectId, created, id)排序,支撑"项目内按时间列出 Agent"的列表查询;idx_agent_project_external_id:(projectId, externalId)上的唯一索引,直接支撑决策文档中"项目状态必须按(projectId, externalId)upsert Agent"的要求——这是跨项目迁移与 git sync 场景下的稳定锚点。
运行机制:步骤只存 agentId,配置在运行开始解析
决策文档称"服务端在运行开始时解析配置",其背后的支撑点有两个:
1. 运行时本来就从 job payload 读取工具,而不是从 flow version 读取。
Agent 运行被建模为ExecuteAgentRunJobData(见 job-data.ts),其结构直接承载了解析后的运行配置:
jobType: 'EXECUTE_AGENT_RUN'、conversationId、flowRunId、waitpointId等运行上下文;source: AgentRunSource(可选)——即下文将详述的"第三个 AgentRunSource";tools: AgentTool[]与flowTools: ResolvedAgentFlowTool[]——工具在入队时已解析好放进载荷;structuredOutput、maxSteps、provider、providerConfigId、modelName、promptOverride、dryRun等。
由于 worker 消费的是这份解析后的 payload,而 payload 在"运行开始"时由服务端生成,所以 Agent 配置的修改自然只影响"下一次运行",完全不需要改动 worker 侧的解析逻辑——正如决策文档所说:"live reference costs no worker change(实时引用不产生任何 worker 改动)"。
2.flow_version.agentIds与extractAgentIds是现成的存量设施。
flow_version表上本就存在agentIds数组列(相关迁移可见 1753641361099-AddExternalIdToAgentId.ts),它源自 2025 年被删除的旧 agents 模块,如今被保留并继续使用。在每次 flow version 变更落库时,服务端会同步重算这一列,见 flow-version.service.ts:
mutatedFlowVersion.connectionIds = flowStructureUtil.extractConnectionIds(mutatedFlowVersion) mutatedFlowVersion.agentIds = flowStructureUtil.extractAgentIds(mutatedFlowVersion)与connectionIds并列地维护agentIds,意味着"哪些流程正在使用这个 Agent"这一反查能力是免费获得的——agentIds列就是反向索引。源码中确实存在利用该列的反查查询:flow.service.ts 使用latest_version."agentIds" && :agentExternalIds来按 Agent 外部 ID 匹配流程版本。这也正是编辑器里"Used in N flows(用于 N 个流程)"提示的数据来源。
项目作用域跟随工具解析:决策文档强调,项目作用域是"跟随工具"的——flow tools 按projectId解析,connections 按ArrayContains([projectId])匹配。也就是说,如果 Agent 被设计为平台作用域(platform-scoped),反而需要在行内重新实现一套项目作用域逻辑;保持项目作用域则可以直接复用既有工具与连接解析链路。
Why:为什么必须是实时引用,而不是快照
决策文档用一句话点明设计动机:
命名一个 Agent 的意义在于改进它一次(improve it once)。快照会让每个流程各自漂移,把"把 Agent 做得更好"重新变成逐个流程的手工编辑——而这正是该功能想要消除的痛苦。
被否决的替代方案正是典型的"快照式"交互:下拉框把配置预填进步骤,链接随即消失。这样做的直接后果是:你修正了 Agent 的指令或更换了模型,所有已经"复制"过配置的流程仍然停留在旧配置上,除非逐个打开重新编辑。实时引用则保证了单一事实来源(single source of truth):一处编辑,处处生效。
影响与权衡(Consequences)
1. 无人值守授权的语义随之扩大
决策 000024(000024-configuring-an-agent-step-tool-authorises-the-action-not-its-arguments.md)确立过"在 Agent 步骤上配置一个工具即视为授权其无人值守运行";由于步骤现在只引用agentId,这份授权从步骤转移到了 Agent 行本身——一旦有人能编辑 Agent,就等于能影响所有引用它的流程的无人值守行为。
因此决策文档明确了配套治理措施:
- 编辑 Agent 被视为编辑他人发布的流程:以
WRITE_AGENT权限门控。源码中该权限在 agent-controller.ts 中多处使用(涉及创建、更新、删除、移动等写操作),并且在 agent-tools.ts 中作为 Agent 工具的运行时授权检查依据(工具名不同时分别要求READ_AGENT或WRITE_AGENT); - 编辑操作写入审计日志(audit-logged);
- 编辑器在保存前展示 "Used in N flows",让编辑者明确知晓影响面;
- 若实践中授权过宽,演进方向是增加每 Agent 的 "allow unattended writes(允许无人值守写入)"开关,而不是回头重新讨论引用机制本身。
2. 跨项目移动会破坏链接,externalId 从第一天起就存在
由于 Agent ID 是项目局部的(project-local),把流程从一个项目移动到另一个项目会打破链接;而 git sync 会携带 connections 却不会携带 agents。为此:
agent行从第一天起就包含externalId(实体中的唯一索引(projectId, externalId)已证实);- 项目状态(project state)在恢复时必须按
(projectId, externalId)对 Agent 做 upsert; - 在项目状态真正支持 upsert Agent 之前,导入流程必须大声失败(fail loudly),而不是静默丢失引用。
3. 清理孤儿表,breaking = true
2025 年旧模块遗留的两张孤儿表agent、agent_run被删除,以便新实体可以使用agent这个理所应当的表名。为保证回滚安全,该变更标记breaking = true(且未使用⛓️💥 breaking-change标签)。这也解释了为什么上文实体与枚举中出现的是全新的表结构与运行模型——它们是在清场之后重新落地的。
4. Autonomy 依然是 Flow:调度不是 Agent 的专属能力
"按计划运行(Run on a schedule)"并不是在 Agent 行上加一个 cron 字段,而是搭建一个真正的流程,其中包含一个链接到该 Agent 的步骤。这样保证整个系统只有一套执行模型、一条可观测链路——Agent 的自主运行与普通流程共享相同的运行、重试、日志与监控路径。
5. 与 Agent 对话是第三个 AgentRunSource
决策文档特别指出:"与 Agent 聊天是第三个AgentRunSource,而不是nullable-agentId检查"。源码中AgentRunSource枚举定义于 job-data.ts:
export enum AgentRunSource { CHAT = 'CHAT', FLOW_STEP = 'FLOW_STEP', AGENT = 'AGENT', AGENT_BUILDER = 'AGENT_BUILDER', }对应关系如下:
| 取值 | 含义 |
|---|---|
FLOW_STEP | 流程中的 Run Agent 步骤(无人值守路径) |
AGENT | 直接与某个 Agent 对话(本决策新增的第三条路径) |
CHAT | 通用聊天 |
AGENT_BUILDER | Agent 构建器内的试运行 |
之所以用显式枚举值而非"agentId是否为空"来区分,是因为每一个门控(gate)本来就按source分支——运行来源是既有的、统一的判别维度;同时它让"Agent 对话"天然地从 Chat 列表中排除(agent-conversation-entity.ts 中对话的source默认值为CHAT,列表查询也按source过滤),无需额外的清理逻辑。
更重要的是受信语义的差异:与流程步骤(无人值守)不同,与 Agent 对话是有人值守(attended)的——taint(污点标记)从false开始,且审批流程不得自动拒绝(approval must not auto-decline)。这保证了聊天场景下用户可以自然地介入确认,而不会被自动化安全策略误伤。
相关决策与延伸阅读
- 本决策的上游依赖:000024-configuring-an-agent-step-tool-authorises-the-action-not-its-arguments.md(工具配置即无人值守授权);
- 决策文档目录:index.md;
- Agent 模块的完整实现位于 packages/server/api/src/app/ee/agent,其中 agent-entity.ts 定义行结构、agent-controller.ts 定义带权限门控的 API、agent-conversation-service.ts 管理对话与来源分支;
- 运行载荷契约(含
AgentRunSource与ExecuteAgentRunJobData)位于 job-data.ts。
总结
本决策的核心价值在于:把 Agent 变成项目内可复用、可集中改进的"活"配置行,而非固化在流程版本里的快照。通过"步骤只存agentId+ 运行开始解析 +flow_version.agentIds反向索引"的组合,Activepieces 在不改动 worker 的前提下获得了"一处编辑、处处生效"的能力,同时用Detach & customise保留了个别步骤差异化的出口;而externalId、WRITE_AGENT权限、审计日志、AgentRunSource枚举与breaking = true的迁移策略,则把这一灵活性的代价(授权扩大、跨项目断链、运行语义区分)逐一显式地治理起来。
【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考