对接AI流式返回踩坑记录

335 阅读2分钟

前言

近期忙于将AI的功能接入到公司的业务中,过程中也踩了很多坑,一一记录一下。以下的问题如果深入研究都可以作为一篇文章,但本文着重讲解决的方式。

1. 多端接入的方式

Web端(基于Angular)

关键:使用fetch的方式获取结果,而流存在于body.getReader()示例代码

try {
  const serviceRes = await fetch(url, {
   xxxxx
  });

  if (serviceRes.ok) {
    const reader = serviceRes.body?.getReader();
    if (reader) {
      await this.readSSE(reader);
      
    }
  } else {
    console.error("HTTP error:", serviceRes.statusText);
  }
} catch (error) {
  console.error("Fetch error:", error);
}

APP端(基于uniapp)

查阅了一定的资料,最终决定app端使用是市场上的插件,把插件跑通即可。就不提供示例代码

2.中断会话

业务场景

当用户觉得会话内容响应时间太长,或者想中断这次会话。

技术方案

关键:使用 AbortController进行中断处理 , MDN相关说明,关键使用的是以下两个内容

  1. AbortController.signal
  2. AbortController.abort()

核心思想:将signal作为参数传入到fetch中,当我们执行中断方法controller.abort(),它会通知使用了signal的异步请求以达到中断的效果。
示例代码

//发起AI请求
async changeAI({ question, sessionId }) {
  const params = {
    question,
    sessionId: this.lastSessionId,
  };
  
  const url = 'xxxxxxx'
  //注意只有通过fetch的方式才能拿到response.body下的stream
  this.controller = new AbortController();
  const signal = this.controller.signal;
  try {
    const serviceRes = await fetch(url, {
      method,
      headers,
      body,
      signal,
    });

    if (serviceRes.ok) {
      const reader = serviceRes.body?.getReader();
      if (reader) {
        const sessionId = await this.readSSE(reader);
        return sessionId;
      }
    } else {
      console.error("HTTP error:", serviceRes.statusText);
    }
  } catch (error) {
    console.error("Fetch error:", error);
  }

}
//中断会话
stopStreaming() {
  if (this.controller) {
    this.controller.abort(); // 中止流
    this.controller = null; // 重置 controller
  }
}

3.滚动条自动触底

业务场景

每次进行发送对话,滚动条都需要沉底,要不然用户得发完一句话,还得自己拖动滚动条才能看到。

技术方案

核心思想

  1. 首先判断当前滚动条是否处于底部,处于底部了就不需要自动滚动了
  2. 倘若不处于底部,则需要scrollTop重新设置

相关代码

画面的布局

  <div class="ai-modal-container" >
    <div class="ai-top-container">
    </div>
    <div #aiContainer class="ai-container">
      <div #listContainer class="list-container">
        <ng-container *ngFor="let item of questionList">
        </ng-container>
      </div>
    </div>
    <div class="ai-bottom-container" #bottomContainer>
    </div>
  </div>

结合图片(很尽力在画了)

image.png 可以看到aiContainer的高度其实是固定的,而listContainer的高度是根据数据项来决定的,如果数据达到一定量时,一定会大于aiContainer的高度的,这时就需要自动滚动了。
例如:图中您好4已经让listContainer>aiContainer了
相关js处理

//滚动处理
  handleScollTop() {
    const overScroll = this.overScrollHeight();
    const scrollContentHeight = this.listContainer.nativeElement.clientHeight;
    if (overScroll > 0) {
      this.aiContainer.nativeElement.scrollTop = scrollContentHeight;
    }
  }
  //判断滚动条是否处于底部
  overScrollHeight() {
    const bottomContentHeight = this.bottomContainer.nativeElement.clientHeight;
    const scrollViewHeight =
      this.aiContainer.nativeElement.clientHeight - bottomContentHeight;
    const scrollContentHeight = this.listContainer.nativeElement.clientHeight;
    return scrollContentHeight - scrollViewHeight;
  }