跳转到内容

Hello World — WebSocket 模式

WebSocket 模式是默认推荐的部署形态——长驻进程 + 完整 6 层 E2EE 栈(含 Ratchet)+ 最低延迟。这一页带你完成”装 SDK → 连上 → 收发消息 → 优雅退出 → 零停机重启”。

如果你只想跑一遍看看,先看 5 分钟创建第一个 Agent。 本页假设你已经走完那一遍。

项目脚手架

Terminal window
mkdir hashee-ws-agent && cd hashee-ws-agent
npm init -y
npm install @hasheeai/agent-sdk-ts
npm install --save-dev typescript tsx @types/node

tsconfig.jsonpackage.json 详见 安装与配置

完整可工作 Agent

新建 src/index.ts

import { HasheeAgent } from "@hasheeai/agent-sdk-ts";
import { writeFile, readFile, mkdir } from "node:fs/promises";
import { dirname, join } from "node:path";
const KEYS_FILE = join(
process.env.HOME ?? ".",
".hashee",
process.env.HASHEE_AGENT_ID ?? "",
"keystore.json",
);
// 1. 启动时尝试加载持久化的私钥
async function loadKeys() {
try {
const buf = await readFile(KEYS_FILE, "utf8");
return JSON.parse(buf);
} catch {
return null;
}
}
// 2. SDK 首次生成密钥时回调持久化
async function persistKeys(keys: object) {
await mkdir(dirname(KEYS_FILE), { recursive: true });
await writeFile(KEYS_FILE, JSON.stringify(keys, null, 2), { mode: 0o600 });
console.log(`[hashee] keys persisted → ${KEYS_FILE}`);
}
const persisted = await loadKeys();
const agent = await HasheeAgent.init({
agentId: process.env.HASHEE_AGENT_ID!,
token: process.env.HASHEE_AGENT_TOKEN!,
baseUrl: process.env.HASHEE_BASE_URL ?? "https://api.hashee.ai",
connectionMode: "websocket",
privateKey: persisted?.privateKey, // 可选;不传 SDK 自动生成
signingPrivateKey: persisted?.signingPrivateKey, // 可选
onKeyGenerated: persisted ? undefined : persistKeys,
});
agent.addStatusHandler((status) => {
console.log(`[hashee] connection: ${status}`);
});
agent.addMessageHandler(async (msg) => {
if (msg.payload?.type !== "text") return;
const text = msg.payload.text;
console.log(`[hashee] inbound text="${text}" from=${msg.sender_id}`);
await agent.typing(msg.conversation_id);
// 这里替换成你的 LLM 调用 / 业务逻辑
const reply = `你好!我收到了 "${text}"`;
await agent.send(msg.conversation_id, {
type: "text",
text: reply,
});
console.log(`[hashee] replied conv=${msg.conversation_id}`);
});
// 3. 优雅退出
const shutdown = async (signal: string) => {
console.log(`\n[hashee] received ${signal}; shutting down...`);
try {
await agent.close();
} catch (e) {
console.error("[hashee] shutdown error", e);
}
process.exit(0);
};
process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));
console.log("[hashee] up; waiting for messages... (Ctrl+C to quit)");

跑起来

Terminal window
HASHEE_AGENT_ID="<your-uuid>" \
HASHEE_AGENT_TOKEN="hsk_..." \
tsx src/index.ts

第一次跑:

[hashee] keys persisted → /Users/you/.hashee/01906abc-.../keystore.json
[hashee] connection: connecting
[hashee] connection: connected
[hashee] up; waiting for messages...

第二次跑(密钥已落盘):

[hashee] connection: connecting
[hashee] connection: connected
[hashee] up; waiting for messages...

状态机

addStatusHandler 收到的连接状态枚举:

状态含义
connecting正在建立 WebSocket(含 TLS 握手)
authenticatingWS 已建立,正在 send auth frame
connected认证成功,可收发消息
reconnecting因网络抖动断开,正在重连(指数退避 1s → 30s)
disconnected主动关闭或重试到上限
error不可恢复错误(看下面 addErrorHandler
agent.addErrorHandler((err) => {
console.error("[hashee] fatal:", err);
// 大多数错误 SDK 已 swallow + retry;走到这里通常是配置错(401/404)
process.exit(1);
});

心跳与重连

SDK 自动管理:

  • WebSocket ping 每 30 秒,90 秒内无 pong → 视为断开。
  • 断开后立即触发指数退避重连:1s, 2s, 4s, 8s, 16s, 30s(cap 30s)。
  • 重连成功后 SDK 自动重新订阅 你之前注册的所有 handler,业务无感。
  • 服务端为每个 Agent 维护投递游标,重连后未投递的消息会续传,不丢。

多实例横向扩容

同一 agent_id 可同时维持最多 3 个并发 WebSocket 连接。常见拓扑:

┌──────────────────────┐
│ Hashee API │
│ (DeliveryShardDO │
│ 把消息按 hash 分配 │
│ 给某条连接) │
└──────────────────────┘
▲ ▲ ▲
│ │ │
┌──────┘ │ └──────┐
Agent Process Agent Process Agent Process
#1 (us-east) #2 (us-west) #3 (eu-west)

实现:每个 region 的进程用同样的 agentId + agentToken + 同一份私钥 启动;后端会 round-robin / hash 把会话分到不同进程。注意所有进程必须 共享同一组私钥(否则解密失败)——把私钥放进集群级 secret store。

超过 3 个连接:第 4 个会被踢掉旧的最久 idle 连接(LRU); 不要靠这个做”软扩容”,需要更高并发用多 agent_id

零停机重启(蓝绿)

利用”3 连接上限”做无损部署:

t0 旧进程在线 (#1)
t1 启动新进程 (#2) 用新版本代码 → 等到 status=connected
t2 流量自动均衡(DeliveryShardDO 会路由新消息给两个连接)
t3 优雅关闭旧进程:发 SIGTERM → SDK 调 close() → 后端释放 #1 槽位
t4 只剩新进程 (#2),部署完成
// shutdown 调用 agent.close() 不丢未投递消息:
// SDK 会:
// 1. 停止 accept 新消息
// 2. 等本地待 send 队列清空(默认 10s 超时)
// 3. 主动 send WS close frame
// 4. 服务端释放连接槽位

演示视频

视频文字版逐节描述
  • 00:00 – 00:08 — 终端运行 tsx src/index.ts,看到三行启动日志: keys persisted / connection: connecting / connection: connected
  • 00:08 – 00:18 — 切到 Hashee app DemoBot 会话,发”测试 1”。回到终端 看到 inbound text="测试 1" + replied conv=...
  • 00:18 – 00:28 — 演示零停机:在第二个终端窗启动新进程,约 3 秒后 显示 connection: connected。原进程仍 idle 等待。
  • 00:28 – 00:38 — 在 Hashee app 连续发 5 条消息,画面分屏展示两个终端 各自处理了 2-3 条(DeliveryShardDO 路由)。
  • 00:38 – 00:48 — 在原进程 Ctrl+C,看到 received SIGINT; shutting down... → SDK 等 close 完成 → 进程退出。新进程接管全部流量,画面无中断。

下一步