开源项目分析 - 前端 - ragbot-starter

420 阅读2分钟

项目地址: github.com/datastax/ra…

这是一个基于 rag 的聊天机器人的网页项目,使用了astra db,next,react等技术栈。

1. 运行

如何运行:执行 npm run dev

但是报错,提示找不到next。

安装一下: npm install next react react-dom

界面: image.png

2. 分析源码:

核心源码:

app/page.tsx
app/api/chat/route.ts
app/hooks/useConfiguration.ts

点击Send之后,是谁处理了事件?

看起来事件传递顺序是:handleSend -> handleSubmit -> useChat

import { useChat, Message } from 'ai/react';

const { append, messages, input, handleInputChange, handleSubmit } = useChat();

const { useRag, llm, similarityMetric, setConfiguration } = useConfiguration();

const handleSend = (e) => {
    handleSubmit(e, { options: { body: { useRag, llm, similarityMetric}}});
}

app/api/chat/route.ts 里的核心代码是一个POST函数,看起来是真正处理点击事件的地方:

export async function POST(req: Request) {
  try {
    const {messages, useRag, llm, similarityMetric} = await req.json();

    const latestMessage = messages[messages?.length - 1]?.content;
    
    ...
}

可是 handleSubmit 里的参数是如何传递给 app/api/chat/route.ts 里的POST函数的呢?

useChat 是如何跟 handleSubmit 产生关联的呢?

import { useChat, Message } from 'ai/react'; 这一句很关键。

它源自这个库:"ai": "^2.2.20",这个依赖库其实是 vercel 发布的一个库。

库的源码:github.com/vercel/ai

相关文档:vercel.com/blog/introd…

image.png

与 rag 相关的核心代码(app/api/chat/route.ts)

SimilarityMetric 是什么?

image.png

SimilarityMetric 的取值有:余弦 cosine,欧几里得距离 euclidean,点积 dot_product。

首先,基于聊天列表里的最后一条信息创建 embeddings:

const latestMessage = messages[messages?.length - 1]?.content;
const {data} = await openai.embeddings.create({input: latestMessage, model: 'text-embedding-ada-002'});

基于刚创建的 embeddings,从 astraDb 查询出前5条相关的内容:

const collection = await astraDb.collection(`chat_${similarityMetric}`);//此处similarityMetric的值默认是 cosine。
//这里有个疑问,collection 会不会占用内存?或者说它其实就是个 dbDao?还没真正开始查询?

const cursor= collection.find(null, {
        sort: {
          $vector: data[0]?.embedding,
        },
        limit: 5,
      });

把前5条记录,拼成一个 docContext:

const documents = await cursor.toArray();
      
docContext = `
  START CONTEXT
  ${documents?.map(doc => doc.content).join("\n")}
  END CONTEXT
`

拼成 ragPrompt,意思是让 chatgpt 从指定的5条记录里总结出问题的答案:

const ragPrompt = [
  {
    role: 'system',
    content: `You are an AI assistant answering questions about Cassandra and Astra DB. Format responses using markdown where applicable.
    ${docContext} 
    If the answer is not provided in the context, the AI assistant will say, "I'm sorry, I don't know the answer".
  `,
  },
]

最后发送请求给 gpt3.5:

const response = await openai.chat.completions.create(
      {
        model: llm ?? 'gpt-3.5-turbo',
        stream: true,
        messages: [...ragPrompt, ...messages],
      }
    );
const stream = OpenAIStream(response);
return new StreamingTextResponse(stream);

全局配置 useConfiguration.ts 的对应界面:

image.png

3. 总结

通过本项目,进一步熟悉了 nextjs 的语法,同时也了解了 rag 的实际应用。