Chat 聊天功能
Chat 主要用于 Chatbot 聊天机器人的实现,帮助业务快速搭建自己的 Chatbot 功能。支持 React、Vue2、Vue3 和微信小程序等多个技术栈。
使用方法
import React from 'react';
import { useChat } from '@tencent/ssv-ai-sdk-react';
import { HunyuanPlugin } from '@tencent/ssv-ai-sdk-plugin-hunyuan';
function ChatComponent() {
const {
input,
setInput,
messages,
assistantStatus,
status,
error,
chat,
stop,
} = useChat({
api: {
chatEndpoint: 'your-sse-endpoint'
},
mode: 'stream',
plugins: [new HunyuanPlugin()]
});
const handleSubmit = async (e) => {
e.preventDefault();
if (status === 'loading' || !input.trim()) return;
await chat({ prompt: input });
};
return (
<div className="chat-container">
{/* 消息列表 */}
<div className="messages">
{messages.map((message) => (
<div key={message.id} className={`message ${message.role}`}>
{message.role}: {message.content}
</div>
))}
</div>
{/* AI 状态显示 */}
<div className="status">
AI 状态: {assistantStatus}
</div>
{/* 输入表单 */}
<form onSubmit={handleSubmit} className="input-form">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="输入消息..."
disabled={status === 'loading'}
/>
{status === 'loading' ? (
<button type="button" onClick={stop}>停止</button>
) : (
<button type="submit">发送</button>
)}
</form>
</div>
);
}
export default ChatComponent;
<template>
<div class="chat-container">
<!-- 消息列表 -->
<div class="messages">
<div
v-for="message in chat.messages"
:key="message.id"
:class="['message', message.role]"
>
{{ message.role }}: {{ message.content }}
</div>
</div>
<!-- AI 状态显示 -->
<div class="status">AI 状态: {{ chat.assistantStatus }}</div>
<!-- 输入表单 -->
<form @submit.prevent="handleSubmit" class="input-form">
<input
:value="chat.input"
@input="(e) => ai.setInput(e.target.value)"
placeholder="输入消息..."
:disabled="chat.status === 'loading'"
/>
<button v-if="chat.status === 'loading'" type="button" @click="ai.stop">
停止
</button>
<button v-else type="submit">发送</button>
</form>
</div>
</template>
<script>
import { ChatMixin } from '@tencent/ssv-ai-sdk-vue2';
import { HunyuanPlugin } from '@tencent/ssv-ai-sdk-plugin-hunyuan';
export default {
mixins: [
ChatMixin({
api: {
chatEndpoint: 'your-sse-endpoint',
},
mode: 'stream',
plugins: [new HunyuanPlugin()],
}),
],
methods: {
async handleSubmit() {
if (this.chat.status === 'loading' || !this.chat.input.trim()) return;
await this.ai.chat({ prompt: this.chat.input });
},
},
};
</script>
<script setup>
import { useChat } from '@tencent/ssv-ai-sdk-vue';
import { HunyuanPlugin } from '@tencent/ssv-ai-sdk-plugin-hunyuan';
const {
messages,
assistantStatus,
status,
chat,
stop,
input,
handleInput,
} = useChat({
api: {
chatEndpoint: 'your-sse-endpoint',
},
mode: 'stream',
plugins: [new HunyuanPlugin()],
});
// 发送消息
const handleSubmit = async () => {
if (status.value === 'loading' || !input.value.trim()) return;
await chat({ prompt: input.value });
};
</script>
<template>
<div class="chat-container">
<!-- 消息列表 -->
<div class="messages">
<div
v-for="message in messages"
:key="message.id"
:class="['message', message.role]"
>
{{ message.role }}: {{ message.content }}
</div>
</div>
<!-- AI 状态显示 -->
<div class="status">
AI 状态: {{ assistantStatus }}
</div>
<!-- 输入表单 -->
<form @submit.prevent="handleSubmit" class="input-form">
<input
:value="input"
@input="handleInput($event.target.value)"
placeholder="输入消息..."
:disabled="status === 'loading'"
/>
<button v-if="status === 'loading'" type="button" @click="stop">
停止
</button>
<button v-else type="submit">发送</button>
</form>
</div>
</template>
// WXML 模板示例:
/*
<view class="chat-container">
<!-- 消息列表 -->
<view class="messages">
<block wx:for="{{chat.messages}}" wx:key="id">
<view class="message {{item.role}}">
{{item.role}}: {{item.content}}
</view>
</block>
</view>
<!-- AI 状态显示 -->
<view class="status">
AI 状态: {{chat.assistantStatus}}
</view>
<!-- 输入表单 -->
<form bindsubmit="handleSubmit" class="input-form">
<input
value="{{chat.input}}"
bindinput="handleInputChange"
placeholder="输入消息..."
disabled="{{chat.status === 'loading'}}"
/>
<button wx:if="{{chat.status === 'loading'}}" bindtap="ai.stop">
停止
</button>
<button wx:else form-type="submit">发送</button>
</form>
</view>
*/
import { ChatBehavior } from '@tencent/ssv-ai-sdk-miniprogram';
import { HunyuanPlugin } from '@tencent/ssv-ai-sdk-plugin-hunyuan';
Component({
behaviors: [
ChatBehavior({
api: {
chatEndpoint: 'your-sse-endpoint'
},
mode: 'stream',
plugins: [new HunyuanPlugin()]
}),
],
methods: {
async handleSubmit() {
if (this.data.chat.status === 'loading' || !this.data.chat.input.trim()) return;
await this.ai.chat({ prompt: this.data.chat.input });
},
handleInputChange(e) {
this.ai.setInput(e.detail.value);
},
},
});
配置 Config
api.chatEndpoint
定义 AI 聊天接口,配置 url
和 responseInterceptor
。
{
// 忽略其他配置...
api: {
chatEndpoint: {
url: 'your-sse-endpoint',
responseInterceptor(result) {
return {
...result,
messageID: result.id,
content: result.content
}
}
}
}
}
api.chatEndpoint.responseInterceptor
协议转换器,可接收特定数据。
interface ChatRespInterceptor {
// 消息 ID
messageID: string;
// 回复内容
content: string;
// 推理信息
reason: string;
// 错误信息
error: Object;
}
mode 模式
接口模式,可选 http
和 stream
。
stream
,流式数据。http
,长连接返回。(不推荐)
{
// 忽略其他配置...
mode: 'stream';
}
streamFormat
流式格式输出,可选 incremental
和 chunked
。会影响到的输出字段有回复内容 content
和思考推理内容 reason
。
incremental
,流式返回为内容叠加,每次后端返回的 content 都为最新的内容,需要在 SDK 内部进行拼接后输出。chunked
,流式返回为完整内容,需要 SDK 内部做覆盖。
{
// 忽略其他配置...
streamFormat: 'incremental';
}
数据 State
input 用户输入
用户输入的文本内容,可以在不同技术栈中使用:
function Component() {
const { input, setInput } = useChat({
// 忽略配置
});
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</div>
);
}
<template>
<input :value="chat.input" @input="handleInputChange" />
</template>
<script setup>
const { chat, ai } = useChat({
// 忽略配置
});
function handleInputChange(e) {
ai.setInput(e.target.value);
}
</script>
<template>
<input v-model="chat.input" />
<!-- 或者使用 methods 处理 -->
<input :value="chat.input" @input="handleInputChange" />
</template>
<script>
export default {
// ...其他配置
methods: {
handleInputChange(e) {
this.ai.setInput(e.target.value);
},
},
};
</script>
<!-- WXML 模板 -->
<input value="{{chat.input}}" bindinput="handleInputChange" />
<!-- JavaScript -->
<script>
// 在 methods 中
handleInputChange(e) {
this.ai.setInput(e.detail.value);
}
</script>
messages 消息列表
用于存储用户与 AI 的对话记录的数组列表 Message[]
。
interface Message {
// 消息 ID
id: string;
// 角色
role: 'user' | 'assistant';
// 回复内容
content: string;
// 推理内容
reason?: any;
// 接口相关的原始数据
extraInfo: any;
// 文件列表(React、Vue3、Vue2)
files?: FileItem[];
// 错误内容
error?: any;
}
可以根据 Message 列表进行 UI 的渲染:
function Component() {
const { messages } = useChat({
// 忽略配置
});
return (
<div>
{messages.map((message) => (
<div key={message.id}>
{message.role}: {message.content}
</div>
))}
</div>
);
}
<template>
<div class="messages">
<div
v-for="message in chat.messages"
:key="message.id"
:class="['message', message.role]"
>
<div class="content">{{ message.content }}</div>
<div v-if="message.error" class="error">
{{ message.error.message }}
</div>
</div>
</div>
</template>
<template>
<div class="message-list">
<div
v-for="message in chat.messages"
:key="message.id"
:class="['message', message.role]"
>
<div class="content">{{ message.content }}</div>
<div v-if="message.error" class="error">
{{ message.error.message }}
</div>
</div>
</div>
</template>
<!-- WXML 模板 -->
<view class="message-list">
<block wx:for="{{chat.messages}}" wx:key="id">
<view class="message {{item.role}}">
<view class="content">{{item.content}}</view>
<view wx:if="{{item.error}}" class="error"> {{item.error.message}} </view>
</view>
</block>
</view>
files 文件/图片列表
用户上传的文件/图片列表,支持图片等文件类型。
interface FileItem {
// 文件 URL
url?: string;
// 文件类型(小程序)
type?: 'image' | 'video' | 'audio' | 'file';
}
assistantStatus 助手 AI 状态
SDK 提供以下几种 AI 当前的运作状态,用于提供给业务进行精细化展示:
idle
,待开始。一般处于 AI 并没有开始回复的情况。loading
,连接中。用户发送内容后,AI 暂无返回内容信息。thinking
,思考中。AI 正在返回了思考推理信息。answering
,回答中。AI 正在回复内容信息。
function Component() {
const { assistantStatus } = useChat({
// 忽略配置
});
return <div>{assistantStatus}</div>;
}
<template>
<div class="status-indicator">
<span v-if="chat.assistantStatus === 'idle'">准备就绪</span>
<span v-else-if="chat.assistantStatus === 'answering'">回答中...</span>
</div>
</template>
<template>
<div class="status-indicator">
<span v-if="chat.assistantStatus === 'idle'">准备就绪</span>
<span v-else-if="chat.assistantStatus === 'answering'">回答中...</span>
</div>
</template>
<!-- WXML 模板 -->
<view class="status">AI 状态: {{chat.assistantStatus}}</view>
assistantStatus
是对 status
的 loading 状态的一个拓展。
status 连接状态
status 是用于展示与当前接口的连接状态,可选值为:
idle
,连接未开始。loading
,接口连接中。success
,接口返回成功。failed
,接口返回失败。stopped
,接口被中断。
function Component() {
const { status, chat, stop } = useChat({});
return (
<div>
<button onClick={() => chat()} disabled={status === 'loading'}>
发送
</button>
{status === 'loading' && <button onClick={stop}>停止</button>}
</div>
);
}
<template>
<button @click="sendMessage" :disabled="chat.status === 'loading'">
发送
</button>
<button v-if="chat.status === 'loading'" @click="stopChat">停止</button>
</template>
<template>
<button @click="sendMessage" :disabled="chat.status === 'loading'">
发送
</button>
<button v-if="chat.status === 'loading'" @click="stopChat">停止</button>
</template>
<!-- WXML 模板 -->
<button bindtap="handleSubmit" disabled="{{chat.status === 'loading'}}">
发送
</button>
<button wx:if="{{chat.status === 'loading'}}" bindtap="stopChat">停止</button>
error 错误信息
展示错误信息,可用于直接展示错误信息代替文本回复的内容。
function Component() {
const { error } = useChat({});
return (
<div>
{error && (
<div className="error-message">{error.message || '网络错误'}</div>
)}
</div>
);
}
<template>
<div v-if="error" class="error-message">
{{ error.message || '网络错误' }}
</div>
</template>
<template>
<div v-if="chat.error" class="error-message">
{{ chat.error.message || '网络错误' }}
</div>
</template>
<!-- WXML 模板 -->
<view wx:if="{{chat.error}}" class="error-message">
{{chat.error.message || '网络错误'}}
</view>
方法 Action
chat 发起对话
发起用户对话,会对 API 的 chatEndpoint 配置的 url 接口发送数据,status
会转为 loading
。
const { chat } = useChat({});
// 发送当前输入框中的内容
await chat({ prompt: input });
// 或者传入自定义参数
await chat({
prompt: '自定义问题',
customData: { key: 'value' },
});
// 发送当前输入框中的内容
ai.chat({ prompt: chat.value.input });
// 或者传入自定义参数
ai.chat({
prompt: '自定义问题',
// 允许代入其他业务参数
});
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.chat.input });
// 或者传入自定义参数
this.ai.chat({
prompt: '自定义问题',
customData: { key: 'value' },
});
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.data.chat.input });
// 或者传入自定义参数
this.ai.chat({
prompt: '自定义问题',
customData: { key: 'value' },
});
stop 停止对话
手动停止 AI 回复,会触发接口 AbortError,用于断开与 AI 接口的连接。手动停止后,status
会转为 stopped
,assistantStatus
会重置为 idle
。
const { stop } = useChat({});
// 触发停止,status 会转为 stopped
stop();
// 停止当前对话
ai.stop();
// 停止当前对话
this.ai.stop();
// 停止当前对话
this.ai.stop();
setMessages 更新会话列表
用于手动更新会话列表数据,比如进行会话列表的编辑和删除时可以使用。
const { messages, setMessages } = useChat({});
// 清空信息列表
setMessages([]);
// 清空信息列表
ai.setMessages([]);
// 添加新消息
const newMessages = [
...chat.value.messages,
{
id: 'new-id',
role: 'user',
content: '新的消息',
},
];
ai.setMessages(newMessages);
// 清空信息列表
this.ai.setMessages([]);
// 添加新消息
const newMessages = [
...this.chat.messages,
{
id: 'new-id',
role: 'user',
content: '新的消息',
},
];
this.ai.setMessages(newMessages);
// 清空信息列表
this.ai.setMessages([]);
// 添加新消息
const newMessages = [
...this.data.chat.messages,
{
id: 'new-id',
role: 'user',
content: '新的消息',
},
];
this.ai.setMessages(newMessages);
setInput 设置输入内容
设置用户输入框内容。
const { setInput } = useChat({});
// 设置输入内容
setInput('预设的问题');
// 清空输入
setInput('');
// 设置输入内容
ai.setInput('预设的问题');
// 清空输入
ai.setInput('');
// 设置输入内容
this.ai.setInput('预设的问题');
// 清空输入
this.ai.setInput('');
// 设置输入内容
this.ai.setInput('预设的问题');
// 清空输入
this.ai.setInput('');
setFiles 设置文件列表
设置用户上传的文件列表。
const { setFiles } = useChat({});
// 设置单个文件
setFiles([
{
id: 'file-1',
url: '图片链接',
},
]);
// 清空文件
setFiles([]);
// 设置单个文件
ai.setFiles([
{
id: 'file-1',
url: '图片链接',
},
]);
// 增加文件
ai.setFiles([...chat.value.files, newFile]);
// 清空文件
ai.setFiles([]);
// 设置单个文件
this.ai.setFiles([
{
id: 'file-1',
url: '图片链接',
},
]);
// 增加文件
this.ai.setFiles([...this.chat.files, newFile]);
// 清空文件
this.ai.setFiles([]);
// 设置文件列表
this.ai.setFiles([
{
id: 'file-1',
type: 'image',
url: '图片链接',
},
]);
// 清空文件
this.ai.setFiles([]);
setOptions 更新选项
动态更新聊天配置选项。
const { setOptions } = useChat({});
setOptions({
api: {
chatEndpoint: {
url: 'new-endpoint-url',
},
},
});
ai.setOptions({
api: {
chatEndpoint: {
url: 'new-endpoint-url',
},
},
});
this.ai.setOptions({
api: {
chatEndpoint: {
url: 'new-endpoint-url',
},
},
});
this.ai.setOptions({
api: {
chatEndpoint: {
url: 'new-endpoint-url',
},
},
});
on/off 事件监听和卸载
事件监听和卸载函数。
const { on, off } = useChat({});
// 在 useEffect 中监听
useEffect(() => {
const handleError = (error) => {
console.error('发生错误:', error);
};
on('error', handleError);
return () => {
off('error', handleError);
};
}, []);
import { onMounted, onBeforeUnmount } from 'vue';
// 在组件挂载时设置监听
onMounted(() => {
ai.on('error', handleError);
});
// 在组件卸载前取消监听
onBeforeUnmount(() => {
ai.off('complete', handleComplete);
ai.off('error', handleError);
});
// 事件处理函数
function handleComplete(data) {
console.log('对话完成:', data);
}
function handleError(err) {
console.error('对话错误:', err);
}
export default {
// 在组件创建时设置监听
created() {
this.ai.on('error', this.handleError);
},
// 在组件销毁前取消监听
beforeDestroy() {
this.ai.off('complete', this.handleComplete);
this.ai.off('error', this.handleError);
},
methods: {
handleComplete(data) {
console.log('对话完成:', data);
},
handleError(error) {
console.error('发生错误:', error);
},
},
};
Component({
// 其他配置...
attached() {
// 监听错误事件
this.ai.on('error', this.handleError);
},
detached() {
// 取消监听
this.ai.off('error', this.handleError);
},
methods: {
handleError(error) {
console.error('发生错误:', error);
},
},
});
事件 Events
message
监听原始消息数据。
const { on, off } = useChat({});
on('message', (data) => {
console.log('收到原始消息:', data);
});
ai.on('message', (data) => {
console.log('收到原始消息:', data);
});
this.ai.on('message', (data) => {
console.log('收到原始消息:', data);
});
this.ai.on('message', (data) => {
console.log('收到原始消息:', data);
});
complete
聊天完成时触发。
on('complete', (data) => {
console.log('聊天完成:', data);
});
ai.on('complete', (data) => {
console.log('聊天完成:', data);
});
this.ai.on('complete', (data) => {
console.log('聊天完成:', data);
});
this.ai.on('complete', (data) => {
console.log('聊天完成:', data);
});
error
监听错误。
on('error', (error) => {
console.error('发生错误:', error);
});
ai.on('error', (error) => {
console.error('发生错误:', error);
});
this.ai.on('error', (error) => {
console.error('发生错误:', error);
});
this.ai.on('error', (error) => {
console.error('发生错误:', error);
});
实用模式
消息的自定义渲染
function MessageList({ messages }) {
return (
<div className="chat-container">
{messages.map((message) => (
<div key={message.id} className={`message ${message.role}`}>
<div className="avatar">{message.role === 'user' ? '👤' : '🤖'}</div>
<div className="content">
{message.content}
{message.error && (
<div className="error">{message.error.message}</div>
)}
</div>
</div>
))}
</div>
);
}
<template>
<div class="chat-container">
<div
v-for="message in chat.messages"
:key="message.id"
:class="['message', message.role]"
>
<div class="avatar">
{{ message.role === 'user' ? '👤' : '🤖' }}
</div>
<div class="content">
{{ message.content }}
<div v-if="message.error" class="error">
{{ message.error.message }}
</div>
</div>
</div>
</div>
</template>
<template>
<div class="chat-container">
<div
v-for="message in chat.messages"
:key="message.id"
:class="['message', message.role]"
>
<div class="avatar">
{{ message.role === 'user' ? '👤' : '🤖' }}
</div>
<div class="content">
{{ message.content }}
<div v-if="message.error" class="error">
{{ message.error.message }}
</div>
</div>
</div>
</div>
</template>
<!-- WXML 模板 -->
<view class="chat-container">
<block wx:for="{{chat.messages}}" wx:key="id">
<view class="message {{item.role}}">
<view class="avatar"> {{item.role === 'user' ? '👤' : '🤖'}} </view>
<view class="content">
{{item.content}}
<view wx:if="{{item.error}}" class="error">
{{item.error.message}}
</view>
</view>
</view>
</block>
</view>
技术实现
Chat 功能基于跨技术栈的 AIChat
类实现,该类继承自 AIBase
,提供了完整的聊天功能支持:
- 流式数据处理:支持 SSE (Server-Sent Events) 流式数据接收
- 多种流格式:支持
incremental
和chunked
两种流式格式 - 状态管理:提供详细的连接状态和助手状态管理
- 错误处理:完善的错误捕获和处理机制
- 事件系统:基于事件的消息通信机制
- 文件支持:支持多媒体文件上传和处理
- 适配器模式:通过适配器支持不同运行环境
更多技术细节可以参考 AIChat
类的源码实现。