聊天机器人是各个行业的必备工具,提供与用户的自动化交互。如今,世界上没有人没有尝试过至少一次 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 并制作更简单的聊天机器人。
请继续关注更多内容,在下一篇文章中我们将讨论为我们制作的聊天代理构建工具
快乐编码!
欢迎查看原帖
以上是使用 OpenAI 和 LangChain 构建强大的聊天机器人的详细内容。更多信息请关注PHP中文网其他相关文章!