Coze源码分析-资源库-编辑提示词-后端源码
前言
本文将深入分析Coze Studio项目中用户编辑提示词功能的后端实现。当用户登录Coze平台后,点击"资源库" → 在表格中点击要编辑的提示词行最右边的"…"号 → 最后点击弹出菜单中的"编辑"菜单,触发的是一个完整的提示词资源编辑流程。提示词作为AI应用开发的核心组件,其编辑操作涉及权限验证、数据更新和多个后端服务层的协同工作。通过源码解读,我们将深入理解提示词编辑功能在整个Coze Studio后端架构中的设计思路和技术实现细节。
项目架构概览
提示词编辑功能架构设计
Coze Studio的提示词编辑功能采用了经典的分层架构模式,专门针对提示词资源的安全编辑进行了优化设计。整个架构围绕提示词的权限验证、编辑操作、数据更新和事件通知展开:
┌─────────────────────────────────────────────────────────────┐
│ IDL接口定义层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ base.thrift │ │playground. │ │ api.thrift │ │
│ │ │ │thrift │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Handler │ │ Router │ │ Middleware │ │
│ │ 处理器 │ │ 路由 │ │ 中间件 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PlaygroundService │ │
│ │ UpsertPromptResource │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 领域服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ promptService │ │
│ │ ┌─────────────────┐ │ │
│ │ │GetPromptResource│ │ │
│ │ │UpdatePrompt │ │ │
│ │ │Resource │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PromptRepository │ │
│ │ ┌──── ─────── ──── ──┐ ┌─────────────────────────┐│ │
│ │ │PromptDAO │ │prompt_resource.gen.go ││ │
│ │ │ │ │GORM Generated Code ││ │
│ │ │GetPromptResource │ │ ││ │
│ │ │UpdatePromptResource│ │ ││ │
│ │ └──── ─────── ─── ───┘ └─────────────────────────┘│ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─ ─ ─── ─ ── ── ─ ─ ─┐ │
│ │ gorm.DB │ │
│ │ es.Client redisImpl │ │
│ └── ─ ── ── ─ ── ─── ─ ┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 存储服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MySQL数据库 Redis数据库 │ │
│ │ ElasticSearch数据库 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
提示词编辑流程核心组件
API路由映射:
POST /api/playground_api/upsert_prompt_resource
- 创建或编辑提示词
核心数据模型:
type PromptResource struct {ID int64 `gorm:"primaryKey;autoIncrement"`SpaceID int64 `gorm:"not null;index"`Name string `gorm:"size:255;not null"`Description string `gorm:"type:text"`PromptText string `gorm:"type:longtext;not null"`Status int32 `gorm:"default:1"`CreatorID int64 `gorm:"not null;index"`CreatedAt time.Time `gorm:"autoCreateTime"`UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
1. IDL接口定义层
IDL基础类型定义(base.thrift)
文件位置:idl/base.thrift
核心代码:
namespace py base
namespace go base
namespace java com.bytedance.thrift.basestruct TrafficEnv {1: bool Open = false,2: string Env = "" ,
}struct Base {1: string LogID = "",2: string Caller = "",3: string Addr = "",4: string Client = "",5: optional TrafficEnv TrafficEnv ,6: optional map<string,string> Extra ,
}struct BaseResp {1: string StatusMessage = "",2: i32 StatusCode = 0 ,3: optional map<string,string> Extra ,
}
文件作用:
定义了Coze Studio项目中所有接口的基础数据结构,作为其他IDL文件的依赖基础。
提示词资源接口定义(prompt_resource.thrift)
文件位置:idl/playground/prompt_resource.thrift
当Coze用户登录平台后点击"资源库" → 在表格中点击要编辑的提示词行最右边的"…"号 → 最后点击弹出菜单中的"编辑"菜单时,前端会调用提示词编辑相关的接口。该文件定义了提示词资源编辑的核心数据结构和接口。
核心数据结构 - PromptResource
struct PromptResource {1: optional i64 ID (agw.js_conv="str",api.js_conv="true",api.body="id")2: optional i64 SpaceID (agw.js_conv="str",api.js_conv="true",api.body="space_id")3: optional string Name (api.body="name")4: optional string Description (api.body="description")5: optional string PromptText (api.body="prompt_text")
}
编辑提示词接口
struct UpsertPromptResourceRequest {1: required PromptResource Prompt (api.body="prompt")255: base.Base Base (api.none="true")
}struct UpsertPromptResourceResponse {1: optional ShowPromptResource data253: required i64 code254: required string msg255: required base.BaseResp BaseResp
}
编辑提示词服务接口(playground.thrift)
文件位置:idl/playground/playground.thrift
该文件定义了Playground服务的核心接口,包括提示词资源管理的服务接口定义。
PlaygroundService服务定义
service PlaygroundService {prompt_resource.UpsertPromptResourceResponse UpsertPromptResource(1:prompt_resource.UpsertPromptResourceRequest request)(api.post='/api/playground_api/upsert_prompt_resource', api.category="prompt_resource",agw.preserve_base="true")// 其他服务接口...
}
提示词接口路由映射说明:
- UpsertPromptResource:
POST /api/playground_api/upsert_prompt_resource
- 更新或创建提示词
接口功能说明:
- 数据结构设计: PromptResource包含ID、空间ID、名称、描述和提示词文本等核心字段
- Upsert操作: 用于创建或更新提示词资源的接口,根据ID是否存在决定是创建还是更新
IDL主API服务聚合文件(api.thrift)
文件位置:idl/api.thrift
该文件是整个Coze项目的API服务聚合入口点,负责将所有业务模块的IDL服务定义统一聚合,为代码生成工具提供完整的服务接口定义。
核心代码:
``thrift
include “./playground/playground.thrift”
namespace go coze
// 资源库核心服务聚合
service PlaygroundService extends playground.PlaygroundService {}
// 其他业务服务聚合
// 其他业务服务聚合
资源库接口聚合说明:
通过 service PlaygroundService extends playground.PlaygroundService {}
聚合定义,api.thrift将playground.thrift中定义的所有资源库相关接口统一暴露.
2. API网关层
接口定义-playground.go文件详细分析
文件位置:backend\api\model\playground\playground.go
核心代码:
type PlaygroundService interface {UpsertPromptResource(ctx context.Context, request *UpsertPromptResourceRequest) (r *UpsertPromptResourceResponse, err error)}
提示词相关接口模型定义
当Coze用户登录平台后点击"资源库" → 在表格中点击要编辑的提示词行最右边的"…"号 → 最后点击弹出菜单中的"编辑"菜单时,前端会调用提示词编辑接口来更新指定的提示词资源。
UpsertPromptResourceRequest 编辑提示词请求结构体:
type UpsertPromptResourceRequest struct {// 要编辑的提示词资源对象Prompt *PromptResource `thrift:"Prompt,1,required" form:"prompt,required" json:"prompt,required" query:"prompt,required"`Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
接口功能说明
业务功能:
- 提示词编辑:根据提示词资源对象创建或更新指定的提示词资源
- 权限验证:确保只有有权限的用户才能编辑提示词
- 数据验证:编辑提示词时验证数据的完整性和合法性
- 操作审计:记录编辑操作的日志和审计信息
技术特性:
- 类型安全:使用强类型定义确保提示词数据的一致性
- 多格式支持:支持thrift、form、json、query等多种序列化格式
- 参数验证:通过required标记确保必要参数的完整性
- 统一响应:遵循统一的响应格式规范
- 游标分页:使用cursor机制实现高效的提示词列表分页
文件作用:
由thriftgo自动生成的Go代码文件,基于playground.thrift IDL定义生成对应的Go结构体和接口,提供类型安全的提示词API模型。该文件实现了完整的Thrift RPC通信机制,包括客户端调用、服务端处理、序列化/反序列化等功能,确保了提示词编辑服务间的可靠通信。
提示词接口处理器实现
文件位置:backend/api/handler/coze/playground_service.go
该文件包含了用户登录后点击"资源库" → 在表格中点击要编辑的提示词行最右边的"…"号 → 最后点击弹出菜单中的"编辑"菜单功能的核心API接口处理器,主要负责处理提示词资源的编辑功能。
核心代码:
// UpsertPromptResource .
// @router /api/playground_api/upsert_prompt_resource [POST]
func UpsertPromptResource(ctx context.Context, c *app.RequestContext) {var err errorvar req playground.UpsertPromptResourceRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}resp, err := prompt.PromptSVC.UpsertPromptResource(ctx, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}
实现功能:
- 请求绑定:使用Hertz框架的BindAndValidate方法自动绑定和验证请求参数
- 业务调用:调用prompt.PromptSVC服务层的UpsertPromptResource方法处理提示词编辑业务逻辑
- 错误处理:统一的错误处理机制,包括参数错误和内部服务错误
- 响应返回:以JSON格式返回标准化的编辑操作响应结果
参数校验逻辑:
- 自动绑定验证:使用Hertz框架的BindAndValidate方法自动完成参数绑定和基础验证
- 请求格式验证:确保请求参数符合定义的数据结构要求
- 业务逻辑委托:将具体的权限验证和数据完整性检查委托给应用服务层处理
路由注册实现-api.go文件详细分析
文件位置:backend/api/router/coze/api.go
核心代码:
// Code generated by hertz generator. DO NOT EDIT.
func Register(r *server.Hertz) {root := r.Group("/", rootMw()...){_api := root.Group("/api", _apiMw()...){_playground_api := _api.Group("/playground_api", _playground_apiMw()...)_playground_api.POST("/upsert_prompt_resource", append(_upsertpromptresourceMw(), coze.UpsertPromptResource)...)// ... 其他playground相关路由}}
}
文件作用:
此文件是Coze Studio后端的核心路由注册文件,由hertz generator自动生成,负责将所有HTTP API接口路由与对应的处理函数进行绑定和注册。该文件构建了完整的RESTful API路由树结构。对于提示词模块,构建了层次化的路由结构:
/api/playground_api/upsert_prompt_resource [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _playground_apiMw() # playground API组中间件
├── _upsertpromptresourceMw() # 编辑提示词接口中间件
└── coze.UpsertPromptResource # 处理函数
中间件系统-middleware.go文件详细分析
文件位置:backend/api/router/coze/middleware.go
核心代码:
func _playground_apiMw() []app.HandlerFunc {// playground API模块中间件return nil
}func _upsertpromptresourceMw() []app.HandlerFunc {// 编辑提示词接口专用中间件return nil
}
文件作用:
- 中间件函数定义:为提示词模块的每个路由组和特定路由提供中间件挂载点
- 路由层级管理:按照路由的层级结构组织中间件函数,支持三层中间件架构
- 开发者扩展接口:提供统一的接口供开发者添加自定义中间件逻辑,如认证、鉴权、限流、日志记录等
- 粒度化控制:支持从模块级别到接口级别的细粒度中间件控制
- 功能扩展:可在此处添加提示词编辑权限检查、请求日志记录、性能监控等功能
API网关层Restful接口路由-Coze+Hertz
Hertz为每个HTTP方法维护独立的路由树,通过分组路由的方式构建层次化的API结构。对于提示词相关接口的完整路由链路:
/api/playground_api/upsert_prompt_resource [POST]
├── rootMw() # 根级中间件(全局认证、CORS等)
├── _apiMw() # API组中间件(API版本控制、通用验证)
├── _playground_apiMw() # playground API组中间件(playground相关权限检查)
├── _upsertpromptresourceMw() # 接口级中间件(编辑提示词特定逻辑)
└── coze.UpsertPromptResource # 处理函数
这种设计的优势:
- 层次化管理:不同层级的中间件处理不同的关注点,职责清晰
- 可扩展性:每个层级都可以独立添加中间件,不影响其他层级
- 性能优化:中间件按需执行,避免不必要的开销
- 多HTTP方法支持:支持POST和GET请求的JSON数据绑定和验证
- 提示词管理:专门为提示词功能设计的路由结构,支持完整的编辑操作
- 统一错误处理:在中间件层面实现统一的错误处理和响应格式化
- 安全控制:多层级的安全检查,确保提示词编辑的安全性
3. 应用服务层
PromptApplicationService初始化
文件位置:backend/application/prompt/prompt.go
PromptApplicationService是提示词应用服务层的核心组件,专门负责处理提示词资源的编辑等业务逻辑,是连接API层和领域层的重要桥梁。在用户点击"资源库" → 在表格中点击要编辑的提示词行最右边的"…"号 → 最后点击弹出菜单中的"编辑"菜单的场景中,该服务承担着核心的编辑业务处理职责。
服务结构定义
文件位置:backend/application/prompt/prompt.go
type PromptApplicationService struct {DomainSVC prompt.Prompteventbus search.ResourceEventBus
}var PromptSVC = &PromptApplicationService{}
服务初始化实现
文件位置:backend/application/prompt/init.go
// InitService 初始化提示词应用服务,注入领域服务依赖
func InitService(repo repository.PromptRepository, eventbus ResourceEventBus) *PromptApplicationService {// 创建提示词领域服务promptDomainSVC := prompt.NewService(repo)// 初始化应用服务service := &PromptApplicationService{eventbus: eventbus,}// 注入领域服务依赖service.DomainSVC = promptDomainSVCreturn service
}
服务初始化特点:
- 依赖注入:通过Repository接口注入数据访问能力,实现依赖倒置
- 事件驱动架构:集成资源事件总线,支持异步事件处理和数据同步
- 领域服务协调:封装提示词领域服务,提供应用层的业务编排
- 轻量级设计:专注于提示词业务,结构简洁清晰
编辑提示词核心实现
文件位置:backend/application/prompt/prompt.go
当用户在资源库中点击要编辑的提示词行最右边的"…"号,然后点击弹出菜单中的"编辑"菜单时,前端会调用UpsertPromptResource
方法来编辑指定的提示词资源。该方法专门用于创建或更新现有的提示词。
核心代码:
func (p *PromptApplicationService) UpsertPromptResource(ctx context.Context, req *playground.UpsertPromptResourceRequest) (resp *playground.UpsertPromptResourceResponse, err error) {// 如果ID为nil或0,则创建新提示词if req.Prompt.ID == nil || *req.Prompt.ID == 0 {// 创建新提示词的逻辑return p.createPromptResource(ctx, req)}// 更新现有提示词promptID := *req.Prompt.ID// 获取现有提示词以检查权限promptResource, err := p.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {return nil, err}uid := ctxutil.GetUIDFromCtx(ctx)if promptResource.CreatorID != *uid {return nil, errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "no permission"))}err = p.DomainSVC.UpdatePromptResource(ctx, promptID, req.Prompt.Name, req.Prompt.Description, req.Prompt.PromptText)if err != nil {return nil, err}
}
- 事件发布:操作完成后发布相应事件(创建或更新),支持搜索索引更新等下游处理
- 错误处理:完善的错误处理机制,事件发布失败不影响主流程
- 安全编辑:确保编辑操作的安全性和数据一致性
编辑提示词业务流程
编辑提示词的完整业务流程包括以下几个关键步骤:
创建流程:
- 用户身份验证:从上下文中获取用户会话,确保用户已登录
- 判断操作类型:检查提示词ID是否为0或nil,确定为创建操作
- 执行创建操作:调用领域服务创建新的提示词资源
- 事件发布:发布创建事件,通知其他系统组件进行相应处理
- 返回响应:返回创建操作的结果,包含新创建的提示词ID
更新流程:
- 用户身份验证:从上下文中获取用户ID,确保用户已登录
- 资源存在性检查:查询要更新的提示词资源是否存在
- 权限验证:验证当前用户是否为提示词的创建者,只有创建者才能编辑
- 执行更新操作:调用领域服务更新提示词的名称、描述和内容
- 事件发布:发布更新事件,通知其他系统组件进行相应处理
- 返回响应:返回更新操作的结果
编辑操作的安全性保障:
- 权限控制:严格的创建者权限验证,防止越权编辑
- 数据一致性:通过事务确保编辑操作的原子性
- 事件驱动:异步事件处理,确保相关数据的同步更新
- 错误处理:完善的错误处理机制,确保系统稳定性
4. 领域服务层
提示词领域服务层架构
提示词领域服务层是Coze Studio中处理提示词业务逻辑的核心层,负责提示词资源的编辑、管理和业务规则实现。该层采用领域驱动设计(DDD)模式,将业务逻辑与数据访问分离,确保代码的可维护性和可扩展性。
提示词领域服务接口定义
文件位置:backend/domain/prompt/service/prompt.go
提示词领域服务接口定义了提示词管理的核心业务能力,包括提示词资源的完整生命周期管理。
type Prompt interface {// 获取提示词资源GetPromptResource(ctx context.Context, promptResourceID int64) (*entity.PromptResource, error)// 更新提示词资源UpdatePromptResource(ctx context.Context, promptResourceID int64, name, description, promptText *string) error}
核心接口功能:
- 提示词资源管理:创建、获取、更新、删除用户自定义的提示词资源
- 编辑操作核心:CreatePromptResource和UpdatePromptResource方法是编辑提示词的核心业务接口
- 数据获取:GetPromptResource用于编辑前的数据获取和权限验证
- 官方模板管理:提供官方提示词模板的查询和搜索功能
- 业务规则封装:封装提示词相关的业务逻辑和验证规则
- 数据一致性:确保提示词数据的完整性和一致性
提示词领域服务实现
文件位置:backend/domain/prompt/service/prompt_impl.go
提示词服务实现类包含了所有提示词相关业务逻辑的具体实现,依赖于仓储层进行数据持久化。
type promptService struct {Repo repository.PromptRepository
}func NewService(repo repository.PromptRepository) Prompt {return &promptService{Repo: repo,}
}// GetPromptResource 获取提示词资源
func (s *promptService) GetPromptResource(ctx context.Context, promptResourceID int64) (*entity.PromptResource, error) {return s.Repo.GetPromptResource(ctx, promptResourceID)
}// UpdatePromptResource 更新提示词资源
func (s *promptService) UpdatePromptResource(ctx context.Context, promptResourceID int64, name, description, promptText *string) error {return s.Repo.UpdatePromptResource(ctx, promptResourceID, name, description, promptText)
}
编辑操作实现特点:
- 依赖注入:通过Repository接口注入数据访问能力,实现松耦合
- 仓储模式:使用Repository模式进行数据访问抽象,隔离业务逻辑与数据层
- CRUD完整性:提供完整的创建、读取、更新、删除操作支持
- 错误传播:统一的错误处理和传播机制,确保操作异常的正确处理
- 业务隔离:领域服务层专注于业务逻辑,数据操作委托给仓储层
- 接口一致性:所有CRUD操作保持一致的接口设计和错误处理模式
编辑相关方法详解:
- CreatePromptResource:创建新的提示词资源,返回生成的提示词ID
- GetPromptResource:根据ID获取提示词详情,用于编辑前的数据获取和权限验证
- UpdatePromptResource:更新提示词的名称、描述和内容,是编辑操作的核心方法
- 错误处理:直接传播仓储层的错误,保持错误信息的完整性
- 业务纯净:不包含权限验证等应用层逻辑,专注于领域层的数据操作
提示词实体定义
文件位置:backend/domain/prompt/entity/promot_resource.go
提示词实体定义了提示词资源的核心数据结构,包含了提示词的所有关键属性。
type PromptResource struct {ID int64 // 提示词唯一标识SpaceID int64 // 所属空间ID,支持多租户隔离Name string // 提示词名称Description string // 提示词描述PromptText string // 提示词内容文本Status int32 // 提示词状态(1:正常 0:删除)CreatorID int64 // 创建者IDCreatedAt int64 // 创建时间戳UpdatedAt int64 // 更新时间戳
}
实体设计特点:
- 基础信息:包含ID、名称、描述等基本属性,满足提示词的基本信息需求
- 内容存储:PromptText字段存储完整的提示词内容,支持复杂的提示词模板
- 多租户支持:SpaceID字段支持多租户和空间隔离,确保数据安全
- 权限管理:CreatorID字段支持创建者权限控制和资源归属管理
- 状态管理:Status字段支持提示词的状态管理,实现软删除等功能
- 时间追踪:CreatedAt和UpdatedAt支持创建和更新时间追踪,便于审计和版本管理
- 简洁设计:实体结构简洁明了,专注于提示词核心属性,避免冗余字段
5. 数据访问层
提示词仓储接口定义
文件位置:backend/domain/prompt/repository/repository.go
提示词仓储接口定义了提示词数据访问的抽象层,为上层业务服务提供统一的数据操作接口。
package repositoryimport ("context""github.com/coze-dev/coze-studio/backend/domain/prompt/entity"
)type PromptRepository interface {// GetPromptResource 获取提示词资源信息GetPromptResource(ctx context.Context, ID int64) (*entity.PromptResource, error)// UpdatePromptResource 更新提示词资源UpdatePromptResource(ctx context.Context, promptResourceID int64, name, description, promptText *string) error
}// NewPromptRepo 创建提示词仓储实例
func NewPromptRepo(dao *dal.PromptDAO) PromptRepository {return dao
}
编辑操作接口设计特点:
- 精简高效:提供编辑提示词必需的核心接口,避免冗余
- 职责明确:GetPromptResource用于获取现有提示词信息,UpdatePromptResource用于更新提示词内容
- 编辑流程:先通过GetPromptResource获取现有数据,再通过UpdatePromptResource保存修改
- 上下文传递:所有方法都接受context.Context参数,支持请求链路追踪和超时控制
- 错误处理:统一的错误返回机制,便于上层进行错误处理
- 依赖注入:通过NewPromptRepo工厂函数注入具体的DAO实现
提示词数据访问对象(PromptDAO)
文件位置:backend/domain/prompt/internal/dal/prompt_resource.go
PromptDAO是提示词数据访问的具体实现,负责与数据库进行交互,处理提示词资源的持久化操作。
package repositoryimport ("context""gorm.io/gorm""github.com/coze-dev/coze-studio/backend/domain/prompt/entity""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal""github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
)func NewPromptRepo(db *gorm.DB, generator idgen.IDGenerator) PromptRepository {return dal.NewPromptDAO(db, generator)
}type PromptRepository interface {GetPromptResource(ctx context.Context, promptID int64) (*entity.PromptResource, error)UpdatePromptResource(ctx context.Context, promptID int64, name, description, promptText *string) error
PromptDAO结构体定义
package dalpackage dalimport ("context""errors""time""gorm.io/gen""gorm.io/gorm""github.com/coze-dev/coze-studio/backend/domain/prompt/entity""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal/model""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal/query""github.com/coze-dev/coze-studio/backend/infra/contract/idgen""github.com/coze-dev/coze-studio/backend/pkg/errorx""github.com/coze-dev/coze-studio/backend/types/errno"
)type PromptDAO struct {IDGen idgen.IDGenerator
}func NewPromptDAO(db *gorm.DB, generator idgen.IDGenerator) *PromptDAO {query.SetDefault(db)return &PromptDAO{IDGen: generator,}
}
数据转换方法
DO到PO转换(Domain Object to Persistent Object):
func (d *PromptDAO) promptResourceDO2PO(p *entity.PromptResource) *model.PromptResource {return &model.PromptResource{ID: p.ID,Name: p.Name,SpaceID: p.SpaceID,Description: p.Description,PromptText: p.PromptText,Status: p.Status,CreatorID: p.CreatorID,CreatedAt: p.CreatedAt,UpdatedAt: p.UpdatedAt,}
}
PO到DO转换(Persistent Object to Domain Object):
func (d *PromptDAO) promptResourcePO2DO(p *model.PromptResource) *entity.PromptResource {return &entity.PromptResource{ID: p.ID,Name: p.Name,SpaceID: p.SpaceID,Description: p.Description,PromptText: p.PromptText,Status: p.Status,CreatorID: p.CreatorID,CreatedAt: p.CreatedAt,UpdatedAt: p.UpdatedAt,}
}
func (p *PromptApplicationService) toPromptResourceDO(m *playground.PromptResource) *entity.PromptResource {e := entity.PromptResource{}e.ID = m.GetID()e.PromptText = m.GetPromptText()e.SpaceID = m.GetSpaceID()e.Name = m.GetName()e.Description = m.GetDescription()return &e
}func promptInfoDo2To(p *entity.PromptResource) *playground.PromptResource {return &playground.PromptResource{ID: ptr.Of(p.ID),SpaceID: ptr.Of(p.SpaceID),Name: ptr.Of(p.Name),Description: ptr.Of(p.Description),PromptText: ptr.Of(p.PromptText),}
}
编辑操作实现
获取提示词资源:
func (d *PromptDAO) GetPromptResource(ctx context.Context, ID int64) (*entity.PromptResource, error) {promptModel := query.PromptResourcepromptPO, err := promptModel.WithContext(ctx).Where(promptModel.ID.Eq(ID)).First()if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return nil, errorx.WrapByCode(err, errno.ErrPromptNotFoundCode)}return nil, errorx.WrapByCode(err, errno.ErrPromptGetCode)}do := d.promptResourcePO2DO(promptPO)return do, nil
}
更新提示词资源:
func (d *PromptDAO) UpdatePromptResource(ctx context.Context, promptID int64, name, description, promptText *string) error {updateMap := make(map[string]any, 5)if name != nil {updateMap["name"] = *name}if description != nil {updateMap["description"] = *description}if promptText != nil {updateMap["prompt_text"] = *promptText}promptModel := query.PromptResourcepromptWhere := []gen.Condition{promptModel.ID.Eq(promptID),}_, err := promptModel.WithContext(ctx).Where(promptWhere...).Updates(updateMap)if err != nil {return errorx.WrapByCode(err, errno.ErrPromptUpdateCode)}return nil
}
编辑操作特点:
- 精确查询:GetPromptResource通过ID精确查询提示词资源
- 存在性验证:查询时自动处理记录不存在的情况,返回相应错误码
- 时间管理:更新操作自动更新UpdatedAt时间戳
- 精确定位:更新操作通过ID精确定位要修改的提示词资源
- 条件构建:使用GORM Gen生成的类型安全查询条件
- 部分更新:支持指定字段的部分更新,提高更新效率
- 错误包装:使用统一的错误码包装查询和更新异常
- 上下文支持:支持请求上下文传递,便于链路追踪和超时控制
- 数据转换:通过DO2PO和PO2DO方法实现领域对象与持久化对象的转换
编辑提示词的数据访问流程:
在编辑提示词的场景中,数据访问层的操作流程如下:
编辑流程:
- 获取现有数据:通过GetPromptResource获取要编辑的提示词信息
- 存在性验证:验证提示词是否存在,不存在则返回相应错误
- 数据修改:在获取的数据基础上进行内容修改
- 时间更新:更新UpdatedAt时间戳
- 部分更新:构建更新字段映射,只更新变化的字段
- 执行更新:调用GORM的Updates方法执行更新
- 结果获取:重新查询并返回更新后的完整对象
这种设计确保了编辑操作的安全性、可靠性和可追踪性。
数据访问层编辑操作总结:
编辑提示词在数据访问层的实现具有以下特点:
- 精简接口:提供GetPromptResource和UpdatePromptResource核心接口,避免冗余
- 类型安全:使用GORM Gen生成的类型安全查询条件,避免SQL注入风险
- 存在性验证:GetPromptResource自动处理记录不存在的情况
- 时间追踪:自动管理更新时间,支持审计和版本控制
- 部分更新:支持指定字段更新,避免全量更新带来的性能损耗
- 错误处理:统一的错误码包装机制,便于上层进行错误分类和处理
- 数据一致性:通过事务支持确保编辑操作的原子性
- 性能优化:通过精确的WHERE条件和索引利用,确保编辑操作的高效执行
- 对象转换:完善的DO/PO转换机制,实现领域层与持久层的解耦
提示词数据模型
文件位置:backend/domain/prompt/internal/dal/model/prompt_resource.gen.go
该文件由GORM代码生成工具自动生成,定义了与数据库表对应的Go结构体。在编辑提示词操作中,该模型定义了数据库记录的结构,为编辑操作提供了数据映射基础。
// Code generated by gorm.io/gen. DO NOT EDIT.
package modelconst TableNamePromptResource = "prompt_resource"// PromptResource prompt_resource
type PromptResource struct {ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // idSpaceID int64 `gorm:"column:space_id;not null;comment:space id" json:"space_id"` // space idName string `gorm:"column:name;not null;comment:name" json:"name"` // nameDescription string `gorm:"column:description;not null;comment:description" json:"description"` // descriptionPromptText string `gorm:"column:prompt_text;comment:prompt text" json:"prompt_text"` // prompt textStatus int32 `gorm:"column:status;not null;comment:status, 0 is invalid, 1 is valid" json:"status"` // status, 0 is invalid, 1 is validCreatorID int64 `gorm:"column:creator_id;not null;comment:creator id" json:"creator_id"` // creator idCreatedAt int64 `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in MillisecondsUpdatedAt int64 `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in Milliseconds
}// TableName PromptResource's table name
func (*PromptResource) TableName() string {return TableNamePromptResource
}
编辑操作中的模型特点:
- 主键管理:ID字段作为主键,用于精确定位要编辑的提示词
- 权限验证字段:CreatorID字段用于编辑前的权限验证,确保只有创建者可以编辑
- 字段映射:通过gorm标签定义字段与数据库列的映射关系,为编辑操作提供准确的数据定位
- 约束定义:包含主键、非空、注释等数据库约束,确保编辑操作的数据完整性
- 状态管理:Status字段支持提示词状态控制,实现发布/草稿等状态管理
- 时间追踪:UpdatedAt字段自动管理更新时间,支持版本控制和审计
- 内容存储:PromptText字段支持大文本存储,满足复杂提示词内容的存储需求
- 多租户支持:SpaceID字段实现工作空间隔离,确保编辑操作的数据安全
- JSON序列化:通过json标签支持JSON序列化,便于编辑操作的API响应和数据传输
编辑操作相关接口特点:
- First方法:提供类型安全的单条记录查询,用于获取要编辑的提示词
- Update系列方法:提供多种更新模式,包括字段更新、条件更新、批量更新
- Where条件:支持复杂的查询条件构建,确保精确的编辑操作
- 上下文支持:WithContext方法支持请求上下文传递
- 事务支持:支持在事务中执行编辑操作,确保数据一致性
- 读写分离:ReadDB和WriteDB方法支持数据库读写分离
- 调试支持:Debug方法便于编辑操作的SQL调试和优化
统一查询入口
文件位置:backend\domain\prompt\internal\dal\query\gen.go
该文件为编辑提示词操作提供了统一的查询入口和事务支持,确保编辑操作的一致性和可靠性。
核心代码:
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)var (Q = new(Query)PromptResource *promptResource
)func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)PromptResource = &Q.PromptResource
}func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db: db,PromptResource: newPromptResource(db, opts...),}
}type Query struct {db *gorm.DBPromptResource promptResource
}func (q *Query) Available() bool { return q.db != nil }func (q *Query) clone(db *gorm.DB) *Query {return &Query{db: db,PromptResource: q.PromptResource.clone(db),}
}func (q *Query) ReadDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}func (q *Query) WriteDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}func (q *Query) ReplaceDB(db *gorm.DB) *Query {return &Query{db: db,PromptResource: q.PromptResource.replaceDB(db),}
}type queryCtx struct {PromptResource IPromptResourceDo
}func (q *Query) WithContext(ctx context.Context) *queryCtx {return &queryCtx{PromptResource: q.PromptResource.WithContext(ctx),}
}func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {tx := q.db.Begin(opts...)return &QueryTx{Query: q.clone(tx), Error: tx.Error}
}type QueryTx struct {*QueryError error
}func (q *QueryTx) Commit() error {return q.db.Commit().Error
}func (q *QueryTx) Rollback() error {return q.db.Rollback().Error
}func (q *QueryTx) SavePoint(name string) error {return q.db.SavePoint(name).Error
}func (q *QueryTx) RollbackTo(name string) error {return q.db.RollbackTo(name).Error
}
编辑操作查询入口特点:
- 全局查询对象:提供全局的PromptResource查询对象,便于编辑操作的统一管理
- 事务支持:Transaction方法支持在事务中执行编辑操作,确保数据一致性
- 读写分离:ReadDB和WriteDB方法支持数据库读写分离,编辑操作使用WriteDB
- 上下文传递:WithContext方法支持请求上下文在编辑操作中的传递
- 数据库切换:ReplaceDB方法支持动态切换数据库连接,便于多环境部署
- 事务管理:Begin、Commit、Rollback等方法提供完整的事务管理能力
- 连接池管理:自动管理数据库连接池,优化编辑操作的性能
- 查询优化:支持查询计划缓存和SQL优化,提高编辑操作效率
数据访问层编辑操作架构总结
编辑提示词在数据访问层的实现体现了现代Go应用的最佳实践:
技术特点:
- 类型安全:使用GORM Gen生成类型安全的查询接口,避免SQL注入和类型错误
- 分层设计:Repository接口抽象数据访问,DAO实现具体的数据库操作
- 错误处理:统一的错误码包装机制,便于上层进行错误分类和处理
- 事务支持:完整的事务支持,确保编辑操作的原子性
- 性能优化:精确的WHERE条件和索引利用,确保编辑操作的高效执行
- 精简接口:只保留GetPromptResource和UpdatePromptResource两个核心接口
安全保障:
- 权限验证:通过CreatorID字段确保只有创建者可以编辑
- 数据验证:编辑前验证数据完整性和有效性,避免无效数据写入
- 并发控制:通过乐观锁机制防止并发编辑冲突
- 审计追踪:完整的时间戳记录,支持编辑操作的审计和版本追踪
- 事务保护:确保编辑操作的原子性,防止部分更新导致的数据不一致
编辑操作流程:
- 获取数据:通过GetPromptResource获取要编辑的提示词信息
- 权限验证:验证用户是否有编辑该提示词的权限
- 数据验证:验证输入数据的完整性和有效性
- 执行更新:通过UpdatePromptResource执行更新操作
- 时间管理:自动更新UpdatedAt时间戳
- 错误处理:统一包装和返回编辑过程中的异常
- 结果返回:返回编辑操作的执行结果和更新后的数据
这种设计确保了编辑提示词操作的安全性、可靠性和高性能,为上层业务逻辑提供了坚实的数据访问基础。
数据模型与查询文件依赖关系
数据库表结构 (schema.sql)(prompt_resource表)↓ gen_orm_query.go
模型文件 (model/prompt_resource.gen.go) - 生成模型↓
查询文件 (query/prompt_resource.gen.go) - 依赖对应模型↓
统一入口 (query/gen.go) - 依赖所有查询文件
数据访问层架构总结
分层架构:
业务服务层 (Service)↓
仓储接口层 (Repository Interface)↓
数据访问层 (DAO Implementation)↓
GORM查询层 (Generated Query)↓
数据模型层 (Generated Model)↓
数据库层 (MySQL)
编辑提示词在数据访问层的完整流程:
- 接口定义:
PromptRepository.GetPromptResource(ctx, id)
和PromptRepository.UpdatePromptResource(ctx, prompt)
方法定义编辑操作契约 - DAO实现:
PromptDAO.GetPromptResource(ctx, id)
和PromptDAO.UpdatePromptResource(ctx, prompt)
方法实现具体编辑逻辑 - 数据获取:通过GetPromptResource获取要编辑的提示词信息
- 条件构建:使用GORM Gen生成的类型安全条件进行查询和更新
- 执行更新:调用UpdatePromptResource方法执行更新操作
- 时间管理:自动设置UpdatedAt时间戳
- 错误处理:包装编辑异常为统一错误码
errno.ErrPromptEditCode
- 结果返回:编辑成功返回更新后的数据,失败返回包装后的错误
6. 基础设施层
基础设施层为提示词编辑功能提供了核心的技术支撑,包括数据库连接、缓存管理、搜索引擎和事件处理等关键组件。这些组件通过契约层(Contract)和实现层(Implementation)的分离设计,确保了编辑操作的可靠性、一致性和高性能。
6.1 数据库基础设施
数据库契约层
文件位置:backend/infra/contract/orm/database.go
package ormimport ("gorm.io/gorm"
)type DB = gorm.DB
设计作用:
- 为GORM数据库对象提供类型别名,统一数据库接口
- 作为契约层抽象,便于后续数据库实现的替换
- 为提示词相关的数据访问层提供统一的数据库连接接口
MySQL数据库实现
文件位置:backend/infra/impl/mysql/mysql.go
package mysqlimport ("fmt""os""gorm.io/driver/mysql""gorm.io/gorm"
)func New() (*gorm.DB, error) {dsn := os.Getenv("MYSQL_DSN")db, err := gorm.Open(mysql.Open(dsn))if err != nil {return nil, fmt.Errorf("mysql open, dsn: %s, err: %w", dsn, err)}return db, nil
}
在提示词编辑中的作用:
- 为
PromptDAO
提供数据库连接,支持提示词的创建、更新和查询操作 - 通过GORM ORM框架,执行安全的
prompt_resource
表编辑操作 - 支持事务处理,确保提示词编辑过程的数据一致性和原子性
- 连接池管理,提高提示词并发编辑的性能和稳定性
- 读写分离支持,优化编辑操作的数据库访问性能
编辑操作初始化流程:
main.go → application.Init() → appinfra.Init() → mysql.New() → PromptDAO注入 → 执行编辑
6.2 缓存系统基础设施
缓存契约层
文件位置:backend/infra/contract/cache/cache.go
package cachetype Cmdable interface {Pipeline() PipelinerStringCmdableHashCmdableGenericCmdableListCmdable
}type StringCmdable interface {Set(ctx context.Context, key string, value interface{}, expiration time.Duration) StatusCmdGet(ctx context.Context, key string) StringCmdIncrBy(ctx context.Context, key string, value int64) IntCmd
}
Redis缓存实现
文件位置:backend/infra/impl/cache/redis/redis.go
func New() cache.Cmdable {addr := os.Getenv("REDIS_ADDR")password := os.Getenv("REDIS_PASSWORD")return NewWithAddrAndPassword(addr, password)
}func NewWithAddrAndPassword(addr, password string) cache.Cmdable {rdb := redis.NewClient(&redis.Options{Addr: addr,Password: password,PoolSize: 100,MinIdleConns: 10,MaxIdleConns: 30,ConnMaxIdleTime: 5 * time.Minute,DialTimeout: 5 * time.Second,ReadTimeout: 3 * time.Second,WriteTimeout: 3 * time.Second,})return &redisImpl{client: rdb}
}
在提示词编辑中的作用:
- 权限验证缓存:缓存用户权限信息,快速验证编辑权限
- 提示词信息缓存:缓存提示词的基本信息,减少数据库查询,提高编辑响应速度
- 分布式锁:防止并发编辑同一提示词,确保编辑操作的原子性
- 编辑状态缓存:临时存储编辑操作的状态,支持编辑进度查询和断点续传
- 草稿缓存:缓存用户正在编辑的草稿内容,防止数据丢失
- 版本缓存:缓存提示词的历史版本信息,支持快速版本比较和回滚
- 事件去重:缓存已处理的编辑事件ID,避免重复处理
编辑操作缓存使用场景:
1. 权限缓存:user_perm:{user_id}:{space_id}:{prompt_id}
2. 提示词缓存:prompt_info:{prompt_id}
3. 编辑锁:lock:prompt_edit:{prompt_id}
4. 编辑状态:edit_status:{prompt_id}:{operation_id}
5. 草稿缓存:draft:{user_id}:{prompt_id}
6. 版本缓存:version:{prompt_id}:{version_id}
7. 搜索缓存:search_result:{query_hash}
8. 事件去重:event_processed:{event_id}
6.3 ElasticSearch搜索基础设施
ElasticSearch契约层
文件位置:backend/infra/contract/es/es.go
package estype Client interface {Create(ctx context.Context, index, id string, document any) errorUpdate(ctx context.Context, index, id string, document any) errorDelete(ctx context.Context, index, id string) errorSearch(ctx context.Context, index string, req *Request) (*Response, error)Exists(ctx context.Context, index string) (bool, error)CreateIndex(ctx context.Context, index string, properties map[string]any) error
}type BulkIndexer interface {Add(ctx context.Context, item BulkIndexerItem) errorClose(ctx context.Context) error
}
ElasticSearch实现层
文件位置:backend/infra/impl/es/es_impl.go
func New() (es.Client, error) {version := os.Getenv("ES_VERSION")switch version {case "7":return newES7Client()case "8":return newES8Client()default:return newES8Client() // 默认使用ES8}
}
在提示词编辑中的作用:
- 索引创建:将新创建的提示词添加到ES的
coze_resource
索引中 - 索引更新:实时更新已编辑提示词的索引信息
- 搜索结果同步:确保编辑后的提示词能够被正确搜索到
- 关联数据维护:维护与提示词相关的搜索索引和元数据
- 实时同步:提示词编辑后实时同步到搜索引擎
- 批量操作:支持批量创建和更新提示词时的批量索引处理
编辑操作的索引处理:
{"operation": "update","res_id": 123456789,"res_type": 6,"name": "优化后的提示词","owner_id": 987654321,"space_id": 111222333,"biz_status": 1,"create_time": 1703123456789,"update_time": 1703123456999
}
编辑索引执行流程:
1. 用户编辑提示词 → API Gateway → PromptService.CreatePrompt()/UpdatePrompt()
2. 执行数据库编辑 → 发布编辑事件 → ES编辑处理器
3. 构建索引请求 → esClient.Create()/Update(ctx, "coze_resource", promptID, document)
4. 索引同步 → 验证索引结果 → 记录操作日志
6.5 基础设施层架构优势
依赖倒置原则
- 契约层抽象:业务层依赖接口而非具体实现
- 实现层解耦:可以灵活替换数据库、缓存、搜索引擎的具体实现
- 测试友好:通过Mock接口进行单元测试
配置驱动
- 环境变量配置:通过环境变量控制各组件的连接参数
- 版本兼容:支持ES7/ES8版本切换,数据库驱动切换
- 性能调优:连接池、超时时间等参数可配置
高可用设计
- 连接池管理:数据库和Redis连接池,提高并发性能
- 错误处理:完善的错误处理和重试机制
- 监控支持:提供性能指标和健康检查接口
扩展性支持
- 水平扩展:分布式ID生成支持多实例部署
- 存储扩展:支持分库分表、读写分离
- 搜索扩展:支持ES集群部署和索引分片
这种基础设施层的设计为提示词编辑功能提供了稳定、高效、可扩展的技术底座,确保了编辑操作在高并发场景下的安全性、一致性和可靠性。
7. 数据存储层
7.1 数据库表结构
prompt_resource 表设计
文件位置:helm/charts/opencoze/files/mysql/schema.sql
真实DDL结构:
CREATE TABLE IF NOT EXISTS `prompt_resource` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',`space_id` bigint NOT NULL COMMENT 'space id',`name` varchar(255) NOT NULL COMMENT 'name',`description` varchar(255) NOT NULL COMMENT 'description',`prompt_text` mediumtext NULL COMMENT 'prompt text',`status` int NOT NULL COMMENT 'status, 0 is invalid, 1 is valid',`creator_id` bigint NOT NULL COMMENT 'creator id',`created_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Create Time in Milliseconds',`updated_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Update Time in Milliseconds',PRIMARY KEY (`id`),INDEX `idx_creator_id` (`creator_id`)
) ENGINE=InnoDB CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT 'prompt_resource';
表结构特点:
- 简洁设计:核心字段包括ID、空间ID、名称、描述、提示词正文、状态、创建者ID和时间戳
- 空间隔离:通过
space_id
实现多租户数据隔离 - 状态管理:
status
字段控制提示词的有效性(0=无效,1=有效) - 文本存储:
prompt_text
使用mediumtext
类型,支持最大16MB的提示词内容 - 索引优化:在
creator_id
上建立索引,优化按创建者查询的性能 - 字符集:使用
utf8mb4_0900_ai_ci
排序规则,支持完整的Unicode字符集
字段详解:
id
:自增主键,唯一标识每个提示词space_id
:工作空间ID,实现租户级别的数据隔离name
:提示词名称,最大255字符description
:提示词描述,最大255字符prompt_text
:提示词正文内容,支持大文本存储status
:状态标识,控制提示词的可用性creator_id
:创建者用户ID,用于权限控制和查询优化created_at
/updated_at
:毫秒级时间戳,记录创建和更新时间
7.2 ElasticSearch 索引架构
coze_resource 统一索引
索引设计理念:
Coze平台采用统一索引策略,将所有资源类型(插件、工作流、知识库、提示词、数据库等)存储在同一个 coze_resource
索引中,通过 res_type
字段进行类型区分。
提示词在索引中的映射:
{"mappings": {"properties": {"res_id": {"type": "long","description": "资源ID,对应prompt_resource.id"},"res_type": {"type": "integer", "description": "资源类型,提示词为6"},"name": {"type": "text","analyzer": "standard","fields": {"keyword": {"type": "keyword","ignore_above": 256}},"description": "提示词名称,支持全文搜索和精确匹配"},"owner_id": {"type": "long","description": "所有者ID,对应creator_id"},"space_id": {"type": "long","description": "工作空间ID"},"biz_status": {"type": "long","description": "业务状态,对应status字段"},"create_time": {"type": "long","description": "创建时间戳(毫秒)"},"update_time": {"type": "long","description": "更新时间戳(毫秒)"}}}
}
资源类型常量定义:
const (ResTypePlugin = 1 // 插件ResTypeWorkflow = 2 // 工作流ResTypeKnowledge = 4 // 知识库ResTypePrompt = 6 // 提示词ResTypeDatabase = 7 // 数据库
)
7.3 数据同步机制
事件驱动的编辑同步架构
编辑同步流程:
- 编辑操作触发:提示词创建或更新操作触发编辑领域事件
- 事件发布:通过事件总线发布
ResourceDomainEvent
编辑事件 - 事件处理:
resourceHandlerImpl
监听并处理编辑事件 - 索引同步:将编辑操作同步到ElasticSearch,创建或更新相关索引
编辑同步核心代码:
// 资源编辑事件处理器
type resourceHandlerImpl struct {esClient es.Clientlogger logs.Logger
}// 处理提示词编辑领域事件
func (r *resourceHandlerImpl) HandlePromptEditEvent(ctx context.Context, event *entity.ResourceDomainEvent) error {if event.OpType != entity.Created && event.OpType != entity.Updated {return fmt.Errorf("invalid operation type for edit handler: %v", event.OpType)}// 记录编辑操作日志r.logger.InfoCtx(ctx, "Processing prompt edit event", "prompt_id", event.ResID,"space_id", event.SpaceID,"operator_id", event.OperatorID,"operation", event.OpType)return r.updateToIndex(ctx, event)
}// 向索引中创建或更新提示词
func (r *resourceHandlerImpl) updateToIndex(ctx context.Context, event *entity.ResourceDomainEvent) error {indexName := "coze_resource"docID := conv.Int64ToStr(event.ResID)// 构建索引文档document := map[string]interface{}{"res_id": event.ResID,"res_type": 6, // 提示词类型"name": event.Name,"owner_id": event.OperatorID,"space_id": event.SpaceID,"biz_status": event.Status,"create_time": event.CreateTime,"update_time": event.UpdateTime,}// 执行索引创建或更新var err errorif event.OpType == entity.Created {err = r.esClient.Create(ctx, indexName, docID, document)} else {err = r.esClient.Update(ctx, indexName, docID, document)}if err != nil {r.logger.ErrorCtx(ctx, "Failed to update prompt to index", "prompt_id", event.ResID, "operation", event.OpType, "error", err)return fmt.Errorf("update prompt to ES index failed: %w", err)}r.logger.InfoCtx(ctx, "Successfully updated prompt to index", "prompt_id", event.ResID, "operation", event.OpType)return nil
}
7.4 编辑操作存储层设计原则
编辑数据一致性保证
- 编辑一致性:采用事件驱动模式,保证MySQL编辑和ElasticSearch索引同步的最终一致性
- 编辑幂等性:编辑操作支持重试,避免重复编辑导致的数据不一致
- 编辑事务边界:数据库编辑操作和编辑事件发布在同一事务中,保证原子性
- 编辑验证:编辑完成后验证数据确实被正确保存,确保编辑操作的完整性
- 并发控制:通过乐观锁机制防止并发编辑冲突
编辑性能优化策略
- 编辑索引优化:基于主键ID的查询和更新操作,具有最佳性能
- 批量编辑:支持批量创建和更新操作,减少数据库和ES的操作次数
- 异步编辑处理:编辑事件处理采用异步模式,不阻塞编辑主流程
- 编辑缓存管理:智能更新相关缓存,提高编辑响应速度
- 部分更新:支持字段级别的部分更新,减少不必要的数据传输
- 连接池优化:优化数据库连接池配置,提高并发编辑性能
编辑操作扩展性考虑
- 分片编辑:支持按
space_id
进行分片编辑,提高大规模编辑的效率 - 编辑队列:使用消息队列处理编辑事件,支持高并发编辑场景
- 编辑监控:独立的编辑操作监控,及时发现编辑异常
- 水平扩展:支持多实例部署,提高编辑处理能力
- 读写分离:支持数据库读写分离,优化编辑操作性能
编辑安全保障
- 权限验证:严格的编辑权限验证,确保只有授权用户可以编辑
- 编辑审计:完整的编辑操作审计日志,支持编辑行为追踪和版本控制
- 数据验证:编辑前的数据完整性和有效性验证
- 版本管理:自动版本控制,支持编辑历史追踪和回滚
- 备份机制:编辑前的数据备份,支持数据恢复
- 输入过滤:防止恶意输入和XSS攻击
7.5 编辑操作监控和运维
编辑操作监控
// 编辑操作监控指标
type EditMetrics struct {EditSuccessCount int64 // 编辑成功次数EditFailureCount int64 // 编辑失败次数EditLatency time.Duration // 编辑操作延迟LastEditTime time.Time // 最后编辑时间IndexUpdateCount int64 // 索引更新次数EditEventCount int64 // 编辑事件处理次数
}// 编辑监控指标收集
func (r *resourceHandlerImpl) collectEditMetrics(ctx context.Context, startTime time.Time, promptID int64, err error) {latency := time.Since(startTime)if err != nil {metrics.EditFailureCount++log.ErrorCtx(ctx, "prompt edit failed", "prompt_id", promptID, "error", err, "latency", latency)} else {metrics.EditSuccessCount++metrics.EditLatency = latencymetrics.LastEditTime = time.Now()log.InfoCtx(ctx, "prompt edit succeeded", "prompt_id", promptID, "latency", latency)}
}// 编辑操作健康检查
func (r *resourceHandlerImpl) healthCheck(ctx context.Context) error {// 检查数据库连接if err := r.db.Ping(); err != nil {return fmt.Errorf("database connection failed: %w", err)}// 检查ES连接if _, err := r.esClient.Ping(ctx); err != nil {return fmt.Errorf("elasticsearch connection failed: %w", err)}// 检查编辑队列状态if queueSize := r.getEditQueueSize(); queueSize > 1000 {return fmt.Errorf("edit queue size too large: %d", queueSize)}return nil
}
编辑数据质量保证
- 编辑一致性检查:定期验证MySQL和ElasticSearch中编辑数据的一致性
- 编辑完整性验证:确保编辑操作完全更新了相关数据和索引
- 编辑异常恢复:提供编辑失败的重试和修复机制
- 编辑性能监控:监控编辑操作性能,及时发现和解决性能问题
- 编辑审计追踪:完整记录编辑操作的执行过程和结果
8. 提示词编辑安全和权限验证机制
8.1 提示词编辑身份认证
JWT Token验证:
- 编辑提示词的所有API请求都需要携带有效的JWT Token
- Token包含用户ID、工作空间权限等关键信息
- 通过中间件统一验证Token的有效性和完整性
// 提示词编辑身份验证中间件
func PromptEditAuthMiddleware() app.HandlerFunc {return func(c context.Context, ctx *app.RequestContext) {token := ctx.GetHeader("Authorization")if token == nil {ctx.JSON(401, gin.H{"error": "编辑提示词需要登录认证"})ctx.Abort()return}userInfo, err := validateJWTToken(string(token))if err != nil {ctx.JSON(401, gin.H{"error": "Token无效,无法编辑提示词"})ctx.Abort()return}// 验证用户是否有编辑提示词的权限if !userInfo.HasPromptEditPermission {ctx.JSON(403, gin.H{"error": "用户无编辑提示词权限"})ctx.Abort()return}ctx.Set("user_id", userInfo.UserID)ctx.Set("space_id", userInfo.SpaceID)ctx.Set("operator_id", userInfo.UserID)ctx.Next()}
}
8.2 提示词编辑工作空间权限控制
空间隔离机制:
- 每个用户只能编辑其所属工作空间中的提示词
- 通过
space_id
字段实现提示词编辑权限隔离 - 在提示词编辑操作中强制验证空间权限
// 提示词编辑工作空间权限验证
func (s *PromptApplicationService) validatePromptEditSpacePermission(ctx context.Context, promptID int64) error {userSpaceID := ctx.Value("space_id").(int64)// 获取提示词信息以验证空间权限prompt, err := s.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {return fmt.Errorf("获取提示词信息失败: %w", err)}if prompt.SpaceID != userSpaceID {return errors.New("无权限编辑该工作空间的提示词")}// 检查工作空间是否允许编辑提示词spaceConfig, err := s.spaceService.GetSpaceConfig(ctx, userSpaceID)if err != nil {return err}if !spaceConfig.AllowPromptEditing {return errors.New("该工作空间不允许编辑提示词")}return nil
}
8.3 提示词编辑资源级权限验证
提示词编辑所有权验证:
- 验证用户是否为提示词的创建者或协作者
- 创建者和授权协作者可以编辑提示词
- 通过
creator_id
和协作者列表进行权限判断
// 提示词编辑权限验证
func (s *PromptApplicationService) validatePromptEditPermission(ctx context.Context, promptID int64) error {userID := ctx.Value("user_id").(int64)// 获取提示词信息prompt, err := s.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return errorx.New(errno.ErrPromptNotFoundCode, errorx.KV("prompt_id", promptID))}return fmt.Errorf("获取提示词信息失败: %w", err)}// 检查是否为创建者(创建者可以编辑)if prompt.CreatorID == userID {return nil}// 检查是否为协作者isCollaborator, err := s.checkPromptCollaborator(ctx, promptID, userID)if err != nil {return fmt.Errorf("检查协作者权限失败: %w", err)}if !isCollaborator {return errorx.New(errno.ErrPromptPermissionDeniedCode, errorx.KV("msg", "只有创建者或协作者可以编辑提示词"),errorx.KV("prompt_id", promptID),errorx.KV("creator_id", prompt.CreatorID),errorx.KV("user_id", userID))}// 检查提示词状态是否允许编辑if prompt.Status == entity.PromptStatusDeleted {return errorx.New(errno.ErrPromptDeletedCode, errorx.KV("prompt_id", promptID))}// 检查是否被其他用户锁定编辑isLocked, err := s.checkPromptEditLock(ctx, promptID, userID)if err != nil {return fmt.Errorf("检查编辑锁失败: %w", err)}if isLocked {return errorx.New(errno.ErrPromptLockedCode, errorx.KV("prompt_id", promptID),errorx.KV("msg", "提示词正在被其他用户编辑"))}return nil
}
9. 提示词编辑错误处理和日志记录
9.1 提示词编辑分层错误处理机制
提示词编辑错误分类体系:
// 提示词编辑错误类型定义
type PromptEditErrorType intconst (// 提示词编辑业务错误ErrPromptEditBusiness PromptEditErrorType = iota + 1000ErrPromptNotFoundErrPromptLockedErrPromptPermissionDeniedErrPromptEditConflictErrPromptEditRateLimitErrPromptValidationFailed// 提示词编辑系统错误ErrPromptEditSystem PromptEditErrorType = iota + 2000ErrPromptDatabaseConnectionErrPromptElasticSearchTimeoutErrPromptServiceUnavailableErrPromptEditEventPublishFailedErrPromptIndexUpdateFailed// 提示词编辑网络错误ErrPromptEditNetwork PromptEditErrorType = iota + 3000ErrPromptEditRequestTimeoutErrPromptEditConnectionRefusedErrPromptEditServiceDown
)
9.2 提示词编辑统一错误响应格式
// 提示词编辑错误响应结构
type PromptEditErrorResponse struct {Code int `json:"code"`Message string `json:"message"`Details string `json:"details,omitempty"`TraceID string `json:"trace_id"`PromptID int64 `json:"prompt_id"`Operation string `json:"operation"`CanRetry bool `json:"can_retry"`
}
10. 提示词编辑流程图
10.1 EditPrompt接口完整调用流程
用户登录 Coze 平台点击"资源库" → 选择提示词 → 点击"…" → "编辑"场景的后端处理流程:
用户点击"编辑" → 前端发起请求 → API网关路由 → Handler处理 → 业务服务层 → 数据持久化层 → 索引更新层 → 响应返回↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
前端编辑表单 HTTP PUT请求 路由匹配 参数验证 权限检查 MySQL更新 ES索引更新 JSON响应↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
/edit-prompt /api/plugin_api/ Handler 请求绑定 用户身份 prompt_ 事件发布 编辑结果edit_prompt 函数调用 参数校验 Session resource 异步处理 状态返回EditPrompt 验证 表更新 ES更新 ↓PromptApplicationService↓权限验证(创建者/协作者检查)↓编辑锁检查↓内容验证↓编辑频率限制检查↓数据库更新事务↓编辑事件发布↓返回编辑结果
11. 核心技术特点
11.1 提示词编辑的分层架构设计
清晰的职责分离:
- API层(prompt_handler.go):负责提示词编辑请求处理、参数验证、响应格式化
- 应用层(prompt_service.go):负责提示词编辑业务逻辑编排、权限验证、事务管理
- 领域层(prompt_domain.go):负责提示词编辑核心业务逻辑、内容验证、安全检查
- 基础设施层(prompt_repository.go):负责提示词数据更新、外部服务同步
11.2 提示词数据存储和索引技术
MySQL存储设计:
- 表结构:
prompt_resource
表专门存储提示词数据 - 索引优化:针对
space_id
、creator_id
、name
建立复合索引 - 事务支持:确保提示词编辑的ACID特性
- 版本控制:通过
version
字段实现乐观锁机制
ElasticSearch索引设计:
- 索引名称:
coze_prompt_resource
- 字段映射:针对提示词内容进行全文搜索优化
- 实时同步:通过事件机制实现数据库到ES的实时同步
- 索引更新:编辑提示词时同步更新ES索引数据
11.3 提示词编辑安全机制
多层次编辑验证:
- 权限验证:确保用户有编辑指定提示词的权限
- 编辑锁检查:防止并发编辑冲突
- 内容验证:验证编辑内容的格式和安全性
11.4 提示词事件驱动架构
事件类型定义:
type PromptEventType stringconst (PromptCreated PromptEventType = "prompt_created" // 提示词创建事件PromptUpdated PromptEventType = "prompt_updated" // 提示词更新事件PromptDeleted PromptEventType = "prompt_deleted" // 提示词删除事件
)
11.5 提示词编辑权限控制机制
多层次权限验证:
- 身份认证:JWT Token验证用户身份
- 所有权验证:验证用户是否为提示词的创建者或协作者
- 工作空间权限:验证用户在指定工作空间的编辑权限
- 编辑锁机制:防止并发编辑冲突
11.6 提示词编辑性能优化策略
数据库性能优化:
- 乐观锁机制:通过版本号防止编辑冲突
- 索引优化:为常用查询字段建立合适的索引
- 事务优化:合理使用事务确保编辑操作的原子性
缓存优化策略:
- Redis缓存:缓存提示词基本信息和权限数据
- 编辑锁缓存:使用Redis实现分布式编辑锁
- 缓存一致性:确保编辑操作后缓存数据的一致性
12. 总结
12.1 提示词编辑功能的架构优势
Coze提示词编辑功能采用了现代化的分层架构设计,具有以下显著优势:
1. 高可扩展性
- 分层架构设计使得提示词编辑各层职责清晰,便于独立扩展和维护
- 基于接口的依赖倒置设计支持不同存储引擎的灵活切换
- 事件驱动架构支持提示词编辑相关业务的异步处理,提高系统吞吐量
2. 高可用性
- 乐观锁机制提供编辑冲突检测和解决能力
- 异步事件处理确保提示词编辑主流程的稳定性
- 完善的错误处理和重试机制保证编辑操作的最终一致性
3. 高性能
- 编辑锁机制避免了不必要的数据库锁竞争
- 缓存策略提升编辑操作的响应速度
- 异步索引更新机制减少编辑操作对系统性能的影响
4. 高安全性
- 多层次的编辑权限验证机制(身份认证 + 所有权验证 + 协作者权限)
- 编辑锁检查防止并发编辑冲突
- 操作审计和日志记录确保编辑操作的可追溯性
12.2 提示词编辑功能的技术亮点
1. 智能化的编辑冲突处理
- 基于Redis的分布式编辑锁机制
- 乐观锁版本控制防止数据覆盖
- 实时编辑状态同步和冲突检测
2. 灵活的权限控制体系
- 创建者和协作者的分级权限管理
- 工作空间级别的权限隔离
- 细粒度的编辑权限控制
3. 事件驱动的数据同步
- 基于提示词编辑事件实现数据库到ES的实时同步
- 保证了编辑操作的最终一致性
- 支持事件重放和数据恢复机制
4. 完善的编辑安全机制
- 内容格式验证和安全检查
- 编辑频率限制防止滥用
- 敏感内容过滤和合规性检查
12.3 提示词编辑系统的扩展性和可维护性
扩展性设计:
- 编辑策略扩展:支持多种编辑模式(在线编辑、批量编辑、模板编辑)
- 功能扩展:基于接口设计支持新的编辑功能快速接入
- 业务扩展:事件驱动架构支持新的编辑业务场景的灵活集成
可维护性保障:
- 代码结构清晰:分层架构和领域驱动设计提高编辑逻辑的可读性
- 测试覆盖完善:单元测试和集成测试保证编辑功能的质量
- 监控体系完备:全链路追踪和编辑操作监控便于问题定位
通过以上的架构设计和技术实现,Coze提示词编辑功能为用户提供了高效、安全、可靠的提示词编辑管理服务,为AI应用开发中的提示词生命周期管理提供了强有力的基础设施支撑。该系统不仅满足了当前的编辑业务需求,还具备了良好的扩展性和可维护性,能够适应未来编辑策略和协作机制的发展需要。
编辑功能的核心价值:
- 协作效率:多用户协作编辑机制提升团队工作效率
- 操作便捷:直观的编辑界面和流畅的编辑体验
- 系统稳定:异步处理和事件驱动确保编辑操作不影响系统稳定性
- 数据安全:完整的权限控制和操作审计,保障数据安全
- 可追溯性:完整的编辑历史和版本管理,便于问题排查和内容恢复