Rna Agent
Rna SDK

会话

创建一个连接真实模型的会话,在运行中插话、排队、撤回和压缩。

一个真实模型会话

从仓库根目录运行。先设置 RNA_MODEL_BASE_URL、RNA_MODEL_ID、RNA_MODEL_API_KEY,RNA_MODEL_PROTOCOL 可选 openai(基址包含 /v1)或 anthropic。执行会产生真实的模型请求。

example-rna.mjs
import { resolve } from 'node:path';
import { createSession, createWorkspaceTools } from './packages/sdk/src/index.mjs';

const model = {
  providerId: 'configured',
  protocol: process.env.RNA_MODEL_PROTOCOL || 'openai',
  baseUrl: process.env.RNA_MODEL_BASE_URL,
  modelId: process.env.RNA_MODEL_ID,
  contextWindow: Number(process.env.RNA_CONTEXT_WINDOW || 32000),
  maxOutputTokens: Number(process.env.RNA_MAX_OUTPUT_TOKENS || 2048),
  reasoning: 'off',
  cacheRetention: 'short',
};

const cwd = process.cwd();
const stateDir = resolve('.rna-sdk-state');
const session = await createSession({
  sessionId: 'example-conversation',
  projectId: 'example-project',
  cwd,
  stateDir,
  model,
  apiKey: process.env.RNA_MODEL_API_KEY,
  tools: await createWorkspaceTools(cwd, { deniedPaths: [stateDir] }),
  systemPrompt: '你是 Rna Agent,结合可读取的项目内容协助用户;不要声称执行了没有执行的操作。',
  onEvent(event) {
    if (event.type === 'message_update' && event.assistantMessageEvent?.type === 'text_delta') {
      process.stdout.write(event.assistantMessageEvent.delta);
    }
  },
});

try {
  await session.prompt('查看项目顶层目录,并简要说明下一步值得关注的事项。');
  console.log('\n', session.snapshot().usage);
} finally {
  await session.dispose();
}

代码中的容量只是这个例子的请求预算,不是供应商规格。用相同的 stateDir + sessionId + projectId 重新创建会话,会读取同一份 JSONL 日志。

运行中的输入

const running = session.prompt('开始检查');
const queued = await session.followUp('检查结束后补充测试建议');
await session.steer('先关注兼容性,再处理其他问题');
await session.withdraw(queued.id); // 仍在队列中时可以撤回
await running;
  • steer 在模型或工具的安全边界进入上下文,不能改变已经发出的 HTTP 请求,也不撤销已执行的副作用。
  • followUp 在当前工作收束后接续。
  • abort() 请求停止,resume() 继续持久会话。
  • yieldAtBoundary({ requestId }) 保存一个合作式暂停请求,在当前请求或工具批次结算后暂停。

压缩

默认开启自动压缩:接近配置上下文的 80% 时,在完整的工具批次之后逐段摘要较早的历史,保留最近两组回应和工具结果。摘要失败或没有减少上下文时,保留原样并停止这一轮。

压缩的做法与 pi 一致,换到上下文更小的模型也能接上:交给摘要的是纯文本记录,工具结果、调用参数、宿主上下文和推理各保留前 2000 字符;服务端报告某一批太长时减半重试(最多三次);回答请求因超长被拒时,压缩到发送量的一半后重试一次。超长错误从各家的报错文字、Codex 的 detail 字段和流里的 response.failed 识别。

手动压缩保留原始日志,并要求你提供真实的摘要:

await session.compact({
  keepLastTurns: 2,
  summarize: async ({ messages, previousSummary, instructions }) => {
    return await yourSummarizer({ messages, previousSummary, instructions });
  },
});

yourSummarizer 需要宿主实现,不是包导出的函数。

请求边界的钩子

钩子时机用途
beforeRequest上一批工具全部完成、下一次请求之前返回新的完整工具集合或带来源的上下文更新
beforeCompletion每个实际模型请求之前,包括压缩预算预留
beforeFinish / afterRun收束前后记录结果;不是新的权限来源
beforeTool / afterTool每个工具调用的参数校验之后、执行之前,和执行之后宿主的工具策略:可以拒绝调用,或把说明附在工具结果后;不是新的权限来源

时限与重试

streamCompletion 默认空闲时限 120 秒、单次请求总时限 15 分钟,SSE 心跳只刷新空闲计时。只有明确的 HTTP 429 或 5xx、并且尚未接受 SSE 响应时才自动重试,最多 2 次,尊重不超过 60 秒的 Retry-After。连接错误、半条流和已执行的工具都不会被隐式重放。

Anthropic 提示缓存

用 Anthropic 协议时,历史只追加(appendOnlyActive),思考内容绑定对话。cacheKeepAlive(input) 把上一个请求以 max_tokens: 0、非流式重发一次,只做预填,不产生输出,却会刷新缓存计时;带 thinking.type: "enabled" 或结构化输出的请求不能这样保温,会被拒绝。它从不重试,由宿主决定何时值得调用。

图片

图片以 { type: 'image', data, mimeType } 通过 session.prompt(text, { images }) 传入。模型必须显式声明 input: ['text', 'image'];不支持时返回明确诊断,不会自动换模型或假装读过图片。

本页内容