示例:3 分钟给你的网站装一个客服组件
目标: 在你自己的网站右下角挂一个聊天气泡,背后是你在 OpenHex 发布的 Agent(比如一个客服「大顺」)。访客不用登录、不用注册就能聊,费用记在 Agent 拥有者头上,访客零门槛。
整套东西就三块:
- 一个已发布的 Agent(拿到它的
agentId) - 一个工作区 API Key(
sk_ws_…)——你的后端拿着它签发访客令牌,密钥永远不进浏览器 - 前端挂上 SDK 的
<ChatWidget>
下面照着做,三分钟跑通。
前提
- 在 app.openhex.tech 发布一个 Agent,进它的详情页拿到
agentId(一个 UUID)。 - 切换到一个服务工作区后,在 设置 → API Key 创建一个工作区 API Key(
sk_ws_…)。密钥只显示一次,存到后端环境变量里(不要用NEXT_PUBLIC_)。 - 安装 SDK:
npm install @openhex-ai/agent-sdk
# 或 pnpm add @openhex-ai/agent-sdk
第 1 步:后端签发访客令牌(约 3 行)
浏览器不能碰 sk_ws_…,所以让你自己的后端拿它换一个短时的访客会话令牌再交给前端。SDK 的 startVisitorSession 把「开通访客成员 + 签发会话」合成一次调用:
app/api/openhex/chat-token/route.ts (Next.js App Router)
import { OpenhexClient } from '@openhex-ai/agent-sdk';
import { cookies } from 'next/headers';
import { randomBytes } from 'node:crypto';
const ws = new OpenhexClient({ apiKey: process.env.OPENHEX_WORKSPACE_KEY! })
.workspaces.workspace('你的工作区-slug'); // 不确定 slug?先跑一次 client.workspaces.whoami()
export async function POST() {
// 给每个访客一个稳定的引用(放 httpOnly cookie),下次来还是同一个人 → 续上同一段对话
const jar = await cookies();
let ref = jar.get('ohx_ref')?.value;
if (!ref) {
ref = `web_${randomBytes(12).toString('hex')}`;
jar.set('ohx_ref', ref, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 60 * 60 * 24 * 180 });
}
const { token, expires_at } = await ws.startVisitorSession({
sp_user_ref: ref, // 稳定引用 → 同一访客始终是同一个成员
display_name: '网站访客',
ttl_seconds: 1800, // 令牌 30 分钟过期,泄漏了也很快失效
});
return Response.json({ token, expiresAt: expires_at });
}
:::tip 不知道 slug?
新拿到 sk_ws_… 却不知道工作区 slug,先调一次 client.workspaces.whoami(),它会返回 { slug, workspace_id, … }。
:::
第 2 步:前端挂上组件
<ChatWidget> 是一个开箱即用的浮动客服气泡:桌面端是角落面板,移动端全屏。给它 agentId 和一个 getToken(它会在请求前自动拿新令牌):
components/SupportWidget.tsx
'use client';
import { useCallback, useRef } from 'react';
import { ChatWidget } from '@openhex-ai/agent-sdk/react';
export default function SupportWidget() {
// 缓存令牌到临过期前,避免每条消息都打一次签发接口
const cache = useRef<{ token: string; exp: number } | null>(null);
const getToken = useCallback(async () => {
const now = Date.now();
if (cache.current && cache.current.exp - now > 60_000) return cache.current.token;
const r = await fetch('/api/openhex/chat-token', { method: 'POST' });
if (!r.ok) throw new Error(`chat-token ${r.status}`);
const d = (await r.json()) as { token: string; expiresAt: string };
cache.current = { token: d.token, exp: new Date(d.expiresAt).getTime() };
return d.token;
}, []);
return (
<ChatWidget
agentId="你的-agent-id"
baseUrl="https://api.openhex.tech"
getToken={getToken}
// 刷新页面也续上同一段对话(配合上面的稳定 cookie)
persist
title="在线客服"
greeting="你好 👋 有什么可以帮你的?"
placeholder="输入你的问题…"
accentColor="#f1d422"
launcherLabel="联系客服"
/>
);
}
把 <SupportWidget /> 放进你的根布局(app/layout.tsx)即可,全站都会有这个气泡。
就这些
访客打开网页 → 组件调 /api/openhex/chat-token → 后端 startVisitorSession 换到令牌 → 组件拿着令牌直接和你的 Agent 流式对话。
几个要点:
- 费用记在 Agent 拥有者头上。 只要目标 Agent 的
billingPayer没设成user(默认就是拥有者付费),匿名访客零余额也能聊,扣的是你的 OpenHex 额度。 - 同一访客续同一段对话。
sp_user_ref稳定(上面用了 httpOnly cookie)→ 始终解析到同一个成员;再加上<ChatWidget persist>,刷新/重开页面都能接着上次聊,连历史一起恢复。 - 密钥安全。
sk_ws_…只在后端;浏览器只拿到 30 分钟过期的短令牌。给签发接口加个按 IP / cookie 的限流更稳妥。
想进一步定制外观、事件回调、或用无头的 useOpenhexChat 自己搭 UI,见 对话 (Chat) 和 参考 (Reference)。工作区成员、积分、会话令牌的完整接口见 工作区 (Workspaces)。