????????為了將大語言模型植入到小程序中,來支持用戶的問答。那我們首先需要做的是什么呢,不是引入大語言模型,而且為大語言模型搭建一個私有化知識庫,但是這是這節呢,我們先不搭建私有化知識庫,在這之前,我們還需要做一個前置工作,那就是數據準備。
? ? ? ? 小版本的語言模型本身的能力不像大參數量的模型那樣,有用強大的知識庫和泛化能力。我們需要為其提供一個外部的知識庫,為模型提供額外的知識儲備,進而利用模型的推理能力回答用戶的問題。搭建知識庫之前,我們就需要將數據,存儲到知識庫中。那么首先,我們就需要解析文件得到元數據。
一、文本解析
為了解析到元數據,我們采用一個開源的工具,Apache Tika進行文件內容解析,這是一個由java開發的內容分析工具包。
<dependency><groupId>org.apache.tika</groupId><artifactId>tika-core</artifactId><version>3.0.0</version>
</dependency>
<dependency><groupId>org.apache.tika</groupId><artifactId>tika-parsers-standard-package</artifactId><version>3.0.0</version>
</dependency>
?我們先創建一個工具類TikaUtil,編寫一個函數用來解析我們傳入的文件內容。
?二、文本切片
?有了向量數據庫后,我們就需要把數據存入向量數據庫中了,在這之前呢,我們需要寫完成一個文件解析的功能,因為,要解釋到元數據,才進行接下來的步驟,我們這里選擇Apache Tika進行文件內容解析,這是一個由java開發的內容分析工具包。
<dependency><groupId>org.apache.tika</groupId><artifactId>tika-core</artifactId><version>3.0.0</version>
</dependency>
<dependency><groupId>org.apache.tika</groupId><artifactId>tika-parsers-standard-package</artifactId><version>3.0.0</version>
</dependency>
?我們先創建一個工具類TikaUtil,編寫一個函數用來解析我們傳入的文件內容。
public String extractText(MultipartFile file) {try {// 創建解析器--在不確定文檔類型時候可以選擇使用AutoDetectParser可以自動檢測一個最合適的解析器Parser parser = new AutoDetectParser();// 用于捕獲文檔提取的文本內容。-1 參數表示使用無限緩沖區,解析到的內容通過此hander獲取BodyContentHandler bodyContentHandler = new BodyContentHandler(-1);// 元數據對象,它在解析器中傳遞元數據屬性---可以獲取文檔屬性Metadata metadata = new Metadata();// 帶有上下文相關信息的ParseContext實例,用于自定義解析過程。ParseContext parseContext = new ParseContext();parser.parse(file.getInputStream(), bodyContentHandler, metadata, parseContext);// 獲取文本return bodyContentHandler.toString();} catch (Exception e) {e.printStackTrace();return null;}}
我們使用該工具即可獲得文件的元數據。但是在問答的時候,我們不能將整個文件全部輸入到大模型中,一方面是模型本身是擁有上上下文窗口大小限制,另一方面是大量的信息可能影響模型的推理時間和準確率。為了解決這種情況,我們需要將文本分塊,把內容切割成一個一個的文本塊,將每個塊作為一個知識單元,再將其轉換為向量表示存入向量數據庫,這樣大模型在檢索的時候只需要將相關的文本塊添加到上下文中,既能保證回答的準確性,也解決了上下文過長的問題。
對于分塊的方法,在Langchain框架中,有多種分塊策略:
固定大小分塊:按固定字符數或單詞數分割文本,簡單直接,但可能破壞句子或段落的完整性。
按句子分塊:使用自然語言處理工具(如NLTK、spaCy)按句子邊界分割文本,適合處理句子級別的任務。
按段落分塊:按段落分割文本,適合處理段落級別的任務,段落通常由換行符分隔。
重疊分塊:在固定大小分塊的基礎上,允許塊之間有重疊部分,避免信息丟失。
遞歸分塊:遞歸地將文本分割成更小的部分,直到滿足特定條件,適合處理復雜文本結構。
語義分塊:根據語義或主題分割文本,通常需要自然語言處理技術識別語義邊界。
在java社區,Langchain4J??旨在為 Java 開發者提供類似于 LangChain(基于 Python)的功能。LangChain4J 的目標是將 LangChain 的核心概念和功能移植到 Java 生態系統中,使 Java 開發者能夠更方便地構建基于大語言模型(LLMs)的應用程序。Langchain4J
Langchain4J也為我們提供了多種文本分塊方法:
DocumentByParagraphSplitter
DocumentByLineSplitter
DocumentBySentenceSplitter
DocumentByWordSplitter
DocumentByCharacterSplitter
DocumentByRegexSplitter
- Recursive:?
DocumentSplitters.recursive(...)
在這之前,我們先新建一個TikaVo類,用作傳輸文檔解析分片后的結果。
@Accessors(chain = true)
@Data
public class TikaVo implements Serializable {private List<String> text;private List<String> metadata;
}
這里,我們采用遞歸分割的方式,將輸入的文本分割成塊。
private TikaVo splitParagraphs(String content) {DocumentSplitter splitter = DocumentSplitters.recursive(TARGET_LENGTH, LENGTH_TOLERANCE, new OpenAiTokenizer());List<TextSegment> split = splitter.split(Document.document(content));return new TikaVo().setText(split.stream().map(TextSegment::text).toList()).setMetadata(split.stream().map(textSegment -> JSON.toJSONString(textSegment.metadata())).toList());}
到這里,文件處理算是完成了,這里,我們可以寫一個接口來測試一下效果,我這里就不展示了。感興趣的小伙伴,也可以試試其他的切片方法。
后面,我們將介紹,如何將處理好的文本,存儲到數據庫中。