
ProChat 官网地址直达: pro-chat.antdigital.dev/
嗨~TechUI 的家人们,欢迎来到 TechUI 的冬季产品发布稿现场!今天我们将带给你们一点有趣又实用的东西 😎 ------ ProChat 1.0。

今年最火 🔥 的技术是什么?想必回答一定会是大模型。而我们所做的 ProChat 正是一款开箱即用的大模型对话前端解决方案 ,提供类 ProTable 的 request
接口和 useProChat
等方便的编程式操作控制,帮助你用 80 行代码 3 分钟 快速搭建 AI 对话产品。
当然,现在市面上已经有很多大模型的前端解决方案了,你可能会问,为什么我要使用 ProChat ,它有什么优势吗?而这个答案,也就在本文的标题中 ------ 因为我们所关注的"亿"点点细节。我知道,"亿"这个字眼可能会让人觉得太过庞大和遥不可及,但在 ProChat 里,每一个"小细节"都值得被放大镜仔细端详。
让我们开始吧!
ProChat 体验细节
默认的流式输出支持
由 ChatGPT 率先实现的流式输出在用户体验上远超传统的 HTTP 请求,SSE(Server Send Event)这项技术也可以说正式登上了主流技术圈的视野中。ProChat 作为 AI 会话的前端解决方案,自然默认集成了这项流式输出的能力。
只需要在 request
中配置一个返回流式文本的 Response
(Web标准的 Response 对象),就可以轻松实现流式效果的集成。
ini
import { ProChat } from '@ant-design/pro-chat';
import { MockResponse } from '../mocks/streamResponse';
export default () => {
const theme = useTheme();
return (
<ProChat
request={async (messages) => {
const mockedData: string = `这是一段模拟的流式字符串数据。本次会话传入了${messages.length}条消息`;
const mockResponse = new MockResponse(mockedData, 50);
return mockResponse.getResponse();
}}
/>
);
};
效果如下:

而同样的,ProChat 的 request
api 也兼容传统的非流式请求:
arduino
/**
* description: 消息将在等待 5s 后返回
*/
import { ProChat } from '@ant-design/pro-chat';
const delay = (text: string) =>
new Promise<string>((resolve) => {
setTimeout(() => {
resolve(text);
}, 5000);
});
export default () => {
return (
<ProChat
request={async (messages) => {
const text = await delay(
`这是一条模拟非流式输出的消息的消息。本次会话传入了${messages.length}条消息`,
);
return new Response(text);
}}
style={{ height: '100vh' }}
/>
);
};
完备的 Markdown、代码块展示
大模型可以通过 Markdown 的语法输出格式化的文本,因此 Markdown 的展示与渲染是一个AI会话组件必不可少的模块。ProChat 中已经完整支持基础的 Markdown 语法,并增强支持了代码块、LaTeX 数学公式等特性。完整效果一览如下:

其中我们特别优化了代码块的交互与体验。
其中:针对单行代码,我们使之更加贴近命令行执行风格,以符合常见的代码块使用习惯。

而针对多行代码块,我们则强化了代码块组件的交互能力,使之具有折叠展开、更换高亮语言等进阶功能,进而帮助你在日常使用 AI 大模型中更好地查看AI生成的代码。

当然,如果你希望在其他场景使用如此丰富与强大的 Markdown 与代码块组件,完全没有问题!ProChat 的 Markdown 语法高亮和代码块借助了 ProEditor 的 Markdown 组件 与 Highlight 组件 得以实现。而你也可以轻松地在你的项目中集成与使用。

会话消息的编辑与重新发送
如果经常使用 AI 大模型会话的同学,应该经常会感受到有时候大模型的输出存在一些瑕疵,导致体验极其不好。譬如上述的代码块场景。能够展示代码块的前提是 ai 标记输出了代码块的语法。但如果此时 AI 直接输出了纯文本,连代码块都没有呈现,请问阁下又该如何应对?

如果你在使用 ChatGPT ,那么你唯一能做的就只是让 AI 包含 Markdown 格式进行重新输出。这个体验并不理想。
而 ProChat 则给了用户一个新的选择:直接改它!

是的没错,在我们这大半年的实践中,我们发现可编辑的 AI 消息,对于用户操作的便捷性与易用度来说都非常有用,体验上更是能有一个巨大的体验。
与此同时,懂行的同学估计也知道,在 Chat 组件中实现可编辑消息的难度和复杂度,也比单纯的展示消息要高出很多。
而这正是 ProChat 的交互前瞻性与技术先进性的体现。
快速上手
好了,既然看了那么多 ProChat 的,那我们就看看如何快速上手使用吧!
安装 & 使用
直接使用 tnpm
进行安装,如果是非内部使用,也可以用 pnpm
、yarn
、bun
这类进行安装
sql
tnpm install @ant-design/pro-chat --save
tnpm install @ant-design/antd-style --save
依赖需求
css
peerDependencies: {
"antd": "^5",
"antd-style": "^3",
"react": "^18"
}
组件使用如下:最简单的情况下,你只需要一个 Request 的 Api 就可以了,详细使用和参考可以看下面的内容。
dart
<ProChat
request={async (messages) => {
// request 发送,Mesaage 作为 参数传入
return Response; // 支持流式 & 非流式
}}
/>
80 行 3 分钟接入通义千问大模型
这是一个快速接入通义千问的例子,这里展示的是 NextJs 项目,如果是 umi 的话,需要一个服务端,目前通义千问咱不支持客户端请求(OpenAI 是支持的)
dart
import { ProChat } from '@ant-design/pro-chat';
export default () => {
return (
<ProChat
style={{
height: "100vh",
width: "100vw",
}}
request={async (messages) => {
const response = await fetch("/api/qwen", {
method: "POST",
body: JSON.stringify({ messages: messages }),
});
const data = await response.json();
return new Response(data.output?.text);
}}
/>
);
};
php
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const { messages = [] }: Partial<{ messages: Array<any> }> =
await request.json();
try {
const apiKey = "Your-Api-Key"; // 你的 API 密钥
const response = await fetch(
"https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
{
method: "POST",
headers: {
Authorization: "Bearer " + apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "qwen-turbo",
input: {
messages: [
{
role: "system",
content: "You are a helpful assistant.",
},
...messages,
],
},
parameters: {},
}),
}
);
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
return NextResponse.error();
}
}
因为我们担心使用个人 Key 给大家体验会有些奇怪的问题,我们结合 CodeFuse 的通义千问接口,给大家搭建了这样一个体验地址,后续会在 TechUI Studio 中持续完善,请关注我们。👇 戳我体验 ProChat + 通义千问
程序化控制
如果你需要使用程序化的方式控制,我们还提供了 chatRef
和 useProChat
两种方式,供你获取 proChat 实例,进而实现程序化控制:
ini
import { ProChat, ProChatInstance } from '@ant-design/pro-chat';
import { useRef } from 'react';
import { Button } from 'antd';
import { MockResponse } from '@/ProChat/mocks/streamResponse';
import { example } from '../mocks/basic';
export default () => {
const proChatRef = useRef<ProChatInstance>();
return (
<div>
<Button
type={'primary'}
onClick={() => {
if (!proChatRef.current) return;
const messages = proChatRef.current.getChatMessages();
const { id, content } = messages[0] || {};
if (!id) return;
proChatRef.current.setMessageContent(id, content + '👋');
}}
>
修改首条消息,添加表情:👋
</Button>
<ProChat
initialChats={example.chats}
chatRef={proChatRef}
request={async (messages) => {
const mockedData: string = `这是一段模拟的流式字符串数据。本次会话传入了${messages.length}条消息`;
const mockResponse = new MockResponse(mockedData, 100);
return mockResponse.getResponse();
}}
/>
</div>
);
};

针对更加复杂的场景,使用 useProChat
hooks 来获取 proChat 实例对象,进而实现更加丰富的业务逻辑:
ini
import { ProChat, ProChatProvider, useProChat } from '@ant-design/pro-chat';
const Control = () => {
const proChat = useProChat();
return (
<Flex style={{ padding: 24 }} gap={8} justify={'space-between'}>
<Flex gap={8}>
<Button
type={'primary'}
onClick={() => {
proChat.sendMessage('这是程序化发送的消息');
}}
>
发送一条消息
</Button>
<Button
onClick={() => {
const messages = proChat.getChatMessages();
const msg = messages.at(-1);
if (msg) {
message.info(msg.content);
} else {
message.warning('会话为空');
}
}}
>
获取最新会话消息
</Button>
</Flex>
<Button
onClick={() => {
const messages = proChat.getChatMessages();
const { id, content } = messages[0] || {};
if (!id) return;
proChat.setMessageContent(id, content + '👋');
}}
>
修改首条消息,添加表情:👋
</Button>
<Flex gap={8}>
<Button
danger
onClick={() => {
const messages = proChat.getChatMessages();
proChat.deleteMessage(messages[0].id);
message.success('已删除第一条消息');
}}
>
删除第一条消息
</Button>
<Button
type={'primary'}
danger
onClick={() => {
proChat.clearMessage();
}}
>
清空消息
</Button>
</Flex>
</Flex>
);
};
export default () => (
<ProChatProvider initialChats={example.chats}>
<Control />
<Divider>🔼 程序化控制 | 🔽 用户控制</Divider>
<Chat />
</ProChatProvider>
);

注意: useProChat
hooks 必须在包裹 ProChatProvider 后方可使用。
开源地址 & 联系我们
- ProChat 官网:pro-chat.antdigital.dev
- Github:github.com/ant-design/...
接下去的一段时间,我们会继续根据用户反馈持续优化产品体验,修复和完善产品功能。如果你在使用中碰到了问题,欢迎来给我们提需求和缺陷,欢迎扫码加入群聊。
