青训营笔记5 | React & FastAPI & Doubao搭建AI对话应用

323 阅读5分钟

使用React&FastAPI&Doubao-lite-32k实现简易AI对话功能

趁着在掘金青训营学习,复习了一下JS和React,随手做了个界面,作为前端实践选题之一;
用到的技术/资源主要有:

  1. React
  2. FastAPI
  3. fastapi_boot
  4. tailwindcss
  5. Ant Design
  6. MDN EventSource
  7. 阮一峰的网络日志 SSE
  8. 火山引擎 豆包模型接口调用
  9. volc-sdk-python Github
  10. md-editor-rt

1. 理论

首先介绍一下SSE:
SSE(Server-Sent Events)是一种服务器主动向客户端推送实时数据的技术,主要有以下特点:

  1. 单向通信:允许服务器向客户端单向推送数据,而客户端不能通过这个连接向服务器发送数据。

  2. 基于HTTP协议:使用标准的HTTP协议进行通信,这意味着它不需要像WebSocket那样创建一个新的协议连接。

  3. 自动重连:支持断线重连,当连接意外断开时,客户端可以自动尝试重新连接。

  4. 文本格式数据:默认传输文本格式的数据,如果需要传输二进制数据,则需要进行编码。

  5. 浏览器支持:大多数现代浏览器都支持SSE,除了早期的Edge和IE浏览器。

  6. 轻量级和简单:与WebSocket相比,SSE 更加轻量级和简单,适用于不需要复杂双向通信的场景。

  7. 事件流:SSE 数据流由一系列的字段组成,每个字段都以键值对的形式出现,字段之间用换行符分隔,包括可选的事件名、必须的数据内容、可选的唯一标识符和重试时间间隔等。由于AI对话的回复要做一个打字机的效果,所以非常适合用流数据渲染,每次在原消息的基础上加上新返回的消息;

  8. 减少网络负担:与传统的轮询方式相比,SSE 通过单一的HTTP连接推送多个事件,减少了网络请求的次数,从而减轻了网络负担。

  9. 适用于实时更新:适用于需要即时更新的应用场景,如实时聊天、在线协作工具、实时数据展示和通知推送等。

2. 准备AI

登录/注册火山引擎,在控制台创建Access Key和Secret Key:

image.png

这里选Doubao-pro-32k做演示: image.png

创建推理接入点:

image.png

这里的Access Key、Secret Key以及接入点名称下面的id后面会用到。

3. 后端

1. config

整体项目目录如下,app用来写前端,server用来写后端:
image.png

config.toml配置及对应关系: image.png

config/DoubaoConfig.py中注入配置并初始化MaasService

代码都有点多,贴出来应该超过伴学笔记的70%了...就放图了

image.png

2. model

model/chat.py存放一些请求、响应和传输的类型

  • AI的一次性回复长这样:

image.png

  • 流式回复消息格式长这样:

image.png image.png image.png

可以看到,AI的响应分为消息本次调用用量,一次性回复会把二者拼起来返回,而流式调用前面的只有消息,最后一次是消息+用量。所以有了如下模型设计: image.png

3. ChatService

主要作用是:

  1. 注入之前写的endpoint_idMaasService依赖;
  2. chat方法中接收消息,调用MaasService实例的chat方法,包装为ChatVO返回;
  3. chat_stream方法中接收消息,调用MaasServicechat_stream方法,返回一个生成器;
  4. 异常处理,参考github文档,调用出现MaasException异常时返回500;

image.png

4. Controller

image.png

主要作用:

  1. 通过POST /chat调用一次性响应接口;
  2. 通过GET /chat调用流式响应接口,并通过gen_sse_str函数把结果封装为SSE相应格式,返回StreamingResponse

使用SSE时需要把响应消息中的\n替换一下,避免被SSE识别为消息结束符,前端收到后再换回来,不然本次传输中\n后面的消息就丢了,后面写好前端会有个测试。

调用时间测试

image.png
在相同问题的情况下,流式返回的时间比一次性更少,而且用户体验明显更好;

5. 启动

image.png

uvicorn main:app --reload

4. 前端

用到的主要依赖前面已经给出来了,下面主要看实现:

1. 类型

image.png

和后端的DTO和VO对应就行,过了;

2. useEventSource

随便写了一个,没有用ReactUse中的;

image.png

主要用法:

  • 初始化时传url,以及开始接收、有新消息、关闭、结束、错误等时间点需要调用的方法;
  • 返回累积的消息、是否正在接收,还有开始(start)和关闭(close)的方法;
  • 因为请求的url都是一样的,每次请求的查询参数可能不同,所以每次请求时只需调用start并传递rolecontent,不需要再use;
  • 需要替换后端返回字符串中的指定字符为\n

3. DoubaoChat.tsx

image.png

界面很简单,就不拆组件了,下面按效果挨个说明用法:

4. 效果

1. 空白界面:header、body和footer

image.png

2. 切换角色、发送消息

动画.gif

没错,头像真的是豆包:

3. 可中断,结果可复制

动画.gif

4. 分辨率宽高适应

image.png

image.png

5. 如果不替换'\n'

image.png

代码、表格、列表中的换行挺多,如果不换格式基本全乱了。

5. 总结

用React、FastAPI、豆包搭了个简单的AI对话应用;

如果后期还想做的话,可以加一些功能,比如:登录注册、对话历史、模型选择、function calling等。