袋鼠云数栈产品中 AI+ 实现原理剖析

这篇具有很好参考价值的文章主要介绍了袋鼠云数栈产品中 AI+ 实现原理剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。

本文作者:修能

生产力工具 + AI 是不可逆转的趋势,慢慢的大模型能力通过 AI Agent 落地的工程化能力也开始趋于成熟。作为大数据产品的数栈也必然是需要借助 AI 能力提升产品竞争力。
去年 12 月,我们在产品中上线了 AI+ 的功能,借助已经开源的大模型的能力,帮助我们探索和落地更多地应用场景。在初版 AI+ 的功能中,我们实现了基础功能的通话。

SSE

在 ChatGPT 中,我们在等待大模型生成回答的时间通常不需要很久。这是因为 ChatGPT 通过 server-sent events(SSE)来实现将生成的部分回答通过事件流传递到前端。而这就让前端不必等回答全部生成后再获取,也就使得不需要请求等待很久。

SSE 是一种基于 HTTP 协议的单向通信机制,用于服务端向客户端推送数据。

SSE WebSocket
基于 HTTP 协议 基于 TCP 连接,本身是一种协议
单向通信 双向通信
简单易用 复杂

入门使用

// 创建 SSE 的实例
const evtSource = new EventSource("//api.example.com/ssedemo.php", {
  withCredentials: true,
});

// 添加监听事件
evtSource.onmessage = (event) => {
  const newElement = document.createElement("li");
  const eventList = document.getElementById("list");

  newElement.textContent = `message: ${event.data}`;
  eventList.appendChild(newElement);
};

// 错误处理
evtSource.onerror = (err) => {
  console.error("EventSource failed:", err);
};

// 关闭事件流
evtSource.close();

需要注意的是,SSE 请求的服务端响应信息头的 MIME 类型必须是text/event-stream,否则会无法监听到事件。
另外,由于是基于 HTTP 协议的,所以在 HTTP/1.1 或更低的时候,会受浏览器最大连接数的限制。


Fields

收到的消息格式一定是具有以下字段的某种组合,其他字段名都将忽略,每行一个:

  • event
  • data
  • id
  • retry
: this is a test stream // 第一条消息,这会被解析会注释

data: some text // 第二条消息

data: another message // 第三条消息
data: with two lines

event: userconnect // 第四条消息
data: {"username": "bobby", "time": "02:33:48"}

如上所示,默认浏览器的 EventSource API 虽然可用,但是限制比较多。

  1. 只支持 url 和 withCredentials 参数。不支持往 body 里传参数。而通常来说 URL 是有最大长度限制的。
  2. 无法自定义请求头。
  3. 只能发起 GET 请求。

其实,我们也可以通过 Fetch 来实现 SSE 的通信,只不过需要额外自行处理数据流的传递。

实现

首先,我们借助 Fetch 的能力来实现请求。

const response = await fetch(url, options);

通过接受用户提供的 url 和 options 发起一个 fetch 的请求。
然后,我们需要排除掉非 SSE 的请求类型,我们可以直接拿响应的 header 中拿 content-type进行判断。

const contentType = response.headers.get('content-type');
if (!contentType?.startsWith('text/event-stream')) {
    throw new Error('SSE 请求必须设置 content-type 为 text/event-stream');
}

接着,我们业务场景中通常直接通过 response.json()获取 JSON 格式的数据了,但这里我们由于是事件流,所以我们通过 response.body 拿到的是一个 ReadableStream。我们需要借助相关的 API 进行流的读取。

const reader = response.body.getReader();
let result: ReadableStreamDefaultReadResult<Uint8Array>;
while (!(result = await reader.read()).done) {
  	// 假定每一次 read 的 value 都是完整的消息
    onmessage(onChunk(result.value));
}

其中 onChunk 函数就是处理事件流中的每一份数据的。

// 伪代码
function onChunk(arr: Uint8Array){
  const links = seekLinks();
  // 待完善
}

在实现 seekLinks 方法之前,我们需要先知道到什么时候算每一行的结束。


从 Fields 可以知道,每一行是以\n作为区分的。

function seekLinks(arr: Uint8Array){
  const lines = [];
  const buffer = arr;
  const bufLength = buffer.length;
  let position = 0;
  let lineStart = 0;
  while(position < bufLength){
    // '\n'.charCodeAt() === 10;
    if(buffer[position] === 10){
      lines.push(buffer.slice(lineStart, position));
      lineStart = position;
    };
    position += 1;
  }
  return lines;
}

在获取到所有行后,针对每一行做处理。

// 伪代码
function onChunk(arr: Uint8Array){
  const links = seekLinks();
  const decoder = new TextDecoder();
  let message = {
    data: '',
    event: '',
    id: '',
    retry: undefined,
  }:
  links.forEach((line) => {
    // ':'.charCodeAt() === 58;
    const colon = line.findIndex(l => l === 58);
    const fieldArr = line.slice(0, colon);
    const valueArr = line.slice(colon);
    if(colon === -1){
      // 当冒号作为开头的时候,解析成注释
      return;
    }
    const field = decoder.decode(fieldArr);
    const value = decoder.decode(valueArr);
    switch (field) {
      case 'data':
          message.data = message.data
              ? message.data + '\n' + value
              : value;
          break;
      case 'event':
          message.event = value;
          break;
      case 'id':
          message.id = value;
          break;
      case 'retry':
          const retry = parseInt(value, 10);
          message.retry = retry
          break;
  	}
  });
  return message;
}

大致完成了最简单的基础功能的解析,而以上伪代码参考 fetch-event-source 的源码。


借助 fetch-event-source 的能力,在数栈产品中调用的方式和 HTTP 请求基本保持一致。

function sse(url: string, params: any, options: FetchEventSourceInit) {
  const headers = {
    'Content-Type': 'application/json',
    accept: 'text/event-stream',
  };
  fetchEventSource(url, {
    method: 'POST',
    body: JSON.stringify(params),
    headers,
    ...options,
  });
}

打字机效果

接着,我们实现具备科技感的打字机效果:
袋鼠云数栈产品中 AI+ 实现原理剖析

输出

这里我们不能直接将响应的消息直接打印到屏幕上,因为响应的消息通常是好多字,这样子会导致打字机效果显得非常卡顿,用户体验不佳。
在数栈产品中,我们通过将响应的消息收集到暂存区中,然后通过每秒从暂存区中取出若干个字符打印到屏幕上,优化打字机卡顿的效果。

function AIGC(){
   const typing = useTyping({
      // 暂存区启动后,每个 delay 的时间都会执行该方法将消息打印到屏幕上
      onTyping(val) {
        // ...
      },
  });
	const handleChat = (message: string) => {
      // 标志暂存区需要开始存响应的消息了
      typing.start();
      requestChat(params, {
        onmessage(event: { data: string }) {
           	const { data } = event;
            // 把响应的消息存入暂存区中
            typing.push(data);
        },
        onclose() {
            // 关闭或失败的话,释放暂存区的数据
            typing.close();
        },
        onerror() {
            typing.close();
        },
    });
  };
}

其中,相关暂存区的代码整理成 useTyping 实现。

export default function useTyping({
    onTyping,
    onEnd,
}: {
    onTyping: (val: string) => void;
    onEnd: () => void;
}) {
    const interval = useRef<number>();
    const queue = useRef<string>('');
    const isStart = useRef<boolean>(false);

    function startTyping() {
        if (interval.current) return;
        let index = 0;
        interval.current = window.setInterval(() => {
            if (index < queue.current.length) {
                const str = queue.current;
                onTyping(str.slice(0, index + 1));
                index++;
            } else if (!isStart.current) {
                // 如果发送了全部的消息且信号关闭,则清空队列
                window.clearInterval(interval.current);
                interval.current = 0;
                onEnd();
            }
            // 如果发送了全部的消息,但是信号没有关闭,则什么都不做继续轮训等待新的消息
        }, 50);
    }

    useEffect(() => {
        return () => {
            window.clearInterval(interval.current);
            interval.current = 0;
        };
    }, []);

    function start() {
        isStart.current = true;
        window.clearInterval(interval.current);
        interval.current = 0;
        queue.current = '';
    }

    function push(str: string) {
        if (!isStart.current) return;
        queue.current += str.replace(/\\n/g, '\n');
        startTyping();
    }

    // 关闭的时候不需要清空队列,因为可能还有一些消息没有发送完毕,统一等消息发送完毕后关闭
    function close() {
        isStart.current = false;
    }

    return { start, push, close };
}

光标

在实现了打字机效果后,我们还需要添加一个闪烁的光标。
原理比较简单,就是在消息区域的最后一个元素的末尾添加元素即可。

.markdown {
  >*:last-child::after {
    content: " ";
    width: 2px;
    height: 13px;
    transform: translate(1px, 2px);
    font-family: Menlo, Monaco, "Courier New", monospace;
    font-weight: normal;
    font-size: 0;
    font-feature-settings: "liga" 0, "calt" 0;
    line-height: 13px;
    letter-spacing: 0;
    display: inline-block;
    visibility: hidden;
    animation: blinker 1s step-end infinite;
    background: #000;
  }

  @keyframes blinker {
    0% {
      visibility: inherit;
    }
    50% {
      visibility: hidden;
    }
    100% {
      visibility: inherit;
    }
  }
}

当然,这里有一些问题,在 markdown 解析出 Code Block 的时候会导致光标错位,这个问题 ChatGPT 同样也有。
袋鼠云数栈产品中 AI+ 实现原理剖析


那么到这里,我们就实现了一个具备基础功能的 AI+ 的需求。

最后

欢迎关注【袋鼠云数栈UED团队】~
袋鼠云数栈 UED 团队持续为广大开发者分享技术成果,相继参与开源了欢迎 star文章来源地址https://www.toymoban.com/news/detail-824272.html

  • 大数据分布式任务调度系统——Taier
  • 轻量级的 Web IDE UI 框架——Molecule
  • 针对大数据领域的 SQL Parser 项目——dt-sql-parser
  • 袋鼠云数栈前端团队代码评审工程实践文档——code-review-practices
  • 一个速度更快、配置更灵活、使用更简单的模块打包器——ko
  • 一个针对 antd 的组件测试工具库——ant-design-testing

到了这里,关于袋鼠云数栈产品中 AI+ 实现原理剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 实战干货|Spark 在袋鼠云数栈的深度探索与实践

    实战干货|Spark 在袋鼠云数栈的深度探索与实践

    Spark 是一个快速、通用、可扩展的大数据计算引擎,具有高性能、易用、容错、可以与 Hadoop 生态无缝集成、社区活跃度高等优点。在实际使用中,具有广泛的应用场景: · 数据清洗和预处理:在大数据分析场景下,数据通常需要进行清洗和预处理操作以确保数据质量和一致

    2024年04月26日
    浏览(14)
  • 袋鼠云数栈前端从 Multirepo 到 Monorepo 研发效率提升探索之路

    袋鼠云数栈前端从 Multirepo 到 Monorepo 研发效率提升探索之路

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:星野 前端代码管理一直是困扰着不少前端开发团队的难题,从开发到发布的整体工作流程中,除了常规的技术问题外,往往

    2024年02月04日
    浏览(12)
  • 性能提升30%!袋鼠云数栈基于 Apache Hudi 的性能优化实战解析

    性能提升30%!袋鼠云数栈基于 Apache Hudi 的性能优化实战解析

    Apache Hudi 是一款开源的数据湖解决方案,它能够帮助企业更好地管理和分析海量数据,支持高效的数据更新和查询。并提供多种数据压缩和存储格式以及索引功能,从而为企业数据仓库实践提供更加灵活和高效的数据处理方式。 在金融领域,企业可以使用 Hudi 来处理大量需要

    2024年02月09日
    浏览(11)
  • 袋鼠云数栈UI5.0设计实战|B端表单这样设计,不仅美观还提效

    袋鼠云数栈UI5.0设计实战|B端表单这样设计,不仅美观还提效

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:大喜 相关文章:袋鼠云出品!数栈UI 5.0全新体验升级,设计背后的故事 表单是B端产品中最常见的组件之一,主要⽤于数据

    2024年02月03日
    浏览(12)
  • 袋鼠云产品功能更新报告05期|应有尽“优”,数栈一大波功能优化升级!

    袋鼠云产品功能更新报告05期|应有尽“优”,数栈一大波功能优化升级!

    这段时间,我们对产品本身以及客户反馈的一些问题进行了持续的更新和优化,包括对离线平台数据同步功能的更新,数据资产平台血缘问题的优化等,力求满足不同行业用户的更多需求,为用户带来极致的产品使用体验。 以下为袋鼠云产品功能更新报告第五期内容,更多探

    2024年02月04日
    浏览(31)
  • 深度剖析SpringBoot自动配置原理,为什么SpringBoot能为我们做那么多东西

    深度剖析SpringBoot自动配置原理,为什么SpringBoot能为我们做那么多东西

    本文基于 spring-boot-2.2.6.RELEASE 版本的源码进行说明,不同版本的源码可能会有一些区别。 要清楚SpringBoot自动配置原理,就要明白 @SpringBootApplication 注解的组成,此注解主要是这三个注解组成: @SpringBootConfiguration , @EnableAutoConfiguration , @ComponentScan 。 下面是源码: @SpringBo

    2024年02月13日
    浏览(12)
  • 数栈产品中的代码编译器

    数栈产品中的代码编译器

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:奇铭 目前数栈的多个产品中都支持在线编辑 SQL 来生成对应的任务。比如离线开发产品和实时开发产品。在使用 MonacoEdito

    2024年02月05日
    浏览(14)
  • 数栈V6.0全新产品矩阵发布,数据底座 EasyMR 焕新升级

    数栈V6.0全新产品矩阵发布,数据底座 EasyMR 焕新升级

    4月20日,袋鼠云成功举行了以“数实融合,韧性生长”为主题的2023春季生长大会。会上,袋鼠云自主研发的一站式大数据基础软件——数栈V6.0产品矩阵全新发布。对旗下大数据基础平台、大数据开发与治理、数据智能分析与洞察三大模块的全线产品进行全新升级,并重点发

    2023年04月27日
    浏览(21)
  • 袋鼠云春季生长大会最新议程来啦!4月20日我们云上见

    袋鼠云春季生长大会最新议程来啦!4月20日我们云上见

    如今,数字经济正逐步走向深化应用、规范发展、普惠共享的新阶段,数字经济与实体经济深度融合、基础软件国产化替代成为数字时代主潮流。数字工具如何让千行百业共同实现韧性生长? 「 2023 袋鼠云春季生长大会」乘风而起,带来数实融合趋势下的产品焕新升级剖析、

    2023年04月17日
    浏览(10)
  • 袋鼠云产品功能更新报告07期|智能、高效、安全,一个都不能少!

    袋鼠云产品功能更新报告07期|智能、高效、安全,一个都不能少!

    欢迎来到袋鼠云07期产品功能更新报告!在瞬息万变的市场环境中,袋鼠云始终将客户需求和反馈置于优化工作的核心位置,本期也针对性地推出了一系列实用性强的功能优化,以满足客户日益增长的业务需求。 以下为袋鼠云产品功能更新报告07期内容,更多探索,请继续阅

    2024年02月08日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包