#连接器
让用户选择这次对话可以使用哪些外部服务(日历、邮箱、代码仓库)。本页用插槽和 onSend 实现它。
recipe-connectors.tsx
import { Chatkit, useChatRuntime } from '@volcengine/chatkit';
import { createMockAdapter } from '@volcengine/chatkit/adapters/mock';
import '@volcengine/chatkit/styles.css';
import { useState } from 'react';
const adapter = createMockAdapter({});
const CONNECTORS = [
{ id: 'calendar', label: '日历' },
{ id: 'mail', label: '邮箱' },
{ id: 'repo', label: '代码仓库' },
];
// 工具栏上的「连接器」按钮:点开是服务列表,每个服务一个开关
function ConnectorMenu({ enabled, onToggle }: { enabled: string[]; onToggle: (id: string) => void }) {
const [open, setOpen] = useState(false);
return (
<span className="demo-menu">
<button type="button" className="demo-chip" onClick={() => setOpen(value => !value)}>
连接器 {enabled.length}
</button>
{open ? (
<ul>
{CONNECTORS.map(item => (
<li key={item.id}>
<button type="button" onClick={() => onToggle(item.id)}>
{item.label}
<span className="demo-badge">{enabled.includes(item.id) ? '已开启' : '未开启'}</span>
</button>
</li>
))}
</ul>
) : null}
</span>
);
}
// 连接器用到两个插槽和 onSend:开启的服务显示在输入框上方,发送时把它们的 id 放进 metadata。
export default function RecipeConnectors() {
const runtime = useChatRuntime({ sessionId: 'recipe-connectors', adapter });
const [enabled, setEnabled] = useState(['calendar']);
const [received, setReceived] = useState<unknown>();
if (!runtime) return null;
const toggle = (id: string) =>
setEnabled(current => (current.includes(id) ? current.filter(item => item !== id) : [...current, id]));
return (
<div className="demo-row">
<div className="demo-main">
<Chatkit
runtime={runtime}
settings={{ welcome: '先开一个连接器', recommendations: ['看看我明天有什么安排'] }}
slots={{
'composer.toolbarStart': <ConnectorMenu enabled={enabled} onToggle={toggle} />,
'composer.top': (
<div className="demo-chips">
这次对话可以使用:
{enabled.length === 0 ? <span className="demo-muted">没有开启的服务</span> : null}
{CONNECTORS.filter(item => enabled.includes(item.id)).map(item => (
<span key={item.id} className="demo-badge">
{item.label}
</span>
))}
</div>
),
}}
onSend={input => {
const next = { ...input, metadata: { ...input.metadata, connectors: enabled } };
setReceived(next.metadata);
return next;
}}
/>
</div>
<aside className="demo-side">
<h4>后端收到的 metadata</h4>
{received ? <pre>{JSON.stringify(received, null, 2)}</pre> : <p className="demo-muted">发一句话看看</p>}
</aside>
</div>
);
}点工具栏的「连接器」开关服务;右侧显示 onSend 准备交给适配器的 metadata新窗口打开
在线示例使用示例数据,不连接日历、邮箱或代码仓库,也没有授权流程。右侧数据由 onSend 保存,仅证明发送前配置了这些字段,不证明后端已接收或执行。
#这个功能是什么
输入框工具栏上的「连接器」按钮展开服务列表,开关状态显示在输入框上方。发送时把开启的服务 id 放进 metadata。后端需要读取这些字段并约束工具权限;仅设置前端开关不会限制 Agent 使用服务。未授权服务的授权入口需由业务界面另行实现。
#用到的入口
| 入口 | 用来做什么 |
|---|---|
slots['composer.toolbarStart'] | 放「连接器」按钮与列表 |
slots['composer.top'] | 显示已开启的服务 |
onSend | 把开启的服务 id 放进 metadata |
renderers.tools | 连接器的工具调用显示服务的名字 |
#代码
import { useState } from 'react';
import { Chatkit, useChatSessions } from '@volcengine/chatkit';
export function App() {
const sessions = useChatSessions({ adapter, activeSessionId });
const [enabled, setEnabled] = useState<string[]>(['calendar']);
return (
<Chatkit
sessions={sessions}
slots={{
'composer.toolbarStart': <ConnectorMenu enabled={enabled} onChange={setEnabled} />,
'composer.top': enabled.length > 0 ? <EnabledConnectors ids={enabled} /> : null,
}}
onSend={input => ({ ...input, metadata: { ...input.metadata, connectors: enabled } })}
renderers={{
tools: {
calendar: { match: /^calendar_/, runningLabel: '正在查看日历', successLabel: '已查看日历' },
},
}}
/>
);
}#变化
- 授权流程:在
ConnectorMenu里打开授权页;授权结果由业务方的后端保存。 - 按会话记住选择:用
useCurrentSession().session?.id作为键保存。
上例的 enabled 属于页面状态,切换会话不会自动隔离或恢复;空会话首次发送前可能还没有会话 id,需要单独保存待创建会话的选择。重新生成复用原输入,编辑后重发不经过 onSend,依赖连接器权限的请求仍需后端逐次校验。
#为什么不内置
有哪些服务、怎么授权、凭据存在哪里、后端怎么按开关限制 Agent,都需要业务方和自己的后端约定。chatkit 不保存任何凭据,也不连接任何外部服务。