RAG系統返回了完美的文本塊,提示詞寫得很漂亮,但LLM還是在產生幻覺;文檔加得越多,回復質量反而越差。這些問題問題不出在提示詞上,而是出在上下文上。
提示工程告訴模型怎么說話;context engineering 控制模型說話時看到什么。以下是把生產系統和Demo區分開的6種上下文工程技術。
![]()
什么是 Context Engineering
Context engineering 是在運行時決定AI模型看到什么信息、何時看到、以何種結構看到的工程實踐。
提示工程關注的是如何提問,context engineering 關注的是提供什么。
實際系統中,大語言模型出錯往往不是因為理解不了指令,而是因為看到了太多不相關的信息、相互矛盾的數據,或者缺失了關鍵事實。Context engineering 把上下文當作一條動態管道來處理,而非一段靜態提示詞:選對文檔而不是全量灌入;將長文檔壓縮為面向任務的摘要;在檢索之前重新表述模糊的用戶查詢;跨會話注入記憶和用戶狀態;用實時工具和數據錨定答案;組織所有輸入,讓模型知道什么最重要。
一句話概括:context engineering 是在生產環境中控制模型注意力的手段。
上下文工程做得好,小模型也能有不錯的表現;做得差,最好的模型照樣幻覺。
選擇性檢索:別再把什么都往里塞
![]()
如果我們把50個文檔全部塞進上下文指望模型自己找到需要的內容。即便上下文窗口足夠大,模型的注意力分布仍然不均勻,它會重點關注開頭和結尾的 Token,中間部分被忽略。這就是所謂的"lost in the middle"效應。
正確的做法是評分、重排、裁剪,只讓相關且不重復的片段進入上下文窗口。
以下三步逐層過濾。
相關性重排:初始搜索基于向量相似度或關鍵詞匹配返回前50個結果,但相似不等于相關。交叉編碼器會把查詢和每個文檔放在一起聯合閱讀,重新排序。速度慢一些準確度高得多,重排之后只保留前5個。
冗余消除:同一個概念在文檔庫里出現在多個地方是常態,比如營銷材料、技術規格、FAQ都可能提到同一功能。用 Embedding 對相似文本塊做聚類,余弦相似度超過0.9的基本就是重復內容刪掉一個即可。模型不需要看同一個事實10遍。
任務感知過濾:利用元數據做篩選。每個文檔都應打上標簽:文檔類型、最后更新日期、產品版本、地區、部門。查詢進來時按相關維度過濾。
舉個實際例子:查詢是"總結最新的退款政策變更"。
過濾之前向量搜索返回50個關于退款的文本塊,有些來自2018年舊政策,有些是其他公司的文檔,有些是從未面向客戶的內部備忘錄。LLM看到了14天和30天窗口期的矛盾說法、不同的排除條款、相互沖突的流程,試圖綜合所有信息后幻覺出了一條根本不存在的政策。
過濾之后加上 region='CN' 和 updated_at >= 2025-01-01 兩個條件,立刻排除40個文本塊。剩余10個用交叉編碼器重排,保留前5個再檢查近似重復(余弦相似度 > 0.9)。最終送進上下文的只有3個高相關、無冗余的文本塊。
LLM現在看到的是清晰的政策聲明、具體的時間線和例外清單——全部最新,全部對齊。沒有幻覺,沒有混淆。
效果:提示更短,回答更清晰,沒有矛盾。實驗數據顯示,移除噪聲上下文后準確率提高15–30%,Token消耗降低20–40%。但真正的收益在于可追溯性——確切知道模型看到了什么上下文,才能調試失敗;50個文本塊全灌進去,只能靠運氣。
實現上可以從簡單處著手:給所有文檔加 last_updated 時間戳,按日期過濾,僅憑這一步就能消除大部分噪聲。接著加重排,再加去重,根據實際效果逐步疊加復雜度。
上下文壓縮:讓每個 Token 都有價值
![]()
長篇原始文檔容易撐破上下文限制,同時稀釋注意力。多個案例研究表明,經過壓縮可以在保持甚至提升準確率的同時砍掉50–75%的 Token。
核心思路是在把長文檔放進上下文之前,將其壓縮成面向當前任務的密集摘要:不是通用摘要,而是針對當前查詢定制的摘要。
三種壓縮策略各有側重。
帶約束的LLM摘要:不要只說"總結這個文檔",而是說"總結這個文檔,只保留2025年1月之后關于定價變更的事實"。約束條件指明了保留什么、丟棄什么。每個檢索到的文檔根據查詢生成各自的約束。產出從3000個 Token 縮減到5–10個要點。
句子級評分:用較小的模型(如BERT變體)為每個句子計算與查詢的相關性分數,按分數排序后只保留前20%。這種方法叫Context-Preserving Compression,速度快,效果好,自動留下最相關的信息。
層次化摘要:適合非常長的文檔。先按章節分塊每個章節獨立生成摘要,再把這些摘要歸納成一個元摘要。最終形成三級結構,完整文檔 → 章節摘要 → 最終摘要。根據上下文預算選用合適的層級。
看一個實際例子。查詢是"比較API文檔中Plan A與Plan B的速率限制"。
API文檔共30頁,涵蓋認證、速率限制、錯誤代碼、Webhook、分頁、SDK和變更日志,其中只有2頁涉及速率限制。檢索管道取回3個相關章節(共30頁),每章10頁。LLM摘要器收到的指令是:"僅提取Plan A和Plan B的速率限制和配額,包括具體數字,忽略認證、示例和其他功能。"
第一個章節的摘要(從10頁壓縮到100個 Token):"Plan A:1000次請求/小時,10,000次/天。Plan B:5000次請求/小時,50,000次/天。兩個計劃均允許1分鐘內20%的突發流量。"
第二個章節的摘要:"速率限制錯誤返回HTTP 429。Retry-After頭部指示等待時間。速率限制在UTC午夜重置。"
第三個章節的摘要:"企業版計劃可定制速率限制。請聯系銷售團隊。"
三份摘要合計500個 Token,加上查詢一起送入最終生成。模型看到的恰好是它需要的內容,不必在認證流程或SDK示例里翻找。
模型拿到的是聚焦后的信息,而不是期望它從30頁文檔里正確提取相關部分。
代價是多了一步延遲,因為每個文檔額外做一次LLM調用,3個文檔就是3次。但最終生成調用中節省的 Token 往往更值錢。具體值不值得要看場景,文檔超過2000個 Token 時,壓縮的收益大于開銷。
層次化布局:結構本身就在傳達重要性
![]()
不要把所有內容混成一面文字墻。LLM對上下文前部和后部的注意力分配不同,結構化的分區能幫助它區分指令、數據和示例。
想想閱讀研究論文的過程:摘要是總結,引言提供背景,方法部分有技術細節,討論部分解讀結果。這種結構讓信息提取變得高效,LLM從同樣的結構中受益。因此需要在上下文內部設計一套固定的、分區清晰的格式。
以下是一個經過驗證的布局:
[System Rules]
You are a precise financial research assistant.
Answer only from provided context.
If information is missing, say "I don't have that information."
Never make assumptions about numerical data.
[Task]
Goal: Answer user question using context below.
Output format: Start with direct answer, then provide supporting details.
[User Profile / Memory]
- Risk tolerance: Low
- Investment horizon: 5-10 years
- Region: India
- Previous sessions: Asked about HDFC Bank 3 times, showed interest in banking sector
- Preferences: Conservative investments, dividend-paying stocks
[Retrieved Context]
DOC 1: HDFC Bank Q4 2024 earnings report
- Revenue: ?45,000 crores (up 15% YoY)
- Net profit: ?12,000 crores (up 18% YoY)
- NPA ratio: 1.2% (improved from 1.5%)
DOC 2: Competitor analysis Q4 2024
- ICICI Bank revenue growth: 12% YoY
- SBI profit growth: 10% YoY
- HDFC Bank leading in digital banking adoption
[Tool Outputs]
- live_price("HDFCBANK"): ?1,842.50 (updated 2 minutes ago)
- news_summary("HDFCBANK"): "Announced dividend of ?19 per share for FY2024. Ex-dividend date March 15, 2025."
- sector_analysis("Banking"): "Banking sector up 8% this month due to positive earnings"
[Question]
User: What's the latest on HDFC Bank?
系統規則排在最前面,模型在任何其他內容之前先看到它們,用來劃定行為邊界。任務說明緊隨其后,讓模型明確目標。用戶檔案提供個性化信息——模型知道該用戶偏好保守投資,之前多次問過HDFC,于是在組織答案時會相應調整側重點。檢索到的文檔被標記為源材料,模型將其視為引用依據。工具輸出標記為實時數據,意味著當前且權威,不是猜測。問題放在最后,模型在看到要回答什么之前已經掌握了所有上下文。
分區帶來的好處很直接:每種信息的角色一目了然,矛盾指令減少,各部分可以獨立替換而不破壞整體。在多智能體系統中這一點尤為關鍵,不同智能體需要不同的上下文布局。
為什么比非結構化上下文更好
可以自己做個測試,把5000 Token 的指令、數據、示例和問題全部混在一起扔給LLM,再用同樣的信息以結構化分區呈現。對比準確率,結構化版本在多數領域勝出10–20%。
原因在于注意力模式:LLM在訓練中學到了開頭和結尾的文本更重要。把關鍵指令放在開頭、問題放在結尾,是在順應模型的注意力偏好而非與之對抗。
動態查詢重構:修復模糊問題
![]()
用戶提出的問題往往是模糊的:缺少關鍵詞、實體、時間范圍。直接拿原始問題去檢索效果不好,應該先讓LLM重寫或擴展查詢。
已有研究證實,在檢索前生成一條優化過的搜索查詢能帶來可觀的準確率提升。
這看起來有點混亂,因為多加一步似乎應該讓事情變復雜,但是其實邏輯很簡單:模糊的查詢返回模糊的結果,精確的查詢返回精確的結果。
三種可行的模式。
澄清優先(適用于智能體場景),與其猜用戶什么意思不如直接問。智能體回復:"要比較業績表現,需要明確幾個信息——哪個時間段?包含哪些競爭對手?最關注哪些指標(收入、利潤還是市場份額)?"用戶給出具體條件后,檢索隨之變得精確。這種策略只適用于多輪對話的智能體,單次問答系統用不上。但在智能體場景下效果很好——用戶并不介意回答兩三個澄清問題,如果換來的是更準確的答案。
HyDE(Hypothetical Document Embeddings)用戶問"產品最新的改進有哪些",不直接用這個問題去搜索,而是讓LLM先生成一段假答案:"最新的產品改進包括重新設計的儀表板、40%的加載速度提升、新增的協作功能和增強的移動端應用。"把這段假答案做 Embedding 后用于檢索。為什么有效?因為假答案的措辭和實際文檔的措辭一致。文檔里不會寫"最新的改進是什么",而會寫"重新設計了儀表板""加載時間縮短了40%"。假答案的 Embedding 比原始問題的 Embedding 更貼近真實文檔。
多查詢擴展,對原始查詢生成3–5個改寫版本。"最新產品改進"可以展開為"最近的產品更新""本季度發布的新功能""產品增強變更日志""2.0版本有什么新內容"。每個查詢分別檢索,合并去重。這種方式能覆蓋語義變體和不同的表述習慣。
舉個實際例子。用戶問"上季度的表現與競爭對手相比如何"。
這個問題里缺了太多信息:哪些競爭對手?哪一年的上季度?衡量什么指標——收入、利潤、市場份額還是用戶增長?
LLM將其重寫為"比較2024年第四季度(2024年10月至12月)公司X與競爭對手A、B、C在內部財務報告中的收入增長和利潤率"。注意具體程度——精確的時間段、精確的競爭對手、精確的指標、明確的數據來源。檢索時還會結合記憶中已知的競爭對手名單(層次化布局的用戶檔案部分記錄了該用戶之前多次查詢過競爭對手A和B)。
最終上下文對齊的是用戶的意圖,而非用戶鍵入的字面內容。LLM拿到的是精確數據,不是模糊相關的內容。
實現模式如下:
User query
↓
LLM rewriter: "Expand this into a precise search query.
Add time ranges, entity names, and specific metrics."
↓
Rewritten query
↓
Retrieval
重寫器需要能訪問當前日期、用戶檔案和會話中的先前查詢,從而推斷出"上季度"對應的具體日期,以及"競爭對手"指的是哪幾家公司。
記憶與狀態:保留的是關系,不只是事實
![]()
初學者容易在這里混淆兩個概念。檢索回答的是當前問題;記憶保留的是用戶關系。
檢索是實時的,每次查詢都在全部文檔中搜索相關文本塊,每次查詢都被當作獨立事件。今天問HDFC銀行,明天再問,檢索都是從零開始。
記憶則不同,它記住的是該用戶三次問過HDFC銀行,偏好保守型股票,住在印度,投資期限5–10年。這些信息跨會話持續存在。
正確的做法是給智能體配備持久化記憶,而不是依賴一條不斷增長的歷史字符串。把摘要、偏好和關鍵事實存下來,在每輪對話時重新注入。
三種記憶類型各有分工。
情景記憶,即過去對話的摘要。例如:"上次會話討論了為法律文檔構建RAG系統,用戶關心的是如何處理100多頁的合同,最終決定采用512 Token 塊配合50 Token 重疊的語義分塊策略。"注意,這不是完整的對話記錄,而是一份200 Token 左右的摘要,只保留了關鍵內容。
語義記憶,存儲在向量數據庫中的過去交互記錄。例如:"用戶3周前問過關于HDFC的類似問題,當時問的是季度收益,當前查詢是關于股息公告,兩者都涉及HDFC的財務表現,可以復用之前查詢中關于公司基本面的上下文。"語義記憶的價值在于發現模式:用戶依次問了HDFC的收益、競爭對手、風險因素,可以判斷他正在對HDFC做深度研究,從而主動推送相關信息。
偏好記憶,很少變化的穩定事實。"用戶是初學投資者""偏好TypeScript""風險承受能力低""在醫療保健領域工作""位于孟買時區"。這些事實影響每一次交互——初學者得到更簡潔的解釋,低風險用戶和高風險用戶收到的建議完全不同。
為什么記憶比塞入完整聊天歷史更好?
設想一個50輪的對話,輕松超過20,000個 Token。這些內容既塞不進上下文,也不應該塞進去——大部分與當前問題無關。換一種做法:50輪對話壓縮為5個情景摘要(1000 Token),提取10個穩定偏好(200 Token),檢索與當前問題最相關的3個歷史情景(600 Token),合計1800 Token。信息量沒有減少,聚焦程度高出一個量級。
只壓縮和選取與當前輪次相關的部分,就能避開上下文限制,保持注意力清晰,讓智能體在不依賴更大模型的情況下呈現出個性化效果。
落地時有兩個執行點。每輪對話結束后做一次LLM摘要——"用戶問了什么?提供了什么?用戶表達了哪些偏好或需求?"——將摘要與 Embedding 一起存入向量數據庫。每輪對話開始前,用當前查詢在歷史情景摘要庫中做向量搜索,取回最相關的3條,再從另一張表加載穩定偏好(這些不需要向量搜索,每次都帶上),全部插入層次化布局的 [User Profile / Memory] 區塊。
用編程助手的場景來說明。
某用戶在構建一個React儀表板,10次會話中先后問過狀態管理、API集成、組件組合和測試方面的問題。存入記憶的內容如下:
- 技術棧:React、TypeScript、Redux、Jest
- 編碼風格:偏好函數組件,使用 Hooks,喜歡描述性變量名
- 項目上下文:為醫療保健數據可視化構建儀表板
- 過去的問題:Redux異步操作曾遇到困難,最終用Redux Toolkit解決
- 常見模式:使用自定義 Hooks 發起API調用,偏好Material-UI
當用戶提了新問題"儀表板中如何處理實時更新",智能體在檢索之前已經從記憶中看到了完整背景:React/TypeScript儀表板、Redux、Hooks偏好、醫療保健數據。于是可以直接給出上下文化的回答:"考慮到現有的Redux架構和對Hooks的偏好,建議使用Redux Toolkit的RTK Query配合WebSocket訂閱,與當前模式完全兼容。"
對比一下沒有記憶的智能體——它會先問"用什么框架?狀態管理是什么?數據類型是什么?"用戶已經回答過這些問題10遍了。記憶消除的正是這種重復帶來的挫敗感。
上下文保持聚焦,智能體行為保持一致,用戶感到被理解。
工具感知上下文:把答案錨定在現實中
![]()
通過Model Context Protocol (MCP)等協議配合函數調用,可以讓模型以統一格式看到來自工具、API和數據庫的實時數據。不要依賴靜態的文本知識。這正是 context engineering 的一部分——改變的是運行時有什么信息進入上下文、以何種形式進入。工具輸出作為結構化上下文注入后,幻覺率會大幅下降,答案的時效性也有保障。
純LLM知識有一個根本問題:它是靜態的。訓練數據有截止日期,不知道今天的股價、當前天氣、最新新聞或內部數據庫的狀態。工具通過運行時數據彌補了這個缺口,但集成不當的工具會引入新的問題。
MCP風格的工具注冊。智能體沒有硬編碼的工具集成,而是在運行時發現可用工具。智能體發出請求:"什么工具可以解決當前問題?"MCP服務器返回工具描述、輸入Schema和能力清單,模型據此決定調用什么。這意味著新增工具無需重新部署智能體——加到MCP服務器上即可,智能體自動發現。生產系統正是靠這種機制擴展到數十乃至上百個工具。
結構化的工具返回值。工具不應返回原始字符串,而應返回帶有明確鍵的JSON:price、date、source、confidence。把這些結果作為層次化布局中的獨立區塊插入,標記為權威事實。例如,工具返回 {"price": 1842.50, "currency": "INR", "timestamp": "2025-02-19T14:30:00Z", "source": "NSE", "change": "+2.3%"},模型看到的是結構化數據,知道1842.50是價格而不是文本中隨意出現的一個數字。
帶護欄的回答。對可靠性至關重要。在指令中寫明:"只從 [Tool Outputs] 和 [Retrieved Context] 中回答。如果信息缺失,說'當前數據源中沒有該信息。'絕不編造數字或事實。"一旦價格工具調用失敗,模型會如實說明,而不是自行猜測。
用一個金融助手的場景貫穿整個流程。
查詢是"HDFCBANK的最新股價和今天的新聞"。
智能體通過MCP發現可用工具:get_live_price、get_news、get_historical_data、get_competitors、get_analyst_ratings。根據查詢判斷需要調用 get_live_price 和 get_news,拿到結構化響應:
{
"get_live_price": {
"symbol": "HDFCBANK",
"price": 1842.50,
"currency": "INR",
"timestamp": "2025-02-19T14:30:00Z",
"change": "+2.3%",
"volume": 12500000
},
"get_news": {
"articles": [
{
"headline": "HDFC Bank Announces ?19 Dividend",
"summary": "Board approves dividend of ?19 per share for FY2024",
"date": "2025-02-19",
"source": "Economic Times"
}
]
}
}
這些內容插入層次化布局的 [Tool Outputs] 區塊,末尾附上用戶問題。模型生成的答案是:"HDFC Bank當前交易價格?1,842.50,今日上漲2.3%。該銀行宣布FY2024每股派息?19。"
注意模型引用了來自工具輸出的具體數字,沒有產生幻覺,也沒有把股息金額和股價搞混。
這里的關鍵信息是:提示詞本身很簡單,真正起作用的是工程化后的運行時上下文。模型表現好,是因為它看到了結構化的權威數據,而不是因為有一段完美的提示詞。
實現路徑上建議從3–5個關鍵工具開始,確保它們穩定可靠后再擴展。不要第一天就試圖接入50個工具——每個工具都需要錯誤處理、重試邏輯、超時管理和結果校驗,先把基礎設施為少數工具搭建好。
決策框架
選擇性檢索適用于文檔集合龐大(1000+個文檔)、檢索返回結果過多(20+個文本塊)、上下文接近容量上限(32K Token 附近)、或需要控制成本的場景。
壓縮適用于文檔篇幅長(單個超過5000 Token)、所需信息深埋在文本中、或按 Token 計費且需要成本優化的場景。如果文檔本身已經很短(少于1000 Token),壓縮帶來的開銷不劃算。
層次化布局適用于多智能體系統(不同智能體需要不同的上下文結構)、多種上下文來源并存(文檔、工具、記憶同時出現)、或需要分段調試(結構化分區讓定位故障變得容易)的場景。單輪問答且只有一個來源時,布局多半是多余的。
查詢重構適用于用戶常提出模糊問題(消費類產品中的常見情況)、領域有專業術語但用戶不使用(醫療、法律、金融領域)、或查詢與文檔之間存在詞匯鴻溝(用戶說"退款",文檔寫"退貨授權")的場景。
記憶適用于對話式智能體(聊天產品、編程助手)、用戶跨會話回訪(有登錄體系的B2B產品)、需要個性化(推薦、偏好起決定性作用)、或對話輪數超過20輪導致歷史上下文溢出的場景。
工具感知上下文適用于答案依賴實時數據(股價、天氣、庫存)、構建的是智能體而非純對話機器人(智能體會采取行動,聊天機器人只輸出文本)、準確性取決于信息時效(不能憑空生成數字)、或需要降低幻覺率(工具提供事實依據)的場景。
總結
每種技術都有代價。重排消耗算力,壓縮需要額外的LLM調用,記憶需要存儲空間,工具需要API調用。收益是否覆蓋成本,需要衡量。一個更簡單的管道準確率稍低一點,有時候比一個復雜10倍、成本也高10倍的管道更合適。
https://avoid.overfit.cn/post/7be5e3180f7641c48da7d2e73da76224
by Divy Yadav
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.