偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

RAGFlow引用機制揭秘:LLM引導(dǎo)與后端驗證如何協(xié)同工作?

人工智能
為啥 RAGFlow 的最終回答中的引用顯示是后端完成的,LLM 通過提示詞引導(dǎo)生成的 [ID:i] 引用標記具體是什么作用,以及這種設(shè)計可以參考的工程化經(jīng)驗。

昨天知識星球內(nèi)有個提問:

RAGFlow 顯示引用為什么不通過提示詞直接顯示在回答中,而是通過分塊后和檢索片段比較向量相似度?判斷引用出處?能不能直接通過提示詞實現(xiàn)。

我當時給的回答是:

不能簡單地通過提示詞讓 LLM 直接、可靠地生成引用,因為這會引入幻覺風(fēng)險。LLM 在生成內(nèi)容時,為了讓回答顯得流暢和可信,可能會編造一個引用來源。此外,當輸出“這句話來自[2]”的時候,無法從技術(shù)上驗證,這也不符合生產(chǎn)實踐要求。換句話說,把生成答案和標注引用兩個步驟解耦,才能保證引用的客觀性。

本來這個對話就結(jié)束了,今天這個星友追評了下在 RAGFlow 的 Github 提了一個相關(guān)問題的 issue,結(jié)果 bot 的回答讓他有些困惑。我之前也沒有仔細了解過 RAGFlow 的相關(guān)源碼設(shè)計,就這這個問題實際看了下之后,覺得值得拿出來專門寫篇文章來做個拆解。

這篇試圖說清楚,為啥 RAGFlow 的最終回答中的引用顯示是后端完成的,LLM 通過提示詞引導(dǎo)生成的 [ID:i] 引用標記具體是什么作用,以及這種設(shè)計可以參考的工程化經(jīng)驗。

以下,enjoy:

1、Issue 中的 BOT 誤導(dǎo)

這個 Issue 的核心問題是:“RAGFlow 是如何生成引用標記的?”bot 的回答顯得搖擺不定:起初它斷言引用完全由后端生成,LLM 本身并不參與;https://github.com/infiniflow/ragflow/issues/8817

但在被用戶以源碼中的 citation_prompt 質(zhì)疑后,它又提出了一種“雙模式競爭”理論,暗示 LLM 和后端是兩條可能沖突的獨立路徑。不過不看源碼就能猜到,這種說法顯然是不合理的。但是具體還是要從源碼中找答案。

2、后端關(guān)鍵函數(shù)分析

要找到引用的源頭,首先應(yīng)該查看后端代碼。在 RAGFlow 的源碼中,我在 rag/nlp/search.py 文件里,找到了一個名為 insert_citations 的關(guān)鍵函數(shù)。

# 代碼出處: rag/nlp/search.py (Dealer 類中)
class Dealer:
    # ... 其他方法 ...


    def insert_citations(self, answer, chunks, chunk_v,
                         embd_mdl, tkweight=0.1, vtweight=0.9):
        # 1. 將LLM的純文本回答切分成句子
        pieces = re.split(r"(```)", answer)
        # ... 省略清洗和聚合代碼 ...
        pieces_ = [...] # 得到干凈的句子列表


        # 2. 對每個句子,獨立計算與所有知識塊的混合相似度
        ans_v, _ = embd_mdl.encode(pieces_)
        cites = {}
        for i, a in enumerate(pieces_):
            sim, _, _ = self.qryr.hybrid_similarity(ans_v[i], chunk_v, ...)
            mx = np.max(sim) * 0.99
            if mx < thr: continue
            # 3. 記錄下所有相似度足夠高的知識塊作為引用
            cites[idx[i]] = list(
                set([str(ii) for ii in range(len(chunk_v)) if sim[ii] > mx]))


        # 4. 將計算出的引用標記 [ID:c] 注入到句子末尾
        res = ""
        for i, p in enumerate(pieces):
            res += p
            # ...
            if i in cites:
                for c in cites[i]:
                    if c in seted: continue
                    res += f" [ID:{c}]"
                    seted.add(c)
        return res, seted

這段代碼的邏輯很清晰的說明了以下三個問題:

1.輸入是純文本: 該函數(shù)的輸入 answer 是 LLM 生成的純凈答案。它完全不關(guān)心 answer 是否已經(jīng)帶有 LLM 自己生成的引用標記。

2.獨立計算: 函數(shù)的核心是 hybrid_similarity,它完全基于內(nèi)容相似度(結(jié)合了向量語義和關(guān)鍵詞文本)來獨立判斷每個句子與知識塊的關(guān)聯(lián)。這是一個從零開始、基于數(shù)據(jù)和算法的計算過程。

3.權(quán)威注入: 函數(shù)最后將自己計算出的引用 [ID:c] 注入到文本中,并返回最終結(jié)果。

初步結(jié)論非常明確,RAGFlow 的引用完全由后端算法基于內(nèi)容相似度獨立生成,擁有最終的、絕對的決定權(quán)。 它不依賴、不修改、也不信任 LLM 可能生成的任何引用。這當然也是符合最佳實踐的做法。

3、前端對應(yīng)溯源

進一步的問題是,既然知道后端生成了帶有 [ID:i] 標記的字符串。那么前端是如何把這個文本標記變成一個可點擊、可交互的鏈接的呢?

3.1message-item 組件

在 web/src/components/message-item/index.tsx 中,可以看到它負責(zé)渲染一個完整的消息氣泡。但它并不親自處理消息內(nèi)容,而是將任務(wù)委托了出去。

# 代碼出處: web/src/components/message-item/index.tsx
// ...
            <div className={/* ... */}>
              {/* 關(guān)鍵:它將原始content和引用數(shù)據(jù)直接傳遞給MarkdownContent */}
              <MarkdownContent
                loading={loading}
                content={item.content} 
                reference={reference}
                clickDocumentButton={clickDocumentButton}
              ></MarkdownContent>
            </div>
// ...

3.2markdown-content 組件

真正的魔法發(fā)生在 web/src/pages/chat/markdown-content/index.tsx。這個組件接收到原始字符串后,執(zhí)行了最終的“查找與替換”操作。

# 代碼出處: web/src/pages/chat/markdown-content/index.tsx
import reactStringReplace from 'react-string-replace'; // 1. 引入關(guān)鍵的替換庫
import { currentReg } from '../utils'; // 2. 引入包含引用正則表達式的文件


// ...


const MarkdownContent = (/* ... */) => {
  // ...
  const renderReference = useCallback(
    (text: string) => {
      // 3. 使用 react-string-replace 對文本進行查找和替換
      let replacedText = reactStringReplace(text, currentReg, (match, i) => {
        // 4. currentReg 就是匹配 [ID:i] 的正則表達式
        //    對于每一個匹配到的 `match` (例如 "[ID:5]"), 執(zhí)行以下邏輯:
        const chunkIndex = getChunkIndex(match); // 提取出數(shù)字 5


        // 5. 返回一個可交互的 React 組件 (Popover) 來替換原始的 [ID:i] 文本
        return (
          <Popover cnotallow={getPopoverContent(chunkIndex)} key={i}>
            <InfoCircleOutlined className={styles.referenceIcon} />
          </Popover>
        );
      });
      return replacedText;
    },
    // ...
  );


  return (
    <Markdown
      // ...
      compnotallow={{
        // 6. 通過重寫組件渲染邏輯,確保所有文本都經(jīng)過 renderReference 函數(shù)的處理
        'custom-typography': ({ children }: { children: string }) =>
          renderReference(children),
        // ...
      }}
    >
      {contentWithCursor}
    </Markdown>
  );
};

前端的處理流程清晰地展現(xiàn)了“職責(zé)分離”原則。MessageItem 負責(zé)消息的整體結(jié)構(gòu),而 MarkdownContent 負責(zé)將后端生成的 [ID:i] 文本標記,通過查找替換的方式,轉(zhuǎn)換為用戶可以交互的 UI 組件。這再次證實了所有引用處理在數(shù)據(jù)到達前端之前,必須已經(jīng)在后端全部完成了。

4、提示詞引導(dǎo)生成的巧思

既然后端和前端的邏輯都很清晰,還沒有回答的一個問題是,如果后端函數(shù)是引用的唯一來源,那為什么 RAGFlow 的源碼中還要在 rag/prompts/citation_prompt.md 中寫下引導(dǎo) LLM 生成引用的規(guī)則,而這個引用最終并不會使用。

# 證據(jù): rag/prompts/citation_prompt.md 的內(nèi)容
## Citation Requirements
- Use a uniform citation format such as [ID:i] [ID:j]...
- Citation markers must be placed at the end of a sentence...
- A maximum of 4 citations are allowed per sentence.
- DO NOT insert citations if the content is not from retrieved chunks.
- ...
- STRICTLY prohibit the use of strikethrough symbols...


## Example START
: Here is the knowledge base:
Document: ... ID: 0
Document: ... ID: 1
...


: What's Elon's view on dogecoin?


: Musk has consistently expressed his fondness for Dogecoin... He has referred to it as his favorite cryptocurrency [ID:0] [ID:1].
...
## Example END

看到這里,就知道為啥 GitHub 機器人所說的“雙模式競爭”純屬瞎編了。citation_prompt 的真正目的,不是為了“結(jié)果”,而是為了“過程”。

換句話說,不是為了得到 LLM 生成的 [ID:i] 這個結(jié)果,而是為了規(guī)范 LLM 生成答案文本的整個過程。它通過這種方式向 LLM 施加了強烈的約束。

1.降低幻覺: 通過強制要求 LLM“必須為你的話找到出處”,系統(tǒng)在源頭上極大地降低了內(nèi)容幻覺。

2.保證內(nèi)容質(zhì)量: LLM 必須生成與原文高度相關(guān)的內(nèi)容。

3.為后端鋪路: 正是這份高質(zhì)量的草稿,讓后端的 insert_citations 函數(shù)能夠游刃有余地進行精準的相似度匹配,并最終完成權(quán)威的標注工作。

5、寫在最后

RAGFlow 的引用生成機制,也形象的展示了 LLM 應(yīng)用落地的核心范式。生產(chǎn)實踐可用的關(guān)鍵不在于對 LLM 能力的盲目相信(當然最好用最先進的 LLM),也不在于過多的依賴傳統(tǒng)的規(guī)則引擎,而在于把 LLM 作為一個強大但需要被引導(dǎo)和驗證的推理核心,并圍繞它構(gòu)建一套由確定性工程邏輯組成的腳手架,最后給出三個類似的樣例作為參考:

1.AI Agent 與工具調(diào)用 (Tool Calling)

讓 LLM 自由思考(Chain of Thought),分析用戶意圖,并決定需要調(diào)用哪個 API(工具)。但比如一旦 LLM 決定調(diào)用 get_weather("北京"),這個 API 本身的執(zhí)行過程是完全確定的。系統(tǒng)不會讓 LLM 去“創(chuàng)造”天氣數(shù)據(jù),而是通過嚴格的函數(shù)調(diào)用獲取真實、可信的結(jié)果。

2.結(jié)構(gòu)化數(shù)據(jù)提取 (JSON Mode)

對于一段非結(jié)構(gòu)化的用戶評論:“我喜歡這款手機的屏幕,但電池太不給力了”,通過強制啟用 JSON Mode,并提供 Pydantic 等模式定義,來約束 LLM 的輸出必須符合{ "positive_feedback": "屏幕", "negative_feedback": "電池" }這樣嚴格的格式。LLM 可以在內(nèi)容上發(fā)揮,但格式被約束,這也保證了下游程序的可解析性。

3.黑盒兜底機制 (Fallback)

在許多客服機器人中,首先嘗試讓 LLM 直接回答用戶問題。但如果比如連續(xù)兩次回答的置信度都低于某個閾值,或者觸發(fā)了特定關(guān)鍵詞,系統(tǒng)會無縫切換到人工客服或預(yù)設(shè)的、基于規(guī)則的流程(確定性)。這也是目前業(yè)界常用的一種經(jīng)典的平衡策略。

責(zé)任編輯:龐桂玉 來源: 韋東東
相關(guān)推薦

2021-01-10 15:28:23

數(shù)據(jù)科學(xué)機器學(xué)習(xí)AI

2020-01-09 07:48:19

ITOT安全網(wǎng)絡(luò)攻擊

2022-09-13 11:38:59

物聯(lián)網(wǎng)邊緣計算人工智能

2020-11-21 10:53:55

RFID工業(yè)物聯(lián)網(wǎng)IIOT

2019-10-18 08:18:00

云計算物聯(lián)網(wǎng)IOT

2022-05-11 10:09:13

大數(shù)據(jù)區(qū)塊鏈

2022-10-12 23:58:20

物聯(lián)網(wǎng)邊緣計算數(shù)據(jù)

2023-03-24 14:51:05

人工智能大數(shù)據(jù)

2023-02-20 15:21:07

物聯(lián)網(wǎng)工業(yè)4.0

2020-07-28 10:23:35

協(xié)同工作首席信息官IT部門

2023-05-08 16:23:42

物聯(lián)網(wǎng)工業(yè)4.0數(shù)字化轉(zhuǎn)型

2023-08-16 14:21:49

物聯(lián)網(wǎng)工業(yè)4.0

2023-08-07 17:45:11

人工智能物聯(lián)網(wǎng)

2020-03-27 15:28:08

物聯(lián)網(wǎng)機器人人工智能

2019-10-17 11:13:27

大數(shù)據(jù)人工智能

2020-06-24 12:11:40

云計算5G技術(shù)

2010-05-21 18:16:13

IIS服務(wù)器

2011-01-06 13:24:48

2010-05-21 17:03:16

2011-05-20 14:56:14

AMD服務(wù)器圖形卡
點贊
收藏

51CTO技術(shù)棧公眾號