发布时间:2025-08-04
浏览次数:0
大家好,我是十三!
“Make Open Great Again!”字节跳动在上月成功将AI IDE平台Trae Agent开源之后,紧接着在上周又宣布了AI Agent平台Coze的开源消息!作为一名专注于服务端研发的主要使用者,我对该平台的后端代码产生了极大的兴趣,为此,我特意花费了一周时间深入探究。
随着人工智能代理和大模型应用的迅猛发展,软件系统的复杂性持续上升。在这种形势下,一个明确、稳固且便于发展的软件架构成为了项目能否成功的关键。领域驱动设计(DDD)与整洁架构(Clean)的结合,成为了应对这种极端复杂性的有力工具。它们一致推崇“关注点分离”和“高内聚、低耦合”的基本思想,Coze 则完全依照 DDD 原则进行架构构建。
接下来,我将深入分析Coze这一生产级AI Agent平台,探讨其业务边界的划分方式,揭示其核心领域模型的具体形态,并阐述用户请求如何在明确层级中流畅地传递。
1. 宏观蓝图:整洁架构的分层艺术
整洁的架构理念,由C.所提出,其核心理念可通过一个经典的“洋葱图”进行直观展示。
Clean
其核心法则在于遵循依赖关系规则,要求源代码间的依赖关系必须严格限定在内部,这意味着层级较高的(更接近业务核心的部分)不应了解层级较低的(更接近具体实现的部分)的任何信息。
Coze 的后端项目结构,完美地诠释了这一思想。
coze-studio/backend/
├── api/ # 这是接口层,主要负责(Interface Adapters)
├── application/ # 应用层,这里定义了应用业务规则
├── 领域层 # 企业业务规则相关
└── 基础设施层,包括框架与驱动程序。
这四个核心目录,清晰地对应了整洁架构的四层模型:
这种结构设计实现了业务逻辑与技术的完全分离intellij idea golang plugin,大幅提升了测试的便捷性、维护的简便性和应用的灵活性。
2. 深入领域层:DDD 的战略与战术设计
DDD 的核心要义是业务复杂性的封装体现在领域模型之中,而Coze的目录则为我们展现了一个极佳的学习实例。
2.1 限界上下文 ( )
DDD的战略规划重点在于明确业务范围,并据此区分出各类“界限区域”。在Coze系统中,这一理念通过目录结构下的子目录布局得到了直观展示。
domain/
├── agent/
├── conversation/
├── knowledge/
├── memory/
├── plugin/
├── user/
└── workflow/
...
每个子目录,例如(如,),均对应一个独立的业务范畴。这样的划分确保了各个模块的高度集中,并各自拥有独特的术语和模型,从而显著减少了整个系统的认知难度。
2.2 聚合、实体与仓储
让我们以核心的 限界上下文为例,深入其战术设计的细节。
实体 ()
实体的定义被封装在一个(跨域)的包内,这表明它是一个被众多限界上下文所共同使用的核心模型。
在coze-studio的backend/api/model/crossdomain/conversation目录下,存在一个名为conversation的Go文件。
type Conversation struct {
标识符类型:int64,JSON标签:`json:"id"`。
SectionID为int64类型,对应的JSON字段名为section_id。
代理标识符 整数64位类型 JSON标签为"agent_id"
连接器标识符为int64类型,在JSON格式中对应的字段名为`connector_id`。
创建者ID类型为int64,在JSON序列化时对应字段名为creator_id。
场景定义,通用场景标识,字段名为“scene”。
状态 会话状态 `定义字段:"status"`
字符串字段,标记为`json:"ext"`。
创建时间 整数64位类型 JSON标签为"created_at"
更新时间 整数64位类型 `json字段名为"updated_at"`
}
该结构体极为简洁,仅包含数据属性。特别要指出的是,它借助诸如“关联字段”等手段与其他聚合体建立联系,然而仅存储其ID信息,而非整个对象内容。这一做法正是领域驱动设计(DDD)中的典范,有助于明确界定聚合体的边界。
仓储接口 ( )
领域模型的持久化方式是怎样的呢?它依赖于存储机制。在领域层,我们仅负责定义存储接口,对其具体实现并不进行关注。
在coze-studio的backend目录下,domain模块中,conversation包的repository子包里,存在一个名为repository的Go文件。
定义ConversationRepo接口需满足以下条件:具备特定功能,并实现一系列方法。
在执行创建操作时,需传入上下文环境和会话实体,操作完成后将返回更新后的会话实体及可能出现的错误信息。
通过ID获取(ctx上下文环境,id为int64类型)一个会话实体(*entity.Conversation类型),并返回可能出现的错误信息。
对指定区域进行更新操作,传入上下文环境和ID值,返回受影响的数据行数及可能出现的错误信息。
删除操作针对指定ID,在给定上下文环境中执行,若成功则无错误返回。
执行列表操作时,需传入上下文环境和列表元数据请求,并返回一个对话列表、一个布尔值以及可能出现的错误。
}
该接口充当领域层与基础设施层之间的“协议”,明确了针对聚合对象的所有持久化操作的规范。
领域服务 ( )
复杂的业务逻辑不适合放在实体中,而是应该由领域服务来承担。
文件位于coze-studio项目中的backend目录下,具体在domain/conversation/conversation/service子目录中,名为conversation.go的文件中。
type Conversation interface {
创建函数,接收上下文参数ctx和创建元数据指针req,返回一个对话实体指针和一个错误对象。
GetByID(ctx context.Context, id int64) (*entity.Conversation, error)
定义函数NewConversationCtx,接收context.Context类型的参数ctx和指向entity.NewConversationCtxRequest类型的指针req,返回指向entity.NewConversationCtxResponse类型的指针和一个error类型的值。
Delete(ctx context.Context, id int64) error
List(ctx context.Context, req *entity.ListMeta) ([]*entity.Conversation, bool, error)
获取当前会话信息,针对传入的上下文环境和请求参数,返回相应的会话实体及可能出现的错误。
}
应用层的代码在执行业务逻辑时,会通过调用该服务接口进行操作,无需深入了解其内部实现所涉及的复杂性。
“贫血”还是“充血”?Coze 的选择与权衡
Coze的设计颇具反常规性,它采纳了一种标准的“贫血领域模型”——在这种模型中intellij idea golang plugin,实体仅包含数据和关系,而所有业务逻辑则被安置在领域服务之中。
这并非与传统的、高度赞誉“充血领域模型”的领域驱动设计(DDD)理念相吻合。“充血模型”主张数据和相应的操作应当紧密集成于单一实体对象之内。
Coze为何会做出这样的决定?我认为这体现了一种务实的考量:
贫血模型与领域服务的结合,其逻辑明确,结构简洁。领域服务不涉及状态,依赖关系通过接口注入实现,这使得单元测试变得十分便捷。相比之下,充血模型下,业务逻辑分散于各个实体之中,这往往会导致依赖关系更加复杂,从而增加测试的难度。为降低 ORM 的复杂性:充血模型往往面临诸如对象生命周期管理、懒加载机制、事务边界设定等挑战,这些问题显著提升了 ORM 的应用难度;相比之下,贫血模型使得数据持久化过程变得更为直观和易于掌控。
Coze的架构设计是在坚持DDD(领域驱动设计)的核心原则,即界定上下文和分层的基础上,结合其采用的技术栈(Go语言)以及工程化的实际需求,进行了一次全面考量与平衡。
3. 串联各层:一次请求的生命周期
以“启动一个对话”为示范,详述一个请求在 Coze 系统不同层级间传递的整个过程:
接口层(API):用户的HTTP POST请求,首先抵达api//coze/api.go文件中设定的路由。该路由将请求分配至api//coze/目录下的处理函数。这些函数承担解析请求参数的任务,随后调用应用层的服务。在应用层,位于//目录下,存在一个用例或应用服务。它接收到了相应的调用指令,随即便调用了领域服务中的特定方法以执行核心业务流程。在领域层的“////.go”文件中,该方法被成功执行。在此过程中,它可能需要对一系列业务规则进行验证。验证完成后,它进一步调用了某个接口的特定方法,目的是请求将新的实体持久化存储。基础设施层(infra)负责提供接口的具体实现,比如在 infra/impl/mysql/.go(路径为推测)中可以看到。该实现类通过GORM(代码依赖中可查)将实体对象转化为数据库记录,并将这些记录存入MySQL数据库的表中。执行过程的结果沿着调用链逐级向上回传,最终在接口层被整合,以HTTP响应的形式呈现给用户。
该流程直观地揭示了依赖反转的强大作用:每一层级仅对其内部接口有所依赖,对外部实现细节一无所知,进而达成了理想的解耦效果。
4. 解耦的艺术:infra 中的 与 impl
在 infra 层及众多其他领域,Coze 实施了契约(即接口)与实现(impl)的分离机制,这种做法充分展示了彻底解耦理念的实践应用。
infra/
├── contract/ # 定义基础设施需要实现的接口
└── impl/ # 提供这些接口的具体技术实现
在领域层,我们可能需要使用一个ID生成器,因此,在层或infra/中,我们将定义一个相应的接口。同时,在infra/impl/idgen/目录中,我们将提供一个具体的实现方案,该方案基于某种算法,例如雪花算法。
此方法极大地简化了技术栈的转换过程。若未来计划将 ID 生成机制由雪花算法更改为分布式 UUID,仅需在 infra/impl 目录下新增一个实现版本,而无需对上层的业务代码进行任何修改。
结论:架构是艺术品的地基
Coze的后端架构让我领悟了在Go语言中如何将DDD(领域驱动设计)与整洁架构相结合的实际应用方法。优秀的架构并非过度的设计,而是对业务复杂性的精准把握与有效管理。我认为,Code的源代码无疑是一份极具参考价值、值得反复研读的艺术杰作。
关于十三Tech
资深服务端研发工程师,AI 编程实践者。
致力于传播真实的技术操作心得,坚信人工智能是程序员最为理想的合作伙伴。
希望能和大家一起写出更优雅的代码!
联系方式:@qq.com
:@
如有侵权请联系删除!
Copyright © 2023 江苏优软数字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服务提供商
13262879759
微信二维码