Coze Studio 深度文档 02:会话、记忆与执行原理

3 阅读11分钟

接续主文档,本篇深入会话/记忆系统、CodeRunner 沙箱、Connector 发布渠道、OpenAPI 鉴权与 Chat SDK 嵌入。

主文档参见:coze-studio-原理与使用文档.md

目录

  1. 会话与记忆系统
  2. CodeRunner Python 沙箱(安全机制)
  3. Connector 发布渠道
  4. OpenAPI 与 PAT 鉴权
  5. Chat SDK 嵌入

1. 会话与记忆系统

会话子系统是 Agent "有记忆"的根。Coze Studio 把它拆成三个清晰的子领域,加上独立的"记忆域",形成"短记忆 + 长记忆 + 结构化记忆"的三段式。

1.1 三个子领域(backend/domain/conversation/)

conversation/
├── conversation/   # 会话生命周期(谁、在哪个 connector、什么场景)
├── message/        # 消息明细(文本/图片/音视频)
└── agentrun/       # 一次 Agent 执行的快照与流程编排
子域核心实体职责
conversationConversation { ID, AgentID, ConnectorID, SectionID, CreatorID, UserID, Scene }创建会话、按 Scene 区分发布渠道(Playground / OpenAPI / WebSDK)、UpdateSection() 创建新一轮上下文
messageMessage { Content, Role, MessageType, SectionID, RunID, Position, Status }消息持久化、多模态内容(Text/Image/Video/Audio)、双向游标分页(up/down)、按 RunID 批量加载
agentrunRunRecordAgentRuntime每次 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 的"长期记忆"靠这三层组合实现:

  1. 会话级:Section/Message 持久化到 MySQL,可按需回放
  2. 变量级:Variables 存可命名的 KV
  3. 数据库级: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)

双层隔离的好处

  1. Deno 层:Deno 自带的 capability-based 安全模型,默认零权限,所有 --allow-* 必须显式给
  2. Pyodide WASM 层:Python 解释器跑在 WebAssembly 里,即使 CPython 自身有漏洞也跳不出 WASM 沙箱
  3. 进程层: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 部署建议

公网部署时务必额外:

  1. coze-server 禁止访问内网网段(Iptables/安全组)
  2. 限制 AllowNet 到必需的外部 API 域名
  3. 缩小 TimeoutSeconds(默认值偏宽松)
  4. 容器层用 seccomp/AppArmor 进一步限制

3. Connector 发布渠道

Connector 是 Coze 把 Bot 触达终端用户的最后一公里抽象。

3.1 三类 Connector(backend/domain/connector/)

ConnectorID 常量用途
WebSDKconsts.WebSDKConnectorIDJavaScript SDK 嵌入网页/应用
APIconsts.APIConnectorIDOpenAPI 调用,适合服务端集成
Cozeconsts.CozeConnectorIDCoze 平台原生(开源版主要是 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/chatPOST开启对话(支持 stream)
/v3/chat/cancelPOST中断对话
/v3/chat/retrieveGET查询对话详情
/v3/chat/message/listGET列举对话消息
/v1/conversation/createPOST显式创建会话
/v1/conversationsGET列举会话
/v1/workflow/runPOST同步运行工作流
/v1/workflow/stream_runPOST流式运行工作流
/v1/workflow/stream_resumePOST中断后恢复工作流
/v1/files/uploadPOST文件上传

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.gobackend/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 节点暂停时:

  1. 节点抛出 WorkflowInterrupted,附带 InterruptEvent { NodeKey, NodeType, NodeTitle, InterruptData }
  2. repo.SaveInterruptEvents(ctx, rootExecuteID, events) 把事件序列化存库
  3. 同时 CheckPointStore.Save() 把 Eino runtime 的执行快照写入 Redis
  4. 前端调用 POST /v1/workflow/stream_resume,带 event_id 与用户回复
  5. repo.PopFirstInterruptEvent(ctx, exeID) 取出待处理事件
  6. 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(openaiModelBuilderarkModelBuilderclaudeModelBuilder...),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 友好

关键路径速查

主题路径
会话 conversationbackend/domain/conversation/conversation/
消息 messagebackend/domain/conversation/message/
Agent 运行backend/domain/conversation/agentrun/internal/run.go
变量 variablesbackend/domain/memory/variables/
数据库 databasebackend/domain/memory/database/
CodeRunnerbackend/infra/coderunner/
沙箱脚本backend/infra/coderunner/impl/script/sandbox.py
Code 节点backend/domain/workflow/internal/nodes/code/code.go
Connectorbackend/domain/connector/service/connector_impl.go
OpenAPI 鉴权中间件backend/api/middleware/openapi_auth.go
PAT 实体backend/domain/openauth/openapiauth/
ChatV3 IDLidl/conversation/run.thrift
Bot OpenAPI IDLidl/app/bot_open_api.thrift
SSE 实现backend/infra/sse/impl/sse/sse.go
Checkpoint Storebackend/infra/checkpoint/redis.go
模型 Builderbackend/bizpkg/llm/modelbuilder/
模型模板backend/conf/model/template/
Chat SDK 包frontend/packages/arch/foundation-sdk/
Chat 核心frontend/packages/common/chat-core/