跳转到内容

会话服务 5 分钟跑通

@tansr/serve 是 tansr 的 Agent 会话引擎:一个 npm 包,嵌进你自己的 Node 服务,对外暴露 /v2 多会话 REST + SSE 协议面,对内用你的应用密钥接通 tansr 平台并真调模型。它不是网关——你的用户怎么登录、怎么限流、怎么计费,都归你自己的体系;引擎只做传输、协议与会话治理。

Android / iOS SDK 连的就是这个服务。

移动端的系统提示词在平台应用配置或 serve 宿主中设置,不放入 /v2prompt。省略 platform.system 使用平台默认;代码管理的业务段放 platform.system,宿主补充说明放 platform.systemAppend。默认 fallback 允许显式业务段(包括 [])替代平台段;prepend 保留平台段在前,即使 SDK 段是 []

这些新增选项尚待 API 与 serve 配套发布。它们只控制文本组合,不替代权限。平台提示词与策略在后续新一轮前刷新,当前执行轮保持既有配置;已有会话、attach 和历史可继续使用,无需为了改角色重开会话。参见应用系统提示词与 SDK 拼接策略中的 serve 完整示例。

  • Node.js ≥ 22.19。
  • 在控制台创建一个移动应用(给 Android / iOS 用)或服务端应用,拿到 appidappkey
  • 你自己的登录态体系:引擎需要你把一个请求映射成一个 endUserId,其余不管。
终端窗口
npm install @tansr/serve

唯一第三方运行时依赖是 zod;tansr 内部包已编译内联,不会出现在你的依赖树里。

会话服务的鉴权分三层,记住归属就不会放错:

凭据 归属
客户端 ↔ 会话服务 你自定的登录态 token(JWT、session cookie、OAuth 皆可) 完全你自己;引擎只经 authenticate 缝取 endUserId
会话服务 ↔ tansr 平台 appid + appkey 服务进程持有;引擎用它为每个终端用户换取 app_user 令牌
客户端持有 appid 公开应用标识 可下发,恒非凭据

红线三条:appkey 恒不下发到端;app_user 令牌恒不出会话服务;会话 id 不是鉴权因子(每个端点都独立校验归属)。

把 appid / appkey 放进环境变量(名字随你,示例沿用官方样板的 TANSR_APP_KEY_ID / TANSR_APP_KEY),不要写进代码或仓库。

最小可跑骨架——鉴权缝、内置真装配、会话落盘、启动:

import {
createAgentSessionFactory,
createServeAgentSessionStore,
registerBuiltinLocales,
startServer,
} from '@tansr/serve';
registerBuiltinLocales(); // 可选:错误体文案本地化
// 真装配:appid/appkey → 每个终端用户一枚短期令牌 → 内核查询环真调模型
const build = createAgentSessionFactory({
platform: {
apiBaseUrl: 'https://api.tansr.com',
appId: process.env.TANSR_APP_KEY_ID!,
appKey: process.env.TANSR_APP_KEY!, // 恒不下发端、恒不入日志
},
store: createServeAgentSessionStore({ dir: '/var/lib/my-agent/sessions' }), // 生产恒接
cwd: process.cwd(),
});
const server = await startServer({
host: '127.0.0.1',
port: 8787,
token: process.env.MY_V1_TOKEN!, // /v1 面的 Bearer;与 /v2 鉴权互不相通
createSession: build.factory,
version: '1.0.0',
v2: {
// 唯一鉴权面:你的登录态 → endUserId;返回 null 即 401
authenticate: async (req) => {
const user = await myAuth.verify(req.headers['authorization']);
return user ? { endUserId: user.id } : null;
},
createSession: build.factory,
store: build.storeReader, // 让休眠会话可列表 / 可 resume
},
});

store 一定要接:不接的话进程一重启,全部活跃会话的上下文全量灭失,客户端 resume409 resume_unavailable

进程信号归宿主:引擎不会替你监听 SIGTERM。至少接上 server.drain({ timeoutMs: 30_000 }) 再退出,详见部署与鉴权

想先离线看协议、不接真平台?仓内示例 examples/serve-demo 用一个回显 Agent 打通了全部 /v2 协议层,起服务一条命令:pnpm --filter tansr-example-serve-demo start,缺省监听 127.0.0.1:8788

用 curl 直接走一遍。<你的登录 token> 是你自己体系签发、authenticate 能验的那枚。

创建会话(带首条 prompt):

终端窗口
curl -s -X POST http://127.0.0.1:8787/v2/sessions \
-H "Authorization: Bearer <你的登录 token>" \
-H "Content-Type: application/json" \
-d '{"prompt":"你好,介绍一下你能做什么"}'
{ "sessionId": "", "resumed": false, "lastSeq": 0 }

后续输入:

终端窗口
curl -s -X POST http://127.0.0.1:8787/v2/sessions/<sessionId>/messages \
-H "Authorization: Bearer <你的登录 token>" \
-H "Content-Type: application/json" \
-d '{"prompt":"再简短一点"}'

返回 202 { "sessionId", "accepted": true }

订阅 SSE 事件流:

终端窗口
curl -N http://127.0.0.1:8787/v2/sessions/<sessionId>/events \
-H "Authorization: Bearer <你的登录 token>"

首帧是 retry: 3000,随后每一条内核事件一帧:id: <seq>data: <KernelEvent JSON>,不带 event: 名;控制帧(工具请求、权限请求、提问)才带 event: server.* 名。每 15 秒一条 : hb 注释心跳。

断线后带上最后消费的 seq 重连,服务端从环形缓冲重放断点之后的事件:

终端窗口
curl -N http://127.0.0.1:8787/v2/sessions/<sessionId>/events \
-H "Authorization: Bearer <你的登录 token>" \
-H "Last-Event-ID: 42"

tansr serve --token <值> 也能起一个常驻服务,但它是单运维方的 /v1 面(POST /v1/sessionsGET /v1/sessions/:id/events 等六端点,同一枚 Bearer token),适合编排系统接入,不是多租户产品后端。tansr serve --v2/v2 面也挂进命令行形态,tansr 命令行 0.6.0 已提供;也可继续使用上文的 @tansr/serve@0.8.0 npm 嵌入形。