Session 来源与类型

如何为你的 Session 选择分类与角色,以及它们决定什么

每个 Session 都有一个来源(origin),由两个字段组成:category 和 type。它们回答两个不同的问题,你需要分别决定。

category 是分类,只有 chat 和 work 两个值。它决定会话存在哪个物理分区,也决定这个会话能不能派发工作。type 是角色,说明这条会话具体是什么。分类回答「它在哪一层」,角色回答「它是什么」。

该用哪个分类

问自己一件事:这条会话是用来对话,还是用来干活。

你要做的事分类角色
和用户持续对话(你的产品主界面)chatagent
接入外部聊天渠道(Telegram、飞书等)chatchannel
群聊里的成员会话chatgroup
浏览器扩展里的问答chatchrome
执行一件明确的工作workwork
由任务调度触发的一次执行worktask

不传 origin 时默认是 { category: "work", type: "work" },也就是一条普通工作会话。

分类决定的两件事

物理分区。 会话落在 sessions/<category>/<session_id>/。分类只有两个,所以一个 Agent 的会话目录最多两层。同一个 session_id 可以分别存在于 chat 和 work 下——它们互不冲突,因为身份由分类和 ID 共同组成。

能不能派发工作。 chat 类的会话可以开启、检查、答复、停止其他会话,也可以归档。work 类的会话专注做被派发的那件事,不再递归派发。归档只有 work 类能做。

角色不参与分区的形成,它只影响具体的行为分支,例如外部聊天要选平台 prompt、群聊成员要校验归属。

开一条对话会话

const session = await agent.sessions.create({
  origin: { category: "chat", type: "agent" },
  workspace,
});

派一件事,让它干活

const work = await agent.sessions.create({
  origin: { category: "work", type: "work" },
  workspace,
});

const turn = await work.prompt({ query: "把这份报告整理成表格" });
await turn.finished;

按分类与角色查会话

list 的第一个维度是分类;分类不足以区分角色时再加角色。

// 某个分类下的全部会话
const conversations = await agent.sessions.list({ category: "chat" });

// 只看 Agent 主会话
const main = await agent.sessions.list({
  category: "chat",
  origin_type: "agent",
});

恢复一个已有会话时传的也是分类:

const session = await agent.sessions.get(session_id, "chat", { workspace });

自定义角色

已知的六个角色覆盖了常见场景。如果你的产品有自己的会话语义,可以声明自定义角色,但必须同时给出分类,否则创建会直接失败:

const session = await agent.sessions.create({
  origin: {
    category: "work",
    type: "automation/daily",
    schedule_id: "daily-report",
  },
});

分类必须在 chat 和 work 之间选一个,因为它是唯一决定分区与派发能力的字段。角色名可以是任意非空字符串,两者之外的字段会随来源一起持久化,由你自己解释。

这个约束是有意的:新增一个角色是零成本的,新增一个分区需要显式表达,不会因为写错一个字符串就静默落到别的目录里。