
先前有試著將 Python Langchain 換成 Node 版,但 Langchain node 版的文件真的有點少,因此,轉換起來有點辛苦,因為常常就算有文件,還是漏了參數,程式就炸了,最後還是得對照Python 版本。
首先,我們來看代理組件(AgentExecutor),代理組件在Langchain 框架中蠻核心的組件,代理能根據用戶輸入的內容,由AI 動態決定要使用那個工具,並給出最終的答案。
// 初化模式
const llm = new ChatOllama({
model: 'llama3.2',
temperature: 0,
maxRetries: 2,
baseUrl: 'http://localhost:11434',
});
const tools = []; // 工具清單
// 初始化聊天範本
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant'], // default role
['placeholder', '{chat_history}'], // default role
['human', '{input}'],
['placeholder', '{agent_scratchpad}'],
]);
const agent = createToolCallingAgent({
llm,
tools,
prompt,
});
const result = await agentExecutor.invoke({ input: 'your question ' });
前面提到,當代理組件,接收使用者問題 > 會依據工具的描述,並讓 AI 決定使用那個工具 > 執行工具 > 最終回應結果給用戶。
import { tool } from '@langchain/core/tools';
...
const greetingTool = tool(
async ({ input }: { input: string }) => {
return input + ' ==';
},
{
name: 'greetingTool',
description: 'if some one say hello',
schema: z.object({
input: z.string(),
})
}
);
...
參數說明
SerpAPI 是一個專門設計用來與搜尋引擎互動的 API,特別是 Google 搜尋,SerpAPI 自動化了網頁抓取的過程,讓開發者無需自行管理搜尋引擎的網頁解析和資料提取,簡化了搜尋結果的獲取過程。
import { SerpAPI } from '@langchain/community/tools/serpapi';
let serpApiTool = new SerpAPI('your key ', {
location: 'Austin,Texas,United States', // ,代表你要模擬的搜尋位置。在這裡,它指定了搜索應基於美國德州奧斯汀的位置,這可以影響搜尋結果的區域相關性。
hl: 'en', // 語言參數,代表要以哪種語言來顯示搜尋結果。'en' 表示結果會以英文顯示。
gl: 'us', // 國家/地區代碼,用來設定你希望搜尋結果的區域依據。'us' 表示結果會根據美國的地區來產生。
}),
大型語言模型,本身是不具備記憶功能,之所謂能讀懂上下文的問題,因為每次將討論的過程,再傳遞給大型語言模型,去詢問。
Langchain 的momory ,也是透過這個機制,因此將每次用戶問的問題存在 Redis ,讓AI Bot 也具備記憶功能。
npm i @langchain/redis @langchain/core redis @langchain/openai --save
import { RedisChatMessageHistory } from '@langchain/community/stores/message/ioredis';
import { BufferMemory } from 'langchain/memory';
...
const client = new Redis('redis://localhost:30001');
const memory = new BufferMemory({
chatHistory: new RedisChatMessageHistory({
sessionId: 'sessionId:' + new Date().toISOString(),
sessionTTL: 300,
client,
}),
aiPrefix: 'ollama',
outputKey: 'output', // 沒有會爆錯
memoryKey: 'chat_history', // 沒有會爆錯
inputKey: 'input', // 沒有會爆錯
returnMessages: true,
const agentExecutor = new AgentExecutor({
agent,
tools,
verbose: true, // enable debug
handleParsingErrors: true,
memory, // redis 記憶功能
returnIntermediateSteps: true,
});
...
import { RedisChatMessageHistory } from '@langchain/community/stores/message/ioredis';
import { Calculator } from '@langchain/community/tools/calculator';
import { SerpAPI } from '@langchain/community/tools/serpapi';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { tool } from '@langchain/core/tools';
import { ChatOllama } from '@langchain/ollama';
import Redis from 'ioredis';
import { AgentExecutor, createToolCallingAgent } from 'langchain/agents';
import { BufferMemory } from 'langchain/memory';
import { NextApiRequest, NextApiResponse } from 'next/types';
import { z } from 'zod';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
return res.status(405).json({ message: 'Only GET requests are allowed' });
}
try {
const magicTool = tool(
async ({ input }: { input: number }) => {
return `${input + 2}`;
},
{
name: 'magic_function',
description: 'Applies a magic function to an input.',
schema: z.object({
input: z.number(),
}),
}
);
const llm = new ChatOllama({
model: 'llama3.2',
temperature: 0,
maxRetries: 2,
baseUrl: 'http://localhost:11434',
// other params...
});
const tools = [
magicTool, // custom tool
new Calculator(),
new SerpAPI('your serp key', {
location: 'Austin,Texas,United States',
hl: 'en',
gl: 'us',
}),
];
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant'], // default role
['placeholder', '{chat_history}'], // default role
['human', '{input}'],
['placeholder', '{agent_scratchpad}'],
]);
const agent = createToolCallingAgent({
llm,
tools,
prompt,
});
const client = new Redis('redis://localhost:30001');
const memory = new BufferMemory({
chatHistory: new RedisChatMessageHistory({
sessionId: 'sessionId:' + new Date().toISOString(),
sessionTTL: 300,
client,
}),
aiPrefix: 'ollama',
outputKey: 'output', // 沒有會爆錯
memoryKey: 'chat_history', // 沒有會爆錯
inputKey: 'input', // 沒有會爆錯
returnMessages: true,
});
const agentExecutor = new AgentExecutor({
agent,
tools,
verbose: true, // enable debug
handleParsingErrors: true,
memory, // redis 記憶功能
returnIntermediateSteps: true,
});
const result = await agentExecutor.invoke({ input: 'what is the value of magic_function(3)?' });
const serpResult2 = await agentExecutor.invoke({ input: 'what is the Pokomon?' });
const result3 = await agentExecutor.invoke({ input: 'what is 5 + 2 *5 =' });
const result4 = await agentExecutor.invoke({ input: 'hello', outputKey: 'key1' });
const result5 = await agentExecutor.invoke({ input: '幫我推薦電腦' });
return res.status(200).json({ result, serpResult2, result3, result4, result5 });
} catch (error) {
return res.status(500).json({ message: 'Internal server error' });
}
}


