

























使用模型:为了省钱,用的本地部署的模型
本来想直接把 Coze Studio 和 Dify 都写完的,由于都写完工期太长,可能导致吃屎都赶不上热乎的,所以先发 Coze Studio
基于 v0.2.6编写,截止 2025-08,后续发布与本文无关。(比如 Chatflow 目前处于 beta 版本)
视频主要演示功能,因此整体录制较快。
(Youtube 视频)
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):
大量残缺的残疾版……迭代速度也不尽如人意。
| 功能 | Coze Studio | Coze |
|---|---|---|
| 工作空间 | × | √ |
| 智能体 - 单 Agent | √ | √ |
| 智能体 - Chatflow | × | √ |
| 智能体 - 多 Agent | × | √ |
| 智能体调试 | × | √ |
| 智能体版本控制 | × | √ |
| 应用创建 | √ | √ |
| 资源库-工作流 | √ | √ |
| 资源库-对话流 | × | √ |
| 资源库 - 插件 | √ | √ |
| 资源库 - 卡片 | × | √ |
| 资源库 - 提示词 | √ | √ |
| 资源库 - 数据库 | √ | √ |
| 资源库 - 音色 | × | √ |
| 知识库添加 - 本地文档 | √ | √ |
| 知识库添加 - 自定义 | √ | √ |
| 知识库添加 - 在线数据 | × | √ |
| 知识库添加 - 飞书 | × | √ |
| 知识库添加 - 公众号 | × | √ |
| 知识库添加 - Notion | × | √ |
| 发布管理 | × | √ |
| 模型管理 | × | √ |
| 效果评测 | × | √ |
| 项目商店 | × | √ |
| OpenAPI | 部分支持 | √ |
| 回调管理 | × | √ |
| OpenAPI Playground | × | √ |
| 授权管理 | × | √ |
| 通用管理 | × | √ |
Coze 整体灵活度一般,从部署配置就能看出来。
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。如果定完了一个模型后你需要换一个模型,或者模型维度配置有误,需要把旧知识库删除后重建后续才能正常使用。
目前不支持多空间,但是预留了数据表。部署后发现只有个人空间,如何支持共享空间,据说 Q3 支持。
用户信息通过 SessionAuth Middleware 进行验证。
为了方便兼容,主要需要处理一下实现,将 backend/application/user/user.go中从自己想要替换的账号体系中映射出来。
简单的一个思路是如果需要替换成 SSO 登录,替换原有的登录界面改成 SSO 登录,回调后自动注册一个账号或者登录已有账号,本系统还是走自己的一套 Session 体系,这样处理起来会比兼容一堆接口和数据结构简单很多。SessionAuth,User表和相关关联都不需要修改了。
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
目前还只支持 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 和节点可能是 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 也相当有限。官方 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 的整体架构。
实话:以下由 AI 撰写。

_%5BCozeDB%5D_ZeALbYaP.png!/format/webp)
Workflow 的执行是一个复杂的数据流转过程,从前端的可视化画布到后端的执行引擎,经历了多个转换层:
_CjaoWGgd.png!/format/webp)
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 实现执行引擎采用事件驱动架构,通过上下文管理、事件处理和回调系统协调工作流的执行:
_jpIGnZQZ.png!/format/webp)
执行流程:
节点系统是 Workflow 的核心执行单元,支持多种执行模式和业务逻辑:
_9arMU3uA.png!/format/webp)
| 接口类型 | 输入模式 | 输出模式 | 典型应用 | 使用场景 |
|---|---|---|---|---|
InvokableNode |
非流式 | 非流式 | Plugin、Code、HTTP | 简单的输入输出处理 |
StreamableNode |
非流式 | 流式 | LLM、实时生成 | 需要实时响应的场景 |
CollectableNode |
流式 | 非流式 | 聚合处理 | 收集流式数据并处理 |
TransformableNode |
流式 | 流式 | 数据转换、过滤 | 流式数据的实时处理 |
Coze Studio 支持三种主要的执行模式,每种模式适用于不同的业务场景:
_8Qp0Czr0.png!/format/webp)
模式特点对比:
| 执行模式 | 数据源 | 持久化 | 监控 | 适用场景 |
|---|---|---|---|---|
| TestRun | 草稿版本 | 最小化 | 基础 | 开发调试、功能验证 |
| Production | 发布版本 | 完整 | 全面 | 生产环境、用户服务 |
| NodeDebug | 草稿版本 | 临时 | 详细 | 节点开发、问题排查 |
工作流执行过程中的状态管理采用有限状态机模式,支持复杂的状态转换和恢复机制:
_ihdcOzvJ.png!/format/webp)
状态持久化机制:
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
Coze Studio 巧妙地利用了 Eino 框架的抽象能力,实现了统一的节点执行接口。这种设计使得:
通过 Canvas 到 Schema 的转换机制,实现了前后端的完全解耦:
采用事件驱动模式,实现了:
支持节点级别的批处理配置,自动生成批处理包装器:
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
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。