跳到主要内容

示例:3 分钟给你的网站装一个客服组件

目标: 在你自己的网站右下角挂一个聊天气泡,背后是你在 OpenHex 发布的 Agent(比如一个客服「大顺」)。访客不用登录、不用注册就能聊,费用记在 Agent 拥有者头上,访客零门槛。

整套东西就三块:

  1. 一个已发布的 Agent(拿到它的 agentId
  2. 一个工作区 API Keysk_ws_…)——你的后端拿着它签发访客令牌,密钥永远不进浏览器
  3. 前端挂上 SDK 的 <ChatWidget>

下面照着做,三分钟跑通。

前提

  • app.openhex.tech 发布一个 Agent,进它的详情页拿到 agentId(一个 UUID)。
  • 切换到一个服务工作区后,在 设置 → API Key 创建一个工作区 API Keysk_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)