TensorFusion Docs

集成与嵌入

把 Tensor Agent 嵌入你的应用 —— 后端一次性签发 JWT,前端嵌入 iframe,会话自动续期。

集成与嵌入

把 Tensor Agent 聊天面板嵌进任何 Web 应用。你只负责在后端签一次 JWT;SDK 和 Tensor Agent 服务端会自动续期会话,你的业务代码不用操心 Token 过期。

架构

  你的应用                           Tensor Agent
  ┌─────────────┐   postMessage    ┌──────────────────┐
  │   你的页面   │ ◄──────────────► │  iframe 面板       │
  └──────┬──────┘                  └─────────┬────────┘
         │ 1. 签发 4h JWT                     │ 2. 每 3h 自动续期
         │    (共享 S2S 密钥)                  │    POST /api/auth/refresh
         ▼                                   ▼
  你的后端                           Tensor Agent 服务端

核心:一把 S2S 密钥,一种 JWT,两个系统互认。 你后端签发的 JWT 和 Tensor Agent 续签的 JWT 都用同一把 Integration S2S 密钥(HS256),双方都能校验。你不需要刷新接口、不需要 refresh token。

快速开始

1. 在控制台创建 Integration

进入 系统设置 → 集成管理,新建一个 Integration(类型选 API),保存后复制 S2S 密钥,存入你后端环境变量 TENSOR_AGENT_S2S_KEY

2. 后端:签一次 4 小时 JWT

用 S2S 密钥签一个代表当前用户的 JWT。默认有效期 4 小时;到期前 1 小时 SDK 会自动替你续。

import { createHmac } from 'node:crypto';

function issueAgentJwt(userId, orgId) {
  const now = Math.floor(Date.now() / 1000);
  const header = { alg: 'HS256', typ: 'JWT' };
  const payload = { sub: userId, org: orgId, iat: now, exp: now + 4 * 3600 };
  const h = Buffer.from(JSON.stringify(header)).toString('base64url');
  const p = Buffer.from(JSON.stringify(payload)).toString('base64url');
  const sig = createHmac('sha256', process.env.TENSOR_AGENT_S2S_KEY)
    .update(`${h}.${p}`)
    .digest('base64url');
  return `${h}.${p}.${sig}`;
}

app.get('/api/agent-token', requireAuth, (req, res) => {
  res.json({ jwt: issueAgentJwt(req.user.id, req.user.orgId) });
});
import base64, hmac, hashlib, json, os, time

def issue_agent_jwt(user_id: str, org_id: str) -> str:
    now = int(time.time())
    header = {"alg": "HS256", "typ": "JWT"}
    payload = {"sub": user_id, "org": org_id, "iat": now, "exp": now + 4 * 3600}
    b64 = lambda d: base64.urlsafe_b64encode(
        json.dumps(d, separators=(",", ":")).encode()
    ).rstrip(b"=").decode()
    h, p = b64(header), b64(payload)
    sig = base64.urlsafe_b64encode(
        hmac.new(os.environ["TENSOR_AGENT_S2S_KEY"].encode(),
                 f"{h}.{p}".encode(), hashlib.sha256).digest()
    ).rstrip(b"=").decode()
    return f"{h}.{p}.{sig}"
func issueAgentJWT(userID, orgID string) string {
    now := time.Now().Unix()
    header := map[string]string{"alg": "HS256", "typ": "JWT"}
    payload := map[string]any{"sub": userID, "org": orgID, "iat": now, "exp": now + 4*3600}
    h, _ := json.Marshal(header); p, _ := json.Marshal(payload)
    hB := base64.RawURLEncoding.EncodeToString(h)
    pB := base64.RawURLEncoding.EncodeToString(p)
    mac := hmac.New(sha256.New, []byte(os.Getenv("TENSOR_AGENT_S2S_KEY")))
    mac.Write([]byte(hB + "." + pB))
    sig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
    return hB + "." + pB + "." + sig
}

JWT claims

Claim类型必填说明
substring外部系统用户 ID
expnumberUnix 秒,建议 iat + 14400(4h)
iatnumber签发时间
orgstring组织 ID;不传则用 Integration 默认 org
nameemailstring展示用

3. 前端:嵌入面板

选一种接入方式:

npm install @nexusgpu/agent-sdk-react
import { TensorAgentProvider, useTensorAgent } from '@nexusgpu/agent-sdk-react';

function App() {
  const [jwt, setJwt] = useState('');
  useEffect(() => {
    fetch('/api/agent-token').then(r => r.json()).then(d => setJwt(d.jwt));
  }, []);
  if (!jwt) return <Loading />;

  return (
    <TensorAgentProvider
      jwt={jwt}
      agentId="my-agent"
      onDataChange={(e) => queryClient.invalidateQueries([e.resource])}
      onAuthFailed={() => {
        // 极少触发:续期彻底失败(比如你轮换了 S2S 密钥)。
        // 重新调用你的后端拿一个新 JWT。
        fetch('/api/agent-token').then(r => r.json()).then(d => setJwt(d.jwt));
      }}
    >
      <YourApp />
    </TensorAgentProvider>
  );
}

function CustomerPage({ customer }) {
  const { setContextPrompts, sendPrompt, open } = useTensorAgent();
  useEffect(() => {
    setContextPrompts([`正在查看客户 ${customer.name}`]);
  }, [customer]);
  return <button onClick={() => { open(); sendPrompt('分析这个客户'); }}>AI 分析</button>;
}
npm install @nexusgpu/agent-sdk-vue
<template>
  <TensorAgentLayout
    :jwt="jwt"
    agent-id="my-agent"
    @data-change="onDataChange"
    @auth-failed="refetchJwt"
  >
    <router-view />
  </TensorAgentLayout>
</template>

<script setup lang="ts">
import { TensorAgentLayout } from '@nexusgpu/agent-sdk-vue';
</script>
npm install @nexusgpu/agent-sdk-js
import { TensorAgent } from '@nexusgpu/agent-sdk-js';

const res = await fetch('/api/agent-token');
const { jwt } = await res.json();

const agent = TensorAgent.init({
  jwt,
  agentId: 'my-agent',
  onDataChange: (e) => refreshList(e.resource),
  onAuthFailed: async () => {
    const r = await fetch('/api/agent-token');
    const { jwt } = await r.json();
    agent.updateToken(jwt);
  },
});

直接在 HTML 里引,不用打包器:

<script src="https://agent.tos.run/sdk/agent-embed.iife.js"></script>
<script>
  fetch('/api/agent-token').then(r => r.json()).then(({ jwt }) => {
    TensorAgent.init({ jwt, agentId: 'my-agent' });
  });
</script>

就这样。Token 会自动续期,你不需要写刷新代码。


事件

iframe 会通过 postMessage 告诉你的页面发生了什么:

事件触发典型用途
data-change智能体调用工具修改了业务数据刷新列表、失效缓存
action智能体触发了 navigate / open-modal 之类路由跳转、打开弹窗
auth-failed自动续期彻底失败(罕见)重新调用你的后端签 JWT,然后 updateToken()
agent.onDataChange('orders', (e) => refreshOrders());
agent.onAnyDataChange((e) => queryClient.invalidateQueries([e.resource]));
agent.onAction((e) => {
  if (e.action === 'navigate') router.push(e.data.path);
});

控制 API

方法说明
setContextPrompts(prompts)注入页面上下文,智能体下次回答时会参考
setContext(ctx)结构化业务上下文
sendPrompt(text)主动发送一条消息
updateToken(jwt)替换当前 JWT(auth-failed 之后调用)
open() / close() / toggle()控制面板显示
destroy()卸载 iframe,恢复页面原状
readyPromise<void>,iframe 加载完成

工作原理

  • 你签发一个 4 小时 的 JWT。
  • iframe 在到期前 1 小时(签发后 3h)自动调用 POST https://agent.tos.run/api/auth/refresh,拿一个新 JWT 继续用。
  • 续签服务用 相同的 S2S 密钥 校验并重新签发;claims(suborgnameemail)保留。
  • 如果用户电脑长时间休眠导致 Token 过期超过 24 小时,续期会失败,SDK 抛出 auth-failed,由你补发一个新 JWT。
  • 要吊销所有会话:在 Integration 管理面板轮换 S2S 密钥。

进阶

自定义 JWT 有效期

任何 exp 都可以,SDK 会根据 exp 计算续期时点。推荐默认保持 4 小时。过短会增加续期请求;过长遇到时钟偏差风险更大。

容器模式(手动布局)

默认的 TensorAgent.init() 会自动改造页面为左右分栏。如果你已经有自己的布局:

import { TensorAgentPanel } from '@nexusgpu/agent-sdk-react';

<div style={{ display: 'flex' }}>
  <MyApp />
  <TensorAgentPanel open={open} onOpenChange={setOpen} jwt={jwt} resizable />
</div>

跨域安全

SDK 对所有 postMessage 做 origin 校验:

  • 宿主 → iframe:限定目标 origin 为 Tensor Agent 服务地址
  • iframe → 宿主:限定为宿主页面 origin(SDK 自动从 URL 参数传递)
  • 收到不匹配 origin 的消息一律丢弃

调试

打开 iframe 里的聊天面板,在 devtools Network 标签过滤 auth/refresh —— 正常每 3 小时一次。手动模拟续期失败:在 Integration 面板轮换 S2S 密钥,iframe 下次续期时会抛 auth-failed


完整参考

TensorAgent.init(options)

参数类型默认说明
jwtstring必填。你后端签发的初始 JWT
hoststringhttps://agent.tos.runTensor Agent 服务地址
agentIdstring指定智能体 ID
contextPromptsstring[]初始页面上下文
contextBusinessContext结构化上下文
defaultWidth / minWidth / maxWidthnumber440 / 320 / 800面板宽度(px)
storageKeystringta-panel-widthlocalStorage 持久化 key
onDataChangecallback数据变更通知
onActioncallback操作指令通知
onAuthFailedcallback自动续期失败,需要补签新 JWT

postMessage 协议

仅在你需要自己写 iframe 通信时才用得到。正常用 SDK 不需要看。

方向类型Payload
Host → Iframetensor-agent:set-context-prompts{ prompts: string[] }
Host → Iframetensor-agent:set-contextBusinessContext
Host → Iframetensor-agent:send-prompt{ text: string }
Host → Iframetensor-agent:update-token{ jwt: string }
Iframe → Hosttensor-agent:ready{}
Iframe → Hosttensor-agent:data-changeDataChangePayload
Iframe → Hosttensor-agent:actionActionPayload
Iframe → Hosttensor-agent:auth-failed{ reason: string }

目录