接续主文档,本篇深入会话/记忆系统、CodeRunner 沙箱、Connector 发布渠道、OpenAPI 鉴权与 Chat SDK 嵌入。
主文档参见:coze-studio-原理与使用文档.md
目录
1. 会话与记忆系统
会话子系统是 Agent "有记忆"的根。Coze Studio 把它拆成三个清晰的子领域,加上独立的"记忆域",形成"短记忆 + 长记忆 + 结构化记忆"的三段式。
1.1 三个子领域(backend/domain/conversation/)
conversation/
├── conversation/ # 会话生命周期(谁、在哪个 connector、什么场景)
├── message/ # 消息明细(文本/图片/音视频)
└── agentrun/ # 一次 Agent 执行的快照与流程编排
| 子域 | 核心实体 | 职责 |
|---|---|---|
| conversation | Conversation { ID, AgentID, ConnectorID, SectionID, CreatorID, UserID, Scene } | 创建会话、按 Scene 区分发布渠道(Playground / OpenAPI / WebSDK)、UpdateSection() 创建新一轮上下文 |
| message | Message { Content, Role, MessageType, SectionID, RunID, Position, Status } | 消息持久化、多模态内容(Text/Image/Video/Audio)、双向游标分页(up/down)、按 RunID 批量加载 |
| agentrun | RunRecord、AgentRuntime | 每次 Agent 运行的快照(Status/Error/Usage),驱动多轮对话流程 |
1.2 SectionID:对话轮次的逻辑容器
这是 Coze 会话设计中最值得拿出来讲的概念。
- 关系:
Conversation 1 ── N Section ── N RunRecord ── N Message - 含义:Section 不是"一条消息",而是"用户点击新建对话"的一段上下文窗口
- 作用:给 Agent 注入历史时,只取当前 Section 的消息,实现"清空对话"而保留物理记录的能力
- API:
UpdateSection()生成新 SectionID,前端"重置"按钮就是它
1.3 多轮对话上下文构建链路
源码:backend/domain/conversation/agentrun/internal/run.go
1. 用户消息进入 → AgentRun() (POST /api/conversation/chat)
2. 创建 RunRecord → RunRecordRepo.Create() 持久化
3. 加载历史 → getHistory() 按 SectionID 取前序消息
4. 注入变量 → Variables Service 把系统变量/用户变量转 KV
5. 调用模型 → AgentRuntime.Run() 走 Eino runtime
6. 持久化输出 → Message Service 保存 Agent 响应
7. 流式推送 → SSE 实时返回 token / tool_call / done
AgentRuntime 是运行时上下文聚合器:
SetHistoryMsg(history)注入历史SetInput(userMsg)注入当次输入SetRunRecord(record)写回执行结果
1.4 记忆域(backend/domain/memory/)
记忆域和会话域故意分开,因为记忆是跨会话的。
1.4.1 Variables(变量记忆)
VariableMeta { Keyword, VariableType, Channel },Channel 区分变量来源:
| Channel | 用途 |
|---|---|
System | 系统变量(用户ID、当前时间、agent_id) |
Feishu | 飞书集成上下文 |
Location | 地理位置(只读) |
| 用户自定义 | Bot 创作者定义的 KV |
变量在工作流节点和 Agent Prompt 中通过 {{var_name}} 引用,运行时 ToVariables() 输出可用对象。
1.4.2 Database(结构化记忆)
Database 实体描述用户自定义表结构(FieldList),物理上拆成两份:
draft_database_info(草稿,编辑时使用)online_database_info(线上,发布后使用)
存储在 MySQL,通过 physicaltable.CreatePhysicalTable() 真的为每个 Database 建一张表。Agent 可通过工作流的 Database 节点(Query/Insert/Update/Delete)直接读写,也可通过 NL2SQL 用自然语言查表(由 infra/nl2sql/ 与 infra/sqlparser/ 提供)。
1.4.3 长期记忆(Long-term Memory)
源码扫描结果:当前开源版没有 mem0 风格的自动总结/向量化记忆。Coze 的"长期记忆"靠这三层组合实现:
- 会话级:Section/Message 持久化到 MySQL,可按需回放
- 变量级:Variables 存可命名的 KV
- 数据库级:Database 存结构化业务数据
要实现"自动从对话中提取事实"这种 mem0 能力,需要在 Application 层自己加 LLM 后处理钩子,或者基于知识库的 Indexer 节点写工作流。
1.5 数据流总览
用户消息
│
▼
Conversation (找/建)──UPDATE──► Section
│ │
▼ ▼
RunRecord (本次执行) 注入 Variables
│
▼
AgentRuntime
│ ┌──load history (Section 内全部 Message)
│ ├──inject variables
│ └──invoke Eino runtime ───► Tool / Plugin / Workflow / Knowledge
│
▼
Message 持久化 + SSE 流式输出
2. CodeRunner Python 沙箱(安全机制)
工作流的 Code 节点允许执行用户编写的 Python 代码,这是 Coze 公网部署最大的攻击面。Coze 用一个双层沙箱方案解决。
2.1 接口抽象(backend/infra/coderunner/)
type Runner interface {
Run(ctx context.Context, request *RunRequest) (*RunResponse, error)
}
两种实现可以在配置里切换:
| 实现 | 用途 | 安全级别 |
|---|---|---|
direct.Runner | 开发/测试模式直接执行 | 不安全(只用于本地) |
sandbox.Runner | 生产环境唯一选择 | Deno + Pyodide 双层沙箱 |
2.2 沙箱方案:Deno + Pyodide
源码:backend/infra/coderunner/impl/script/sandbox.py
cmd = [
"deno", "run",
"--allow-env=...", # 环境变量白名单
"--allow-read=...", # 文件读白名单
"--allow-write=...", # 文件写白名单
"--allow-net=...", # 网络白名单
"--v8-flags=--max-old-space-size=...", # 内存上限
PKG_NAME, # @langchain/pyodide-sandbox@0.0.4
"--code", code,
]
subprocess.run(cmd, timeout=timeout_seconds, capture_output=True)
双层隔离的好处
- Deno 层:Deno 自带的 capability-based 安全模型,默认零权限,所有
--allow-*必须显式给 - Pyodide WASM 层:Python 解释器跑在 WebAssembly 里,即使 CPython 自身有漏洞也跳不出 WASM 沙箱
- 进程层:
subprocess.run()本身是独立进程,有 timeout 强制 kill
2.3 配置项(backend/infra/coderunner/)
type Config struct {
AllowEnv []string // 环境变量白名单
AllowRead []string // 文件读白名单
AllowWrite []string // 文件写白名单
AllowNet []string // 网络访问白名单(支持 CIDR、域名)
AllowRun []string // 可执行命令白名单
AllowFFI []string // FFI 库白名单
TimeoutSeconds float64 // 执行超时
MemoryLimitMB int64 // V8 堆内存上限
}
默认配置非常保守:AllowNet 为空 → 代码完全不能访问网络;AllowRead/Write 仅限 node_modules。
2.4 工作流 Code 节点
源码:backend/domain/workflow/internal/nodes/code/code.go
- 当前只支持 Python,JavaScript 占位但未实现(
return nil, fmt.Errorf("js not supported yet")) - 入参通过
args.params字典访问 - 出参必须是字典(自动映射到节点 Output)
- 内置模块白名单(json、asyncio 等)、黑名单(curses、tkinter、syslog 等高危模块)
2.5 部署建议
公网部署时务必额外:
- coze-server 禁止访问内网网段(Iptables/安全组)
- 限制
AllowNet到必需的外部 API 域名 - 缩小
TimeoutSeconds(默认值偏宽松) - 容器层用 seccomp/AppArmor 进一步限制
3. Connector 发布渠道
Connector 是 Coze 把 Bot 触达终端用户的最后一公里抽象。
3.1 三类 Connector(backend/domain/connector/)
| Connector | ID 常量 | 用途 |
|---|---|---|
| WebSDK | consts.WebSDKConnectorID | JavaScript SDK 嵌入网页/应用 |
| API | consts.APIConnectorID | OpenAPI 调用,适合服务端集成 |
| Coze | consts.CozeConnectorID | Coze 平台原生(开源版主要是 Playground) |
商业版还有微信/飞书/Telegram 等,开源版未包含。
实现:backend/domain/connector/service/connector_impl.go
3.2 发布完整流程
┌────────────────────────────────────────────────────────────┐
│ 1. 用户在 IDE 点击"发布" │
│ POST /api/bot/publish { bot_id, connectors[] } │
└─────────────────────┬──────────────────────────────────────┘
▼
┌────────────────────────────────────────────────────────────┐
│ 2. 应用层 PublishApp / PublishBot │
│ - 创建 single_agent_publish 记录(版本快照) │
│ - 为每个 Connector 创建 connector_workflow_version │
│ - 生成调用凭证(API Connector → API Key, │
│ WebSDK Connector → SDK 配置) │
└─────────────────────┬──────────────────────────────────────┘
▼
┌────────────────────────────────────────────────────────────┐
│ 3. 外部访问 │
│ - API: POST /v3/chat (带 Bearer PAT) │
│ - WebSDK: <script src=".../coze-chat.js"> 初始化 │
└────────────────────────────────────────────────────────────┘
3.3 跨 Connector 隔离
每个 Conversation 都带 ConnectorID,Message / RunRecord 也都用 ConnectorID 索引,这样:
- 同一个 Bot 在 API 和 WebSDK 上的对话互相不可见
- 商业版可以做按 Connector 计费、按 Connector 分流量
4. OpenAPI 与 PAT 鉴权
4.1 IDL 定义位置
OpenAPI 端点完全由 Thrift IDL 定义:
4.2 完整 OpenAPI 端点表
| 端点 | 方法 | 用途 |
|---|---|---|
/v3/chat | POST | 开启对话(支持 stream) |
/v3/chat/cancel | POST | 中断对话 |
/v3/chat/retrieve | GET | 查询对话详情 |
/v3/chat/message/list | GET | 列举对话消息 |
/v1/conversation/create | POST | 显式创建会话 |
/v1/conversations | GET | 列举会话 |
/v1/workflow/run | POST | 同步运行工作流 |
/v1/workflow/stream_run | POST | 流式运行工作流 |
/v1/workflow/stream_resume | POST | 中断后恢复工作流 |
/v1/files/upload | POST | 文件上传 |
4.3 PAT(Personal Access Token)生命周期
源码:backend/domain/openauth/openapiauth/、backend/api/middleware/openapi_auth.go
生成
CreateApiKey { Name, Expire, UserID, AkType }:
- 后端生成随机 Token
- MD5 哈希后存库,原始 Token 仅在创建时返回一次给用户(类似 GitHub PAT)
- 用户必须自己保管,丢失只能重新生成
校验(中间件)
// openapi_auth.go 核心逻辑
md5Hash := md5.Sum([]byte(apiKey))
md5Key := hex.EncodeToString(md5Hash[:])
apiKeyInfo, err := openauth.OpenAuthApplication.CheckPermission(ctx, md5Key)
// apiKeyInfo 含 CreatorID、ConnectorID,后续业务用它做权限判定
请求头 Authorization: Bearer pat_xxxxx,中间件提取 → 哈希 → 查库 → 注入 ctx。
4.4 ChatV3 协议
请求(ChatV3Request)
{
"bot_id": "...",
"user_id": "end_user_xxx",
"conversation_id": "...", // 可选,不传则新建
"stream": true,
"additional_messages": [...], // 当次输入
"custom_variables": {...}, // 注入到 Variables 域
"connector_id": "..."
}
流式响应(SSE 事件类型)
| 事件 | 含义 |
|---|---|
conversation.chat.created | 对话已创建 |
conversation.chat.in_progress | 执行中 |
conversation.message.delta | 消息增量(逐字 token) |
conversation.message.completed | 单条消息完成 |
conversation.chat.required_action | 需要动作(Function Call 暂停) |
conversation.chat.completed | 对话完成 |
conversation.chat.failed | 失败 |
conversation.chat.cancelled | 已取消 |
conversation.error | 错误 |
事件常量定义在 backend/domain/conversation/agentrun/entity/const.go。
4.5 SSE 实现
源码:backend/infra/sse/impl/sse/sse.go、backend/api/handler/coze/agent_run_service.go
c.SetStatusCode(http.StatusOK)
c.Response.Header.Set("X-Accel-Buffering", "no") // 关键:禁用 Nginx 缓冲
sseSender := sseImpl.NewSSESender(sse.NewStream(c))
err = conversation.ConversationOpenAPISVC.OpenapiAgentRun(ctx, sseSender, &req)
X-Accel-Buffering: no 是关键:Nginx 默认会缓冲响应,导致 SSE 不能实时下发。Coze Web 容器的 Nginx 配置也需对应放开。
4.6 中断-恢复(工作流场景)
工作流 QuestionAnswer 节点暂停时:
- 节点抛出
WorkflowInterrupted,附带InterruptEvent { NodeKey, NodeType, NodeTitle, InterruptData } repo.SaveInterruptEvents(ctx, rootExecuteID, events)把事件序列化存库- 同时
CheckPointStore.Save()把 Eino runtime 的执行快照写入 Redis - 前端调用
POST /v1/workflow/stream_resume,带event_id与用户回复 repo.PopFirstInterruptEvent(ctx, exeID)取出待处理事件- Eino 从 Checkpoint 恢复,从断点继续执行
Checkpoint Store(Redis)
源码:backend/infra/checkpoint/redis.go
- Key 格式:
checkpoint_key:{checkpointID}(UUID) - Value:Eino Compose 框架的执行快照(二进制序列化)
- TTL:7 天(604800 秒),意味着工作流可以被人类暂停最长 7 天后再恢复
4.7 多模型抽象与 Function Call
源码:backend/bizpkg/llm/modelbuilder/、backend/conf/model/template/
接口收敛
// 所有模型最终都是这两个 Eino 接口
type BaseChatModel = model.BaseChatModel
type ToolCallingChatModel = model.ToolCallingChatModel
每家协议有自己的 Builder(openaiModelBuilder、arkModelBuilder、claudeModelBuilder...),Build() 返回统一的 ToolCallingChatModel,Function Call 协议差异在 Builder 内部转换,上层无感知。
模型能力声明
backend/conf/model/template/*.yaml:
capability:
function_call: true
image_understanding: true
video_understanding: false
json_mode: false
support_multi_modal: true
input_tokens: 128000
output_tokens: 16384
工作流 LLM 节点和 Agent runtime 在创建模型时读这些字段,决定:
- 是否注册 Tool(只有
function_call: true才挂工具) - 是否启用 JSON Mode 输出
- 是否允许图片/视频输入
5. Chat SDK 嵌入
5.1 前端 SDK 包结构
| 包 | 职责 |
|---|---|
| frontend/packages/arch/foundation-sdk/ | 基础设施(账户、布局、用户系统) |
| frontend/packages/common/chat-area/ | 可复用聊天 UI(消息、输入、操作栏) |
| frontend/packages/common/chat-core/ | 聊天协议核心(SSE 解析、状态机) |
| frontend/packages/workflow/sdk/ | 工作流客户端 |
5.2 嵌入方式
开源版不直接提供 1 行集成的 <script> SDK(那是 coze.cn 商业版的能力),但提供构建素材:
- 开发者可自行打包:基于
chat-area+chat-core自己打个嵌入式 bundle - 直接调 OpenAPI:简单场景直接前端调
/v3/chat,处理 SSE,自己渲染 UI - iframe 嵌入:把整个 coze-web 的
/chat?bot_id=xxx路径嵌进自己页面(最快,但不够定制)
5.3 通信协议
| 场景 | 协议 |
|---|---|
| 流式对话 | SSE(text/event-stream; charset=utf-8) |
| 历史/管理操作 | REST + JSON |
| WebSocket | 当前代码未使用 |
SSE 是 Coze 的统一选择,理由:
- 比 WebSocket 简单,HTTP/1.1 上就能跑
- 单向(服务器→客户端)足以满足 LLM 流式输出
- 走标准 HTTP,代理/CDN 友好
关键路径速查
| 主题 | 路径 |
|---|---|
| 会话 conversation | backend/domain/conversation/conversation/ |
| 消息 message | backend/domain/conversation/message/ |
| Agent 运行 | backend/domain/conversation/agentrun/internal/run.go |
| 变量 variables | backend/domain/memory/variables/ |
| 数据库 database | backend/domain/memory/database/ |
| CodeRunner | backend/infra/coderunner/ |
| 沙箱脚本 | backend/infra/coderunner/impl/script/sandbox.py |
| Code 节点 | backend/domain/workflow/internal/nodes/code/code.go |
| Connector | backend/domain/connector/service/connector_impl.go |
| OpenAPI 鉴权中间件 | backend/api/middleware/openapi_auth.go |
| PAT 实体 | backend/domain/openauth/openapiauth/ |
| ChatV3 IDL | idl/conversation/run.thrift |
| Bot OpenAPI IDL | idl/app/bot_open_api.thrift |
| SSE 实现 | backend/infra/sse/impl/sse/sse.go |
| Checkpoint Store | backend/infra/checkpoint/redis.go |
| 模型 Builder | backend/bizpkg/llm/modelbuilder/ |
| 模型模板 | backend/conf/model/template/ |
| Chat SDK 包 | frontend/packages/arch/foundation-sdk/ |
| Chat 核心 | frontend/packages/common/chat-core/ |