Home > Web Front-end > JS Tutorial > body text

Building a Powerful Chatbot with OpenAI and LangChain

王林
Release: 2024-09-12 10:33:02
Original
1082 people have browsed it

介紹

聊天機器人是各行業的必備工具,提供與使用者的自動化互動。如今,世界上沒有人沒有嘗試過至少一次 Chat GPT(或任何其他人工智慧驅動的聊天機器人)。使用 OpenAI 的 GPT 模型和 LangChain 庫,我們可以建立一個聊天機器人,透過串流響應系統處理會話和處理用戶訊息,在後面的文章中我們將與我們的 API 進行通訊並創建專門處理某些事情的代理。

以下是我們將要介紹的內容:

  • 使用中間件設定 Express 伺服器。
  • 建立一個 `AgentManager` 來處理聊天機器人代理程式。
  • 建立一個 ChatAgent 來處理聊天機器人代理程式。
  • 將聊天機器人回應即時串流回用戶。

設定環境

首先,我們需要一些關鍵依賴項:

  • 用於處理 API 請求的 Express。
  • LangChain管理GPT模型和工具。
  • OpenAI 用於 GPT 模型交互作用。我們需要從 Open AI 獲取令牌才能使用生成會話並與聊天機器人互動

安裝依賴項

我們要做的第一件事是初始化新專案並安裝我們將使用的必要模組。

npm init -Y
npm install express langchain openai uuid class-validator class-transformer mutex
Copy after login

設定快速路線

首先,我們將定義兩條主要路線:

第一個路由將建立一個新的聊天會話,而第二個路由將向現有會話發送訊息。

router.post('/session', APIKeyMiddleware, createSession);
router.post('/session/:id/message', APIKeyMiddleware, postMessage);
Copy after login

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 {}
}
Copy after login

建立總代理

現在我們需要建立通用聊天代理,它將獲取參數,例如auth 或您需要的任何其他參數,並且能夠與API 通信,但現在我們將擴展現有的ChatAgent此步驟僅此而已。

export class GeneralChatAgent extends ChatAgent {
    constructor() {
        super();
    }
}
Copy after login

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' });
    }
}
Copy after login

這裡,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));
                }
            }
        }
    };
}
Copy after login

在呼叫方法中,代理處理使用者的輸入並以區塊的形式回傳回應。每個區塊要么是模型中的令牌,要么是指示流結束的訊息。

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();
    }
}

Copy after login

結論

恭喜!您現在擁有一個基本的聊天機器人,可以處理聊天會話並將回應串流回客戶端。該架構可以使用其他工具、更複雜的邏輯或不同的 GPT 模型輕鬆擴展,但目前我們已經有了更複雜的聊天機器人的框架。

透過使用OpenAI強大的語言模型和LangChain的工具管理,您可以為各個領域創建更高級和互動的聊天機器人。您可以擴展聊天機器人的功能,並以您想要的方式製作它,但另一方面您不需要使用 Langchain ,如果您願意,您可以使用 OpenAI 並製作更簡單的聊天機器人。

請繼續關注更多內容,在下一篇文章中我們將討論為我們製作的聊天代理建立工具
快樂編碼!

歡迎查看原文

Building a Powerful Chatbot with OpenAI and LangChain

Building a Powerful Chatbot with OpenAI and LangChain

In this post, we'll walk through how to create a basic but powerful chatbot using OpenAI and LangChain

bojanjagetic.com

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!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!