聊天機器人是各行業的必備工具,提供與使用者的自動化互動。如今,世界上沒有人沒有嘗試過至少一次 Chat GPT(或任何其他人工智慧驅動的聊天機器人)。使用 OpenAI 的 GPT 模型和 LangChain 庫,我們可以建立一個聊天機器人,透過串流響應系統處理會話和處理用戶訊息,在後面的文章中我們將與我們的 API 進行通訊並創建專門處理某些事情的代理。
以下是我們將要介紹的內容:
首先,我們需要一些關鍵依賴項:
我們要做的第一件事是初始化新專案並安裝我們將使用的必要模組。
npm init -Y npm install express langchain openai uuid class-validator class-transformer mutex
首先,我們將定義兩條主要路線:
第一個路由將建立一個新的聊天會話,而第二個路由將向現有會話發送訊息。
router.post('/session', APIKeyMiddleware, createSession); router.post('/session/:id/message', APIKeyMiddleware, postMessage);
APIKeyMiddleware 確保只有經過驗證的請求才能存取這些路由。請注意,您可以實現適合您需求的中間件。
我們將建立一個 AgentManager 類別來處理聊天代理程式。此類負責創建新代理並管理活動會話,因此可以將此類想像為我們 API 的主要入口點,因為它將處理負責聊天的代理。第一個使用者需要建立會話,稍後該會話將用於聊天。
export class AgentManager { private __lock = new Mutex(); private __agents: Map<string, AgentInstance> = new Map(); async createAgent(authorization: string): Promise<string> { const uuid = uuidv4(); const release = await this.__lock.acquire(); try { this.__deleteExpiredAgentsLockless(); let agent: ChatAgent | null = agent = new GeneralChatAgent(authorization); this.__agents.set(uuid, { agent, createdAt: Date.now() }); return uuid; } finally { release(); } } async getAgent(uuid: string): Promise<ChatAgent | null> { const release = await this.__lock.acquire(); try { this.__deleteExpiredAgentsLockless(); const agentInstance = this.__agents.get(uuid); return agentInstance ? agentInstance.agent : null; } finally { release(); } } private __deleteExpiredAgentsLockless(): void {} }
現在我們需要建立通用聊天代理,它將獲取參數,例如auth 或您需要的任何其他參數,並且能夠與API 通信,但現在我們將擴展現有的ChatAgent此步驟僅此而已。
export class GeneralChatAgent extends ChatAgent { constructor() { super(); } }
createAgent 方法初始化代理,鎖定進程,並將其指派給唯一的會話 ID。代理在指定的會話持續時間後過期,這是由 __deleteExpiredAgentsLockless 方法處理的,但我們將在下一次迭代中實現它,您現在可以避免它。
接下來,讓我們定義會話建立和訊息處理路由:
export const createSession = async (req: Request, res: Response): Promise<void> => { const authorization = req.headers['authorization'] as string; try { const sessionId = await agentManager.createAgent(authorization, AgentType.WEB); res.json({ sessionId }); } catch (err) { if (err instanceof Error) { res.status(400).json({ error: err.message }); } else { res.status(500).json({ error: 'An unknown error occurred' }); } } } export const postMessage = async (req: Request, res: Response): Promise<void> => { const { id } = req.params; const { message } = req.body; if (!id || !message) { return res.status(400).json({ error: 'Bad request. Missing session ID or message' }); } try { const agent = await agentManager.getAgent(id); if (!agent) { return res.status(400).json({ error: `No agent found with id ${id}` }); } const iterable = await agent.invoke(message); await streamResponse(res, iterable); } catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : 'An unknown error occurred' }); } }
這裡,createSession 設定一個新會話,並且 postMessage 將使用者的訊息傳送給代理程式。如果未提供會話或訊息,則會傳回 400 Bad Request 錯誤。
串流響應
現在,讓我們的聊天機器人感覺反應迅速且具有互動性的關鍵:串流回應。
async invoke(input: string): Promise<AsyncIterable<Chunk>> { const release = await this.__lock.acquire(); try { const tool = this.determineTool(input); if (tool) { const toolOutput = await tool.call(input); this.callbackQueue.enqueue({ type: ChunkType.TOKEN, value: toolOutput }); this.callbackQueue.enqueue({ type: ChunkType.FINISH, value: '' }); } else { await this.chat.invoke([new HumanMessage(input)], { callbacks: [ { handleLLMNewToken: (token: string) => { this.callbackQueue.enqueue({ type: ChunkType.TOKEN, value: token }); }, handleLLMEnd: () => { this.callbackQueue.enqueue({ type: ChunkType.FINISH, value: '' }); }, handleLLMError: (error: Error) => { this.callbackQueue.enqueue({ type: ChunkType.ERROR, value: error.message }); } } ] }); } return this.createAsyncIterable(this.callbackQueue); } finally { release(); } } private createAsyncIterable(callbackQueue: AgentCallbackQueue): AsyncIterable<Chunk> { return { [Symbol.asyncIterator]: async function* () { let finished = false; while (!finished) { const chunk = await callbackQueue.dequeue(); if (chunk) { yield chunk; if (chunk.type === ChunkType.FINISH || chunk.type === ChunkType.ERROR) { finished = true; } } else { await new Promise(resolve => setTimeout(resolve, 100)); } } } }; }
在呼叫方法中,代理處理使用者的輸入並以區塊的形式回傳回應。每個區塊要么是模型中的令牌,要么是指示流結束的訊息。
createAsyncIterable 方法允許我們一一產生這些區塊並將它們流回客戶端。
最後,我們希望在收到回應時將回應串流傳輸給客戶端,不想等待一段時間直到完成並返回整個回應,更好的解決方案是將回應分塊串流傳輸。
const delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms)); export async function streamResponse(res: Response, iterable: AsyncIterable<Chunk>) { res.setHeader('Content-Type', 'application/x-ndjson'); res.setHeader('Transfer-Encoding', 'chunked'); try { let buffer = ''; for await (const chunk of iterable) { switch (chunk.type) { case ChunkType.TOKEN: buffer += chunk.value; res.write(buffer); if (res.flush) res.flush(); buffer = ''; break; case ChunkType.ERROR: console.error('Error chunk:', chunk.value); if (!res.headersSent) { res.status(500).json({ error: 'Streaming failed.' }); } return; case ChunkType.FINISH: if (buffer.trim()) { res.write(`${buffer.trim()}\n`); } return; } } } catch (err) { console.error('Error during streaming:', err); if (!res.headersSent) { res.status(500).json({ error: 'Streaming failed.' }); } } finally { res.end(); } }
恭喜!您現在擁有一個基本的聊天機器人,可以處理聊天會話並將回應串流回客戶端。該架構可以使用其他工具、更複雜的邏輯或不同的 GPT 模型輕鬆擴展,但目前我們已經有了更複雜的聊天機器人的框架。
透過使用OpenAI強大的語言模型和LangChain的工具管理,您可以為各個領域創建更高級和互動的聊天機器人。您可以擴展聊天機器人的功能,並以您想要的方式製作它,但另一方面您不需要使用 Langchain ,如果您願意,您可以使用 OpenAI 並製作更簡單的聊天機器人。
請繼續關注更多內容,在下一篇文章中我們將討論為我們製作的聊天代理建立工具
快樂編碼!
歡迎查看原文
The above is the detailed content of Building a Powerful Chatbot with OpenAI and LangChain. For more information, please follow other related articles on the PHP Chinese website!