import OpenAI from 'openai'; import { ComponentService } from '@/lib/services/component-service'; import { ConversationService } from '@/lib/services/conversation-service'; import { Component } from "@prisma/client"; const OPENAI_API_KEY = process.env.OPENAI_API_KEY; const OPENAI_BASE_URL = process.env.OPENAI_BASE_URL; if (!OPENAI_API_KEY) { throw new Error("OPENAI_API_KEY is not set"); } // 定义消息类型(兼容OpenAI格式) type MessageParam = OpenAI.Chat.Completions.ChatCompletionMessageParam; type FunctionCall = OpenAI.Chat.Completions.ChatCompletionMessageToolCall; type Message = OpenAI.Chat.Completions.ChatCompletionMessage; export class AIClient { private openai: OpenAI; private tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [ { type: "function", function: { name: "query-components", description: "查询PC配件信息,支持按类型、品牌、价格范围、关键词等条件搜索。单次最多返回5个配件。", parameters: { type: "object", properties: { type: { type: "string", enum: ["CPU", "内存", "硬盘", "主板", "显卡", "机箱"], description: "配件类型" }, brand: { type: "string", description: "品牌名称" }, minPrice: { type: "number", description: "最低价格" }, maxPrice: { type: "number", description: "最高价格" }, search: { type: "string", description: "搜索关键词,会在名称、品牌、型号、描述中搜索" }, page: { type: "integer", description: "页码,默认为1" }, } } } }, { type: "function", function: { name: "show-components", description: "根据提供的配件ID列表,向用户以卡片形式展示一个或多个具体型号的配件。如果你提到了某些特定配件,请用此工具更好地向用户展示。", parameters: { type: "object", properties: { component_ids: { type: "array", description: "一个包含一个或多个要展示的配件ID的数组。这些ID来自`query-components`工具的查询结果。", items: { type: "string", description: "单个配件的唯一ID" } } }, required: ["component_ids"] } } } ]; constructor() { this.openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_BASE_URL || "https://api.openai.com/v1" }); } // 查询配件 private async queryComponents(args: any): Promise { try { const result = await ComponentService.queryComponents({ type: args.type, brand: args.brand, minPrice: args.minPrice, maxPrice: args.maxPrice, search: args.search, page: args.page || 1, limit: 5 }) if (result.components.length === 0) { return "未找到符合条件的配件" } // 返回格式化的结果 return JSON.stringify(result.components.map(c => ({ id: c.id, name: c.name, brand: c.brand, model: c.model, price: c.price, description: c.description, stock: c.stock, specifications: c.specifications, }))) } catch (error) { console.error('查询配件失败:', error) return `查询配件时发生错误: ${error instanceof Error ? error.message : '未知错误'}` } } async *processQuery( query: string, userId: string, conversationId?: string ): AsyncGenerator { let messages: MessageParam[]; let currentConversationId = conversationId; // 如果提供了对话ID,加载现有对话 if (conversationId) { const conversation = await ConversationService.getConversation(conversationId, userId); if (conversation) { messages = [...conversation.messages as MessageParam[]]; // 添加新的用户消息 messages.push({ role: "user", content: query }); } else { throw new Error('对话不存在或无权限访问'); } } else { // 创建新对话 currentConversationId = await ConversationService.createConversation(userId, query); // 发送新对话ID给前端 yield `conversation_id:${currentConversationId}`; // 获取新创建的对话消息 const conversation = await ConversationService.getConversation(currentConversationId, userId); if (!conversation) { throw new Error('创建对话失败'); } messages = conversation.messages as MessageParam[]; } try { let maxIterations = 10; // 防止无限循环 let currentIteration = 0; while (currentIteration < maxIterations) { currentIteration++; console.log("Send Req", messages); const stream = await this.openai.chat.completions.create({ model: ["web-rev-claude-sonnet-4-20250514", "gemini-2.5-flash"][1], max_tokens: 2048, messages, tools: this.tools, stream: true, }); let assistantMessage: MessageParam = { role: "assistant", content: "", tool_calls: [] }; // 处理流式响应 for await (const chunk of stream) { const delta = chunk.choices[0]?.delta; if (!delta) continue; // 处理文本内容 if (delta.content) { assistantMessage.content += delta.content; yield delta.content; } // 处理工具调用 if (delta.tool_calls) { for (const toolCall of delta.tool_calls) { const index = toolCall.index; // 初始化工具调用 if (!assistantMessage.tool_calls) { assistantMessage.tool_calls = []; } if (!assistantMessage.tool_calls[index]) { assistantMessage.tool_calls[index] = { id: toolCall.id || "", type: "function", function: { name: toolCall.function?.name || "", arguments: "" } }; yield `\n\n**使用工具**: ${toolCall.function?.name}\n\n\`\`\`\n`; } // 累积工具调用参数 if (toolCall.function?.arguments) { assistantMessage.tool_calls[index].function.arguments += toolCall.function.arguments; yield toolCall.function.arguments; } } } } // 如果有工具调用,结束代码块 if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) { yield `\n\`\`\``; } // 将助手消息添加到对话中 messages.push(assistantMessage); // 如果没有工具调用,结束循环 if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) { break; } // 处理工具调用 let needRerun = false; for (const toolCall of assistantMessage.tool_calls) { if (!toolCall.function.name) continue; let toolArgs: any = {}; try { toolArgs = JSON.parse(toolCall.function.arguments); } catch (error) { console.error('解析工具参数失败:', error); continue; } let toolResult = ""; switch (toolCall.function.name) { case "query-components": toolResult = await this.queryComponents(toolArgs); needRerun = true; yield 'next_block'; break; case "show-components": const components = await ComponentService.getComponentsByIds(toolArgs.component_ids); toolResult = `成功向用户展示了 ${components.map(c => c?.id).join(',')}`; needRerun = true; yield `show_card: ${JSON.stringify(components)}`; break; default: console.error(`未知工具调用: ${toolCall.function.name}`); toolResult = `未知工具: ${toolCall.function.name}`; } // 添加工具调用结果消息 messages.push({ role: "tool", content: toolResult, tool_call_id: toolCall.id }); } if (!needRerun) break; } if (currentIteration >= maxIterations) { yield "\n\n⚠️ 达到最大处理轮次,对话结束。"; } // 保存更新后的对话到数据库 if (currentConversationId) { // 转换消息格式以兼容现有的ConversationService const compatibleMessages = messages.map((msg): any => { if (msg.role === "tool") { // 将工具消息转换为用户消息格式以保持兼容性 return { role: "user", content: `Tool result: ${msg.content}` }; } return { role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }); await ConversationService.updateConversationMessages(currentConversationId, userId, compatibleMessages); } } catch (error) { console.error('处理查询时发生错误:', error); yield `\n\n❌ 抱歉,处理您的请求时遇到了问题: ${error instanceof Error ? error.message : '未知错误'}`; // 即使出错也要保存对话 if (currentConversationId) { try { const compatibleMessages = messages.map((msg): any => { if (msg.role === "tool") { return { role: "user", content: `Tool result: ${msg.content}` }; } return { role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }); await ConversationService.updateConversationMessages(currentConversationId, userId, compatibleMessages); } catch (saveError) { console.error('保存对话失败:', saveError); } } } } /** * 获取用户的对话历史 */ async getUserConversations(userId: string, page = 1, limit = 20) { return await ConversationService.getUserConversations(userId, page, limit); } /** * 获取特定对话的详情 */ async getConversation(conversationId: string, userId: string) { return await ConversationService.getConversation(conversationId, userId); } /** * 删除对话 */ async deleteConversation(conversationId: string, userId: string) { return await ConversationService.deleteConversation(conversationId, userId); } /** * 更新对话标题 */ async updateConversationTitle(conversationId: string, userId: string, title: string) { return await ConversationService.updateConversationTitle(conversationId, userId, title); } }