Session 是 Qoder Cloud Agents 中实际执行任务的单元。它将 Agent(配置)和 Environment(基础设施)组合在一起,形成一个有状态的工作会话。你向 Session 发送消息,它处理后返回事件流。
Session 生命周期
创建 → idle
新 Session 进入 idle,等待输入。
idle → processing
发送 user.message 事件后,状态切换到 processing。
processing → idle
本轮完成后回到 idle,可继续下一轮。
processing → canceling → idle
取消正在执行的 Session 后,状态先经过 canceling,再回到 idle,Session 仍可继续使用。
archived(终态)
归档是终态,Session 不再可恢复。
Session 是一个状态机,核心状态如下:
| 状态 | 说明 | 可流转到 |
|---|
idle | 空闲,等待用户消息 | processing |
processing | 正在处理,Agent 执行中 | canceling, idle |
canceling | 取消指令已发出,等待中断完成 | idle |
archived | 已归档 | — (终态) |
字段说明
| 字段 | 类型 | 说明 |
|---|
id | string | 系统生成,sess_ 前缀 |
type | string | 固定为 "session" |
agent | string/object | 绑定的 Agent(字符串=最新版本,对象 {id,version}=锁版本)。创建请求只接受此字段。响应始终返回完整的 Agent 对象快照 |
agent_id | string | 仅在响应中返回,等于绑定的 Agent ID;为向后兼容保留 |
environment_id | string | 绑定的 Environment ID |
status | string | 当前状态:idle / processing / canceling |
turn_status | string | 当前轮次状态:idle / running / canceling |
title | string | 会话标题,默认 "" |
memory_store_ids | array | 关联的 Memory Store ID 列表,默认 [] |
vault_ids | array | 关联的 Vault ID 列表,默认 [] |
resources | array | 附加到 Session 的资源(文件等),默认 [] |
environment_variables | object | 归一化后的 Session 级环境变量,默认 {} |
created_at | string | 创建时间 |
updated_at | string | 最后更新时间 |
Token 用量数据(usage)不在 REST 接口响应中返回——它仅出现在每轮结束的 SSE session.status_idle 事件中。
创建 Session
创建 Session 时需要指定 agent 和 environment_id:
绑定该 Agent 的最新版本:# 使用 Agent ID 创建 Session
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428"
}' | jq .
绑定 Agent 的特定版本,确保行为一致性:# 指定 Agent 版本创建 Session
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": {
"id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"version": 2
},
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428"
}' | jq .
成功返回 201 Created:
{
"id": "sess_019e5ce0bf9074b69c3481e93771a522",
"agent": {
"created_at": "2026-05-18T10:00:00Z",
"description": "",
"id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"mcp_servers": [],
"model": "ultimate",
"name": "code-reviewer",
"system": "你是代码审查专家。",
"tools": [
{
"type": "agent_toolset_20260401",
"enabled_tools": ["Bash", "Read", "Write"]
}
],
"type": "agent",
"updated_at": "2026-05-18T10:00:00Z",
"version": 2
},
"agent_id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"status": "idle",
"title": "",
"turn_status": "idle",
"memory_store_ids": [],
"resources": [],
"vault_ids": [],
"environment_variables": {},
"type": "session",
"created_at": "2026-05-18T12:00:00Z",
"updated_at": "2026-05-18T12:00:00Z"
}
在生产环境中建议使用指定版本形式,避免因 Agent 更新导致行为变化。
环境变量
创建 Session 时可以通过 environment_variables 传入 Session 级环境变量。请求值必须是一个用分号或换行分隔的 KEY=VALUE 字符串:
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"environment_variables": "NODE_ENV=production; FEATURE_FLAG=1"
}' | jq .
响应中会返回归一化后的对象形式:
{
"environment_variables": {
"NODE_ENV": "production",
"FEATURE_FLAG": "1"
}
}
变量名必须匹配 [A-Za-z_][A-Za-z0-9_]*。服务端会拒绝重复变量名、超过 64 个变量、单个值超过 8 KiB、变量名和值合计超过 64 KiB、保留名称 SERVER_ENDPOINT、USER_ID、WORK_DIR,以及 CAW_、QODER_ 前缀。self-hosted Environment 不支持该字段。
创建时附带资源
创建 Session 时可以通过 resources、memory_store_ids、vault_ids 三个字段挂载 4 种资源,让 Agent 在启动后即可访问代码、文件、记忆和凭证。
| 资源类型 | 请求字段 | 用途 |
|---|
| 文件 | resources[] (type: "file") | 挂载已上传的文件供 Agent 读取 |
| GitHub 仓库 | resources[] (type: "github_repository") | 自动克隆仓库到容器中 |
| 记忆存储 | memory_store_ids | 关联 Memory Store,Agent 可读写长期记忆 |
| 密钥库 | vault_ids | 关联 Vault,为 Agent 提供凭证(如 API Key) |
文件资源
通过 Files API 上传文件后,将 file_id 传入 resources 数组:
| 字段 | 类型 | 必填 | 说明 |
|---|
type | string | 是 | 固定为 "file" |
file_id | string | 是 | 文件 ID(通过 Files API 上传后获得) |
path | string | 否 | 文件在容器中的挂载路径,默认 /data/{file_id} |
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"resources": [
{"type": "file", "file_id": "file_019e392c0d1e74e095d21ea4c6b41def"}
]
}' | jq .
GitHub 仓库资源
Agent 启动后会自动将仓库克隆到容器中,无需手动上传代码:
| 字段 | 类型 | 必填 | 说明 |
|---|
type | string | 是 | 固定为 "github_repository" |
url | string | 是 | 仓库 URL(HTTP 或 HTTPS) |
mount_path | string | 否 | 克隆到容器中的路径,默认根据仓库名自动推断 |
authorization_token | string | 是 | GitHub Personal Access Token,用于访问仓库 |
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"resources": [
{
"type": "github_repository",
"url": "https://github.com/your-org/your-repo"
}
]
}' | jq .
记忆存储
关联 Memory Store 后,Agent 可在会话中读写长期记忆:
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"memory_store_ids": ["ms_019e5ce0bf307a1a8f952eb814aea3d5"]
}' | jq .
密钥库(Vault)
关联 Vault 后,Agent 可以在运行时访问存储的凭证:
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"vault_ids": ["vault_019e5ce0bf307a1a8f952eb814aea3d5"]
}' | jq .
综合示例
一次请求同时挂载多种资源:
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"agent": {"id": "agent_019e5ce0bf307a1a8f952eb814aea3d5", "version": 2},
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"resources": [
{"type": "github_repository", "url": "https://github.com/your-org/your-repo"},
{"type": "file", "file_id": "file_019e392c0d1e74e095d21ea4c6b41def"}
],
"memory_store_ids": ["ms_019e5ce0bf307a1a8f952eb814aea3d5"],
"vault_ids": ["vault_019e5ce0bf307a1a8f952eb814aea3d5"]
}' | jq .
agent 字段格式
| 格式 | 示例 | 行为 |
|---|
| 字符串 | "agent_019e5ce0bf307a1a8f952eb814aea3d5" | 使用该 Agent 最新版本 |
| 对象 | {"id": "agent_019e5ce0bf307a1a8f952eb814aea3d5", "version": 2} | 锁定到指定版本 |
状态流转
事件请求体格式
POST /sessions/{id}/events 的请求体必须使用 events 数组包装,content 是内容块数组:
| 字段 | 类型 | 必填 | 说明 |
|---|
events | array | 是 | 事件数组,单次请求可包含一个或多个事件 |
events[].type | string | 是 | 事件类型,如 user.message |
events[].content | array | 是 | 内容块数组 |
events[].content[].type | string | 是 | 内容块类型,如 text |
events[].content[].text | string | 是 | 文本内容 |
idle → processing
当你向 Session 发送 user.message 事件时,状态从 idle 变为 processing:
# 发送消息触发处理
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522/events" \
-H "Authorization: Bearer $QODER_PAT" \
-H "Content-Type: application/json" \
-d '{
"events": [{
"type": "user.message",
"content": [{"type": "text", "text": "分析当前目录下所有 Python 文件的代码复杂度。"}]
}]
}' | jq .
processing → idle
Agent 完成处理后自动回到 idle,通过事件流会收到 session.status_idle 事件。
取消 Session
中断正在执行的 Session:
# 取消 Session
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522/cancel" \
-H "Authorization: Bearer $QODER_PAT"
仅在 processing 状态下 cancel 才有实际中断效果:状态先变为 canceling,中断完成后回到 idle,Session 可继续复用——直接发送下一条 user.message 即可。对已处于 idle 的 Session 调用 cancel 是空操作,同样返回 200,不会报错,状态保持 idle 不变。
查询 Session
# 获取单个 Session 详情
curl -s "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522" \
-H "Authorization: Bearer $QODER_PAT"
# 列出所有 Session(支持分页)
curl -s "https://api.qoder.com/api/v1/cloud/sessions?limit=10" \
-H "Authorization: Bearer $QODER_PAT"
分页响应:
{
"data": [
{
"id": "sess_019e5ce0bf9074b69c3481e93771a522",
"type": "session",
"agent_id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
"environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
"status": "idle",
"turn_status": "idle",
"title": "",
"memory_store_ids": [],
"vault_ids": [],
"environment_variables": {},
"resources": [],
"created_at": "2026-05-18T12:00:00Z",
"updated_at": "2026-05-18T12:30:00Z"
}
],
"first_id": "sess_019e5ce0bf9074b69c3481e93771a522",
"last_id": "sess_019e5ce0bf9074b69c3481e93771a522",
"has_more": false
}
Usage 统计
每轮的 token 用量不通过 REST 接口返回。要查看用量,请监听每轮结束的 SSE session.status_idle 事件,其 usage 字段携带累计计数:
| 字段 | 说明 |
|---|
usage.input_tokens | 本轮消耗的输入 token 数 |
usage.output_tokens | 本轮产生的输出 token 数 |
usage.cache_read_input_tokens | 命中缓存的输入 token 数 |
usage.cache_creation_input_tokens | 写入缓存的输入 token 数 |
SSE 事件中的示例 payload:
{
"type": "session.status_idle",
"usage": {
"input_tokens": 3840,
"output_tokens": 2156,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0
}
}
Session 与 Agent 版本绑定
Session 创建时会快照 Agent 的配置:
- 使用字符串形式
"agent": "agent_xxx" 绑定创建时刻的最新版本
- 使用对象形式
"agent": {"id": "agent_xxx", "version": N} 绑定精确版本
- Session 创建后,修改 Agent 不会影响已存在的 Session
- 想要使用新版 Agent,需要创建新的 Session
Session A — 绑定 Agent v1
在更新前创建。即使 Agent 后续更新到 v2,Session A 仍然继续使用 v1 配置。
Session B — 绑定 Agent v2
在更新后创建,使用新的 v2 配置。
多轮对话
Session 支持多轮对话。每次发送消息后等待 session.status_idle,然后继续发送:
#!/bin/bash
# 多轮对话示例
BASE_URL="https://api.qoder.com/api/v1/cloud"
SESSION_ID="sess_019e5ce0bf9074b69c3481e93771a522"
HEADERS=(
-H "Authorization: Bearer $QODER_PAT"
)
# 第一轮:提出需求
curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/events" \
"${HEADERS[@]}" \
-H "Content-Type: application/json" \
-d '{"events": [{"type": "user.message", "content": [{"type": "text", "text": "创建一个 Python Flask 项目脚手架。"}]}]}'
# 等待处理完成...(轮询或监听 SSE)
sleep 30
# 第二轮:追加要求
curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/events" \
"${HEADERS[@]}" \
-H "Content-Type: application/json" \
-d '{"events": [{"type": "user.message", "content": [{"type": "text", "text": "给项目添加单元测试和 CI 配置。"}]}]}'
状态相关错误码
向 Session 发送事件时,常见的状态相关错误:
| HTTP | type | 触发条件 |
|---|
| 409 | conflict_error | 向 processing 状态的 Session 发消息(需先 cancel 当前轮或等待 idle) |
| 404 | not_found_error | Session 不存在或已被删除 |
409 错误响应示例:
{
"type": "error",
"error": {
"type": "conflict_error",
"message": "Session is currently processing a turn. Cancel the current turn or wait for completion."
}
}
最佳实践
- 版本锁定 — 生产环境始终使用
{"id": ..., "version": ...} 形式创建 Session
- 及时取消 — 不再需要的 Session 及时 cancel,释放资源
- 监控用量 — 定期检查
usage 字段,避免意外消耗
- 元数据标记 — 用
metadata 记录业务上下文(任务 ID、触发来源等)
常见问题
Q: Session 有超时机制吗?
A: Session 在 idle 状态下会保持一段时间。长时间不活跃的 Session 可能被系统自动归档。建议按需创建,任务完成后主动 cancel。
Q: 向 processing 状态的 Session 发消息会怎样?
A: 会返回 HTTP 409 conflict_error,错误信息为 Session is currently processing a turn. Cancel the current turn or wait for completion.。需要先取消当前轮(cancel)或等待 Session 回到 idle,再发送新消息。
Q: 一个 Session 最多支持多少轮对话?
A: 没有硬性轮次限制,但受模型上下文窗口大小约束。随着对话增长,早期内容可能被截断。
Q: 如何获取 Session 的完整对话历史?
A: 通过 GET /sessions/{id}/events 获取该 Session 的所有事件,包括用户消息和 Agent 响应。
Q: 取消后的 Session 还能继续用吗?
A: 可以。cancel 后状态从 canceling 自动回到 idle,Session 保持可用——直接发送下一条 user.message 即可继续对话。仅 archived 是终态,归档后无法恢复。
Q: 长时间不用的 Session,容器磁盘上的文件还在吗?
A: 不一定。容器临时存储仅保留 24 小时,超时后磁盘可能被回收。Session 本身不受影响,可以继续对话,但之前在磁盘上产生的文件(代码、中间产物等)将丢失。如需跨天保留文件,请在每轮结束时通过 Files API 上传关键产物。详见 容器参考 - 文件持久化。
下一步