Skip to content
On this page

Chat 聊天功能

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

设计理念

Chat SDK 基于 Headless UI 概念设计,专注于提供数据状态管理和业务逻辑处理,不包含任何预置的 UI 组件

优点:

  • 🎨 高度可定制化:完全自由地设计和实现 UI 界面
  • 🔧 灵活性强:适配各种设计系统和 UI 框架
  • 📦 轻量级:不包含 UI 代码,包体积更小
  • 🔄 技术栈无关:可与任何 UI 库或组件库结合使用

注意事项:

  • ⚠️ 需要自行实现 UI:开发者需要根据业务需求自行设计和实现聊天界面
  • 🛠️ 需要处理样式:消息展示、输入框、按钮等 UI 元素需要自行开发
  • 📱 适配工作:不同平台的 UI 适配需要开发者自行处理

推荐组件库:

架构图

下图展示了 Chat SDK 的三层架构关系:

架构说明

  • UI 组件层:各种 UI 组件库和自定义组件,负责聊天界面展示
  • Chat SDK 层:🎯 核心抽象层,提供 Headless UI 的状态管理和业务逻辑,支持多端适配
  • AI 模型层:各种大语言模型,提供 AI 聊天能力

Chat SDK 的核心价值

  • 📦 解耦 UI 与逻辑:UI 层可以自由选择,SDK 专注于状态管理
  • 🔄 统一多端接口:一套 API 适配多个技术栈和 AI 模型
  • 🛠️ 简化开发复杂度:封装网络通信、状态管理等底层细节

使用方法

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 聊天接口,配置 urlrequestInterceptorresponseInterceptor

js
{
  // 忽略其他配置...
  api: {
    chatEndpoint: {
      url: 'your-sse-endpoint',
      requestInterceptor(options) {
        // 可以对请求参数进行拦截和修改
        options.headers = {
          ...options.headers,
          'Authorization': 'Bearer your-token',
          'Custom-Header': 'custom-value'
        };
        return options;
      },
      responseInterceptor(result) {
        return {
          ...result,
          messageID: result.id,
          content: result.content
        }
      }
    }
  }
}

api.chatEndpoint.requestInterceptor

请求拦截器,用于对发送给 AI 接口的请求进行拦截和修改。主要用于添加自定义请求头、身份验证信息或其他请求参数。

typescript
interface RequestInterceptor {
  (options: RequestOptions): RequestOptions;
}

interface RequestOptions {
  // 请求头信息
  headers?: Record<string, string>;
  // 其他请求配置
  [key: string]: any;
}

常见使用场景:

  • 添加身份验证令牌
  • 设置自定义请求头
  • 添加 API 版本信息
  • 修改请求超时时间等配置

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="input" @input="handleInputChange" />
</template>

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

function handleInputChange(e) {
  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 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 正在返回了思考推理(reason)信息。
  • answering,回答中。AI 正在回复内容(content)信息。
jsx
function Component() {
  const { assistantStatus } = useChat({
    // 忽略配置
  });

  return <div>{assistantStatus}</div>;
}
vue
<template>
  <div class="status-indicator">
    <span v-if="assistantStatus === 'idle'">准备就绪</span>
    <span v-else-if="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="status === 'loading'">发送</button>
  <button v-if="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

参数说明chat 方法的参数会直接发送给 chatEndpoint 接口,参数结构需要与后端接口保持一致,不是固定格式。用户输入框的内容可以通过 input 字段获取。

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

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

// 或者传入自定义参数(根据后端接口需求调整)
await chat({
  message: '自定义问题',
  user_id: 'user123',
  session_id: 'session456',
  // 其他业务相关参数...
});
js
const { chat, input } = useChat({});

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

// 或者传入自定义参数(根据后端接口需求调整)
chat({
  message: '自定义问题',
  context: { user_id: 'user123' },
  // 参数结构需要与 chatEndpoint 接口保持一致
});
js
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.chat.input });

// 或者传入自定义参数(根据后端接口需求调整)
this.ai.chat({
  text: '自定义问题',
  metadata: { key: 'value' },
  // 参数结构需要与 chatEndpoint 接口保持一致
});
js
// 发送当前输入框中的内容
this.ai.chat({ prompt: this.data.chat.input });

// 或者传入自定义参数(根据后端接口需求调整)
this.ai.chat({
  content: '自定义问题',
  extra_data: { key: 'value' },
  // 参数结构需要与 chatEndpoint 接口保持一致
});

stop 停止对话

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

js
const { stop } = useChat({});
// 触发停止,status 会转为 stopped
stop();
js
const { stop } = useChat({});

// 停止当前对话
stop();
js
// 停止当前对话
this.ai.stop();
js
// 停止当前对话
this.ai.stop();

setMessages 更新会话列表

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

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

// 清空信息列表
setMessages([]);
js
const { messages, setMessages } = useChat({});

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

// 添加新消息
const newMessages = [
  ...messages.value,
  {
    id: 'new-id',
    role: 'user',
    content: '新的消息',
  },
];
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
const { setInput } = useChat({});

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

// 清空输入
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: '图片链接',
    type: 'image',
  },
]);

// 清空文件
setFiles([]);
js
const { files, setFiles } = useChat({});

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

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

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

// 增加文件
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
const { setOptions } = useChat({});

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';

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

// 在组件挂载时设置监听
onMounted(() => {
  on('error', handleError);
});

// 在组件卸载前取消监听
onBeforeUnmount(() => {
  off('complete', handleComplete);
  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
const { on } = useChat({});

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

stateChange 状态变化

状态变化事件,当任何状态发生变化时触发,包括消息列表、输入内容、文件列表等。

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

on('stateChange', (state) => {
  console.log('状态变化:', state);
  // state 包含: messages, error, status, assistantStatus, input, files
});
js
const { on } = useChat({});

on('stateChange', (state) => {
  console.log('状态变化:', state);
});
js
this.ai.on('stateChange', (state) => {
  console.log('状态变化:', state);
});
js
this.ai.on('stateChange', (state) => {
  console.log('状态变化:', state);
});

startReply 开始响应

AI 开始回复事件,在收到第一条流式消息时触发,仅触发一次。

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

on('startReply', (state) => {
  console.log('AI 开始回复:', state);
});
js
const { on } = useChat({});

on('startReply', (state) => {
  console.log('AI 开始回复:', state);
});
js
this.ai.on('startReply', (state) => {
  console.log('AI 开始回复:', state);
});
js
this.ai.on('startReply', (state) => {
  console.log('AI 开始回复:', state);
});

startAnswer 开始回答

AI 开始回答事件,当助手状态从思考转为回答时触发。

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

on('startAnswer', (state) => {
  console.log('AI 开始回答:', state);
});
js
const { on } = useChat({});

on('startAnswer', (state) => {
  console.log('AI 开始回答:', state);
});
js
this.ai.on('startAnswer', (state) => {
  console.log('AI 开始回答:', state);
});
js
this.ai.on('startAnswer', (state) => {
  console.log('AI 开始回答:', state);
});

error 错误

错误事件,当发生错误时触发。

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

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

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

Released under the MIT License.