Skip to content
On this page

Chat 聊天功能

Chat 主要用于 Chatbot 聊天机器人的实现,帮助业务快速搭建自己的 Chatbot 功能。支持 React、Vue2、Vue3 和微信小程序等多个技术栈。

使用方法

jsx
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;
vue
<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>
vue
<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>
js
// 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 聊天接口,配置 urlresponseInterceptor

js
{
  // 忽略其他配置...
  api: {
    chatEndpoint: {
      url: 'your-sse-endpoint',
      responseInterceptor(result) {
        return {
          ...result,
          messageID: result.id,
          content: result.content
        }
      }
    }
  }
}

api.chatEndpoint.responseInterceptor

协议转换器,可接收特定数据。

typescript
interface ChatRespInterceptor {
  // 消息 ID
  messageID: string;
  // 回复内容
  content: string;
  // 推理信息
  reason: string;
  // 错误信息
  error: Object;
}

mode 模式

接口模式,可选 httpstream

  • stream,流式数据。
  • http,长连接返回。(不推荐)
js
{
  // 忽略其他配置...
  mode: 'stream';
}

streamFormat

流式格式输出,可选 incrementalchunked。会影响到的输出字段有回复内容 content 和思考推理内容 reason

  • incremental,流式返回为内容叠加,每次后端返回的 content 都为最新的内容,需要在 SDK 内部进行拼接后输出。
  • chunked,流式返回为完整内容,需要 SDK 内部做覆盖。
js
{
  // 忽略其他配置...
  streamFormat: 'incremental';
}

数据 State

input 用户输入

用户输入的文本内容,可以在不同技术栈中使用:

jsx
function Component() {
  const { input, setInput } = useChat({
    // 忽略配置
  });

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
    </div>
  );
}
vue
<template>
  <input :value="chat.input" @input="handleInputChange" />
</template>

<script setup>
const { chat, ai } = useChat({
  // 忽略配置
});

function handleInputChange(e) {
  ai.setInput(e.target.value);
}
</script>
vue
<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>
html
<!-- WXML 模板 -->
<input value="{{chat.input}}" bindinput="handleInputChange" />

<!-- JavaScript -->
<script>
  // 在 methods 中
  handleInputChange(e) {
    this.ai.setInput(e.detail.value);
  }
</script>

messages 消息列表

用于存储用户与 AI 的对话记录的数组列表 Message[]

ts
interface Message {
  // 消息 ID
  id: string;
  // 角色
  role: 'user' | 'assistant';
  // 回复内容
  content: string;
  // 推理内容
  reason?: any;
  // 接口相关的原始数据
  extraInfo: any;
  // 文件列表(React、Vue3、Vue2)
  files?: FileItem[];
  // 错误内容
  error?: any;
}

可以根据 Message 列表进行 UI 的渲染:

jsx
function Component() {
  const { messages } = useChat({
    // 忽略配置
  });

  return (
    <div>
      {messages.map((message) => (
        <div key={message.id}>
          {message.role}: {message.content}
        </div>
      ))}
    </div>
  );
}
vue
<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>
vue
<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>
html
<!-- 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 文件/图片列表

用户上传的文件/图片列表,支持图片等文件类型。

ts
interface FileItem {
  // 文件 URL
  url?: string;
  // 文件类型(小程序)
  type?: 'image' | 'video' | 'audio' | 'file';
}

assistantStatus 助手 AI 状态

SDK 提供以下几种 AI 当前的运作状态,用于提供给业务进行精细化展示:

  • idle,待开始。一般处于 AI 并没有开始回复的情况。
  • loading,连接中。用户发送内容后,AI 暂无返回内容信息。
  • thinking,思考中。AI 正在返回了思考推理信息。
  • answering,回答中。AI 正在回复内容信息。
jsx
function Component() {
  const { assistantStatus } = useChat({
    // 忽略配置
  });

  return <div>{assistantStatus}</div>;
}
vue
<template>
  <div class="status-indicator">
    <span v-if="chat.assistantStatus === 'idle'">准备就绪</span>
    <span v-else-if="chat.assistantStatus === 'answering'">回答中...</span>
  </div>
</template>
vue
<template>
  <div class="status-indicator">
    <span v-if="chat.assistantStatus === 'idle'">准备就绪</span>
    <span v-else-if="chat.assistantStatus === 'answering'">回答中...</span>
  </div>
</template>
html
<!-- WXML 模板 -->
<view class="status">AI 状态: {{chat.assistantStatus}}</view>

assistantStatus 是对 status 的 loading 状态的一个拓展。

status 连接状态

status 是用于展示与当前接口的连接状态,可选值为:

  • idle,连接未开始。
  • loading,接口连接中。
  • success,接口返回成功。
  • failed,接口返回失败。
  • stopped,接口被中断。
jsx
function Component() {
  const { status, chat, stop } = useChat({});

  return (
    <div>
      <button onClick={() => chat()} disabled={status === 'loading'}>
        发送
      </button>
      {status === 'loading' && <button onClick={stop}>停止</button>}
    </div>
  );
}
vue
<template>
  <button @click="sendMessage" :disabled="chat.status === 'loading'">
    发送
  </button>
  <button v-if="chat.status === 'loading'" @click="stopChat">停止</button>
</template>
vue
<template>
  <button @click="sendMessage" :disabled="chat.status === 'loading'">
    发送
  </button>
  <button v-if="chat.status === 'loading'" @click="stopChat">停止</button>
</template>
html
<!-- WXML 模板 -->
<button bindtap="handleSubmit" disabled="{{chat.status === 'loading'}}">
  发送
</button>
<button wx:if="{{chat.status === 'loading'}}" bindtap="stopChat">停止</button>

error 错误信息

展示错误信息,可用于直接展示错误信息代替文本回复的内容。

jsx
function Component() {
  const { error } = useChat({});

  return (
    <div>
      {error && (
        <div className="error-message">{error.message || '网络错误'}</div>
      )}
    </div>
  );
}
vue
<template>
  <div v-if="error" class="error-message">
    {{ error.message || '网络错误' }}
  </div>
</template>
vue
<template>
  <div v-if="chat.error" class="error-message">
    {{ chat.error.message || '网络错误' }}
  </div>
</template>
html
<!-- WXML 模板 -->
<view wx:if="{{chat.error}}" class="error-message">
  {{chat.error.message || '网络错误'}}
</view>

方法 Action

chat 发起对话

发起用户对话,会对 API 的 chatEndpoint 配置的 url 接口发送数据,status 会转为 loading

js
const { chat } = useChat({});

// 发送当前输入框中的内容
await chat({ prompt: input });

// 或者传入自定义参数
await chat({
  prompt: '自定义问题',
  customData: { key: 'value' },
});
js
// 发送当前输入框中的内容
ai.chat({ prompt: chat.value.input });

// 或者传入自定义参数
ai.chat({
  prompt: '自定义问题',
  // 允许代入其他业务参数
});
js
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.chat.input });

// 或者传入自定义参数
this.ai.chat({
  prompt: '自定义问题',
  customData: { key: 'value' },
});
js
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.data.chat.input });

// 或者传入自定义参数
this.ai.chat({
  prompt: '自定义问题',
  customData: { key: 'value' },
});

stop 停止对话

手动停止 AI 回复,会触发接口 AbortError,用于断开与 AI 接口的连接。手动停止后,status 会转为 stoppedassistantStatus 会重置为 idle

js
const { stop } = useChat({});
// 触发停止,status 会转为 stopped
stop();
js
// 停止当前对话
ai.stop();
js
// 停止当前对话
this.ai.stop();
js
// 停止当前对话
this.ai.stop();

setMessages 更新会话列表

用于手动更新会话列表数据,比如进行会话列表的编辑和删除时可以使用。

js
const { messages, setMessages } = useChat({});

// 清空信息列表
setMessages([]);
js
// 清空信息列表
ai.setMessages([]);

// 添加新消息
const newMessages = [
  ...chat.value.messages,
  {
    id: 'new-id',
    role: 'user',
    content: '新的消息',
  },
];
ai.setMessages(newMessages);
js
// 清空信息列表
this.ai.setMessages([]);

// 添加新消息
const newMessages = [
  ...this.chat.messages,
  {
    id: 'new-id',
    role: 'user',
    content: '新的消息',
  },
];
this.ai.setMessages(newMessages);
js
// 清空信息列表
this.ai.setMessages([]);

// 添加新消息
const newMessages = [
  ...this.data.chat.messages,
  {
    id: 'new-id',
    role: 'user',
    content: '新的消息',
  },
];
this.ai.setMessages(newMessages);

setInput 设置输入内容

设置用户输入框内容。

js
const { setInput } = useChat({});

// 设置输入内容
setInput('预设的问题');

// 清空输入
setInput('');
js
// 设置输入内容
ai.setInput('预设的问题');

// 清空输入
ai.setInput('');
js
// 设置输入内容
this.ai.setInput('预设的问题');

// 清空输入
this.ai.setInput('');
js
// 设置输入内容
this.ai.setInput('预设的问题');

// 清空输入
this.ai.setInput('');

setFiles 设置文件列表

设置用户上传的文件列表。

js
const { setFiles } = useChat({});

// 设置单个文件
setFiles([
  {
    id: 'file-1',
    url: '图片链接',
  },
]);

// 清空文件
setFiles([]);
js
// 设置单个文件
ai.setFiles([
  {
    id: 'file-1',
    url: '图片链接',
  },
]);

// 增加文件
ai.setFiles([...chat.value.files, newFile]);

// 清空文件
ai.setFiles([]);
js
// 设置单个文件
this.ai.setFiles([
  {
    id: 'file-1',
    url: '图片链接',
  },
]);

// 增加文件
this.ai.setFiles([...this.chat.files, newFile]);

// 清空文件
this.ai.setFiles([]);
js
// 设置文件列表
this.ai.setFiles([
  {
    id: 'file-1',
    type: 'image',
    url: '图片链接',
  },
]);

// 清空文件
this.ai.setFiles([]);

setOptions 更新选项

动态更新聊天配置选项。

js
const { setOptions } = useChat({});

setOptions({
  api: {
    chatEndpoint: {
      url: 'new-endpoint-url',
    },
  },
});
js
ai.setOptions({
  api: {
    chatEndpoint: {
      url: 'new-endpoint-url',
    },
  },
});
js
this.ai.setOptions({
  api: {
    chatEndpoint: {
      url: 'new-endpoint-url',
    },
  },
});
js
this.ai.setOptions({
  api: {
    chatEndpoint: {
      url: 'new-endpoint-url',
    },
  },
});

on/off 事件监听和卸载

事件监听和卸载函数。

js
const { on, off } = useChat({});

// 在 useEffect 中监听
useEffect(() => {
  const handleError = (error) => {
    console.error('发生错误:', error);
  };

  on('error', handleError);

  return () => {
    off('error', handleError);
  };
}, []);
js
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);
}
js
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);
    },
  },
};
js
Component({
  // 其他配置...

  attached() {
    // 监听错误事件
    this.ai.on('error', this.handleError);
  },

  detached() {
    // 取消监听
    this.ai.off('error', this.handleError);
  },

  methods: {
    handleError(error) {
      console.error('发生错误:', error);
    },
  },
});

事件 Events

message

监听原始消息数据。

js
const { on, off } = useChat({});

on('message', (data) => {
  console.log('收到原始消息:', data);
});
js
ai.on('message', (data) => {
  console.log('收到原始消息:', data);
});
js
this.ai.on('message', (data) => {
  console.log('收到原始消息:', data);
});
js
this.ai.on('message', (data) => {
  console.log('收到原始消息:', data);
});

complete

聊天完成时触发。

js
on('complete', (data) => {
  console.log('聊天完成:', data);
});
js
ai.on('complete', (data) => {
  console.log('聊天完成:', data);
});
js
this.ai.on('complete', (data) => {
  console.log('聊天完成:', data);
});
js
this.ai.on('complete', (data) => {
  console.log('聊天完成:', data);
});

error

监听错误。

js
on('error', (error) => {
  console.error('发生错误:', error);
});
js
ai.on('error', (error) => {
  console.error('发生错误:', error);
});
js
this.ai.on('error', (error) => {
  console.error('发生错误:', error);
});
js
this.ai.on('error', (error) => {
  console.error('发生错误:', error);
});

实用模式

消息的自定义渲染

jsx
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>
  );
}
vue
<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>
vue
<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>
html
<!-- 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) 流式数据接收
  • 多种流格式:支持 incrementalchunked 两种流式格式
  • 状态管理:提供详细的连接状态和助手状态管理
  • 错误处理:完善的错误捕获和处理机制
  • 事件系统:基于事件的消息通信机制
  • 文件支持:支持多媒体文件上传和处理
  • 适配器模式:通过适配器支持不同运行环境

更多技术细节可以参考 AIChat 类的源码实现。

Released under the MIT License.