首页 >web前端 >js教程 >使用 OpenAI 和 LangChain 构建强大的聊天机器人

使用 OpenAI 和 LangChain 构建强大的聊天机器人

王林
王林原创
2024-09-12 10:33:021206浏览

介绍

聊天机器人是各个行业的必备工具,提供与用户的自动化交互。如今,世界上没有人没有尝试过至少一次 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

设置快速路线

首先,我们将定义两条主要路线:

第一个路由将创建一个新的聊天会话,而第二个路由将向现有会话发送消息。

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 并制作更简单的聊天机器人。

请继续关注更多内容,在下一篇文章中我们将讨论为我们制作的聊天代理构建工具
快乐编码!

欢迎查看原帖

Building a Powerful Chatbot with OpenAI and LangChain

使用 OpenAI 和 LangChain 构建强大的聊天机器人

在这篇文章中,我们将介绍如何使用 OpenAI 和 LangChain 创建一个基本但功能强大的聊天机器人

bojanjagetic.com

以上是使用 OpenAI 和 LangChain 构建强大的聊天机器人的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn