如何搭建一個(gè)百煉 RAG 應(yīng)用
方案概覽
本案例將給大家演示,如何通過魔筆搭建一個(gè)基于RAG的企業(yè)知識(shí)問答應(yīng)用。我們會(huì)基于百煉的智能體應(yīng)用,利用魔筆的連接器集成百煉應(yīng)用,通過魔筆聊天Chat組件搭建一個(gè)企業(yè)知識(shí)問答的 Web 應(yīng)用。
前期準(zhǔn)備
搭建百煉應(yīng)用,參考百煉文檔知識(shí)檢索增強(qiáng)(RAG);
發(fā)布百煉應(yīng)用后,獲得 API-KEY、應(yīng)用 ID;
建議先在百煉中把對(duì)話的效果調(diào)試好,后邊搭建魔筆應(yīng)用可以直接看到模型的效果。當(dāng)魔筆應(yīng)用已經(jīng)搭建或發(fā)布,只要 API-KEY 和百煉應(yīng)用 ID 沒有變化,再調(diào)整百煉應(yīng)用后,魔筆也會(huì)立即看到調(diào)整后的效果。
要想體驗(yàn) RAG 效果,一定要配置自己的百煉的數(shù)據(jù)中心和知識(shí)庫,在百煉應(yīng)用中引用知識(shí)庫作為“外掛”。
創(chuàng)建集成資源
接下來我們開始搭建魔筆應(yīng)用,首先配置資源,我們會(huì)通過百煉連接器建立一個(gè)百煉應(yīng)用的資源
進(jìn)入魔筆控制臺(tái),選擇資源,選擇集成
創(chuàng)建集成,選擇百煉應(yīng)用連接器
配置參數(shù),主要是百煉的 API-KEY
確認(rèn)保存
這里我們只配置了開發(fā)環(huán)境,可以按需選擇配置生產(chǎn)環(huán)境的參數(shù)。可以通過給不同環(huán)境配置不同的參數(shù),實(shí)現(xiàn)環(huán)境的不同配置策略。
創(chuàng)建應(yīng)用和集成操作
創(chuàng)建魔筆應(yīng)用,這里我們選擇不從模板創(chuàng)建,從零演示搭建過程。(通過模板創(chuàng)建,選擇企業(yè)知識(shí)問答模板,也是不錯(cuò)的選擇)
首先我們創(chuàng)建一個(gè)布局,可以按需修改默認(rèn)參數(shù),如改應(yīng)用標(biāo)題、去掉左側(cè)導(dǎo)航等
切換到首頁,并應(yīng)用布局,效果如下
下邊我們開始搭建對(duì)話功能,選擇聊天(Chat)組件,拖入到畫布中,調(diào)整位置如圖
然后我們建立一個(gè)百煉的集成操作,主要參數(shù)會(huì)用到百煉應(yīng)用 ID,注意打開 SSE 和 has_thoughts。SSE 是流式輸出,has_thoughts 屬性是返回推理過程。
重要注意打開 has_thoughts 屬性,這樣百煉才會(huì)返回推理過程。
配置完成后,就可以點(diǎn)擊【運(yùn)行】測(cè)試一下,如果返回正確,效果如圖。如果反饋失敗,可以看看是否因?yàn)樘崾驹~ {{chat1.currentMessage.content}} 導(dǎo)致,因?yàn)檫@個(gè)時(shí)候 chat1 組件可能還沒有消息,可以先發(fā)起一次對(duì)話。
回到主頁面,配置 chat1 組件的交互屬性,配置剛剛建立的百煉集成操作,配置好后再次對(duì)話,它會(huì)回答和你的知識(shí)庫相關(guān)的回答,這里如果信息來自知識(shí)庫,會(huì)有角標(biāo),我們稍后會(huì)提供更好的展示方式
我們通過幾個(gè)屬性讓對(duì)話效果更好看些,比如助手的名稱、頭像、歡迎語、默認(rèn)的推薦問題等,你需要用到以下屬性:
屬性
說明
配置參考
歡迎信息/類型
選擇卡片,可以配置默認(rèn)問題(快捷信息)
卡片
歡迎信息/內(nèi)容
歡迎語
我是您的辦公助手,可以回答工作中常見的問題,有事就請(qǐng)問吧!
歡迎信息/快捷信息
可以輸入多條推薦的問題
婚假的規(guī)則和申請(qǐng)注意事
助手內(nèi)容配置/頭像
助手內(nèi)容配置/名稱
企業(yè)員工助手
樣式/模式
可以配置是左右對(duì)話模式,還是全靠左的文檔模式
聊天
接下里我們優(yōu)化回答的展示效果,首先是角標(biāo),這里需要用到chat1的增強(qiáng)渲染,把反饋結(jié)果中的“ref”標(biāo)簽改為 “a”標(biāo)簽
配置好后,展示效果如下
高級(jí)RAG文檔索引和召回展示
下邊進(jìn)行一些高級(jí)展示效果的搭建,RAG最有特色的就是大模型反饋的文檔索引和召回信息。如果你使用百煉的對(duì)話體驗(yàn),應(yīng)該可以看到這些特性。那么我們?cè)谀ЧP中如何實(shí)現(xiàn)哪?我們需要使用到【聊天】組件的幾個(gè)屬性
屬性 | 說明 | 配置參考 |
內(nèi)容/信息 | 自定義的聊天對(duì)話內(nèi)容,數(shù)組類型,默認(rèn){{[]}}。當(dāng)需要存儲(chǔ)額外信息的時(shí)候,需要自己定義。比如我們要存儲(chǔ)的文檔索引和召回信息。 | 我們先聲明一個(gè)數(shù)組變量,chats 并引用如:{{chats.value}} |
交互/事件/回答完成 | 這個(gè)事件在問題回答完成時(shí)候執(zhí)行,我們這里需要配置執(zhí)行一段代碼。用來把百煉反饋的文檔索引等額外信息保存到chats變量中,好在后邊自定義渲染中使用 | |
助理內(nèi)容配置/ 開啟額外區(qū)域 和額外信息 | 開啟額外區(qū)域開關(guān),配置額外信息(string)可以在反饋的答案下顯示定制化信息,支持html片段渲染 |
創(chuàng)建一個(gè)數(shù)組變量chats,配置chat1的【內(nèi)容/信息】屬性為{{chats.value}},如圖
編寫回答完成后的信息提取代碼,并保存到chats數(shù)組中
//獲取當(dāng)前的聊天記錄 let currentDataSource = JSON.parse(JSON.stringify(chat1.context)); //獲取百煉返回的推理過程 const thoughts = bailian1.rawData?.thoughts; //獲取百煉反饋的文檔索引信息 const doc_references = bailian1.rawData?.doc_references; //補(bǔ)充最后當(dāng)前反饋內(nèi)容的額外信息 const length = currentDataSource.length; currentDataSource[length - 1].thoughts = thoughts; currentDataSource[length - 1].doc_references = doc_references; //更新變量chats chats.setValue(currentDataSource);
配置【助理內(nèi)容配置】額外區(qū)域?qū)傩裕a如下
(function generateMarkdown(rawData) { const data = rawData?.thoughts || []; const doc_references = rawData?.doc_references || []; if (doc_references?.length === 0) { return ''; } let docRefStr = '<div style="display: flex;color: #5F6E87;margin-bottom: 5px"><div>來源:</div><div>'; rawData?.doc_references.forEach((doc, index) => { docRefStr += `<div><ref>[${doc?.index_id}]</ref>《${doc?.doc_name}》</div>\n` }) docRefStr += '</div></div>' if(data.length === 0) { return docRefStr + ''; } return docRefStr + data.map(item => { if (!item?.action) { return ''; }; const docType = item?.action === 'retrieve'; const ragOutput = JSON.parse(item?.observation); const result = Array.isArray(ragOutput) ? ragOutput.map(ragEl => { return ` <details> <summary>${ragEl.dataName}(${ragEl.score.toFixed(2)}分)</summary> ${ragEl.content} </details> ` }).join('\n') : ragOutput; return ` <details> <summary>推理結(jié)果: ${item.action_name || item.action} </summary> <h5 style="font-size: 12px;">召回結(jié)果:</h5> ${result} </details> ` }).join('\n'); })(currentItem)
展示效果如圖
重要注意額外信息的類型是字符串類型,代碼需要用{{}}表達(dá)式
展示RAG索引文檔的詳細(xì)信息
這里通過一個(gè)【抽屜】組件來展示索引的詳細(xì)內(nèi)容,注意有多角標(biāo)的形式如【1】【2】,這個(gè)時(shí)候會(huì)同時(shí)顯示多條文檔索引,所以我么會(huì)用的【列表】組件來展示。
首先配置抽屜組件,標(biāo)題、按鈕可以配置,如圖
增加頁面變量,用于保存當(dāng)前的文檔信息
給自定義標(biāo)簽,增加點(diǎn)擊操作,執(zhí)行代碼提取當(dāng)前文檔信息
//角標(biāo)的解析函數(shù) function extractArrayFromString(input) { // 定義正則表達(dá)式以匹配方括號(hào)中的數(shù)字 const regex = /\[(\d+)\]/g; // 創(chuàng)建一個(gè)數(shù)組來存儲(chǔ)匹配到的數(shù)字 const resultArray = []; // 使用正則表達(dá)式匹配,并將結(jié)果存儲(chǔ)到數(shù)組中 let match; while ((match = regex.exec(input)) !== null) { resultArray.push(Number(match[1])); } return resultArray; } if (child?.[0] && typeof child[0] === 'string') { //調(diào)用解析函數(shù) const refIndex = extractArrayFromString(child[0]); const doc = (refIndex || []).map(el => currentItem?.doc_references[el - 1]) //保存到頁面的變量中,被抽屜組件內(nèi)的列表使用 currentDocRefs.setValue(doc) if (doc && doc.length) { drawerFrame1.show() } }
配置抽屜組件的內(nèi)容,展示提取出來的文檔信息,因?yàn)榭赡馨ǘ鄺l文檔,所以我們使用【列表】組件。其中再增加【文檔】組件來展示標(biāo)題和正文,注意正文可以使用 markdown 格式,還有上對(duì)齊,高度自適應(yīng)等屬性。
預(yù)覽效果
更多體驗(yàn)
可以參考更多應(yīng)用模板的實(shí)現(xiàn),一步給RAG應(yīng)用增加對(duì)話歷史記錄、自定義對(duì)話的工具條、知識(shí)庫操作、用戶注銷等功能。創(chuàng)建應(yīng)用時(shí)候選擇【從模板創(chuàng)建】應(yīng)用,如圖