CodeSky 代码之空

随手记录自己的学习过程

Agent Platform Self-Host 开源项目对比 - Coze Studio

2025-09-01 19:54分类: Go评论: 0

使用模型:为了省钱,用的本地部署的模型

本来想直接把 Coze Studio 和 Dify 都写完的,由于都写完工期太长,可能导致吃屎都赶不上热乎的,所以先发 Coze Studio

Coze Studio

基于 v0.2.6编写,截止 2025-08,后续发布与本文无关。(比如 Chatflow 目前处于 beta 版本)

视频主要演示功能,因此整体录制较快。

(Youtube 视频)

基础依赖

  • 机器最低要求:2C4G

Dockerfile: https://github.com/coze-dev/coze-studio/blob/main/docker/docker-compose.yml

服务 版本 用途
MySQL 8.4.5 主数据库
Redis 8.0 缓存服务
Elasticsearch 8.18.0 搜索引擎 (支持中文分词)
Milvus 2.5.10 向量数据库 (用于 embeddings)
MinIO 对象存储
NSQ 消息队列
etcd 配置管理
另外,Helm 中默认使用的是 RocketMQ:
https://github.com/coze-dev/coze-studio/blob/main/helm/charts/opencoze/values.yaml

可兼容的模块配置,只需要改环境变量配置就能切换(https://github.com/coze-dev/coze-studio/blob/main/docker/.env.example):

  • 上传组件:火山云 ImageX / 文件存储
  • 文件存储(实际上这三者 API 是兼容的): minio / tos / s3
  • MQ: nsq (Docker 版本默认使用) / kafka / rmq (Helm 版本默认使用)
  • 向量存储:milvus / vikingdb
  • Embedding:ark / openai / ollama / gemini / http
  • Rerank (default=rrf):vikingdb / rrf
  • OCR:ve / paddleocr
  • Document Parser:builtin / paddleocr
  • 模型:openai / ark / deepseek / ollama / qwen / gemini
  • Code Runner Mode: sandbox(deno + pyodide) / local (venv)

和 Coze 的功能对比

大量残缺的残疾版……迭代速度也不尽如人意。

功能 Coze Studio Coze
工作空间 ×
智能体 - 单 Agent
智能体 - Chatflow ×
智能体 - 多 Agent ×
智能体调试 ×
智能体版本控制 ×
应用创建
资源库-工作流
资源库-对话流 ×
资源库 - 插件
资源库 - 卡片 ×
资源库 - 提示词
资源库 - 数据库
资源库 - 音色 ×
知识库添加 - 本地文档
知识库添加 - 自定义
知识库添加 - 在线数据 ×
知识库添加 - 飞书 ×
知识库添加 - 公众号 ×
知识库添加 - Notion ×
发布管理 ×
模型管理 ×
效果评测 ×
项目商店 ×
OpenAPI 部分支持
回调管理 ×
OpenAPI Playground ×
授权管理 ×
通用管理 ×

配置

Coze 整体灵活度一般,从部署配置就能看出来。

模型

  1. 部署前需要人工 copy 一个你需要的模型模版,配置完成后再启动。由于是本地模型,好巧不巧我 copy 了一个 model_template_basic.yaml成功踩到了第一个坑:Protocol 是有枚举值的,而 template_basic 刚好不在枚举值中:
1const (
2	ProtocolOpenAI   Protocol = "openai"
3	ProtocolClaude   Protocol = "claude"
4	ProtocolDeepseek Protocol = "deepseek"
5	ProtocolGemini   Protocol = "gemini"
6	ProtocolArk      Protocol = "ark"
7	ProtocolOllama   Protocol = "ollama"
8	ProtocolQwen     Protocol = "qwen"
9	ProtocolErnie    Protocol = "ernie" // ernie 虽然出现在枚举中,但没有 builder 实现,所以也不能用
10)
11

要扩展也是可以扩展的,backend/infra/impl/chatmodel/default_factory.go 进行修改:

1// 新增 Factory,代码里对应 DefaultFactory 替换成 CustomFactory
2func NewCustomFactory() chatmodel.Factory {
3	return NewFactory(customFactoryMap)
4}
5

这里你遇到了本项目的第一个槽点:明明都这么抽象成工厂了,却不是顶层依赖注入,而有整整三个地方调用了,全都需要修改:https://github.com/coze-dev/coze-studio/blob/f19761fa31fed69290d21ecabe34f1265a785b3d/backend/infra/impl/chatmodel/default_factory.go#L40

实际上大部分我们都是兼容 OpenAI 的接口,直接用 OpenAI 模版改起来更快……这是后面看到的。

另外,Coze 目前对 Think 标签的显示处理是有 Bug 的,试用时未修复。

插件

插件分为官方插件市场和 Space 级插件增删改查,全局官方插件市场走的是配置化,https://github.com/coze-dev/coze-studio/tree/main/backend/conf/plugin/pluginproduct

同样的需要新增一个配置 yaml,完成后重启服务。而且在很久之前就有人提出集成插件管理的 issue,但无人处理。

Space 级别的插件支持配置 HTTP 插件,就和官方线上版本类似了。

知识库

知识库需要配置 embedding model,同样需要配置在 .env。如果定完了一个模型后你需要换一个模型,或者模型维度配置有误,需要把旧知识库删除后重建后续才能正常使用。

Space

目前不支持多空间,但是预留了数据表。部署后发现只有个人空间,如何支持共享空间,据说 Q3 支持。

改造

账号体系

用户信息通过 SessionAuth Middleware 进行验证。

为了方便兼容,主要需要处理一下实现,将 backend/application/user/user.go中从自己想要替换的账号体系中映射出来。

简单的一个思路是如果需要替换成 SSO 登录,替换原有的登录界面改成 SSO 登录,回调后自动注册一个账号或者登录已有账号,本系统还是走自己的一套 Session 体系,这样处理起来会比兼容一堆接口和数据结构简单很多。SessionAuthUser表和相关关联都不需要修改了。

1type Session struct {
2    UserID int64
3    Locale string
4    CreatedAt time.Time
5    ExpiresAt time.Time
6}
7
8type User struct {
9	UserID int64
10
11	Name         string // nickname
12	UniqueName   string // unique name
13	Email        string // email
14	Description  string // user description
15	IconURI      string // avatar URI
16	IconURL      string // avatar URL
17	UserVerified bool   // Is the user authenticated?
18	Locale       string
19	SessionKey   string // session key
20
21	CreatedAt int64 // creation time
22	UpdatedAt int64 // update time
23}
24
25

CodeRunner 语言扩展

目前还只支持 Python,官方在线版本实际可以支持 Python + JavaScript。目前前端界面选项未放出,放出 JavaScript 后还需要修改服务端的 infra/coderunner自行实现一套 JS Runner:

1func (runner *runner) Run(ctx context.Context, request *coderunner.RunRequest) (*coderunner.RunResponse, error) {
2	if request.Language == coderunner.JavaScript {
3		return nil, fmt.Errorf("js not supported yet")
4	}
5	// ...	
6}
7

Workflow 节点

Workflow 和节点可能是 Coze 最有技术含量写得最好的部分了,但一大部分应该有赖于 Eino 的抽象而不是 Coze 本身的研发……

要新增一个节点可以参考:https://github.com/coze-dev/coze-studio/wiki/11.-新增工作流节点类型(后端)

简单概括一下,首先需要实现 Invoke,Invoke 为节点的执行逻辑,比如代码节点就是:

1type Runner struct {
2	outputConfig map[string]*vo.TypeInfo
3	code         string
4	language     coderunner.Language
5	runner       coderunner.Runner
6	importError  error
7}
8
9func (c *Runner) Invoke(ctx context.Context, input map[string]any) (ret map[string]any, err error) {
10	if c.importError != nil {
11		return nil, vo.WrapError(errno.ErrCodeExecuteFail, c.importError, errorx.KV("detail", c.importError.Error()))
12	}
13	response, err := c.runner.Run(ctx, &coderunner.RunRequest{Code: c.code, Language: c.language, Params: input})
14	if err != nil {
15		return nil, vo.WrapError(errno.ErrCodeExecuteFail, err, errorx.KV("detail", err.Error()))
16	}
17
18	result := response.Result
19	ctxcache.Store(ctx, coderRunnerRawOutputCtxKey, result)
20
21	output, ws, err := nodes.ConvertInputs(ctx, result, c.outputConfig)
22	if err != nil {
23		return nil, vo.WrapIfNeeded(errno.ErrCodeExecuteFail, err, errorx.KV("detail", err.Error()))
24	}
25
26	if ws != nil && len(*ws) > 0 {
27		logs.CtxWarnf(ctx, "convert inputs warnings: %v", *ws)
28		ctxcache.Store(ctx, coderRunnerWarnErrorLevelCtxKey, *ws)
29	}
30
31	return output, nil
32
33}
34

然后在 backend/domain/workflow/entity/node_meta.go定义节点信息:

1NodeTypeCodeRunner: {
2		ID:           5,
3		Key:          NodeTypeCodeRunner,
4		DisplayKey:   "Code",
5		Name:         "代码",
6		Category:     "logic",
7		Desc:         "编写代码,处理输入变量来生成返回值",
8		Color:        "#00B2B2",
9		IconURL:      "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Code-v2.jpg",
10		SupportBatch: false,
11		ExecutableMeta: ExecutableMeta{
12			PreFillZero: true,
13			PostFillNil: true,
14			UseCtxCache: true,
15		},
16		EnUSName:        "Code",
17		EnUSDescription: "Write code to process input variables to generate return values.",
18	}
19

然后每个节点需要 Config 和 NodeAdapt 用来做前端画布 Schema 到服务端运行时 Schema 的转换:

1type Config struct {
2	Code     string
3	Language coderunner.Language
4
5	Runner coderunner.Runner
6}
7
8func (c *Config) Adapt(_ context.Context, n *vo.Node, _ ...nodes.AdaptOption) (*schema.NodeSchema, error) {
9	ns := &schema.NodeSchema{
10		Key:     vo.NodeKey(n.ID),
11		Type:    entity.NodeTypeCodeRunner,
12		Name:    n.Data.Meta.Title,
13		Configs: c,
14	}
15	inputs := n.Data.Inputs
16
17	code := inputs.Code
18	c.Code = code
19
20	language, err := convertCodeLanguage(inputs.Language)
21	if err != nil {
22		return nil, err
23	}
24	c.Language = language
25
26	if err := convert.SetInputsForNodeSchema(n, ns); err != nil {
27		return nil, err
28	}
29
30	if err := convert.SetOutputTypesForNodeSchema(n, ns); err != nil {
31		return nil, err
32	}
33
34	return ns, nil
35}
36

domain/workflow/internal/canvas/adaptor/to_schema.go 中注册 Adapt,实现前端和服务端 Node 的关联:

1nodes.RegisterNodeAdaptor(entity.NodeTypeCodeRunner, func() nodes.NodeAdaptor {
2    return &code.Config{}
3})
4

还需要为 Config 实现 NodeBuilder,Builder 将配置转化为可执行的节点实例。

1func (c *Config) Build(_ context.Context, ns *schema.NodeSchema, _ ...schema.BuildOption) (any, error) {
2
3	if c.Language != coderunner.Python {
4		return nil, errors.New("only support python language")
5	}
6
7	importErr := validatePythonImports(c.Code)
8
9	return &Runner{
10		code:         c.Code,
11		language:     c.Language,
12		outputConfig: ns.OutputTypes,
13		runner:       code2.GetCodeRunner(),
14		importError:  importErr,
15	}, nil
16}
17

此时,我们就实现了一个 Invoke 模式的 Code,由于 Coze Studio 底层使用的是 Enio,所以同样你可以实现其他三种节点。

函数名 模式说明 交互模式名称 Lambda 构造方法
Invoke 输入非流式、输出非流式 Ping-Pong 模式 compose.InvokableLambda()
Stream 输入非流式、输出流式 Server-Streaming 模式 compose.StreamableLambda()
Collect 输入流式、输出非流式 Client-Streaming compose.CollectableLambda()
Transform 输入流式、输出流式 Bidirectional-Streaming compose.TransformableLambda()

前端也要新增一个节点,前端这里使用的是 flowgram,迭代比起 Coze Studio 要积极很多,我之前也提过好多 issue 修复和处理速度很快,但是 Coze 基于 flowgram 实际上还是做了一些能力增强的,可以参考:https://github.com/coze-dev/coze-studio/wiki/10.-新增工作流节点类型(前端)

支持的 OpenAPI

由于本身是个阉割版本,因此支持的 OpenAPI 也相当有限。官方 API 太多,而开源版本太少,就不像功能对比一样列对比表了。

Reference:https://github.com/coze-dev/coze-studio/wiki/6.-API-参考

功能 API
创建会话 /v1/conversation/create
查看会话列表 /v1/conversations
发起会话 /v3/chat
查看消息列表 /v1/conversation/message/list
清除上下文 /v1/conversations/:conversation_id/clear
执行工作流 /v1/workflow/run
执行工作流(流式响应) /v1/workflow/stream_run
恢复运行中的工作流 /v1/workflow/stream_resume
执行对话流(流式响应) /v1/workflows/chat

总结

半玩具级别开源,如果需要公司级投产使用,才能满足老板心中所谓的「我就想要和 Coze」一模一样,属于 Coze Lily。

其中还有一些录屏中也能看到的 Bug 以及上文提到的问题,都是已有 issue,比如 Think 标签无法正常解析

如果想要使用,首先得先克服 Lily 版本同时进行二开,还要承担 Coze Studio 本身 Bug 的修复工作,从慈善的角度这确实是个贡献开源的大好机会,但是站在牛马的角度可能得因此重新衡量一下,使用这样一个开源版本到底能节约多少时间,其实还是个谜(个人感觉不如直接抄 Workflow 的部分其他不要)。

附录:Workflow 整体设计

如上面所说的,Workflow 可能是最有技术含量的部分,因此看下 Workflow 的整体架构。

实话:以下由 AI 撰写。

核心设计理念

  1. 前后端分离的架构转换: 前端的可视化画布(Canvas)数据通过适配器转换为后端的执行架构(Schema)
  2. 事件驱动的执行引擎: 基于回调和事件机制实现异步执行和流式响应
  3. 插件化的节点系统: 通过统一的节点接口支持各种业务逻辑扩展
  4. 状态持久化和恢复: 支持工作流中断、恢复和检查点机制
  5. 多种执行模式: 同步/异步/流式执行,支持调试和生产环境

整体架构

数据流转过程

Workflow 的执行是一个复杂的数据流转过程,从前端的可视化画布到后端的执行引擎,经历了多个转换层:

核心组件详解

1. Canvas 到 Schema 的转换

Coze Studio 最巧妙的设计之一是将前端的可视化画布数据无缝转换为后端可执行的工作流架构。这个过程通过 canvas/adaptor 模块实现:

1// 前端画布数据结构
2type Canvas struct {
3    Nodes []*Node `json:"nodes"`
4    Edges []*Edge `json:"edges"`
5}
6
7// 后端执行架构
8type WorkflowSchema struct {
9    Nodes       []*NodeSchema     `json:"nodes"`
10    Connections []*Connection     `json:"connections"`
11    Hierarchy   map[NodeKey]NodeKey `json:"hierarchy"`
12    Branches    map[NodeKey]*BranchSchema `json:"branches"`
13}
14

转换过程包括:

  • 节点适配: 每种节点类型都有对应的 NodeAdaptor 实现
  • 连接规范化: 处理端口映射和分支逻辑
  • 层次结构构建: 处理嵌套工作流和批处理模式
  • 验证和优化: 移除孤立节点,验证连接有效性
2. 执行引擎

执行引擎采用事件驱动架构,通过上下文管理、事件处理和回调系统协调工作流的执行:

执行流程

  1. 上下文设置: 初始化根、子工作流和节点上下文
  2. 事件分发: 根据节点执行状态发送相应事件
  3. 回调处理: 通过处理器响应事件并更新状态
  4. 结果收集: 聚合执行结果并持久化状态
  5. 流式响应: 实时推送执行进度和结果
3. 节点系统架构

节点系统是 Workflow 的核心执行单元,支持多种执行模式和业务逻辑:

4.节点执行模式
接口类型 输入模式 输出模式 典型应用 使用场景
InvokableNode 非流式 非流式 Plugin、Code、HTTP 简单的输入输出处理
StreamableNode 非流式 流式 LLM、实时生成 需要实时响应的场景
CollectableNode 流式 非流式 聚合处理 收集流式数据并处理
TransformableNode 流式 流式 数据转换、过滤 流式数据的实时处理

Coze Studio 支持三种主要的执行模式,每种模式适用于不同的业务场景:

模式特点对比

执行模式 数据源 持久化 监控 适用场景
TestRun 草稿版本 最小化 基础 开发调试、功能验证
Production 发布版本 完整 全面 生产环境、用户服务
NodeDebug 草稿版本 临时 详细 节点开发、问题排查
5. 状态管理与流转

工作流执行过程中的状态管理采用有限状态机模式,支持复杂的状态转换和恢复机制:

状态持久化机制

  • 检查点(Checkpoint): 关键节点执行前保存状态
  • 中断事件存储: 记录中断位置和上下文
  • 恢复策略: 支持从任意检查点恢复执行
  • 状态同步: 多实例间的状态一致性保证

Workflow 提供了完善的错误处理和重试机制,确保系统的稳定性和可靠性:

错误处理配置示例

1type ExceptionConfig struct {
2    TimeoutMS   int64                `json:"timeout_ms"`   // 超时时间
3    MaxRetry    int64                `json:"max_retry"`    // 最大重试次数
4    DataOnErr   string               `json:"data_on_err"`  // 错误时返回的默认数据
5    ProcessType *ErrorProcessType    `json:"process_type"` // 错误处理类型
6}
7

技术特色与创新点

1. 基于 Eino 的抽象层设计

Coze Studio 巧妙地利用了 Eino 框架的抽象能力,实现了统一的节点执行接口。这种设计使得:

  • 节点扩展变得简单: 只需实现对应的接口即可
  • 执行模式灵活切换: 同一节点可支持多种执行模式
  • 流式处理原生支持: 无需额外的流式处理层
2. 前后端架构分离

通过 Canvas 到 Schema 的转换机制,实现了前后端的完全解耦:

  • 前端专注可视化: 画布设计、用户交互、可视化展示
  • 后端专注执行: 工作流调度、状态管理、性能优化
  • 数据结构转换: 自动处理前后端数据格式差异
3. 事件驱动的响应式架构

采用事件驱动模式,实现了:

  • 实时状态推送: 执行进度实时可见
  • 异步执行管理: 长时间运行的工作流不阻塞界面
  • 错误快速响应: 问题发生时立即反馈
4. 灵活的批处理机制

支持节点级别的批处理配置,自动生成批处理包装器:

1// 自动将普通节点包装为批处理节点
2func parseBatchMode(n *vo.Node) (*vo.Node, bool, error) {
3    // 创建父级批处理节点
4    parentN := &vo.Node{
5        Type: entity.NodeTypeBatch.IDStr(),
6        Blocks: []*vo.Node{innerN}, // 包含原始节点
7    }
8    return parentN, true, nil
9}
10

评论 (0)