LangChain 實戰專案:Chatbot – 加入工具與查詢能力
簡介
在現代 AI 應用中,Chatbot 已不只是單純的對話機器人**,更需要具備即時查詢、資料擷取與外部工具整合的能力**。只有這樣的 Chatbot 才能在真實業務場景中提供有價值的回應,例如即時查詢庫存、呼叫第三方 API、或在對話中執行簡單計算。
LangChain 作為一套 「語言模型 + 工具」 的框架,讓開發者可以輕鬆把 LLM(大型語言模型)與外部資源串接。透過 Tool(工具)與 Retriever(檢索器),Chatbot 能在對話過程中自動決定何時呼叫外部服務、何時從知識庫取得資訊,從而大幅提升回應的正確性與實用性。
本篇文章將從 核心概念、程式碼範例、常見陷阱與最佳實踐,一步步帶你在 LangChainJS 中為 Chatbot 加入工具與查詢能力,並說明這些功能在實務上的應用場景。
核心概念
1. Tool(工具)
Tool 是一個 可被 LLM 呼叫的函式,通常用來執行以下任務:
| 類型 | 範例 |
|---|---|
| API 呼叫 | 查詢天氣、股票、航班資訊 |
| 資料庫操作 | 讀寫 MySQL、MongoDB |
| 計算 | 數學運算、文字統計 |
| 系統指令 | 執行 Shell、產生檔案 |
LangChainJS 中的 Tool 必須符合以下介面:
interface Tool {
name: string; // 工具名稱,LLM 會使用這個名稱呼叫
description: string; // 工具說明,提供給 LLM 作為參考
func: (input: string) => Promise<string>; // 執行函式,接受字串輸入回傳字串
}
2. Retriever(檢索器)
Retriever 用來 從向量資料庫或全文檢索系統中取得相關文件,並把結果提供給 LLM 作為上下文。常見的實作有:
- Pinecone、Weaviate:雲端向量資料庫
- FAISS:本機向量索引
- ElasticSearch:全文檢索
LangChainJS 提供 VectorStoreRetriever、ElasticSearchRetriever 等抽象類別,讓開發者只要提供向量化模型與資料來源,即可快速建立檢索器。
3. Agent(代理人)
Agent 是 組合 LLM、Tool、Retriever 的核心,它會根據對話內容自行決策:
- 判斷是否需要工具(例如:「請幫我查一下今天台北的天氣」)
- 呼叫相應工具,取得結果
- 將結果與原始問題一起送回 LLM,產生最終回應
LangChainJS 中的 ChatAgent 只需要提供 LLM、tool list、retriever(可選)即可完成。
程式碼範例
以下範例均使用 LangChainJS(v0.2+) 搭配 OpenAI 的 ChatGPT,語言為 TypeScript/JavaScript。若使用 Python,概念相同,只是語法會略有差異。
範例 1:建立一個簡易的「天氣查詢」Tool
import { OpenAI } from "langchain/llms/openai";
import { Tool } from "langchain/tools";
// 假設有一個第三方天氣 API
async function fetchWeather(city: string): Promise<string> {
const response = await fetch(`https://api.weatherapi.com/v1/current.json?key=YOUR_KEY&q=${city}`);
const data = await response.json();
return `🌤 ${city} 現在 ${data.current.temp_c}°C,${data.current.condition.text}`;
}
// 包裝成 LangChain 的 Tool
const weatherTool: Tool = {
name: "weather",
description: "查詢指定城市的即時天氣。輸入格式:城市名稱,例如「台北」",
func: async (input: string) => {
const city = input.trim();
if (!city) return "請提供城市名稱。";
try {
return await fetchWeather(city);
} catch (e) {
return "呼叫天氣服務失敗,請稍後再試。";
}
},
};
重點:Tool 的
description必須寫得 清楚且具體,讓 LLM 能正確判斷何時使用此工具。
範例 2:使用 FAISS 建立本機向量檢索器
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { FAISS } from "langchain/vectorstores/faiss";
import { Document } from "langchain/document";
// 假設有一批產品說明文件
const docs: Document[] = [
new Document({ pageContent: "iPhone 15 採用 A17 仿生晶片,支援 5G。" }),
new Document({ pageContent: "MacBook Pro 2023 搭載 M2 Pro 晶片,配備 14 吋 Retina 螢幕。" }),
// …更多文件
];
// 建立向量化模型與 FAISS 索引
const embeddings = new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY });
const vectorStore = await FAISS.fromDocuments(docs, embeddings);
// 產生 Retriever(取前 3 個最相似的文件)
const retriever = vectorStore.asRetriever({ k: 3 });
提示:在正式環境中,向量資料庫建置只需一次,之後只要呼叫
vectorStore.asRetriever()即可重複使用。
範例 3:組合 LLM、Tool 與 Retriever 成為 ChatAgent
import { ChatOpenAI } from "langchain/chat_models/openai";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { ConversationalRetrievalQAChain } from "langchain/chains";
// 1. LLM
const llm = new ChatOpenAI({
temperature: 0, // 為了可預測性,設定為 0
openAIApiKey: process.env.OPENAI_API_KEY,
});
// 2. Tool 列表(加入 weatherTool)
const tools = [weatherTool];
// 3. 建立 Agent
const agentExecutor = await initializeAgentExecutorWithOptions(tools, llm, {
agentType: "chat-zero-shot-react-description",
// 可選:加入檢索器,使 Agent 能在需要時查詢文件
retriever,
});
// 測試對話
async function ask(question: string) {
const result = await agentExecutor.run(question);
console.log(`❓ ${question}\n🤖 ${result}\n`);
}
// 範例對話
await ask("今天台北的天氣如何?");
await ask("請告訴我 iPhone 15 的處理器規格。");
執行結果示例:
❓ 今天台北的天氣如何?
🤖 🌤 台北現在 26°C,晴時多雲
❓ 請告訴我 iPhone 15 的處理器規格。
🤖 iPhone 15 採用 A17 仿生晶片,具備更高效能與更低功耗。
範例 4:自訂「數學計算」Tool,結合 LLM 的推理
import { Tool } from "langchain/tools";
const mathTool: Tool = {
name: "calculator",
description: "執行簡單的算術運算,支援 + - * / 以及括號。例如:2 * (3 + 4)",
func: async (input: string) => {
try {
// 使用 Function constructor 只允許數學表達式
const result = Function(`"use strict"; return (${input})`)();
return `計算結果:${result}`;
} catch {
return "無效的算式,請重新輸入。";
}
},
};
// 把 mathTool 加入 Agent
const agentWithMath = await initializeAgentExecutorWithOptions(
[weatherTool, mathTool],
llm,
{ agentType: "chat-zero-shot-react-description", retriever }
);
// 測試
await agentWithMath.run("請幫我算一下 12 / (2 + 4) * 3 的結果。");
範例 5:從外部資料庫查詢庫存(MongoDB 示例)
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();
const collection = client.db("shop").collection("products");
// Tool:查詢商品庫存
const stockTool: Tool = {
name: "stock_lookup",
description: "根據商品名稱查詢即時庫存。輸入格式:商品名稱,例如「MacBook Pro」",
func: async (input: string) => {
const product = await collection.findOne({ name: { $regex: input, $options: "i" } });
if (!product) return `找不到「${input}」的商品。`;
return `商品「${product.name}」目前庫存 ${product.stock} 件。`;
},
};
// 加入 Agent
const agentFull = await initializeAgentExecutorWithOptions(
[weatherTool, mathTool, stockTool],
llm,
{ agentType: "chat-zero-shot-react-description", retriever }
);
// 測試查詢庫存
await agentFull.run("我想知道 MacBook Pro 的庫存情況。");
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| Tool 說明不清楚 | LLM 依賴 description 判斷何時使用工具,描述過於模糊會導致錯誤呼叫或不呼叫。 |
使用具體範例(例如「輸入格式:城市名稱」),並在說明中列出可接受的參數類型。 |
| 工具回傳非字串 | LangChain 預期 func 回傳 string,若回傳物件會拋錯。 |
在 func 內先 JSON.stringify,或自行格式化為文字。 |
| 向量檢索成本過高 | 每次對話都重新建立向量索引會非常慢。 | 一次性建立索引,並持久化(FAISS.save、Pinecone)後直接載入。 |
| LLM Hallucination(幻覺) | 即使加入檢索器,LLM 仍可能產生未根據文件的回應。 | 使用 RAG with source grounding:在 Prompt 中加入「只使用檢索結果」的指示,或使用 ConversationalRetrievalQAChain 強制引用來源。 |
| 安全性問題 | 允許工具執行任意程式碼(如 eval)可能導致注入攻擊。 |
嚴格限制工具的執行環境,僅允許白名單函式,並在 func 中加入驗證。 |
| 上下文窗口限制 | LLM 的 token 上限會被檢索結果與工具回傳佔滿。 | 摘要檢索結果,或只取最相關的 k 個文件(k 建議 2~5)。 |
最佳實踐清單
- 先定義需求:明確列出 Chatbot 必須支援的外部服務(天氣、庫存、計算等)。
- 分離層級:Tool 只負責「資料取得/運算」,LLM 僅負責「自然語言生成」。
- 使用 Zero‑Shot‑React Prompt:LangChain 提供的
chat-zero-shot-react-description已內建「思考 → 決策 → 呼叫工具」的流程,除非有特殊需求,建議直接使用。 - 測試每個 Tool:單元測試確保工具在各種輸入下都有正確回傳,避免 LLM 因錯誤結果產生錯誤對話。
- 監控與日誌:記錄每次工具呼叫的輸入、輸出與 LLM 回答,方便日後除錯與性能分析。
實際應用場景
| 場景 | 需求 | 可能的 Tool 組合 |
|---|---|---|
| 客服中心 | 查詢訂單狀態、退貨流程、常見問題 | order_lookup(資料庫)、faq_retriever(向量檢索) |
| 行銷助理 | 產生活動文案、即時分析社群情緒 | text_generator(LLM)+ sentiment_analysis(API) |
| 內部知識庫 | 員工問答、文件搜尋、程式碼範例 | doc_retriever(ElasticSearch)+ code_snippet_tool |
| 金融諮詢 | 股價查詢、風險評估、計算投資回報率 | stock_api、calculator、risk_model |
| 智慧家庭 | 控制 IoT 裝置、查詢天氣、設定提醒 | iot_control(MQTT)、weather、reminder |
範例:在客服中心中,使用
order_lookup直接從 MySQL 取得訂單進度,若找不到則讓 LLM 回覆「抱歉,未找到相關訂單,請確認訂單號碼」;同時結合faq_retriever,讓 Chatbot 能在同一回應中提供常見問題的參考連結,提升客戶滿意度。
總結
- LangChain 為 LLM 加上「工具」與「檢索」的雙重能力,讓 Chatbot 從單純的文字生成,變成能即時查詢、執行計算、存取資料的多功能助理。
- 透過 Tool(明確的
name、description、func)以及 Retriever(向量或全文檢索),開發者可以快速構建具備 RAG(Retrieval‑Augmented Generation) 的對話系統。 - 本文提供了 5 個實作範例:天氣查詢、FAISS 檢索、ChatAgent 組合、數學計算、MongoDB 庫存查詢,涵蓋從 API 呼叫到資料庫操作的常見需求。
- 為避免 Hallucination、上下文爆炸與安全風險,建議遵守 最佳實踐:清晰描述 Tool、一次性建立索引、限制工具執行環境、加入來源引用指示。
掌握了這些概念與技巧後,你就能在 LangChain 上打造 實務導向、可靠且可擴充的 Chatbot,不論是客服、金融、行銷或內部知識庫,都能快速上線,為使用者提供即時、正確且具備行動力的回應。祝開發順利,期待看到你在實戰中發揮創意! 🚀