--- name: stello-agent-usage description: StelloAgent 运行时使用教程。覆盖 Session 生命周期、createSession、turn/stream 对话、fork 配置合成链、orchestrator-facing 数据 SDK、runtime 管理、热更新等运行时 API。 --- # StelloAgent 运行时使用教程 > 前置知识:`createStelloAgent(config)` 的配置方式见 skill `stello-agent-creation`。 > 本文档聚焦于 Agent 构建完成后的**运行时操作**。 --- ## 1. Root Session 创建 对话起点是一个普通 Session(`parentId === null`),由 `agent.createSession()` 创建——**不传** `parentId` 即为新 root。 ```typescript const root = await agent.createSession({ label: 'Main' }) await agent.enterSession(root.id) ``` `agent.createSession({ parentId?, label? })` 做了什么: 1. 调用 `sessions.createSession({ parentId, label })` 创建拓扑节点(`parentId` 缺省即 `parentId === null`) 2. 返回 `TopologyNode`(含 `id / parentId / children / refs / depth / label` 等) Root 没有特殊待遇——它就是一个普通 Session。多个 root 合法(森林)。全局默认 systemPrompt / skills 等配置在 `sessionDefaults` 即可。 --- ## 2. Session 生命周期 ``` createSession → enterSession → turn / stream (× N) → leaveSession → archiveSession ``` ### 2.1 进入 Session ```typescript const bootstrap = await agent.enterSession(sessionId) // bootstrap.context — 组装好的上下文(MemoryEngine 视角) // bootstrap.session — SessionMeta(id, label, status, turnCount 等) ``` **行为**:触发 `lifecycle.bootstrap()`,初始化 Engine runtime。如果该 session 已有活跃 Engine,复用而非重建。 ### 2.2 运行对话轮次(turn) ```typescript const result = await agent.turn(sessionId, '帮我分析市场趋势') // result.turn.finalContent — 最终文本回复(tool loop 结束后) // result.turn.toolRoundCount — 经历了几轮 tool call 循环 // result.turn.toolCallsExecuted — 实际执行了多少个 tool // result.turn.rawResponse — 原始最终 LLM 响应 ``` ### 2.3 流式模式(stream) ```typescript const streamResult = await agent.stream(sessionId, '帮我分析市场趋势') for await (const chunk of streamResult) { process.stdout.write(chunk) } const result = await streamResult.result console.log(result.turn.finalContent) ``` 若本轮包含 tool call,iterator 会按产生顺序继续输出每个 LLM 子轮的文本;工具执行期间暂时没有 chunk。`result.turn.finalContent` 仍只表示最后一个不再请求客户端 tool 的响应,可能不等于所有 chunk 的简单拼接。 ### 2.4 TurnRunnerOptions ```typescript await agent.turn(sessionId, input, { maxToolRounds: 5, // 限制 tool call 循环轮数(默认无限) signal: abortController.signal, // 支持取消(中断当前轮 LLM/tool 调用) onToolCall: (toolCall) => { /* ... */ }, onToolResult: (result) => { /* ... */ }, }) ``` ### 2.5 离开与归档 ```typescript await agent.leaveSession(sessionId) // 触发 consolidation 调度(fire-and-forget) await agent.archiveSession(sessionId) // 标记归档,之后不应再 turn() ``` --- ## 3. Fork — 创建子 Session ### 3.1 两种触发方式 | 方式 | 触发者 | 入口 | |------|--------|------| | LLM 发起 | LLM 调用 `stello_create_session` 内置 tool | 需在 `capabilities.tools` opt-in 注册 | | 代码发起 | 应用层调用 `agent.forkSession()` | 手动编排 | ### 3.2 `forkSession` 参数 ```typescript const child = await agent.forkSession(sessionId, { // ── 必填 ── label: '市场分析-深度研究', // ── SessionConfig 字段(可选,参与合成链)── systemPrompt: '你是市场分析专家...', llm: specializedLlm, tools: customTools, skills: ['search', 'summarize'], consolidateFn: customConsolidateFn, compressFn: customCompressFn, // ── Fork 专属字段(可选)── prompt: '请深入分析半导体行业', // fork 后立即发送的首条消息 context: 'inherit', // 'none'(默认)| 'inherit' | ForkContextFn topologyParentId: otherNodeId, // 显式指定拓扑父节点(不传 = 当前 sessionId) profile: 'researcher', // 引用预注册的 ForkProfile 名称 profileVars: { region: '北美' }, // ForkProfile.systemPromptFn 的模板变量 }) // child: TopologyNode // child.id — 新 session 的 ID // child.parentId — 拓扑父节点 ID // child.sourceSessionId — fork 时的上下文来源 session ID // child.depth — 拓扑深度(root = 0) // child.label — 显示名称 ``` Fork 后需要单独 `enterSession(child.id)` 才能在子 session 上 turn()。 ### 3.3 上下文继承策略(`context`) ```typescript await agent.forkSession(sessionId, { label: '子任务', context: 'none' }) // 空白开始(默认) await agent.forkSession(sessionId, { label: '深度研究', context: 'inherit' }) // 完整继承 L3 await agent.forkSession(sessionId, { label: '摘要子任务', context: async (parentMessages) => parentMessages.slice(-10), // 自定义裁剪 }) ``` ### 3.4 Fork 配置合成链 fork 时按 `sessionDefaults → 父 session 固化 config → ForkProfile → EngineForkOptions` 顺序合成,后者覆盖前者。root 也是普通 session,从 root fork 会正常继承 root 的固化 config。 详见 skill `fork-design`。 --- ## 4. Orchestrator-facing 数据 SDK 需要在创建 agent 时注入 `storage: SessionStorage`(顶层)。这套 API 让外部 orchestrator(应用层 / Claude Code / Codex / Kitkit 等)能够在对话之外直接读取和回写每个 Session 的数据。 ### 4.1 拓扑与 Session 列表 ```typescript const roots = await agent.listRoots() // TopologyNode[] const forest = await agent.getTopology() // SessionTreeNode[](嵌套森林) const node = await agent.getTopologyNode(sessionId) // 单个 TopologyNode const sessions = await agent.listSessions({ status: 'active' }) // SessionMeta[] ``` ### 4.2 单个 Session 视图 ```typescript const view = await agent.getSessionMetadata(sessionId) // view.memory — string | null(持久;不进 send 上下文) // view.insight — string | null(一次性 inbox;下次 send 注入并 clear) ``` ### 4.3 批量 digest ```typescript const digests = await agent.listSessionDigests({ status: 'active' }) // digests[i] = { id, label, status, memory, insight } ``` 应用层把这份数据喂给反思层 LLM,由它产出 per-session insight,再调用 `agent.putInsight` 定向回写。完整模式见 skill `session-usage`。 ### 4.4 L3 消息读取 ```typescript const messages = await agent.listMessages(sessionId, { limit: 100 }) ``` ### 4.5 写入 ```typescript await agent.putMemory(sessionId, '当前进展摘要...') // 持久 memory(替换语义) await agent.putInsight(sessionId, '需要重新评估方向...') // 一次性 insight(send 消费后 clear) await agent.clearInsight(sessionId) // 主动清除 ``` 未在 agent 创建时注入 `storage` 时,这些方法会抛错。 --- ## 5. Runtime 管理(多连接场景) 适用于 WebSocket 等多客户端连接场景,通过引用计数管理 Engine 生命周期。 ```typescript await agent.attachSession(sessionId, connectionId) // WS 连接建立 await agent.detachSession(sessionId, connectionId) // WS 连接断开 agent.hasActiveEngine(sessionId) // 是否有活跃 Engine agent.getEngineRefCount(sessionId) // 当前引用计数 ``` **回收策略**: ```typescript createStelloAgent({ runtime: { resolver: myResolver, recyclePolicy: { idleTtlMs: 30_000 }, }, }) // 运行时更新 agent.updateConfig({ runtime: { idleTtlMs: 60_000 } }) ``` --- ## 6. 典型使用模式 ### 6.1 单 root 对话 ```typescript const root = await agent.createSession({ label: 'Main' }) await agent.enterSession(root.id) await agent.turn(root.id, '你好') await agent.turn(root.id, '继续上个话题') await agent.leaveSession(root.id) ``` ### 6.2 代码驱动的并行 Fork ```typescript const root = await agent.createSession({ label: 'Main' }) await agent.enterSession(root.id) await agent.turn(root.id, '我需要研究三个市场') const children = await Promise.all([ agent.forkSession(root.id, { label: '美国市场', systemPrompt: '你是美国市场专家' }), agent.forkSession(root.id, { label: '欧洲市场', systemPrompt: '你是欧洲市场专家' }), agent.forkSession(root.id, { label: '亚洲市场', systemPrompt: '你是亚洲市场专家' }), ]) await Promise.all( children.map(async (child) => { await agent.enterSession(child.id) await agent.turn(child.id, '分析半导体供应链') await agent.leaveSession(child.id) // 触发 consolidation }), ) ``` ### 6.3 多 root 并存(森林) ```typescript // 独立的研究/写作两条线,互不影响 const research = await agent.createSession({ label: 'Research' }) const writing = await agent.createSession({ label: 'Writing' }) await agent.enterSession(research.id) await agent.turn(research.id, '调研材料 ...') await agent.enterSession(writing.id) await agent.turn(writing.id, '基于已有材料写一份 ...') const all = await agent.listRoots() // 两个 root 都会出现 ``` ### 6.4 外部 reflection 循环(自行实现 integrate) ```typescript async function reflect(agent: StelloAgent, llm: LLMAdapter): Promise { const digests = await agent.listSessionDigests({ status: 'active' }) // ... 应用层 prompt 把 digests 喂给 llm,解析出 per-target insight ... for (const [id, content] of Object.entries(insightsByTarget)) { await agent.putInsight(id, content) } } ``` 详见 stello-agent-creation §7。 ### 6.5 WebSocket 连接管理 ```typescript ws.on('connection', async (socket) => { const holderId = socket.id socket.on('enter', async ({ sessionId }) => { await agent.attachSession(sessionId, holderId) await agent.enterSession(sessionId) }) socket.on('message', async ({ sessionId, input }) => { const stream = await agent.stream(sessionId, input) for await (const chunk of stream) { socket.send(JSON.stringify({ type: 'chunk', data: chunk })) } const result = await stream.result socket.send(JSON.stringify({ type: 'done', data: result })) }) socket.on('close', async () => { for (const sessionId of socket.sessions) { await agent.detachSession(sessionId, holderId) } }) }) ``` --- ## 7. 并发语义 - **同 sessionId 内串行**:同一 session 上的 turn() 不会并发执行 - **不同 sessionId 之间并行**:可同时在多个 session 上 turn() - **所有异步副作用 fire-and-forget**:consolidation / hooks 不阻塞 turn() 返回 - **错误不中断对话**:副作用抛错时 emit error 事件,对话循环继续 --- ## 8. 公开方法速查 ### 编排 | 方法 | 返回值 | 说明 | |------|--------|------| | `createSession({ parentId?, label? })` | `Promise` | 创建拓扑节点(不传 parentId 即新 root;多 root 合法) | | `enterSession(id)` | `Promise` | 进入 session,触发 bootstrap | | `turn(id, input, opts?)` | `Promise` | 同步对话轮次(含 tool call 循环) | | `stream(id, input, opts?)` | `Promise` | 流式对话轮次 | | `leaveSession(id)` | `Promise<{ sessionId }>` | 离开 session,触发 consolidation 调度 | | `forkSession(id, opts)` | `Promise` | 创建子 session,执行配置合成链 | | `archiveSession(id)` | `Promise<{ sessionId }>` | 归档 session | | `consolidateSession(id)` | `Promise` | 手动触发该 session 的 consolidation | | `attachSession(id, holderId)` | `Promise` | 附着 runtime 持有者 | | `detachSession(id, holderId)` | `Promise` | 释放 runtime 持有者 | | `hasActiveEngine(id)` | `boolean` | 是否有活跃 Engine | | `getEngineRefCount(id)` | `number` | 当前引用计数 | | `updateConfig(patch)` | `void` | 热更新运行时配置 | ### Orchestrator-facing 数据 SDK(需注入 `storage`) | 方法 | 返回值 | 说明 | |------|--------|------| | `listSessions(filter?)` | `Promise` | 列出所有 session | | `listRoots()` | `Promise` | 列出所有 root | | `getTopology()` | `Promise` | 完整森林(嵌套树) | | `getTopologyNode(id)` | `Promise` | 单个节点 | | `getSessionMetadata(id)` | `Promise<{ memory, insight }>` | 单 session 的 memory + insight | | `listSessionDigests(filter?)` | `Promise` | 批量收集所有 Session 的 digest | | `listMessages(id, options?)` | `Promise` | 读取 L3 消息 | | `putMemory(id, content)` | `Promise` | 写入 memory | | `putInsight(id, content)` | `Promise` | 写入 insight(一次性) | | `clearInsight(id)` | `Promise` | 清除 insight | ### 只读属性 | 属性 | 类型 | 说明 | |------|------|------| | `config` | `StelloAgentConfig` | 归一化后的完整配置 | | `sessions` | `SessionTree` | 拓扑树 | | `memory` | `MemoryEngine` | 记忆引擎 | | `storage` | `SessionStorage \| undefined` | 数据存储(未注入时 data-IO 方法不可用) | | `profiles` | `ForkProfileRegistry \| undefined` | Fork 模板注册表 |