首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >OpenClaw 的聊天 RPC 接口 —— chat.ts 中的历史查询、发送与中止逻辑

OpenClaw 的聊天 RPC 接口 —— chat.ts 中的历史查询、发送与中止逻辑

作者头像
jack.yang
发布2026-03-16 11:30:03
发布2026-03-16 11:30:03
5410
举报
文章被收录于专栏:openclaw系列openclaw系列

关键词:ACP RPC|历史截断|运行中止|消息注入|内存安全|会话控制

在 OpenClaw 架构中,src/core/chat.ts 是智能体与外部世界交互的核心 RPC 接口层。它不仅处理用户消息的收发,更承担着会话状态管理、资源保护与紧急干预等关键职责。

所有客户端(Web、WhatsApp、CLI)均通过 ACP(Agent Communication Protocol)调用 chat.* 方法,而 chat.ts 则确保这些操作在高并发、长上下文、多会话环境下依然安全、高效、可控。

本文将详解其三大核心接口的设计与实现:

  1. chat.history:按字节截断防 OOM
  2. chat.abort:按 session 或 runId 精准中止
  3. chat.inject:管理员手动注入消息(审计/调试)

一、整体架构:chat.ts 在系统中的位置

image
image

chat.ts 是 ACP 方法的实现入口,不直接执行 AI 逻辑,而是协调会话状态与执行引擎

二、接口一:chat.history —— 安全获取对话历史

需求

  • 用户切换设备时需加载历史
  • Web UI 需渲染完整聊天记录
  • 但历史可能长达数 MB,直接返回会导致 OOM

解法:按字节截断 + 最近优先

代码语言:javascript
复制
// chat.ts → history()
export async function history(
  sessionKey: string,
  maxBytes: number = 8192 // 默认 8KB
): Promise<ChatMessage[]> {
  const rawHistory = await sessionStore.getMessages(sessionKey);
  
  // 从最新消息开始累加,直到接近 maxBytes
  let totalBytes = 0;
  const result: ChatMessage[] = [];
  
  for (let i = rawHistory.length - 1; i >= 0; i--) {
    const msg = rawHistory[i];
    const msgBytes = new TextEncoder().encode(JSON.stringify(msg)).length;
    
    if (totalBytes + msgBytes > maxBytes) break;
    
    result.unshift(msg); // 保持时间顺序
    totalBytes += msgBytes;
  }
  
  return result;
}

关键特性

  • 默认 8KB:足够显示最近 10~20 条消息
  • 可配置上限:Web UI 可请求 maxBytes=65536 加载更多
  • 永不超限:即使会话有 100MB 历史,也只返回安全片段

历史可用,但绝不失控

三、接口二:chat.abort —— 精准中止运行中的任务

场景

  • 用户发送“取消”命令
  • 管理员发现危险操作需紧急中断
  • 超时自动终止

挑战

  • 一个会话可能有多个并行任务(如后台日志监听 + 前台命令)
  • 需区分“中止整个会话” vs “中止特定运行”

解法:两级中止机制

1. runId 中止(精准打击)

每次工具调用或 LLM 思考生成唯一 runId

代码语言:javascript
复制
// agent-runner.ts
const runId = nanoid();
activeRuns.set(runId, { sessionKey, abortController });

chat.abort 支持按 runId 终止:

代码语言:javascript
复制
// chat.ts → abort()
export function abort(params: { runId?: string; sessionKey?: string }) {
  if (params.runId) {
    const run = activeRuns.get(params.runId);
    if (run) {
      run.abortController.abort(); // 触发 AbortSignal
      activeRuns.delete(params.runId);
      logger.info(`Aborted run ${params.runId}`);
    }
  } else if (params.sessionKey) {
    // 中止该会话所有运行(见下文)
  }
}
2. sessionKey 中止(全面清理)
代码语言:javascript
复制
if (params.sessionKey) {
  for (const [runId, run] of activeRuns.entries()) {
    if (run.sessionKey === params.sessionKey) {
      run.abortController.abort();
      activeRuns.delete(runId);
    }
  }
  // 同时清除会话输入锁(防止卡死)
  inputLocks.delete(params.sessionKey);
}

执行器响应 AbortSignal

所有异步操作必须监听信号:

代码语言:javascript
复制
// exec.ts
const { signal } = abortController;
await execa(cmd, { signal }); // execa 支持 AbortSignal

// llm.ts
const response = await fetch(llmEndpoint, { signal });

中止不是建议,而是强制指令

四、接口三:chat.inject —— 管理员消息注入

用途

  • 审计:模拟用户提问测试行为
  • 调试:在特定上下文插入指令
  • 紧急通知:“系统将在 5 分钟后维护”

安全约束

  • 仅限管理员(通过 ACP 认证上下文校验)
  • 不可伪造用户身份
  • 注入消息标记为 role: "system"

实现

代码语言:javascript
复制
// chat.ts → inject()
export async function inject(
  sessionKey: string,
  content: string,
  opts: { asSystem?: boolean } = {}
) {
  // 1. 权限检查
  if (!currentACPContext.isAdmin) {
    throw new Error("Permission denied");
  }

  // 2. 构造消息
  const message: ChatMessage = {
    id: generateId(),
    role: opts.asSystem ? 'system' : 'user',
    content,
    timestamp: Date.now(),
    injectedBy: currentACPContext.userId // 审计字段
  };

  // 3. 写入会话历史
  await sessionStore.appendMessage(sessionKey, message);

  // 4. 若会话空闲,触发 AI 响应
  if (!inputLocks.has(sessionKey)) {
    await agentRunner.processMessage(sessionKey, message);
  }

  logger.info(`Injected message into ${sessionKey} by admin`);
}

使用示例(ACP 调用)

代码语言:javascript
复制
{
  "method": "chat.inject",
  "params": {
    "sessionKey": "wa:+1234567890",
    "content": "注意:数据库备份正在进行,请勿重启服务。",
    "asSystem": true
  }
}

注入是特权,不是后门

五、内存与并发安全

1. 会话锁(Input Lock)

  • 每个 sessionKeyinputLocks 标记
  • 防止用户连发消息导致 LLM 并行推理(浪费资源)

2. 历史读写分离

  • 写入:追加到 .jsonl 文件(原子 append)
  • 读取:内存缓存 + 文件 fallback,避免频繁 I/O

3. 中止信号传播

  • AbortControllerchat.abort 一路传递至:
    • HTTP 请求(LLM 调用)
    • 子进程(execa
    • 远程节点连接(SSH)

六、ACP 接口规范

image
image

接口即契约,安全即默认

结语:控制权交给用户,责任留在系统

chat.ts 的设计哲学是:赋予用户充分的控制能力(查询、中止、注入),同时由系统承担安全与稳定性责任。无论是防止内存爆炸的历史截断,还是精准到毫秒的运行中止,都体现了对资源与体验的双重尊重。

这不仅是 RPC 接口,更是人机协作的操作协议——清晰、可靠、可审计。

在下一篇中,我们将探讨 OpenClaw 的部署模型演进:从单机 Docker 到 Kubernetes Operator。

下一篇预告第 18 篇:OpenClaw 架构下Skills System —— 为什么“文档即工具”是 OpenClaw 的扩展灵魂

您的 AI 助手,从此由您定义。若感兴趣可以浏览本书其他章节内容:

第 1 篇:OpenClaw 是什么?—— 工业级 AI 智能体网关的定位与愿景

第 2 篇:三位一体架构详解 —— 网关层、协议层、智能体系如何协同工作

第 3 篇:ACP 协议设计哲学 —— 为什么 OpenClaw 选择自研 Agent Client Protocol

第 4 篇:启动与配置体系 —— openclaw.mjsconfig.yaml 与环境变量管理

第 5 篇:run.ts 上篇 —— 模型调度、账号轮询与上下文守护机制

第 6 篇:run.ts 下篇 —— 故障转移、重试策略与结果封装

第 7 篇:记忆系统基石 —— memory-search.ts 中的 RAG 配置解析与合并逻辑

第 8 篇:向量检索实战 —— OpenClaw 如何实现混合搜索(向量 + 全文)

第 9 篇:长期记忆与会话同步 —— 如何让 AI “记住”跨天对话

第 10 篇:exec.ts 上篇 —— 安全执行 Shell 命令的三层隔离模型

第 11 篇:exec.ts 下篇 —— 用户审批、后台任务与权限提升控制

第 12 篇:process.ts —— AI 如何像开发者一样管理后台进程

第 13 篇:安全边界设计 —— OpenClaw 如何防范 AI 滥用系统权限

第 14 篇:server-channels.ts —— 渠道插件生命周期管理器

第 15 篇:WhatsApp 深度集成 —— session.ts 与 Baileys 的健壮连接管理

第 16 篇:消息流入中枢 —— monitor-inbox.ts 如何解析、去重与防抖

第 17 篇:聊天 RPC 接口 —— chat.ts 中的历史查询、发送与中止逻辑

第 18 篇:Skills System —— 为什么“文档即工具”是 OpenClaw 的扩展灵魂

第 19 篇:可观测性工程 —— ws-log.ts 如何让 WebSocket 日志可读可用

第 20 篇:从零部署 OpenClaw —— 实战:接入 WhatsApp + 创建自定义 Skill

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、整体架构:chat.ts 在系统中的位置
  • 二、接口一:chat.history —— 安全获取对话历史
    • 需求
    • 解法:按字节截断 + 最近优先
    • 关键特性
  • 三、接口二:chat.abort —— 精准中止运行中的任务
    • 场景
    • 挑战
    • 解法:两级中止机制
      • 1. 按 runId 中止(精准打击)
      • 2. 按 sessionKey 中止(全面清理)
    • 执行器响应 AbortSignal
  • 四、接口三:chat.inject —— 管理员消息注入
    • 用途
    • 安全约束
    • 实现
    • 使用示例(ACP 调用)
  • 五、内存与并发安全
    • 1. 会话锁(Input Lock)
    • 2. 历史读写分离
    • 3. 中止信号传播
  • 六、ACP 接口规范
  • 结语:控制权交给用户,责任留在系统
    • 第 1 篇:OpenClaw 是什么?—— 工业级 AI 智能体网关的定位与愿景
    • 第 2 篇:三位一体架构详解 —— 网关层、协议层、智能体系如何协同工作
    • 第 3 篇:ACP 协议设计哲学 —— 为什么 OpenClaw 选择自研 Agent Client Protocol
    • 第 4 篇:启动与配置体系 —— openclaw.mjs、config.yaml 与环境变量管理
    • 第 5 篇:run.ts 上篇 —— 模型调度、账号轮询与上下文守护机制
    • 第 6 篇:run.ts 下篇 —— 故障转移、重试策略与结果封装
    • 第 7 篇:记忆系统基石 —— memory-search.ts 中的 RAG 配置解析与合并逻辑
    • 第 8 篇:向量检索实战 —— OpenClaw 如何实现混合搜索(向量 + 全文)
    • 第 9 篇:长期记忆与会话同步 —— 如何让 AI “记住”跨天对话
    • 第 10 篇:exec.ts 上篇 —— 安全执行 Shell 命令的三层隔离模型
    • 第 11 篇:exec.ts 下篇 —— 用户审批、后台任务与权限提升控制
    • 第 12 篇:process.ts —— AI 如何像开发者一样管理后台进程
    • 第 13 篇:安全边界设计 —— OpenClaw 如何防范 AI 滥用系统权限
    • 第 14 篇:server-channels.ts —— 渠道插件生命周期管理器
    • 第 15 篇:WhatsApp 深度集成 —— session.ts 与 Baileys 的健壮连接管理
    • 第 16 篇:消息流入中枢 —— monitor-inbox.ts 如何解析、去重与防抖
    • 第 17 篇:聊天 RPC 接口 —— chat.ts 中的历史查询、发送与中止逻辑
    • 第 18 篇:Skills System —— 为什么“文档即工具”是 OpenClaw 的扩展灵魂
    • 第 19 篇:可观测性工程 —— ws-log.ts 如何让 WebSocket 日志可读可用
    • 第 20 篇:从零部署 OpenClaw —— 实战:接入 WhatsApp + 创建自定义 Skill
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档