:從Prompt到Agent編排的完整指南)
有一次同事拿著一份 LangChain 教程從環(huán)境配置敲到模型初始化屏幕上的 agent 也真的回答了問題。結(jié)果一接到真實項目就卡住加日志報錯換一個模型服務(wù)商連密鑰都識別不了更別提讓 Agent 正確調(diào)用工具。后來排查半天問題不在代碼而在版本和抽象之間的錯配。這個經(jīng)歷很典型。學習 LangChain 最容易被低估的一點是它不是一個“安裝后就能一直用”的普通庫而是一套仍在快速演進的應用編排體系。從最早期的 Chain到現(xiàn)在的 LCEL、LangGraph、MCP 接入概念層一直在變化。如果只是跟著某篇教程敲代碼很容易在換場景后失去方向。LangChain 真正要解決的核心問題不是“怎么調(diào)大模型 API”而是“怎么把大模型應用做成一條可維護、可復用、可觀測、可接入外部工具的流水線”。順著這條主線環(huán)境配置、模型初始化、Middleware 中間件、ReAct、AI Agent、MCP、Skills、Prompt 這些看起來分散的概念其實都屬于同一張地圖上的不同層次。1. 先別急著寫代碼LangChain 解決的是 LLM 應用里的“編排問題”很多人第一次接觸 LangChain會以為它只是把prompt和model拼在一起的封裝庫。這不算錯但只理解到這個程度遠遠不夠。如果只是“輸入一段文本返回一段文本”原生 SDK 甚至 HTTP 請求就夠了根本不需要 LangChain 這種抽象層。真正需要 LangChain 的場景是當你的業(yè)務(wù)不止一步調(diào)用時。比如先判斷用戶意圖再決定是否需要查數(shù)據(jù)庫查完數(shù)據(jù)庫后再根據(jù)結(jié)果調(diào)用某個外部工具工具返回后還要把這些信息整理成一段結(jié)構(gòu)化報告。如果每一步都用原生 SDK 手寫第一次能跑通第二次會開始復制粘貼第三次邏輯分支一多代碼就變得不可維護。LangChain 的價值在于它把大模型應用拆解成可以組合的零件模型封裝、提示模板、輸出解析、記憶存儲、工具注冊、鏈路編排、狀態(tài)流轉(zhuǎn)。你不需要自己為每個模型服務(wù)商都寫一套適配層也不需要為了“先檢索再總結(jié)”這種流程去手工拼字符串。但這里有一個反直覺的點LangChain 并沒有替你解決“架構(gòu)決策”。它只是提供了更多選擇。1.1 一個 LLM 應用要控制的要素遠不止“提問”實際落地一個 LLM 應用時需要控制的變量至少有這些模型調(diào)用層不同供應商、不同模型版本、超時時間、溫度參數(shù)、密鑰管理提示詞層系統(tǒng)提示、用戶輸入模板、few-shot 示例、工具使用說明輸入輸出層把用戶文本轉(zhuǎn)成結(jié)構(gòu)化請求把模型返回轉(zhuǎn)成可解析結(jié)果外部工具層數(shù)據(jù)庫查詢、文件讀取、API 調(diào)用、MCP Server 接入狀態(tài)與記憶層多輪對話、Agent 內(nèi)部狀態(tài)、任務(wù)歷史可觀測層耗時、Token 消耗、失敗原因、調(diào)用鏈路這些要素之間的關(guān)系是“分層”的。模型是底座Prompt 是輸入規(guī)范鏈和 Agent 是流程組織方式工具是外部能力MCP 是工具接入?yún)f(xié)議中間件負責橫切邏輯。很多教程只講其中某一層導致讀者以為 LangChain 就是“Prompt 拼接工具”。實際上LangChain 的核心抽象思路是凡是能進入一條“處理流水線”的零件都可以被統(tǒng)一成 Runnable。在較新的寫法里一個 Prompt 模板、一個模型對象、一個輸出解析器甚至一個外部工具都可以用管道符串聯(lián)起來。# 常見寫法把 prompt、model、parser 串成一條鏈 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI model ChatOpenAI(modelgpt-4o-mini, temperature0.2) prompt ChatPromptTemplate.from_messages([ (system, 你是一個嚴謹?shù)闹形募夹g(shù)助手。), (human, {input}), ]) chain prompt | model | StrOutputParser() result chain.invoke({input: 用一句話解釋 LangChain 是什么}) print(result)這段代碼不是要你背 API而是理解其中的管道思想。prompt負責把輸入整理成消息結(jié)構(gòu)model拿到消息后調(diào)用模型StrOutputParser負責把模型返回的內(nèi)容轉(zhuǎn)換成文本。每一步都是獨立零件可以替換也可以插樁。1.2 版本變化不是噪音而是框架演進的信號LangChain 的演進路徑會讓人困惑早期有Chain后來主推LCEL再往后 Agent 編排越來越多地出現(xiàn)在 LangGraph 或 Agent 相關(guān)模塊里。加上langchain-openai這類按服務(wù)商拆分的包舊教程里的from langchain.llms import OpenAI可能已經(jīng)跑不通。這其實不是框架故意折騰人而是它在從“簡單鏈式封裝”走向“可控的圖狀態(tài)編排”。你越早理解這個方向就越不容易被版本變化打亂節(jié)奏。我的建議是不要把 LangChain 當成一套固定 API 去背而是把它當成一組能表達 LLM 應用結(jié)構(gòu)的“語言”。你首先要知道自己需要的是線性鏈、條件分支、循環(huán)還是復雜狀態(tài)流然后才去查對應模塊應該怎么用。2. 環(huán)境配置與模型初始化先跑通最小鏈路再談中間件和 Agent環(huán)境配置是 LangChain 學習中最沒技術(shù)含量、卻最容易卡住人的環(huán)節(jié)。問題往往不在 Python 本身而在依賴組合和版本匹配。常見的坑是這樣的某個包要求langchain-core的版本范圍是 A另一個包卻要求 B或者網(wǎng)上教程安裝的是舊版組合你照著裝的時候已經(jīng)是最新版本接口自然對不上。所以環(huán)境配置的第一步不是“裝最新”而是“固定一套自己能復現(xiàn)的依賴組合”。2.1 環(huán)境準備虛擬環(huán)境 依賴鎖定先建一個干凈的虛擬環(huán)境避免和本機其他 Python 項目互相污染。python -m venv .venv source .venv/bin/activate # Windows 下執(zhí)行 .venv\Scripts\activate python -m pip install --upgrade pip安裝時不要一次性把所有包盲裝。LangChain 生態(tài)里核心包、具體服務(wù)商包、工具組件包是分開的。你需要明確自己要接哪個模型服務(wù)商。舉個例子如果接 OpenAI 風格的接口通常需要langchain、langchain-core、langchain-openai這類相關(guān)包如果要接其他服務(wù)商則要裝對應的langchain-xxx。不同供應商和不同服務(wù)商的依賴關(guān)系不一樣這就意味著與其直接抄一條安裝命令不如在項目里用requirements.txt或pyproject.toml把版本鎖定。等能正常跑通后再用鎖文件把整套依賴固定下來。2.2 模型初始化與密鑰管理模型初始化看起來簡單但有一個非常值得注意的習慣不要把 API Key 硬編碼在代碼里。常見的做法是放在.env文件里程序啟動時加載環(huán)境變量。# .env 示例 LLM_API_KEYyour_api_key_here LLM_BASE_URLhttps://api.example.com/v1 LLM_MODEL_NAMEgpt-4o-mini然后在代碼中從環(huán)境變量讀取配置。import os from langchain_openai import ChatOpenAI model ChatOpenAI( modelos.getenv(LLM_MODEL_NAME, gpt-4o-mini), temperature0.2, api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL, None), )這里需要區(qū)分幾個參數(shù)的含義model決定模型的能力量級。不同模型在邏輯推理、指令遵循、成本、延遲上差異很大。temperature控制隨機性。Agent 場景通常不建議太高否則工具選擇會不穩(wěn)定。base_url有些服務(wù)商兼容 OpenAI 協(xié)議可以通過這個參數(shù)指向自己的服務(wù)地址。如果你的模型來自不同服務(wù)商就需要選擇對應的 LangChain 包。密鑰管理看起來是很小的習慣但實際影響很大。一旦把 Key 寫死在代碼里項目很可能因為一次代碼同步就把密鑰泄露出去。更合理的是讓密鑰與代碼分離同時為不同環(huán)境準備不同配置。2.3 最小鏈路驗證先證明“輸入到輸出”沒有斷很多項目失敗不是因為后期 Agent 設(shè)計復雜而是從一開始就沒有把最小鏈路驗證清楚。所謂最小鏈路就是構(gòu)造一個最簡單的問題。調(diào)用一次模型。拿到文本結(jié)果。確認整個流程沒有報錯。順便記錄一次 Token 消耗。不要在一開始就引入向量庫、Agent、MCP 這些組件。每多引入一個組件就多一個排查變量。先用單次調(diào)用確認密鑰有效、模型名稱正確、網(wǎng)絡(luò)能通、Prompt 和輸出解析器能正常工作。只有最基礎(chǔ)的鏈條穩(wěn)定后面的中間件和 Agent 才有意義。3. 新版 Middleware 中間件把橫切邏輯從業(yè)務(wù)鏈里抽出來項目標題里頻繁出現(xiàn)“新版 Middleware 中間件應用”這其實是 LangChain 系列應用從“能跑”走向“能維護”的分水嶺。中間件這個概念本身不神秘。它是在請求進入和結(jié)果返回的路徑上插入一段與具體業(yè)務(wù)無關(guān)的公共邏輯。你不需要改動主鏈路的業(yè)務(wù)代碼就能完成日志、限流、緩存、重試、輸入校驗等操作。LangChain 里的管道式寫法天然適合這種橫切邏輯。你可以把一次invoke()想象成這樣一條流水線用戶請求 - 中間件前的檢查限流 / 入?yún)⑷罩?/ 身份注入 - Prompt 模板 - LLM 模型調(diào)用 - 輸出解析 - 中間件后的處理耗時日志 / Token 統(tǒng)計 / 結(jié)果緩存 - 返回給業(yè)務(wù)代碼中間件不修改“這道菜怎么做”而是在菜從廚房端出來的前后負責記錄、檢查、過濾和補償。3.1 為什么中間件在 AI 應用里尤其重要傳統(tǒng)服務(wù)端有攔截器、過濾器、中間件體系是因為請求會經(jīng)過網(wǎng)關(guān)節(jié)。但 LangChain 應用的調(diào)用鏈不像 Web 請求那樣天然分層。每個業(yè)務(wù)鏈內(nèi)部都可能多次調(diào)用模型如果日志、重試、限流邏輯全部散落在代碼里排查起來會非常痛苦。舉一個典型例子你有一個任務(wù)需要先總結(jié)用戶輸入再調(diào)用工具查詢訂單最后生成回復。如果這三步都要記錄耗時和 Token沒有中間件時你就得在每一步前后手動加日志。一旦鏈路變長日志就會混進業(yè)務(wù)代碼別人讀代碼時根本分不清哪些是業(yè)務(wù)邏輯哪些是公共邏輯。中間件解決的是讓“系統(tǒng)級關(guān)注點”和“業(yè)務(wù)級關(guān)注點”分離。業(yè)務(wù)代碼只關(guān)心“怎么完成任務(wù)”日志、限流、重試這類邏輯放到公共層統(tǒng)一處理。3.2 哪些邏輯適合放中間件根據(jù)實踐有四類橫切邏輯比較適合放進中間件層日志與可觀測記錄輸入摘要、輸出摘要、耗時、Token 消耗。特別要注意脫敏不要把用戶的隱私字段、API Key、完整上下文都打出來。限流與配額在進入模型調(diào)用之前做檢查和等待避免單個任務(wù)把請求額度打滿。緩存對完全可復用、且和用戶上下文無關(guān)的結(jié)果做緩存。緩存命中時可以直接跳過模型調(diào)用能節(jié)省大量成本。重試與超時控制模型偶發(fā)超時或返回格式異常時進行有限次重試。這里特別提醒一句重試要小心。如果一條鏈只做“文本生成”重試通常安全。但如果 Agent 已經(jīng)調(diào)用了真實業(yè)務(wù)工具比如創(chuàng)建訂單、發(fā)消息、扣減庫存等盲目重試可能導致重復執(zhí)行。所以重試邏輯必須配合冪等設(shè)計或者在調(diào)用非冪等工具前增加人工確認。3.3 中間件不是“背 API”而是先理解前置與后置關(guān)于新版中間件的具體類名和包結(jié)構(gòu)不同版本差別比較大。我不建議讀者直接去記憶某個固定實現(xiàn)而是先理解它的結(jié)構(gòu)在模型調(diào)用前要做哪些事在模型返回后要做哪些事在異常發(fā)生時要不要補償或重試。用一個通用偽代碼來表達這種結(jié)構(gòu)# 通用中間件偽代碼僅用于理解結(jié)構(gòu) class LoggingMiddleware: def wrap(self, runnable): def handler(inputs): print(before:, truncate(inputs)) result runnable.invoke(inputs) print(after:, truncate(result)) return result return handler實際項目里的實現(xiàn)可能復雜很多但原理是一致的。先把這種“前置邏輯 / 主調(diào)用 / 后置邏輯”的思維模型建立起來再去看具體版本對應什么語法就不會被接口變化繞暈。中間件的邊界同樣要明確。不要把強業(yè)務(wù)分支判斷塞進中間件也不要在中間件里塞太多統(tǒng)計邏輯。否則你會在排查問題時遇到“流程總在中間件中斷但業(yè)務(wù)代碼里沒有任何線索”的窘境。4. ReAct 與 AI Agent不是多調(diào)一次模型而是進入循環(huán)聊到 AI Agent很多資料都會提 ReAct。這個詞有一個很容易踩的混淆點它和前端框架 React 沒有任何關(guān)系。雖然拼寫接近但 ReAct 在智能體語境里代表的是“Reasoning Acting”也就是“推理與行動交替進行”。你會在搜索框里看到 React 18 的更新批處理機制、React 面試題、React Router 等內(nèi)容那些屬于前端領(lǐng)域。LangChain 里的 ReAct討論的是模型如何在一輪又一輪的推理和行動中完成復雜任務(wù)。4.1 為什么普通 Chain 不夠用一條普通 Chain本質(zhì)上是“輸入 - Prompt - 模型 - 解析 - 輸出”。它適合任務(wù)路徑完全確定的場景。但現(xiàn)實里很多任務(wù)并不能一次性決定完整的執(zhí)行路徑。比如用戶問幫我查一下訂單 A7 的狀態(tài)如果失敗了就觸發(fā)一次補償重試。如果用普通 Chain你會先讓模型回答一個 JSON里面包含是否查詢、查詢參數(shù)、后續(xù)動作。但這沒有真正解決“動態(tài)決策”的問題因為你不能提前知道訂單狀態(tài)是什么也就不能把分支預先寫在 Prompt 里。ReAct 的思路是把決策和執(zhí)行拆成循環(huán)Thought思考現(xiàn)在需要判斷“訂單 A7 是什么狀態(tài)”。Action行動調(diào)用訂單查詢工具傳入訂單號。Observation觀察工具返回“訂單失敗原因是庫存不足”。Thought再思考庫存不足并不適合直接重試應該返回用戶并提示補充庫存狀態(tài)。Final Answer最終回答向用戶說明失敗原因。在這個過程中模型不是一次性“背出”答案而是根據(jù)工具返回結(jié)果動態(tài)決定下一步。這才是 Agent 與 Chain 的本質(zhì)差別。4.2 一個最小 ReAct 流程的直觀理解下面這段不是某個具體框架的代碼而是幫助理解循環(huán)結(jié)構(gòu)的偽示意Task: 查詢訂單 A7 狀態(tài)并決定是否重試 Thought: 我需要先查詢訂單狀態(tài)。 Action: query_order(order_idA7) Observation: statusfailed, reasoninventory_not_enough Thought: 庫存不足時不適合直接重試應該結(jié)束。 Final Answer: 訂單 A7 失敗原因是庫存不足建議補貨后再處理。關(guān)鍵點在于Action 的參數(shù)和下一輪 Thought 都依賴 Observation 的結(jié)果。所以 Agent 必須有“狀態(tài)”概念它要記住目前已經(jīng)觀察到什么、已經(jīng)調(diào)用過什么工具、下一步還剩哪些選擇。4.3 LangGraph 和 LangChain 的分工很多搜索詞里都會出現(xiàn)“LangChain 和 LangGraph 的區(qū)別”。簡單說LangChain 提供的是各種組件和抽象LangGraph 提供的則是能承載循環(huán)和狀態(tài)流轉(zhuǎn)的“運行框架”。如果任務(wù)是“查一個值然后回復”普通的 Chain 就夠了。如果任務(wù)是“模型動態(tài)決定要不要多次調(diào)用工具并根據(jù)中間結(jié)果調(diào)整策略”就進入 Agent 場景。Agent 場景通常需要這么幾個能力維護多輪思考與觀察的狀態(tài)。支持條件分支和循環(huán)。能在超時或達到最大輪數(shù)時停止。能插入人工審核節(jié)點。這些能力如果全部自己實現(xiàn)會很繁瑣。LangGraph 這類方案的價值不是讓代碼更酷而是讓你把 Agent 決策過程變成可觀測、可恢復、可中止的狀態(tài)流。給一個實際建議不要為了“用 Agent 而用 Agent”。如果一個任務(wù)用鏈加條件判斷就能解決不要引入循環(huán)和狀態(tài)如果一個任務(wù)確實依賴動態(tài)工具調(diào)用再考慮Agent。Agent 越多不可控邊界越大。4.4 讓 Agent 更容易成功控制工具范圍模型不是工具越多就越強。相反當上下文里塞滿十幾個工具描述時模型做出錯誤選擇或漏