基于MCP+Tabelstore架構實現知識庫答疑系統
- 整體流程設計
- (一)Agent 架構
- (二)知識庫存儲
- (1)向量數據庫Tablestore
- (2)MCP Server
- (三)知識庫構建
- (1)對文本進行切段并提取 FAQ
- (2)寫入知識庫和 FAQ 庫
- (四)知識庫檢索
- (五)知識庫問答
- 項目實踐一
- (1)創建知識庫存儲實例
- (2)啟動MCP Server
- (3)導入知識庫
- (4)檢索知識庫
- (5)基于知識庫進行問答
- 項目實踐二:利用CherryStudio實現MCP
- (1)效果
- 1.1 寫入到Tablestore
- 1.2 搜索文檔
- (2)流程
- (3)本地運行
- 3.1 下載源碼
- 3.2 準備環境
- 3.3 配置環境變量
- 3.4 Embedding
- 3.5 運行 MCP 服務
- (4)集成三方工具
- 4.1 Cherry Studio
- (5)拓展應用場景
整體流程設計
主要分為兩部分:知識庫構建和檢索。
1.知識庫構建
- 文本切段:對文本進行切段,切段后的內容需要保證文本完整性以及語義完整性。
- 提取 FAQ:根據文本內容提取 FAQ,作為知識庫檢索的一個補充,以提升檢索效果。
- 導入知識庫:將文本和 FAQ 導入知識庫,并進行 Embedding 后導入向量。
2.知識檢索(RAG)
- 問題拆解:對輸入問題進行拆解和重寫,拆解為更原子的子問題。
- 檢索:針對每個子問題分別檢索相關文本和 FAQ,針對文本采取向量檢索,針對 FAQ 采取全文和向量混合檢索。
- 知識庫內容篩選:針對檢索出來的內容進行篩選,保留與問題最相關的內容進行參考回答。
相比傳統的 Naive RAG,在知識庫構建和檢索分別做了一些常見的優化,包括 Chunk 切分優化、提取 FAQ、Query Rewrite、混合檢索等。
(一)Agent 架構
整體架構分為三個部分:
- 知識庫:內部包含 Knowledge Store 和 FAQ Store,分別存儲文本內容和 FAQ 內容,支持向量和全文的混合檢索。
- MCP Server:提供對 Knowledge Store 和 FAQ Store 的讀寫操作,總共提供 4 個 Tools。
- 功能實現部分:完全通過 Prompt + LLM 來實現對知識庫的導入、檢索和問答這幾個功能。
具體實現
所有代碼開源在這里,分為兩部分:
- Python 實現的 Client 端:實現了與大模型進行交互,通過 MCP Client 獲取 Tools,根據大模型的反饋調用 Tools 等基本能力。通過 Prompt 實現了知識庫構建、檢索和問答三個主要功能。
- Java 實現的 Server 端:基于 Spring AI 框架實現 MCP Server,由于底層存儲用的是 Tablestore,所以主體框架是基于這篇文章的代碼進行改造。
(二)知識庫存儲
(1)向量數據庫Tablestore
知識庫存儲選擇 Tablestore(向量檢索功能介紹),主要原因為:
- 簡單易用:僅一個創建實例步驟后即可開始使用,Serverless 模式無需管理容量和后續運維。
- 低成本:完全按量計費,自動根據存儲規模水平擴展,最大可擴展至 PB 級。當然如果采用本地知識庫肯定是零成本,但這里實現的是一個企業級、可通過云共享的知識庫。
- 功能完備:支持全文、向量和標量等檢索功能,支持混合檢索。
(2)MCP Server
實現了 4 個 Tools(具體注冊代碼可參考 TablestoreMcp),相關描述如下:
Tools | 功能 | 給 LLM 的描述 | 輸入參數 | 輸出結果 |
---|---|---|---|---|
storeKnowledge | 寫入知識庫內容,同時進行 Embedding 后寫入向量。 | Store document into knowledge store for later retrieval. | { “content”:“知識庫內容”, “meta_data”: { “source”: “文檔” } } | { “content”: [ { “type”: “text”, “text”: “null” } ], “isError”: false } |
searchKnowledge | 對知識庫進行向量檢索提取內容 | Search for similar documents on natural language descriptions from knowledge store. | { “query”: “知識庫內容”, “size”: 100 } | { “content”: [ { “type”: “text”, “text”: “[{“content”:“知識庫內容”,“meta_data”:{“source”:“文檔”}}]}” } ], “isError”: false } |
storeFAQ | 寫入 FAQ 內容,問題和答案分別寫入 Question 和 Answer 兩個字段,Question 字段額外進行 Embedding 后寫入向量。 | Store document into FAQ store for later retrieval. | { “question”: “問題”, “answer”: “答案” } | { “content”: [ { “type”: “text”, “text”: “null” } ], “isError”: false } |
searchFAQ | 通過對 Question 的全文和向量的混合檢索來提取內容。 | Search for similar documents on natural language descriptions from FAQ store. | { “query”: “問題”, “size”: 100 } | { “content”: [{ “type”: “text”, “text”: “[{“question”:“問題”,“answer”:“答案”}]” } ], “isError”: false } |
(三)知識庫構建
(1)對文本進行切段并提取 FAQ
完全通過提示詞來完成,可根據自己的要求進行調優。
提示詞 | 輸入 | 輸出 |
---|---|---|
需要將以下文本切段,并根據文本內容整理 FAQ。文本切段的要求: <1>保證語義的完整性:不要將一個完整的句子切斷,不要把表達同一個語義的不同句子分割開 <2>保留足夠多的上下文信息:如果切割后的文本段必須依賴上下文信息才能表達正確的語義,那就不能切割開 <3>過濾無效信息:過濾格式化內容如大量填充的空格,過濾不完整的段落以及過濾 <4>移除 markdown 內容的標記,層次關系按 1, 2, 3 以及 1.1, 2.1 來標記 <5>只保留文本內容,移除鏈接等信息 <6>不要對標題單獨切段:僅對正文進行切段,標題可以與正文合并一起,或者是作為上下文信息擴充進正文分段內 <7>切段后的文本如果不是一個完整的句子,請修改為一個完整表述的句子 | 表格存儲向量檢索在原有的 DiskANN 算法之上進行優化,提供大規模和高性能的向量檢索服務。前期,我們也先對用戶提供了基于 HNSW 的圖算法,但是發現明顯的性能問題,HNSW 對內存要求很高,很難支撐大規模數據,當內存不足時,HNSW 訪問磁盤的性能較差。表格存儲上的用戶大多數數量相對較大,HNSW 的規模和成本問題不符合我們產品的定位,我們希望可以讓更多的數據可以低成本的使用上向量能力,因此我們后續廢棄掉了 HNSW 算法,使用 DiskAnn 重新實現了向量檢索能力。 | { “Chunks”: [ “表格存儲向量檢索在原有的 DiskANN 算法之上進行優化,提供大規模和高性能的向量檢索服務。前期,我們也先對用戶提供了基于 HNSW 的圖算法,但是發現明顯的性能問題。”, “HNSW 對內存要求很高,很難支撐大規模數據,當內存不足時,HNSW 訪問磁盤的性能較差。表格存儲上的用戶大多數數量相對較大,HNSW 的規模和成本問題不符合我們產品的定位。”, “我們希望可以讓更多的數據可以低成本使用上向量能力,因此我們后續廢棄掉了 HNSW 算法,使用 DiskAnn 重新實現了向量檢索能力。” ] } { “FAQ”: [ {“Question”: “表格存儲向量檢索使用了什么算法?”, “Answer”: “表格存儲向量檢索在原有的 DiskANN 算法之上進行了優化,提供大規模和高性能的向量檢索服務。” }, {“Question”: “為什么最初選擇了 HNSW 圖算法?”, “Answer”: “前期對用戶提供了基于 HNSW 的圖算法,但發現其存在明顯的性能問題。” }, {“Question”: “HNSW 算法的主要缺點是什么?”, “Answer”: “HNSW 對內存要求很高,難以支撐大規模數據,且在內存不足時訪問磁盤的性能較差。”}, { “Question”: “為什么廢棄了 HNSW 算法?”, “Answer”: “HNSW 的規模和成本問題不符合表格存儲產品的定位,無法滿足大規模數據低成本使用向量能力的需求。” }, {“Question”: “表格存儲最終采用什么算法替代 HNSW?”, “Answer”: “后續廢棄了 HNSW 算法,使用 DiskANN 重新實現了向量檢索能力。” } ] } |
以上是一個示例,可以看到通過大模型能比較準確的對文本進行切段并提取 FAQ。這種方式的優勢是切段的文本能保證完整性以及語義一致性,能夠比較靈活的對格式做一些處理。提取的 FAQ 很全面,對于簡單問題的問答通過直接搜索 FAQ 是最準確直接的。最大的缺點就是執行比較慢并且成本較高,一次會消耗大量的 Token,不過好在是一次性的投入。
(2)寫入知識庫和 FAQ 庫
這一步也是通過提示詞來完成,基于 MCP 架構可以非常簡單的實現,樣例如下:
操作類型 | 提示詞模板 |
---|---|
寫入知識庫 | 將以下內容存儲入 Knowledge 知識庫內:%s |
寫入 FAQ | 將以下內容存儲入 FAQ 庫內: Question:%s Answer:%s |
(四)知識庫檢索
同樣這一步也是通過提示詞加 MCP 來實現,非常的簡單,樣例如下:
提示詞模板 | 檢索條件 | 檢索結果 |
---|---|---|
你是產品答疑助手,在回答問題之前請先檢索 Knowledge 庫和 FAQ 庫: 1. 先理解問題并對問題進行拆解,拆解成多個子問題。 2. 每個子問題同時檢索 Knowledge 庫和 FAQ 庫,每次檢索結果不超過 20 條 3. 對檢索的內容進行篩選,保留與問題最相關的內容,Knowledge 和 FAQ 分別不超過 10 條。 最后合并檢索內容,返回與檢索內容最相關的 20 條,直接返回檢索的結果,樣例如下: 1. 如果是 Knowledge,格式為:Knowledge:<結果一> 2. 如果是 FAQ,格式為:FAQ:<結果二> 檢索內容:%s | Tablestore 底層向量索引算法選擇了哪種實現? | Knowledge:表格存儲向量檢索在原有的 DiskANN 算法之上進行優化。 Knowledge:表格存儲上的用戶大多數數據量相對較大。 FAQ:為什么最初選擇了 HNSW 圖算法? FAQ:為了給用戶提供一種向量檢索的服務選項。 FAQ:表格存儲向量檢索是基于什么算法實現的? FAQ:表格存儲向量檢索是在原有的 DiskANN 算法基礎上進行了優化。 |
通過提示詞描述實現了一個稍微復雜點的檢索:
-
1.先對問題進行拆解,拆解為更原子的子問題。
-
2.每個子問題分別檢索知識庫和 FAQ,檢索結果匯總后篩選留下與問題最相關的內容。
-
3.按照格式返回結果。
(五)知識庫問答
直接看下提示詞和效果
提示詞模板 | 問題 | 答案 |
---|---|---|
你是產品答疑助手,在回答問題之前請先檢索 Knowledge 庫和 FAQ 庫: 1. 先理解問題并對問題進行拆解,拆解成多個子問題,子問題盡量是比較小比較原子的問題。 2. 每個子問題同時檢索 Knowledge 庫和 FAQ 庫,每次檢索結果不超過 10 條,最后合并所有子問題的檢索結果。 對檢索的內容進行篩選,保留與問題最相關的檢索結果,利用檢索到的內容進行回答,不要亂給答案。 我的問題是:%s | Tablestore 底層向量索引算法選擇了哪種實現? | Tablestore 底層向量索引算法選擇了 DiskANN 算法實現。 最初,Tablestore 曾嘗試使用 HNSW (Hierarchical Navigable Small World) 圖算法來提供向量檢索服務。 然而,HNSW 對內存要求很高,難以支撐大規模數據。當內存不足時,HNSW 訪問磁盤的性能極差。考慮到大多數用戶的大量數據需求以及希望讓更多的數據能夠以較低成本使用上向量能力的愿望, 最終決定不再使用 HNSW 算法,而是基于 DiskANN 算法進行了優化,提供了大規模和高性能的向量檢索服務。 |
從 MCP Server 的 Log 內可以看到自動調用了知識庫和 FAQ 的檢索工具,并能根據之前導入的內容進行回答。
項目實踐一
開源地址:aliyun/alibabacloud-tablestore-mcp-server
(1)創建知識庫存儲實例
可通過命令行工具(下載地址)來創建 Tablestore 實例,參考這個文檔先進行配置。
配置成功后執行以下命令進行實例創建,實例名自行選擇,需要保證 Region 內唯一。
tablestore> create_instance -r cn-hangzhou -d 'Knowledge store for AI Agent.' -n 'my-store' done
(2)啟動MCP Server
啟動前需要在環境變量內配置如下幾個參數:
變量名 | 必填 | 含義 | 默認值 |
---|---|---|---|
TABLESTORE_INSTANCE_NAME | 是(yes) | 實例名 | - |
TABLESTORE_ENDPOINT | 是(yes) | 實例訪問地址 | - |
TABLESTORE_ACCESS_KEY_ID | 是(yes) | 秘鑰 ID | - |
TABLESTORE_ACCESS_KEY_SECRET | 是(yes) | 秘鑰 SECRET | - |
可參考代碼庫 README 內的步驟進行啟動,也可將項目導入 IDE 后直接運行 App 這個類,啟動后會自動初始化表和索引。
(3)導入知識庫
這一步需要執行代碼庫內的 knowledge_manager.py 工具,執行前需要先配置訪問大模型的 API-KEY,默認采用 qwen-max。
export LLM_API_KEY=sk-xxxxxx
請自行準備知識庫文檔,使用 markdown 格式,執行如下:
(4)檢索知識庫
執行如下:
(5)基于知識庫進行問答
項目實踐二:利用CherryStudio實現MCP
(1)效果
這里展示 2 個 tool 的能力,一個是存儲工具,一個是搜索工具。 我們使用的軟件是熱門的開源軟件 cherry-studio, 使用的大模型是通義千問的 qwen-max
模型
1.1 寫入到Tablestore
cherry-studio
使用示例如下圖:
python
Server 端代碼的寫入日志如下圖:
Tablestore(表格存儲) 控制臺數據存儲結果如下圖:
1.2 搜索文檔
Tablestore(表格存儲) 的多元索引支持向量、標量、全文檢索等各種類型的組合查詢,該示例代碼中使用了混合檢索,如需更復雜的查詢,可以參考文章最后的“貢獻代碼和二次開發”章節了解如何自定義開發。
cherry-studio
搜索查詢示例如下圖:
python
Server 端的查詢日志如下圖:
Tablestore(表格存儲) 控制臺數據也可以進行查詢,這里以全文檢索示例:
(2)流程
MCP server 提供的 2 個工具十分簡單:
- 寫入: 文檔經過 MCP server 內置的 Embedding ( 默認為 BAAI/bge-base-zh-v1.5 ) 模型,寫入到Tablestore(表格存儲)即可。
- 查詢: 用戶的查詢文本經過 MCP server 內置的 Embedding 模型轉成向量,然后調用表格存儲的 多元索引即可,其內部使用了 向量檢索 和 全文檢索 進行混合查詢,最終召回用戶期望的結果。
(3)本地運行
3.1 下載源碼
- 使用
git clone
將代碼下載到本地。 - 進入 python 源碼的根目錄:
cd tablestore-mcp-server/tablestore-python-mcp-server
3.2 準備環境
代碼需要 python3.10
版本以上進行構建,使用了 uv
進行包和環境管理。
安裝 uv:
# 方式1:使用現有 python3 安裝 uv
pip3 install uv
# 方式2:源碼安裝 uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
準備 Python 環境:
如果本地有
python3.10
版本以上環境,無需執行這一小步。
因為我們項目至少需要 python3.10
版本,這里使用 python3.12
進行示例。
# 查看當前有哪些 python 環境
uv python list
# 如果沒有python 3.12.x 相關版本,請安裝 python3.12 版本. 內部會從 github 下載 uv 官方維護的 python 包。
uv python install 3.12
創建虛擬環境:
# 使用 python 3.12 版本當做虛擬環境
uv venv --python 3.12
3.3 配置環境變量
代碼里所有的配置是通過環境變量來實現的,出完整的變量見下方表格。 主要依賴的數據庫 Tablestore(表格存儲) 支持按量付費,使用該工具,表和索引都會自動創建,僅需要在控制臺上申請一個實例即可。
變量名 | 必填 | 含義 | 默認值 |
---|---|---|---|
SERVER_HOST | 否 | MCP server 的 host | 0.0.0.0 |
SERVER_PORT | 否 | MCP server 的 port | 8001 |
TABLESTORE_INSTANCE_NAME | 是(yes) | 實例名 | - |
TABLESTORE_ENDPOINT | 是(yes) | 實例訪問地址 | - |
TABLESTORE_ACCESS_KEY_ID | 是(yes) | 秘鑰 ID | - |
TABLESTORE_ACCESS_KEY_SECRET | 是(yes) | 秘鑰 SECRET | - |
TABLESTORE_TABLE_NAME | 否 | 表名 | ts_mcp_server_py_v1 |
TABLESTORE_INDEX_NAME | 否 | 索引名 | ts_mcp_server_py_index_v1 |
TABLESTORE_VECTOR_DIMENSION | 否 | 向量維度 | 768 |
TABLESTORE_TEXT_FIELD | 否 | 文本字段名 | _content |
TABLESTORE_VECTOR_FIELD | 否 | 向量字段名 | _embedding |
EMBEDDING_PROVIDER_TYPE | 否 | Embedding 模型提供者 | hugging_face(當前僅支持 hugging_face) |
EMBEDDING_MODEL_NAME | 否 | Embedding 模型名字 | BAAI/bge-base-zh-v1.5(維度是768,和 TABLESTORE_VECTOR_DIMENSION 呼應) |
TOOL_STORE_DESCRIPTION | 否 | 寫入的 MCP tool 的描述文字 | 參考 settings.py |
TOOL_SEARCH_DESCRIPTION | 否 | 查詢的 MCP tool 的描述文字 | 參考 settings.py |
3.4 Embedding
為了方便,這里不使用云服務的Embedding能力,而使用了內置的本地Embedding模型,示例代碼僅支持了 HuggingFace 的本地Embedding模型,使用十分簡單,如果網絡不好,可以配置 HuggingFace 的鏡像。
export HF_ENDPOINT=http://hf-mirror.com
3.5 運行 MCP 服務
# 加速下載 Hugging 的 Embedding Model
export HF_ENDPOINT=http://hf-mirror.comexport TABLESTORE_ACCESS_KEY_ID=xx
export TABLESTORE_ACCESS_KEY_SECRET=xx
export TABLESTORE_ENDPOINT=xxx
export TABLESTORE_INSTANCE_NAME=xxx
# 默認以 sse 模式運行,如果希望以 stdio 模式運行可以添加: `--transport stdio`
uv run tablestore-mcp-server
(4)集成三方工具
4.1 Cherry Studio
Cherry-Studio,是一個熱門的開源的 AI Client 軟件, 免費使用,其支持 MCP 服務。
安裝 :Github鏈接 下載最新版本的適合自己機器運行環境的安裝包. 比如我的電腦是m1芯片的mac,因此下載 Cherry-Studio-1.1.4-arm64.dmg 進行安裝。安裝好后,需要配置大模型的 api-key 相關信息,這里不再一一描述。
按照如下所示創建MCP服務:
在聊天里使用MCP服務(可以把一些模版填充到 Cherry Studio 的模版里,生成一個自己的特殊助手,后續可以直接使用):
(5)拓展應用場景
MCP 的 Tool 的能力和場景是 Tool 的描述來提供的,因此我們可以定義一些特殊的能力,可以發揮你的想象力。另外,當前我們沒有接入一些復雜的多字段自由 Filter 能力、稀疏向量(Sparse Vector)能力,后續有時間會繼續進行集成。
僅需要修改如下配置即可, 如何寫可以參考 settings.py
export TOOL_STORE_DESCRIPTION="你的自定義的描述"export TOOL_SEARCH_DESCRIPTION="你的自定義的描述"
修改后從 MCP Client 中可以看到工具 (Tool) 的描述已經變成了自定義的描述,那么大模型(LLM)就會根據你的描述去使用工具(Tool)。