昨天我們初步認識了 LangChain,了解到它是一個專為 LLM 應用程式設計的框架,能幫助我們用模組化的方式組合大型語言模型應用。
今天我們要動手實作,從零開始建立一個最小可行的 LangChain 專案,並透過範例快速體驗它的基本用法。
今天的目標改寫先前的 CLI 聊天機器人,我們將使用 LangChain 串接 ChatOpenAI
模型,打造一個可進行多輪對話的聊天機器人。完成後,你可以將這個版本與先前直接使用 openai
套件呼叫 API 的版本相比較,觀察在程式結構、可讀性與擴充性上的差異。
在開始之前,請先初始化一個新的專案,命名為 llm-chatbot
,我們將在這個專案中完成實作內容。
Note:如果你對 Node.js 專案初始化流程還不熟悉,可以先回顧 Day 01 中「建立 Node.js 專案與 TypeScript 開發環境」的內容。
建立專案環境後,請在專案根目錄中執行以下指令,安裝所需依賴套件:
npm install @langchain/core @langchain/openai dotenv
@langchain/core
:LangChain 的核心模組,提供所有開發元件的共通介面與執行邏輯。@langchain/openai
:LangChain 提供的 OpenAI 模型整合套件。dotenv
:用來讀取 .env
檔案中的環境變數,例如 API 金鑰。為了讓程式能成功呼叫 OpenAI API,我們需要在專案根目錄建立一個 .env
檔案,並填入你的 OpenAI API 金鑰:
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@langchain/openai
預設會從process.env.OPENAI_API_KEY
讀取金鑰。如果你使用.env
搭配dotenv
,在程式啟動時就會自動載入並生效。若你希望使用不同的變數名稱,也可以在建立OpenAI
client 時,手動指定apiKey
。
接下來,我們撰寫一個互動式命令列程式,讓使用者可以在終端機中輸入訊息並即時獲得 GPT 回覆。這次我們將使用 @langchain/openai
的 ChatOpenAI
來取代原生 OpenAI SDK,為後續整合 LangChain 功能鋪路。
請打開 src/index.ts
並輸入以下程式碼:
// src/index.ts
import 'dotenv/config';
import readline from 'readline';
import { ChatOpenAI } from '@langchain/openai';
async function main() {
const llm = new ChatOpenAI({
model: 'gpt-4o-mini',
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log('LLM Chatbot 已啟動,輸入訊息開始對話(按 Ctrl+C 離開)。\n');
rl.setPrompt('> ');
rl.prompt();
rl.on('line', async (input) => {
try {
const response = await llm.invoke(input);
console.log(`\n${response.content}\n`);
} catch (err) {
console.error(err);
}
rl.prompt();
});
}
main();
在上述程式中,我們主要完成了以下工作:
ChatOpenAI
建立 LangChain 封裝的 OpenAI 模型實例,並指定使用 gpt-4o-mini
。readline
模組建立互動式命令列介面,持續接收使用者輸入並顯示輸出。llm.invoke(input)
將內容傳遞給模型,並取得回覆。response.content
即時輸出到終端機。這樣我們就完成了一個最簡單的 LangChain 版 CLI 聊天機器人,能即時與 OpenAI GPT 模型互動。
完成程式後,就可以來測試看看效果了。在專案根目錄下執行以下指令啟動程式:
npm run dev
終端機會顯示以下訊息,代表聊天機器人已經啟動成功:
LLM Chatbot 已啟動,輸入訊息開始對話(按 Ctrl+C 離開)。
> Hello!
Hello! How can I assist you today?
此時你可以在 >
後面輸入任何訊息,例如提問或聊天,GPT 都會即時回覆你。程式會持續等待輸入,直到你按下 Ctrl+C
才會中止。
上一步我們已經完成了單輪對話的 CLI Chatbot。接下來要進一步升級,讓它能記住對話歷程,實現多輪對話。這需要在程式中額外維護一個 messages
陣列,用來保存使用者與 AI 的所有訊息。
在使用原生 OpenAI SDK 時,訊息通常是以簡單物件的方式存在,例如:
[
{ role: 'system', content: '你是一個樂於助人的 AI 助理。' },
{ role: 'user', content: '你好!' }
]
而在 LangChain 中,則提供了清楚的訊息類別:
SystemMessage
:設定 AI 的角色或行為。HumanMessage
:代表使用者輸入。AIMessage
:代表模型回覆。另外還有 FunctionMessage
、ToolMessage
等進階訊息型別,用來處理工具調用。
以下是修改後的程式碼:
// src/index.ts
import 'dotenv/config';
import readline from 'readline';
import { ChatOpenAI } from '@langchain/openai';
import { AIMessage, BaseMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
async function main() {
const llm = new ChatOpenAI({
model: 'gpt-4o-mini',
});
const messages: BaseMessage[] = [
new SystemMessage('你是一個樂於助人的 AI 助理。'),
];
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log('LLM Chatbot 已啟動,輸入訊息開始對話(按 Ctrl+C 離開)。\n');
rl.setPrompt('> ');
rl.prompt();
rl.on('line', async (input) => {
messages.push(new HumanMessage(input));
try {
const response = await llm.invoke(messages);
console.log(`\n${response.content}\n`);
messages.push(new AIMessage(response));
} catch (err) {
console.error(err);
}
rl.prompt();
});
}
main();
在上述程式中,我們主要完成了以下工作:
ChatOpenAI
建立 LangChain 封裝的 OpenAI 模型實例,並指定使用 gpt-4o-mini
。SystemMessage
設定助理角色,並以 messages
陣列保存完整對話歷程。HumanMessage
並加入 messages
。llm.invoke(messages)
,將完整的對話上下文傳遞給模型,以產生回覆。AIMessage
的形式存回 messages
,持續累積對話狀態。這樣一來,我們就成功讓 CLI Chatbot 具備多輪對話的能力,而不再是單次問答。
目前的實作雖然能夠進行多輪對話,但每次輸入訊息後,都要等模型完整生成回覆才會一次性輸出。若我們想模擬更接近「即時打字」的效果,就可以改用 串流模式(Streaming)。
在 LangChain 中,我們可以透過 stream()
方法來逐步接收模型的輸出,並即時將文字輸出到終端機。
以下是修改後的範例:
import 'dotenv/config';
import readline from 'readline';
import { ChatOpenAI } from '@langchain/openai';
import { AIMessage, BaseMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
async function main() {
const llm = new ChatOpenAI({
model: 'gpt-4o-mini',
});
const messages: BaseMessage[] = [
new SystemMessage('你是一個樂於助人的 AI 助理。'),
];
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log('LLM Chatbot 已啟動,輸入訊息開始對話(按 Ctrl+C 離開)。\n');
rl.setPrompt('> ');
rl.prompt();
rl.on('line', async (input) => {
messages.push(new HumanMessage(input));
try {
const stream = await llm.stream(messages);
let aiMessage = '';
process.stdout.write('\n');
for await (const chunk of stream) {
const content = chunk?.content ?? '';
process.stdout.write(content.toString());
aiMessage += content;
}
process.stdout.write('\n\n');
messages.push(new AIMessage(aiMessage));
} catch (err) {
console.error(err);
}
rl.prompt();
});
}
main();
在上述程式中,我們主要完成了以下工作:
ChatOpenAI
建立 LangChain 封裝的 OpenAI 模型實例,並指定使用 gpt-4o-mini
。SystemMessage
設定助理的角色,並以 messages
陣列保存完整的對話上下文。HumanMessage
並加入 messages
。llm.stream(messages)
以串流模式呼叫模型,逐步接收回覆。for await...of
即時輸出模型回應,同時將逐字輸出的內容累積為完整的 aiMessage
。aiMessage
封裝為 AIMessage
加入 messages
,確保後續對話能持續保持上下文。這樣一來,當使用者輸入問題時,模型的回覆會邊生成邊顯示,互動體驗更加自然。
今天我們實作了一個 LangChain 版 CLI 聊天機器人,從零開始快速體驗 LangChain 的核心用法:
ChatOpenAI
建立模型實例,並在 CLI 中接收使用者輸入、回傳 GPT 模型回應。SystemMessage
、HumanMessage
、AIMessage
,維護 messages
陣列,讓對話能具備上下文,實現多輪互動。stream()
方法改寫呼叫流程,實現逐字輸出的串流模式,讓回覆更即時自然。這只是起點,接下來我們將深入探索 LangChain 的進階元件與整合能力,打造更具擴充性與可維護性的 LLM 應用。