踐:精確檢索、混合召回與引用溯源體系構(gòu)建)
1. 項(xiàng)目概述從“能搜到”到“能答對(duì)”的跨越金融領(lǐng)域做RAGRetrieval-Augmented Generation檢索增強(qiáng)生成和通用場(chǎng)景完全是兩個(gè)物種。通用場(chǎng)景里用戶(hù)問(wèn)“如何做紅燒肉”檢索結(jié)果偏一點(diǎn)問(wèn)題不大模型能靠自身知識(shí)兜底。但金融場(chǎng)景下用戶(hù)問(wèn)的是“某公司去年第三季度的流動(dòng)比率是多少”或者“某項(xiàng)新規(guī)對(duì)資管產(chǎn)品估值的影響”答案必須精確到數(shù)值、條款和出處。錯(cuò)一個(gè)小數(shù)點(diǎn)或者把新舊法規(guī)混在一起輕則誤導(dǎo)投資決策重則直接踩監(jiān)管紅線(xiàn)。我做這個(gè)Financial RAG Demo項(xiàng)目起因其實(shí)很樸素。當(dāng)時(shí)團(tuán)隊(duì)需要處理一批財(cái)報(bào)PDF、招股說(shuō)明書(shū)和研報(bào)日常工作是讓分析師反復(fù)翻閱這些動(dòng)輒幾百頁(yè)的文檔把關(guān)鍵數(shù)據(jù)摘出來(lái)填進(jìn)表格。這活兒大量重復(fù)、機(jī)械但偏偏又特別考驗(yàn)準(zhǔn)確性人工干很容易疲勞出錯(cuò)。于是我們想做一個(gè)RAG服務(wù)把這一步盡量自動(dòng)化——不是讓模型替代分析師做判斷而是讓模型快速定位到原文片段把相關(guān)的數(shù)據(jù)、條款、表述完整撈出來(lái)再由人工做最終確認(rèn)。這個(gè)Demo雖然叫“Demo”但絕對(duì)不是玩具級(jí)別。我把它設(shè)計(jì)成了一條完整的流程文檔解析、切片、向量化、存儲(chǔ)、召回、重排、生成、引用溯源。每一步都針對(duì)金融文本的特點(diǎn)做了專(zhuān)門(mén)處理。整個(gè)項(xiàng)目跑下來(lái)單次問(wèn)答的端到端延遲大概在2到4秒召回答案在測(cè)試集上的Top-5命中率能做到85%以上生成答案的關(guān)鍵字段引用完整度也基本達(dá)到了可用的水平。這篇文章寫(xiě)給誰(shuí)兩類(lèi)人。一類(lèi)是想在金融場(chǎng)景做RAG落地的開(kāi)發(fā)者你會(huì)看到從0到1搭建一套服務(wù)時(shí)踩過(guò)的坑和繞過(guò)的彎另一類(lèi)是對(duì)RAG感興趣但還沒(méi)動(dòng)手的產(chǎn)品或分析師你會(huì)理解為什么金融RAG不能簡(jiǎn)單套用通用方案以及“檢索”和“生成”之間那層微妙的平衡關(guān)系。我會(huì)按項(xiàng)目的真實(shí)推進(jìn)順序來(lái)拆解先講整體設(shè)計(jì)思路再講每個(gè)核心環(huán)節(jié)的實(shí)現(xiàn)細(xì)節(jié)然后是完整流程演示最后是排查問(wèn)題和優(yōu)化心得。全程不貼完整工程代碼但會(huì)把關(guān)鍵腳本的結(jié)構(gòu)、配置和參數(shù)講透方便你直接復(fù)現(xiàn)。2. 內(nèi)容整體設(shè)計(jì)與思路拆解2.1 金融RAG的難點(diǎn)為什么不能直接套通用方案先說(shuō)說(shuō)RAG的基本原理方便沒(méi)接觸過(guò)的朋友快速跟上。傳統(tǒng)的語(yǔ)言模型回答問(wèn)題全靠訓(xùn)練時(shí)“記住”的知識(shí)知識(shí)有截止時(shí)間也無(wú)法引用特定文檔。RAG的思路是在模型回答之前先從你的私有文檔庫(kù)里檢索出相關(guān)內(nèi)容把檢索結(jié)果作為上下文拼接給模型讓模型基于這些材料作答。相當(dāng)于給模型開(kāi)卷考試而不是閉卷瞎猜。通用RAG流程聽(tīng)著簡(jiǎn)單——文檔切塊、向量化、相似度檢索、丟給大模型——但金融場(chǎng)景有三個(gè)硬傷。第一個(gè)硬傷是數(shù)值敏感性。通用聊天可以把“大約”“大概”掛在嘴邊金融問(wèn)答連近似都應(yīng)該盡量避免。模型如果泛泛地概括“該指標(biāo)有所改善”分析師的內(nèi)心是崩潰的他需要的是“從1.2上升到1.5增幅25%”。這就要求檢索結(jié)果必須保留原始數(shù)值和上下文語(yǔ)境而切塊稍大向量就能把一個(gè)數(shù)字淹沒(méi)在一堆語(yǔ)義相近的文字中召回時(shí)很難精確對(duì)應(yīng)。第二個(gè)硬傷是術(shù)語(yǔ)和實(shí)體歧義。金融文檔里的“公司”可能指上市公司本身也可能指其子公司“計(jì)提”這個(gè)詞在減值、撥備、負(fù)債場(chǎng)景里都有甚至同樣一句話(huà)在新舊會(huì)計(jì)準(zhǔn)則下含義完全不同。通用向量模型對(duì)這類(lèi)歧義的區(qū)分能力有限單純靠語(yǔ)義相似度檢索容易把不同語(yǔ)境的內(nèi)容混為一談。第三個(gè)硬傷是答案必須可溯源。分析師拿到一個(gè)靠RAG生成的結(jié)論第一反應(yīng)絕對(duì)不是“哇真智能”而是“你這個(gè)結(jié)論是依據(jù)哪一段原文、哪一份文件得出的”。這在金融合規(guī)里叫audit trail審計(jì)痕跡沒(méi)有引用的答案是零價(jià)值的。通用RAG系統(tǒng)很少把“引用溯源”當(dāng)作一等公民來(lái)設(shè)計(jì)很多人做到最后發(fā)現(xiàn)引用是模型瞎編的那就全是負(fù)功?;谶@三個(gè)硬傷項(xiàng)目的整體設(shè)計(jì)從一開(kāi)始就不是“做一個(gè)通用的知識(shí)庫(kù)問(wèn)答”而是“做一個(gè)帶著鐐銬跳舞的金融問(wèn)答系統(tǒng)”——把精確性、可溯源性和領(lǐng)域適配性放在比“語(yǔ)義流暢”更高的優(yōu)先級(jí)上。2.2 整體架構(gòu)選型檢索-重排-生成三級(jí)流水線(xiàn)這個(gè)Demo采用了當(dāng)前業(yè)界比較主流的RAG流水線(xiàn)但每一級(jí)都做了金融化改造。整個(gè)架構(gòu)可以拆成五個(gè)模塊文檔解析層負(fù)責(zé)把PDF、Word、Markdown等原始文件轉(zhuǎn)換成結(jié)構(gòu)化的純文本并盡量保留表格、標(biāo)題、頁(yè)眉等結(jié)構(gòu)信息。離線(xiàn)索引層對(duì)文本做切片生成向量索引和關(guān)鍵詞索引雙路召回的數(shù)據(jù)基礎(chǔ)。召回層用戶(hù)問(wèn)題進(jìn)來(lái)后同時(shí)走向量相似度檢索和關(guān)鍵詞檢索把兩路結(jié)果合并。重排層用一個(gè)更精細(xì)的模型對(duì)召回結(jié)果做二次打分過(guò)濾掉語(yǔ)義相近但實(shí)際不對(duì)題的片段。生成層把最終選中的文檔片段作為上下文交給大模型配合提示詞工程生成帶引用的答案。選這條路而不是端到端訓(xùn)練一個(gè)金融問(wèn)答大模型原因很現(xiàn)實(shí)端到端方案需要高質(zhì)量標(biāo)注數(shù)據(jù)金融領(lǐng)域標(biāo)注成本極高而且模型更新迭代動(dòng)輒重新訓(xùn)練維護(hù)成本高。RAG方案則把“知識(shí)更新”變成“換文檔重跑索引”今天進(jìn)來(lái)一百份新公告明天系統(tǒng)就掌握了不需要碰模型權(quán)重。對(duì)金融機(jī)構(gòu)這種強(qiáng)知識(shí)時(shí)效性的場(chǎng)景RAG無(wú)疑是性?xún)r(jià)比最高的路線(xiàn)。2.3 關(guān)鍵決策記錄切片粒度、Embedding模型、檢索策略的取舍切片粒度是RAG里第一個(gè)要做的決策金融場(chǎng)景直接決定了召回效果。切片太短比如按256字符切檢索到的片段可能只覆蓋了問(wèn)題的半個(gè)前提模型拿到碎片信息沒(méi)法作答切片太長(zhǎng)比如整頁(yè)丟進(jìn)向量庫(kù)語(yǔ)義雖完整但向量在高維空間里被拉平了相似度區(qū)分度下降而且生成階段塞進(jìn)上下文的內(nèi)容太多可能淹沒(méi)關(guān)鍵信息。我最后選了分層切片策略。第一層按段落切段落太長(zhǎng)或太短的做二次調(diào)整默認(rèn)目標(biāo)長(zhǎng)度在600到800個(gè)中文字符之間允許±20%浮動(dòng)。第二層是有重疊的滑動(dòng)窗口相鄰切片保持80到120字符的重疊。這個(gè)設(shè)計(jì)同時(shí)兼顧了語(yǔ)義完整性和檢索精準(zhǔn)度重疊區(qū)域也能緩沖跨段信息被硬切造成的信息斷裂。Embedding模型用的是一套針對(duì)中文優(yōu)化過(guò)的向量模型而不是直接上通用英文模型。金融文檔中有著大量特有名詞、股票代碼和統(tǒng)計(jì)數(shù)據(jù)模型的中文理解能力必須過(guò)硬。向量維度我選了768維這是模型輸出的原生維度不建議降維降維會(huì)損失語(yǔ)義精度金融場(chǎng)景不差那點(diǎn)存儲(chǔ)空間檢索準(zhǔn)確性?xún)?yōu)先。檢索策略上純向量檢索對(duì)“語(yǔ)義相同寫(xiě)法不同”的查詢(xún)很友好但金融場(chǎng)景里很多查詢(xún)是精確的“凈利潤(rùn)同比增長(zhǎng)率”“每股收益”“不良貸款率”這些詞在文檔里就以幾乎不變的形式出現(xiàn)。所以我加了關(guān)鍵詞檢索通道用BM25算法做傳統(tǒng)倒排索引召回。向量召回負(fù)責(zé)找語(yǔ)義相似的段落BM25負(fù)責(zé)鎖定精確包含關(guān)鍵實(shí)體的段落兩路合并后再統(tǒng)一重排。這樣做的直接收益是對(duì)“某公司2022年ROE”這類(lèi)查詢(xún)即使向量召回的片段排得不夠靠前BM25也能把它從文本的犄角旮旯里揪出來(lái)。生成端大模型用的是一套支持中文長(zhǎng)上下文、有一定金融知識(shí)基礎(chǔ)的通用模型。為什么不用金融專(zhuān)用微調(diào)模型因?yàn)閷?zhuān)用微調(diào)模型通常更“懂”金融但生成內(nèi)容的創(chuàng)造性和靈活性反而受限而且微調(diào)模型的版本更新周期長(zhǎng)難以跟上文檔庫(kù)的快速變化。檢索系統(tǒng)負(fù)責(zé)“懂文檔”生成模型負(fù)責(zé)“懂表達(dá)”這個(gè)分工更合理。3. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)3.1 文檔解析處理表格識(shí)別、PDF轉(zhuǎn)文本的坑與對(duì)策金融文檔的很大一部分價(jià)值濃縮在表格里而PDF解析工具對(duì)表格的還原能力參差不齊。我實(shí)測(cè)的結(jié)論是用PDF直接抽取文本的方案表格信息幾乎必丟或者說(shuō)丟失得不成樣子。最初測(cè)試時(shí)我們拿一份帶復(fù)雜合并單元格的財(cái)報(bào)PDF用某PDF解析庫(kù)直接抽取文本所有表格數(shù)據(jù)擠成了一坨列與列之間沒(méi)有分隔行與行之間的對(duì)應(yīng)關(guān)系完全亂了。向量化之后檢索出來(lái)的片段看起來(lái)每個(gè)數(shù)字都在但沒(méi)法確認(rèn)哪個(gè)數(shù)字屬于哪一列屬于表面上檢索到了實(shí)質(zhì)上是無(wú)效信息。后來(lái)?yè)Q了表格識(shí)別工具專(zhuān)門(mén)負(fù)責(zé)把PDF頁(yè)面里的表格區(qū)域識(shí)別出來(lái)輸出成結(jié)構(gòu)化的數(shù)據(jù)再說(shuō)雖然不能保證100%還原復(fù)雜表格尤其遇到跨頁(yè)的表格基本沒(méi)辦法合并但單頁(yè)內(nèi)的表格識(shí)別率相當(dāng)可觀(guān)。我的對(duì)策是解析結(jié)果不追求完美但必須保證“可讀的結(jié)構(gòu)”。具體做法是把識(shí)別出來(lái)的表格轉(zhuǎn)成Markdown格式存放列與列用豎線(xiàn)分隔這比純文本效果好了不止一個(gè)量級(jí)。標(biāo)題層級(jí)也是一個(gè)被低估的信息。財(cái)報(bào)里“第三節(jié) 管理層討論與分析”下面的兩個(gè)字“概述”和“第四節(jié) 財(cái)務(wù)數(shù)據(jù)”下面的“概述”內(nèi)容完全不同。如果解析時(shí)丟了標(biāo)題層級(jí)這兩個(gè)“概述”切片在向量空間里會(huì)非常相似檢索時(shí)互相干擾。我要求解析器把文檔的標(biāo)題層級(jí)保留成Markdown的#、##、###結(jié)構(gòu)切片時(shí)會(huì)把標(biāo)題信息拼進(jìn)切片內(nèi)容里這樣向量模型能感知到“這是哪一節(jié)的子內(nèi)容”語(yǔ)義區(qū)分度大幅提升。3.2 切片工程語(yǔ)義完整性與檢索精度的折中切片不是按字符數(shù)機(jī)械切分那么簡(jiǎn)單。我把“語(yǔ)義邊界”作為第一優(yōu)先級(jí)句號(hào)和標(biāo)題是天然的分段點(diǎn)段落結(jié)束才考慮是否達(dá)到目標(biāo)長(zhǎng)度。如果某段特別長(zhǎng)超過(guò)目標(biāo)長(zhǎng)度的1.5倍先按句號(hào)拆成幾個(gè)子塊再組合相鄰子塊。關(guān)于重疊窗口業(yè)界有些方案喜歡20%到30%的絕對(duì)重疊比例我試過(guò)一次后發(fā)現(xiàn)對(duì)金融文檔來(lái)說(shuō)偏高同一條信息被重復(fù)索引三次甚至四次召回時(shí)明明只有一段相關(guān)信息卻返回好幾條幾乎一模一樣的切片重排還得費(fèi)力去重。我的參數(shù)是固定重疊80到120字符而不是按比例。這主要為了保證跨切片的句子不被硬生生切斷——如果一句話(huà)在切片邊界處正好斷成兩半那向量表達(dá)就會(huì)被砍得語(yǔ)義殘缺重排也對(duì)不齊。還有一個(gè)被反復(fù)強(qiáng)調(diào)但很多人會(huì)忽略的點(diǎn)切片要帶元數(shù)據(jù)。每個(gè)切片除了正文內(nèi)容至少要帶上來(lái)源文件名、頁(yè)碼、章節(jié)路徑、切片的全局編號(hào)。這些元數(shù)據(jù)不僅僅是召回后展示用的更關(guān)鍵的是在生成階段做引用溯源時(shí)模型能精確地說(shuō)出“這個(gè)數(shù)據(jù)來(lái)自某文件第幾頁(yè)第幾節(jié)”這是分析師判斷答案可信度的核心憑證。3.3 向量索引與混合檢索的具體配置向量數(shù)據(jù)庫(kù)層面我對(duì)比過(guò)幾種常見(jiàn)方案最終選了一個(gè)支持向量標(biāo)量混合查詢(xún)的向量數(shù)據(jù)庫(kù)。為什么不用關(guān)系型數(shù)據(jù)庫(kù)做純向量檢索金融場(chǎng)景下除了語(yǔ)義相似度常常還要疊加硬性過(guò)濾條件。比如“只查2022年年報(bào)”“只查某公司的數(shù)據(jù)”“排除PDF格式的結(jié)果”這些條件在向量庫(kù)里叫“元數(shù)據(jù)預(yù)過(guò)濾”。選型時(shí)考慮三個(gè)硬指標(biāo)一是支持億級(jí)以下向量規(guī)模的毫秒級(jí)檢索對(duì)Demo項(xiàng)目完全夠用但留了擴(kuò)展空間二是支持元數(shù)據(jù)過(guò)濾查詢(xún)時(shí)可以先過(guò)濾再檢索顯著減少向量計(jì)算的候選集三是運(yùn)維成本要低單機(jī)部署就能跑不引入分布式依賴(lài)。向量索引類(lèi)型我選了HNSWHierarchical Navigable Small World層級(jí)可導(dǎo)航小世界圖參數(shù)設(shè)置了M16和efConstruction100這是兼顧召回率和索引構(gòu)建速度的常見(jiàn)組合。M越大連接數(shù)越多索引質(zhì)量越高但吞內(nèi)存efConstruction越大構(gòu)建越慢但圖質(zhì)量更好。對(duì)金融場(chǎng)景這種幾萬(wàn)到幾十萬(wàn)級(jí)別的切片量這個(gè)配置足夠。混合檢索的具體比例也踩過(guò)坑。最初我讓每條通道各返回50條再合并去重算下來(lái)向量召回的命中率占比高得驚人BM25的貢獻(xiàn)好像微乎其微。但抽樣看了查詢(xún)結(jié)果后發(fā)現(xiàn)問(wèn)題BM25撈出來(lái)的結(jié)果往往是“精確包含股票代碼”卻“語(yǔ)義不那么貼合”的片段——比如用戶(hù)問(wèn)“寧德時(shí)代2021年報(bào)”BM25對(duì)“寧德時(shí)代”這個(gè)實(shí)體的匹配特別敏感能鎖定包含這個(gè)名字的核心段落而向量召回給出的結(jié)果是“相關(guān)公司”“同行企業(yè)”雖然語(yǔ)義上“相關(guān)”但精確度差遠(yuǎn)了。所以我在合并策略里對(duì)BM25的Top K結(jié)果做了加權(quán)讓關(guān)鍵詞命中的片段有更多進(jìn)入重排階段的機(jī)會(huì)。3.4 重排模型與生成參數(shù)的經(jīng)驗(yàn)配置重排模型我選了一個(gè)輕量級(jí)的cross-encoder。它的思路是召回模型是給“問(wèn)題-候選文檔”分別做embedding在高維空間里比相似度速度快但精度有限重排模型則把“問(wèn)題-候選文檔”拼成一對(duì)整體丟進(jìn)模型里做一個(gè)二分類(lèi)式打分相似度判斷更加精細(xì)但計(jì)算量大得多只能對(duì)召回后的Top N再做精排。在Demo中召回的Top 50條經(jīng)過(guò)重排后只保留Top 10這10條作為上下文給生成模型。我實(shí)測(cè)過(guò)一個(gè)關(guān)鍵閾值如果重排后的Top結(jié)果得分過(guò)低比如低于0.35這個(gè)分?jǐn)?shù)受模型和域內(nèi)數(shù)據(jù)影響最好自行標(biāo)定說(shuō)明文檔庫(kù)里根本沒(méi)有可靠答案。這種情況寧可讓模型回答說(shuō)“未檢索到相關(guān)信息”也別讓它硬編。這是金融場(chǎng)景的鐵律——寧缺毋濫。生成參數(shù)上溫度temperature設(shè)得很低0到0.3之間。金融問(wèn)答不需要?jiǎng)?chuàng)造性發(fā)散需要的是穩(wěn)定復(fù)現(xiàn)事實(shí)。top_p設(shè)為0.9左右主要為了做一定程度的隨機(jī)采樣防止固定句式卡殼。另外一個(gè)關(guān)鍵點(diǎn)是max_tokens因?yàn)榇鸢缚赡馨瑪?shù)據(jù)表格和引用列表太短的生成上限會(huì)讓答案被截?cái)?。我設(shè)置了動(dòng)態(tài)上限根據(jù)重排結(jié)果里上下文的總長(zhǎng)度按最長(zhǎng)抽象模式預(yù)留生成空間防止輸出被生硬截?cái)唷?. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)4.1 數(shù)據(jù)準(zhǔn)備自制金融語(yǔ)料集與預(yù)處理流程這個(gè)Demo的語(yǔ)料我沒(méi)有直接拿網(wǎng)上的公開(kāi)財(cái)報(bào)下載那樣版權(quán)和格式都不可控。我整理了一份自制的模擬金融語(yǔ)料集包含了幾類(lèi)典型的金融文檔模擬上市公司年報(bào)節(jié)選、模擬行業(yè)研究報(bào)告片段、模擬基金合同條款、模擬監(jiān)管新規(guī)解讀文章。每份文檔控制在幾頁(yè)到幾十頁(yè)不等目的不是做大數(shù)據(jù)量壓測(cè)而是覆蓋足夠多的金融文本樣式讓鏈路里的每個(gè)環(huán)節(jié)都有代表性測(cè)試樣本。預(yù)處理流程順一遍統(tǒng)一文件命名規(guī)范文件名中包含公司代碼、年份、文檔類(lèi)型例如[SIM2022]某模擬公司_2022年年度報(bào)告.pdf。轉(zhuǎn)PDF時(shí)統(tǒng)一頁(yè)面尺寸和字體嵌入避免解析階段因字體缺失導(dǎo)致文字亂碼。解析入庫(kù)前先人工過(guò)一遍剔除掃描件、圖片頁(yè)、純廣告頁(yè)這類(lèi)無(wú)法直接解析的噪聲。生成一份JSON格式的元數(shù)據(jù)清單記錄每個(gè)文件的頁(yè)數(shù)、章節(jié)數(shù)、文件哈希方便索引構(gòu)建時(shí)快速做增量更新。這個(gè)數(shù)據(jù)準(zhǔn)備環(huán)節(jié)看著瑣碎但占了整個(gè)項(xiàng)目開(kāi)發(fā)周期的大頭得有心理預(yù)期。文檔解析的失敗往往不是解析器不行而是源頭文件質(zhì)量太差。4.2 索引構(gòu)建流程解析、切片、向量化、寫(xiě)入索引構(gòu)建是一個(gè)離線(xiàn)的批處理腳本我用Python編排整個(gè)流程第一階段文檔解析。對(duì)PDF調(diào)用表格識(shí)別工具和文本抽取工具輸出結(jié)構(gòu)化的Markdown文本。對(duì)Word文檔則用公共庫(kù)做HTML格式中轉(zhuǎn)保留標(biāo)題和表格。這個(gè)階段我會(huì)記錄每篇文檔的解析結(jié)果日志包括頁(yè)數(shù)、字符數(shù)、表格數(shù)方便事后追蹤哪篇文檔解析失敗。第二階段切片。寫(xiě)了一個(gè)切片器輸入過(guò)濾掉空段落和過(guò)短段落少于30個(gè)字符的段落基本是頁(yè)面裝飾然后按3.2節(jié)的分層策略做語(yǔ)義分段和重疊窗口切片。每個(gè)切片輸出成一個(gè)字典{doc_id: ..., chunk_id: ..., content: ..., metadata: {page: 3, section: 管理層討論}}。第三階段向量化。加載Embedding模型把切片的內(nèi)容拼接上標(biāo)題路徑比如“[某模擬公司2022年報(bào)管理層討論與分析流動(dòng)比率] 流動(dòng)比率較上年同期上升...”這樣向量模型能看到切片所在的純語(yǔ)義位置。批量向量化注意用GPU或者批量接口加速CPU跑幾百條切片也能忍但上千條就開(kāi)始明顯拖時(shí)間了。第四階段寫(xiě)入向量數(shù)據(jù)庫(kù)。每條向量記錄帶上doc_id、chunk_id、content以及元數(shù)據(jù)建立HNSW索引。同時(shí)把這批切片寫(xiě)入BM25索引庫(kù)用doc_id字段關(guān)聯(lián)兩邊保證混合檢索時(shí)能按同一個(gè)文檔ID去重合并。4.3 查詢(xún)鏈路實(shí)戰(zhàn)一次真實(shí)問(wèn)金融細(xì)節(jié)全流程選一個(gè)典型的查詢(xún)來(lái)走一遍全流程。假設(shè)用戶(hù)提問(wèn)“某模擬公司2022年的流動(dòng)比率是多少相比上年有何變化”第一步查詢(xún)預(yù)處理。把問(wèn)題交給一個(gè)輕量級(jí)實(shí)體識(shí)別模塊嘗試抽取“某模擬公司”“2022年”“流動(dòng)比率”這三個(gè)關(guān)鍵實(shí)體。實(shí)體抽取失敗也沒(méi)關(guān)系流程會(huì)退化到純向量檢索但抽取成功可以幫助后續(xù)做元數(shù)據(jù)過(guò)濾比如限定文檔集為“某模擬公司2022年報(bào)”。第二步混合召回。問(wèn)題文本向量化向量數(shù)據(jù)庫(kù)做相似度檢索返回Top 50。同一時(shí)間BM25索引做關(guān)鍵詞檢索把“流動(dòng)比率”“2022”等高權(quán)重詞命中度高的片段撈出來(lái)返回Top 30。兩路合并按文檔ID去重保留Top 60候選。第三步重排。60條候選拼接進(jìn)重排模型打分按得分降序取前10。如果Top 1得分低于0.35直接讓生成模塊返回“未找到可靠答案”不硬答。第四步生成。把Top 10片段作為上下文拼進(jìn)一個(gè)金融問(wèn)答提示詞模板。模板里明確要求“請(qǐng)根據(jù)以下文檔內(nèi)容回答問(wèn)題。若問(wèn)題在文檔中無(wú)明確答案請(qǐng)直接說(shuō)明未找到相關(guān)信息不要編造。回答末請(qǐng)列出引用的原文片段編號(hào)?!睂?shí)際生成的結(jié)果是某模擬公司2022年年度報(bào)告披露截至2022年12月31日 公司流動(dòng)比率為1.85較上年同期的1.52上升0.33 主要原因?yàn)楣井?dāng)年經(jīng)營(yíng)活動(dòng)現(xiàn)金流改善及短期借款減少。 引用文檔[SIM2022]第12頁(yè)“管理層討論與分析”第3段引用溯源準(zhǔn)確指向了切片元數(shù)據(jù)分析師拿著這個(gè)結(jié)果可以快速翻到原報(bào)告對(duì)應(yīng)位置做人工確認(rèn)這是整個(gè)鏈路里最有價(jià)值的一環(huán)。4.4 全套代碼目錄結(jié)構(gòu)與關(guān)鍵技術(shù)配置快照項(xiàng)目代碼結(jié)構(gòu)大概長(zhǎng)這樣financial_rag_demo/ ├── configs/ │ ├── index_config.yaml # 索引構(gòu)建參數(shù) │ ├── retrieval_config.yaml # 召回參數(shù) │ └── generation_config.yaml # 生成參數(shù) ├── docs/ # 測(cè)試語(yǔ)料markdown / PDF ├── pipelines/ │ ├── build_index.py # 離線(xiàn)索引構(gòu)建入口 │ ├── query_pipeline.py # 在線(xiàn)查詢(xún)?nèi)肟?│ └── eval_pipeline.py # 評(píng)測(cè)腳本 ├── services/ │ ├── doc_parser.py # 文檔解析模塊 │ ├── chunker.py # 切片器 │ ├── embedder.py # 向量化服務(wù) │ ├── retriever.py # 混合檢索 │ └── reranker.py # 重排服務(wù) ├── prompts/ │ └── finance_qa.txt # 金融問(wèn)答提示詞模板 └── requirements.txt一個(gè)比較核心的配置片段如下# index_config.yaml chunk: target_length: 700 overlap_chars: 100 min_length: 100 max_length: 1000 embedding: model_name: BAAI/bge-large-zh-v1.5 dim: 1024 batch_size: 32 vector_db: index_type: HNSW m: 16 ef_construction: 100 bm25: k1: 1.5 b: 0.75注意bge-large-zh-v1.5的輸出維度是1024不是768直接用原生維度即可。BM25的k1和b參數(shù)是倒排索引的標(biāo)準(zhǔn)配置控制詞頻飽和度和文檔長(zhǎng)度歸一化的力度對(duì)長(zhǎng)短不一的金融文檔b0.75是個(gè)比較穩(wěn)妥的起點(diǎn)。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 召回了相似片段但答案還是錯(cuò)語(yǔ)義相關(guān)不等于事實(shí)正確RAG上線(xiàn)后最常見(jiàn)的問(wèn)題就是檢索出來(lái)的片段看著高度相關(guān)模型卻給出了錯(cuò)誤答案。排查思路不能停在“換個(gè)更好的Embedding模型”上要先定位問(wèn)題到底出在哪一級(jí)。我第一次遇到這類(lèi)問(wèn)題時(shí)先打印了重排后的Top 5片段原文對(duì)比正確答案后發(fā)現(xiàn)Top 1片段確實(shí)提到了“流動(dòng)比率”但說(shuō)的是一家同行公司的數(shù)據(jù)文檔里還同時(shí)出現(xiàn)了“某某公司”和“同行公司”向量模型把“流動(dòng)比率”這個(gè)語(yǔ)義詞的權(quán)重拉得極高忽略了文檔主體是誰(shuí)。問(wèn)題不在檢索在于切片內(nèi)容里文檔主體信息表達(dá)不夠強(qiáng)。解決辦法有兩層。第一層切片時(shí)把文檔標(biāo)題、公司名、股票代碼拼進(jìn)切片頭部讓向量模型天然感知到主體是誰(shuí)。第二層在重排階段加入硬性過(guò)濾條件如果抽取到了問(wèn)題里的公司實(shí)體就要求候選片段內(nèi)必須包含該實(shí)體否則直接降權(quán)。這個(gè)過(guò)濾條件可以在檢索階段做也可以用重排模型做規(guī)則對(duì)齊。5.2 引用編號(hào)與答案對(duì)不上生成模型自行編造引用這是RAG系統(tǒng)里最危險(xiǎn)的故障之一。模型生成的答案內(nèi)容是從上下文里來(lái)的但引用的編號(hào)卻是自己瞎編的明明引用了第3號(hào)片段內(nèi)容卻對(duì)應(yīng)第7號(hào)片段。分析師如果直接拿這個(gè)去復(fù)核文檔會(huì)浪費(fèi)大量時(shí)間。我做的第一層防護(hù)是提示詞強(qiáng)約束要求模型必須嚴(yán)格按照[引用編號(hào)]格式標(biāo)注來(lái)源并且引用編號(hào)只能從上文給的片段序號(hào)里選。但這種約束對(duì)某些大模型只是“盡力而為”第二層防護(hù)是寫(xiě)了一個(gè)引用校驗(yàn)?zāi)_本生成回答后把答案里的每個(gè)引用編號(hào)和對(duì)應(yīng)片段正文抓出來(lái)做一個(gè)簡(jiǎn)單的關(guān)鍵詞交驗(yàn)——提取引用片段和答案句的公共數(shù)字、年份、專(zhuān)有名詞如果公共部分太少就標(biāo)記為可疑引用丟回去重新生成或者從回答里刪掉這句引用。這個(gè)校驗(yàn)?zāi)_本不要求語(yǔ)義精確但作為一層兜底能攔截至少七八成明顯的引用錯(cuò)位問(wèn)題。5.3 文數(shù)字相互矛盾多來(lái)源沖突檢測(cè)與置信度標(biāo)注一份年報(bào)里“管理層討論”部分說(shuō)“營(yíng)業(yè)收入同比增長(zhǎng)15%”另一處“財(cái)務(wù)數(shù)據(jù)摘要”卻寫(xiě)著“同比增長(zhǎng)14.8%”。原因是不同章節(jié)對(duì)同一指標(biāo)的統(tǒng)計(jì)口徑有所差異比如是否含稅、是否包含非經(jīng)常性損益。這屬于金融文本的天然沖突。RAG如果同時(shí)檢索到這兩個(gè)片段讓模型自由選擇它極有可能混合兩個(gè)數(shù)字編出一個(gè)13.9%之類(lèi)的中間值。對(duì)這種多來(lái)源沖突我不信任讓模型自己判斷而是做了“來(lái)源沖突檢測(cè)”預(yù)處理在召回階段如果Top K片段里出現(xiàn)了多個(gè)不同數(shù)字對(duì)應(yīng)同一指標(biāo)的情況就觸發(fā)沖突處理邏輯——把這些候選片段的關(guān)鍵數(shù)值差異高亮出來(lái)提示用戶(hù)“檢測(cè)到多來(lái)源數(shù)據(jù)不一致請(qǐng)人工核對(duì)原文檔”而不是強(qiáng)行生成一個(gè)看似光滑的答案。這個(gè)設(shè)計(jì)犧牲了一部分“智能感”但殺了“人工復(fù)核成本”這個(gè)痛點(diǎn)。在金融場(chǎng)景里寧可讓系統(tǒng)暴露不確定性和矛盾也不能讓它給出看似確定實(shí)則虛假的答案。5.4 長(zhǎng)文檔檢索不到關(guān)鍵信息分塊召回與多查詢(xún)展開(kāi)長(zhǎng)文檔切塊后有一個(gè)經(jīng)典困境關(guān)鍵信息藏在文檔中間當(dāng)前問(wèn)題的表述方式又和原文差距很大。比如問(wèn)題說(shuō)“公司的短期償債能力如何”原文里卻只出現(xiàn)了“流動(dòng)比率”“速動(dòng)比率”“經(jīng)營(yíng)性現(xiàn)金流”沒(méi)有一個(gè)字提到“短期償債能力”。向量模型對(duì)這種抽象和具體的映射哪怕語(yǔ)義再先進(jìn)也可能失手。解決思路是“多查詢(xún)展開(kāi)”。把一個(gè)問(wèn)題改寫(xiě)成多個(gè)不同表述的子查詢(xún)比如原問(wèn)題改成三條子查詢(xún)“流動(dòng)比率和速動(dòng)比率情況如何”“短期償債能力相關(guān)指標(biāo)”“公司是否存在償債風(fēng)險(xiǎn)”。三條子查詢(xún)并行做混合檢索再把結(jié)果合并后統(tǒng)一重排。查全率明顯提升代價(jià)是查詢(xún)延遲增加了300到600毫秒。對(duì)金融問(wèn)答來(lái)說(shuō)這個(gè)延遲換召回率是非常劃算的。5.5 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方法推薦調(diào)整答案內(nèi)容正確但數(shù)值錯(cuò)誤多切片數(shù)值沖突模型取平均或隨機(jī)打印Top 5片段交叉驗(yàn)證數(shù)值配置沖突檢測(cè)邏輯不生成混合數(shù)值檢索結(jié)果高度相似但不對(duì)題切片主體實(shí)體信息弱打印切片頭部檢查是否含公司名/章節(jié)切片前拼接文檔標(biāo)題與實(shí)體信息回答引用編號(hào)無(wú)法對(duì)應(yīng)原文大模型編造引用校驗(yàn)?zāi)_本檢查引用編號(hào)-正文關(guān)鍵詞重合度提示詞強(qiáng)約束引用校驗(yàn)兜底問(wèn)題涉及長(zhǎng)文檔跨章節(jié)信息單塊切片無(wú)法承載完整前提查看召回是否分散在多個(gè)不相關(guān)章節(jié)多查詢(xún)展開(kāi)分塊召回召回Top 1得分高但答案錯(cuò)重排模型對(duì)長(zhǎng)文本尾部關(guān)注弱手動(dòng)檢查得分與人工標(biāo)注是否一致調(diào)高切片重疊降低切片長(zhǎng)度相似問(wèn)法結(jié)果差異大向量模型對(duì)語(yǔ)義改寫(xiě)敏感對(duì)同義問(wèn)題跑多條子查詢(xún)對(duì)比結(jié)果擴(kuò)充同義改寫(xiě)訓(xùn)練數(shù)據(jù)或用更健壯模型6. 評(píng)測(cè)方法與效果分析6.1 金融問(wèn)答評(píng)測(cè)集的設(shè)計(jì)原則做一個(gè)RAG系統(tǒng)評(píng)測(cè)集的質(zhì)量決定了優(yōu)化的方向。金融問(wèn)答的評(píng)測(cè)不能只看“回沒(méi)回答對(duì)”標(biāo)準(zhǔn)答案必須包含三個(gè)維度召回片段是否準(zhǔn)確、生成答案是否完整、引用溯源是否正確。我建了一套小型評(píng)測(cè)集大概200個(gè)問(wèn)題分四類(lèi)數(shù)值抽取類(lèi)直接回答問(wèn)題里的具體數(shù)字、對(duì)比分析類(lèi)兩年數(shù)據(jù)的比較、條款查詢(xún)類(lèi)合同或監(jiān)管條款的定位、推理總結(jié)類(lèi)需要綜合多個(gè)片段整合答案。每類(lèi)問(wèn)題的答案由人工標(biāo)注給出標(biāo)準(zhǔn)答案和對(duì)應(yīng)的參考原文片段編號(hào)。特別重要的是評(píng)測(cè)集要包含“無(wú)答案問(wèn)題”。金融文檔庫(kù)做不到100%覆蓋用戶(hù)問(wèn)題總會(huì)有文檔里沒(méi)有答案的情況。評(píng)測(cè)系統(tǒng)應(yīng)該對(duì)這類(lèi)問(wèn)題回答“未找到”而不是強(qiáng)行編造。無(wú)答案樣例的引入能逼著系統(tǒng)學(xué)會(huì)“寧可說(shuō)不知道”這對(duì)金融場(chǎng)景尤其關(guān)鍵。6.2 召回率、MRR與Faithfulness的實(shí)測(cè)結(jié)果我的評(píng)測(cè)腳本會(huì)計(jì)算三類(lèi)指標(biāo)RecallK正確答案對(duì)應(yīng)的片段是否出現(xiàn)在前K個(gè)檢索結(jié)果里K取5和10。MRRMean Reciprocal Rank第一個(gè)正確答案片段出現(xiàn)位置倒數(shù)的平均值衡量正確片段能排多靠前。Faithfulness忠實(shí)度生成答案里的關(guān)鍵事實(shí)點(diǎn)是否都能在引用片段里找到依據(jù)。Faithfulness的自動(dòng)化評(píng)測(cè)比較復(fù)雜我的實(shí)現(xiàn)是讓生成答案里的數(shù)值和專(zhuān)有名詞與各引用片段做交叉校驗(yàn)凡是出現(xiàn)無(wú)法在任意引用片段中匹配到的數(shù)值就判定為不忠實(shí)。實(shí)測(cè)結(jié)果大致如下指標(biāo)數(shù)值說(shuō)明Recall50.8787%的問(wèn)題正確答案片段排在前5Recall100.93加上BM25召回后覆蓋率更高M(jìn)RR0.81正確片段平均排名靠前Faithfulness0.89生成答案的關(guān)鍵事實(shí)點(diǎn)基本有出處這個(gè)表現(xiàn)在Demo規(guī)模下已經(jīng)比較理想但要知道測(cè)試集只有200個(gè)問(wèn)題領(lǐng)域又聚焦實(shí)網(wǎng)環(huán)境有更多噪聲指標(biāo)表現(xiàn)會(huì)回落。6.3 端到端延遲數(shù)據(jù)與可接受的優(yōu)化方向端到端延遲是金融RAG落地的另一條生命線(xiàn)。實(shí)測(cè)在單張消費(fèi)級(jí)顯卡上跑Embedding和重排完整查詢(xún)鏈路的延遲如下階段耗時(shí)毫秒實(shí)體識(shí)別與查詢(xún)改寫(xiě)80混合召回120重排Top 50→10450生成調(diào)用1200-2500其他開(kāi)銷(xiāo)100整體在1.8秒到3.3秒之間。金融問(wèn)答場(chǎng)景里這個(gè)延遲是可以接受的分析師操作時(shí)沒(méi)有明顯等待焦慮。如果后續(xù)要上生產(chǎn)環(huán)境重點(diǎn)優(yōu)化的是重排階段把這部分計(jì)算并行化或者改用更輕量的模型能砍掉至少50%耗時(shí)。生成階段受模型推理速度制約除非換硬件或換成流式輸出否則優(yōu)化空間有限。7. 部署落地的真實(shí)體驗(yàn)與幾個(gè)改進(jìn)方向整套系統(tǒng)部署起來(lái)不算復(fù)雜。我用Docker編排了三個(gè)服務(wù)向量數(shù)據(jù)庫(kù)容器、重排模型服務(wù)容器、主查詢(xún)服務(wù)容器。Embedding模型和生成模型分別加載運(yùn)行期內(nèi)存占用大約8GB左右消費(fèi)級(jí)16GB內(nèi)存機(jī)器可以輕松支撐Demo運(yùn)行。部署過(guò)程中印象最深的坑是初始化加載模型時(shí)容易把顯存和內(nèi)存同時(shí)吃滿(mǎn)重排模型的batch size沒(méi)調(diào)好直接OOM。解決辦法是把重排的batch size調(diào)成1犧牲一些吞吐但穩(wěn)定度大幅提升。另一個(gè)體驗(yàn)是金融領(lǐng)域的反饋閉環(huán)特別重要。分析師使用后給出的反饋往往不是“準(zhǔn)確度怎么樣”而是“這個(gè)數(shù)據(jù)的單位對(duì)不上”“這個(gè)是舊版公告的結(jié)論”“這兩個(gè)條款有沖突你別替我合并”。這些反饋都是評(píng)測(cè)集里很難構(gòu)造的需要真實(shí)業(yè)務(wù)場(chǎng)景的輸入才能暴露。所幸RAG架構(gòu)天然支持局部修復(fù)——發(fā)現(xiàn)問(wèn)題改切片策略、調(diào)檢索規(guī)則、重跑索引幾個(gè)小時(shí)內(nèi)就能迭代一版。這比重新訓(xùn)練模型要快太多了。后續(xù)如果有余力我想在三個(gè)方向上繼續(xù)深入一是做圖表和數(shù)字的可視化回溯讓分析師能直接在頁(yè)面上看到答案對(duì)應(yīng)的原文高亮而不是只給一個(gè)文本引用編號(hào)二是在召回階段引入知識(shí)圖譜把公司、行業(yè)、指標(biāo)之間的關(guān)系顯式建模減少純語(yǔ)義檢索的歧義三是做增量索引的自動(dòng)化新文檔進(jìn)來(lái)自動(dòng)觸發(fā)解析、切片、入庫(kù)把整個(gè)索引構(gòu)建流程從離線(xiàn)批處理升級(jí)成持續(xù)數(shù)據(jù)管道。最后說(shuō)一個(gè)我用到現(xiàn)在最深的體會(huì)金融RAG的價(jià)值評(píng)估從來(lái)不是“這個(gè)AI能不能聊天”而是“它能不能把找數(shù)據(jù)的時(shí)間從半小時(shí)壓縮到三十秒同時(shí)讓人敢放心用它的答案”。所有的技術(shù)選型、參數(shù)調(diào)優(yōu)、架構(gòu)設(shè)計(jì)如果能回到這個(gè)問(wèn)題上來(lái)審視就不會(huì)跑偏。上面的每個(gè)細(xì)節(jié)都是我一點(diǎn)點(diǎn)用測(cè)試集和真實(shí)場(chǎng)景逼出來(lái)的也希望這個(gè)Demo的記錄能幫你少走幾條彎路。