LangChain 基礎概念:什麼是 LangChain?
簡介
在當前的 AI 與自然語言處理(NLP)熱潮中,大型語言模型(LLM) 已經成為開發智慧型應用的核心工具。儘管 LLM 本身具有強大的語言生成與理解能力,將它們與外部資料、工具、工作流程結合卻往往需要大量樣板程式碼與繁瑣的整合工作。
LangChain 正是為了填補這個缺口而誕生的,它提供了一套 模組化、可組合 的框架,讓開發者能夠輕鬆構建「語言模型驅動的應用程式」——從聊天機器人、文件問答系統,到自動化工作流、資料分析助理,皆可在幾行程式碼內完成。
本篇文章將帶你快速了解 LangChain 的核心概念、實作方式,以及在真實專案中常見的陷阱與最佳實踐,幫助你在 LLM + 應用 的領域快速上手。
核心概念
LangChain 的設計哲學可以歸納為 四大支柱:
| 支柱 | 說明 |
|---|---|
| Chains | 將多個 LLM 呼叫、工具或資料來源串接成有序流程。 |
| Agents | 具備「思考」與「執行」能力的智能體,能根據 LLM 的回應動態決定要呼叫哪個工具。 |
| Memory | 保存對話或狀態,使模型能夠在多輪互動中保持上下文。 |
| Data Loaders & Indexes | 為文件、資料庫或網頁等外部資源建立向量索引,支援語意檢索。 |
下面分別說明這些概念,並提供實作範例。
1. Chains:把「小工具」串成「大流程」
最簡單的 Chain 可能只是「LLM → Prompt → 回應」的單一步驟,但實務上常會加入前處理、後處理或其他 API 呼叫。
範例 1:基本的 Prompt Chain(使用 OpenAI 的 gpt‑3.5‑turbo)
import { OpenAI } from "langchain/llms/openai";
import { PromptTemplate } from "langchain/prompts";
import { LLMChain } from "langchain/chains";
// 1. 建立 Prompt 範本
const prompt = new PromptTemplate({
inputVariables: ["question"],
template: "請以簡潔的口吻回答以下問題:{question}",
});
// 2. 初始化 LLM(此處使用 OpenAI)
const llm = new OpenAI({ modelName: "gpt-3.5-turbo", temperature: 0.7 });
// 3. 組合成 Chain
const chain = new LLMChain({ llm, prompt });
// 4. 執行
const response = await chain.run({ question: "什麼是向量資料庫?" });
console.log(response);
重點:
PromptTemplate讓 prompt 可重複使用且易於維護;LLMChain把 LLM 與 prompt 包裝成一個可呼叫的物件。
2. Agents:讓模型自行決定要使用哪個工具
當需求超出單純的文字生成,例如「查詢天氣」或「計算數學式」,我們可以讓 LLM 扮演「指揮官」的角色,根據回應決定要呼叫哪個工具。
範例 2:使用 OpenAI Functions 實作簡易 Agent
import { OpenAI } from "langchain/llms/openai";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { SerpAPI } from "langchain/tools/serpapi";
// 1. 建立可呼叫的工具(此例使用 SerpAPI 做網路搜尋)
const searchTool = new SerpAPI(process.env.SERPAPI_API_KEY);
// 2. 初始化 LLM(開啟 function calling 功能)
const llm = new OpenAI({
modelName: "gpt-3.5-turbo-0613",
temperature: 0,
// 讓模型可以返回 function 呼叫的 JSON
openAIApiKey: process.env.OPENAI_API_KEY,
});
// 3. 建立 Agent,讓它自動選擇工具
const executor = await initializeAgentExecutorWithOptions(
[searchTool],
llm,
{
agentType: "openai-functions", // 使用 OpenAI Functions
verbose: true,
}
);
// 4. 執行任務
const result = await executor.call({ input: "今天台北的天氣如何?" });
console.log(result.output);
說明:Agent 會先讓 LLM 產生「思考」的文字,再根據模型產出的
function_call觸發searchTool,最後把搜尋結果回饋給 LLM 完成最終回答。
3. Memory:保持多輪對話的上下文
LLM 本身是 無狀態 的,每一次呼叫都是獨立的。若要讓聊天機器人記得前面說過的內容,就需要額外的 記憶機制。
範例 3:使用 ConversationBufferMemory 建立簡易聊天機器人
import { OpenAI } from "langchain/llms/openai";
import { ConversationChain } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";
// 1. 初始化 LLM
const llm = new OpenAI({ modelName: "gpt-3.5-turbo", temperature: 0.6 });
// 2. 建立 Memory(會把所有對話紀錄保存在陣列中)
const memory = new ConversationBufferMemory({
memoryKey: "history", // 在 Prompt 中使用 {history}
});
// 3. 建立 Chain,將 Memory 注入
const chat = new ConversationChain({ llm, memory });
// 4. 多輪對話示範
await chat.call({ input: "嗨,我叫小明。" });
await chat.call({ input: "今天要怎麼安排時間比較好?" });
要點:
ConversationBufferMemory會自動把先前的input與output合併成history,在 Prompt 中使用{history}即可讓模型參考過去的對話。
4. Data Loaders & Indexes:把文件變成可檢索的向量
在文件問答(Document Q&A)或企業知識庫的情境下,我們需要先把大量文字資料轉換成向量,然後根據使用者的問題做語意檢索。
範例 4:使用 RecursiveCharacterTextSplitter + FAISS 建立簡易索引
import { Document } from "langchain/document";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { FAISS } from "langchain/vectorstores/faiss";
import { OpenAI } from "langchain/llms/openai";
import { RetrievalQAChain } from "langchain/chains";
// 1. 假設有一段長文件
const rawText = `LangChain 是一個用於建構 LLM 應用的框架...
(此處省略長篇說明)`;
// 2. 轉成 Document 物件
const docs = [new Document({ pageContent: rawText })];
// 3. 切分成較小的片段(方便向量化)
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 50 });
const splitDocs = await splitter.splitDocuments(docs);
// 4. 建立向量嵌入
const embeddings = new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY });
const vectorStore = await FAISS.fromDocuments(splitDocs, embeddings);
// 5. 建立檢索器
const retriever = vectorStore.asRetriever(4); // 取前 4 個最相關片段
// 6. 結合 LLM 與檢索器形成 QA 系統
const llm = new OpenAI({ temperature: 0 });
const qaChain = RetrievalQAChain.fromLLM(llm, retriever);
// 7. 提問
const answer = await qaChain.run("LangChain 的核心概念是什麼?");
console.log(answer);
關鍵:
RecursiveCharacterTextSplitter讓長文件切成適合向量化的大小;FAISS為高效的向量搜尋引擎;RetrievalQAChain自動把檢索結果注入 Prompt,產生精確的回答。
5. 多模組結合:從 Prompt → Retrieval → Agent
有時候一個問題需要 先檢索文件,再根據檢索結果決定是否呼叫外部 API。下面示範如何把前面的概念串成完整流程。
範例 5:檢索文件後自動呼叫天氣 API 的完整 Chain
import { OpenAI } from "langchain/llms/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { FAISS } from "langchain/vectorstores/faiss";
import { RetrievalQAChain } from "langchain/chains";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { WeatherAPI } from "./tools/weather"; // 假設自行實作的工具
// 1. 建立文件檢索器(同前例)
const docs = [new Document({ pageContent: "台北今天天氣預報:晴時多雲,最高 28°C。" })];
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 300, chunkOverlap: 30 });
const splitDocs = await splitter.splitDocuments(docs);
const embeddings = new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY });
const vectorStore = await FAISS.fromDocuments(splitDocs, embeddings);
const retriever = vectorStore.asRetriever(3);
// 2. 建立 QA Chain(先回答是否需要天氣資訊)
const llm = new OpenAI({ temperature: 0 });
const qaChain = RetrievalQAChain.fromLLM(llm, retriever);
// 3. 建立天氣工具
const weatherTool = new WeatherAPI(process.env.WEATHER_API_KEY);
// 4. 把 QA Chain 包裝成 Agent,讓它決定是否呼叫 weatherTool
const executor = await initializeAgentExecutorWithOptions(
[weatherTool],
llm,
{ agentType: "openai-functions", verbose: true }
);
// 5. 執行完整流程
const userQuestion = "今天要去台北市中心開會,請告訴我天氣與建議穿著。";
const retrievedInfo = await qaChain.run(userQuestion); // 先取得文件相關資訊
const finalAnswer = await executor.call({ input: `${retrievedInfo}\n\n${userQuestion}` });
console.log(finalAnswer.output);
說明:此範例展示 檢索 → 思考 → 呼叫工具 → 回答 的完整管線,凸顯 LangChain 在多模組協同上的威力。
常見陷阱與最佳實踐
| 陷阱 | 可能的影響 | 建議的最佳實踐 |
|---|---|---|
忘記設定 temperature |
回答過於隨機或不一致 | 依需求調整:0 產生確定性答案,0.7 產生創意回應 |
| Prompt 過長或缺乏指示 | LLM 產生不相關或冗長文字 | 使用 PromptTemplate,將變數與固定指令分離,保持指令簡潔明確 |
| Memory 無限制累積 | 記憶體爆炸、回應變慢 | 採用 ConversationSummaryMemory 或自行截斷舊訊息 |
| 向量索引未正規化 | 相似度計算失真 | 使用 OpenAIEmbeddings 或 HuggingFaceEmbeddings 時,確保 normalize 參數正確 |
| 工具呼叫失敗未捕捉 | Agent 中斷、回傳錯誤訊息 | 在自訂 Tool 中加入 try/catch,並在 Agent 設定 handleParsingErrors: true |
| API 金鑰硬編碼 | 安全風險、部署失敗 | 使用 環境變數 或 Secret Manager,切勿把金鑰寫入程式碼庫 |
小技巧
- 使用
verbose: true觀察每一步的 Prompt 與回傳,方便除錯。 - 分層測試:先測試單一
LLMChain,再逐步加入Memory、Retriever、Agent。 - 向量索引快取:在開發階段可將
FAISS索引存檔 (saveLocal) 以免每次重啟都重新向量化。 - Prompt 版本管理:將重要 Prompt 放在獨立檔案(如
prompts/*.txt),配合 Git 追蹤變更。
實際應用場景
| 場景 | LangChain 角色 | 為什麼適合 |
|---|---|---|
| 企業內部知識庫問答 | RetrievalQAChain + ConversationBufferMemory |
能快速從海量文件中檢索答案,同時保持多輪對話上下文。 |
| 客服聊天機器人 | Agent + 多個工具(訂單查詢、退貨流程) |
Agent 可根據使用者意圖自動選擇相應 API,提升自動化程度。 |
| 程式碼生成助理 | LLMChain + OpenAI Functions(執行測試、格式化) |
讓模型在生成程式碼後直接呼叫測試工具,回饋錯誤訊息。 |
| 資料分析報告自動化 | Chain 結合 Pandas、Matplotlib 工具 |
透過 LLM 產生分析需求,Chain 依序執行資料處理、圖表產生,最後回傳報告。 |
| 行銷文案自動產出 | PromptTemplate + LLMChain + Memory |
記錄過往的品牌語調與風格,確保新文案與品牌一致。 |
總結
- LangChain 為 LLM 應用提供了 模組化、可組合 的基礎建設,讓開發者不必從零開始實作檢索、記憶、工具調度等繁雜功能。
- 透過 Chains、Agents、Memory、Data Loaders 四大支柱,我們可以快速構建從 簡單問答 到 複雜自動化工作流 的完整系統。
- 在實作時,注意 Prompt 設計、記憶管理、向量索引正規化 以及 錯誤處理,可以大幅提升系統的穩定性與使用者體驗。
掌握以上概念後,你就能在 LLM 時代 以最少的程式碼,打造出功能完整、可擴充的智慧型應用。未來隨著模型與工具的持續演進,LangChain 也將持續提供更彈性的介面,讓開發者專注於 業務邏輯 而非底層基礎建設。祝你玩得開心,開發順利! 🚀