From 4108ea882e3c2ee8d775ee5b5ea482c8447a09a2 Mon Sep 17 00:00:00 2001 From: geyun <854914462@qq.com> Date: Fri, 21 Aug 2026 08:47:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8D=A2=20ai=20apikey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server2.js | 525 +++++++++++++++++++++++++++-------------------------- 1 file changed, 267 insertions(+), 258 deletions(-) diff --git a/server2.js b/server2.js index 921fe30..0e6089f 100755 --- a/server2.js +++ b/server2.js @@ -121,9 +121,13 @@ app.use(express.urlencoded({ extended: true })); // 支持 form-data 字段 // 初始化客户端 const client = new OpenAI({ + //apiKey: "4pJOKPLhkW7qJQOeQBe9ACXyBrcu7A8bAjTgo2auV5lSUZbmcn1rJQQJ99CEACHYHv6XJ3w3AAAAACOGk1hV", + //baseURL: "https://tech-mp6cvddb-eastus2.cognitiveservices.azure.com/", + //apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", - baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-4.1", - defaultQuery: { "api-version": "2024-04-01-preview" }, + baseURL: "https://satabot.services.ai.azure.com/openai/v1/", + //baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-4.1", + //defaultQuery: { "api-version": "2024-04-01-preview" }, timeout: 120000, fetch: fetchIPv4, // 关键点:把自定义 fetch 注入 SDK // 确保底层也传 agent(可选,但保险) @@ -132,9 +136,14 @@ const client = new OpenAI({ } }); const client1 = new OpenAI({ + //apiKey: "4pJOKPLhkW7qJQOeQBe9ACXyBrcu7A8bAjTgo2auV5lSUZbmcn1rJQQJ99CEACHYHv6XJ3w3AAAAACOGk1hV", + //baseURL: "https://tech-mp6cvddb-eastus2.cognitiveservices.azure.com/", + //apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", - baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-5-mini/", - defaultQuery: { "api-version": "2024-04-01-preview" }, + baseURL: "https://satabot.services.ai.azure.com/openai/v1/", + + //baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-5-mini/", + //defaultQuery: { "api-version": "2024-04-01-preview" }, timeout: 120000, fetch: fetchIPv4, // 关键点:把自定义 fetch 注入 SDK // 确保底层也传 agent(可选,但保险) @@ -160,8 +169,8 @@ async function directCall() { console.timeEnd("directCall"); } -test1(); -directCall(); +//test1(); +//directCall(); // 获取 access_token // getAccessToken(); @@ -1391,7 +1400,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); // } // } - + // console.log("====== 开始图像分析11... ======"); // const { requestId } = req.body; @@ -1445,7 +1454,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", // [sessId, userId || null] // ); - + // console.log("====== 开始图像分析22... ======"); // // 构建历史消息数组 @@ -1474,7 +1483,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // return message; // }); - + // console.log("====== 开始图像分析33... ======"); // // ------------------ 保存用户提问 ------------------ @@ -1485,7 +1494,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // // ------------------ 图像分析 ------------------ // let sceneText = ""; - + // console.log("====== 开始图像分析55... ======"); // // If an image is uploaded, start with image analysis @@ -1539,7 +1548,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // ].filter(Boolean), // }, // ]; - + // console.log("====== 开始图像分析666... ======"); // const completion = await client.chat.completions.create({ @@ -1550,7 +1559,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // console.log("====== 开始图像分析... ======"); // /* TLS 加密缓冲 */ - + // for await (const chunk of completion) { // if (stopFlags.get(requestId)) { @@ -1722,7 +1731,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // ); // console.log("生成的标题:", title); - + // await pool.query( // "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title,thinking) VALUES (?, ?, 'assistant', ?, ?,?)", // [sessId, userId || null, llmText, title,sceneText] @@ -1799,7 +1808,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); // } // } - + // console.log("====== 开始图像分析11... ======"); // const { requestId } = req.body; @@ -1814,7 +1823,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // res.flushHeaders?.(); // res.socket.setNoDelay(true); // 禁用缓冲,实时推送 - + // let aborted = false; // req.on("close", () => { // console.log("🔴 前端断开连接,停止整个分析流程"); @@ -1856,7 +1865,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", // [sessId, userId || null] // ); - + // console.log("====== 开始图像分析22... ======"); // // 构建历史消息数组 @@ -1885,7 +1894,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // return message; // }); - + // console.log("====== 开始图像分析33... ======"); // // ------------------ 保存用户提问 ------------------ @@ -1896,7 +1905,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // // ------------------ 图像分析 ------------------ // let sceneText = ""; - + // console.log("====== 开始图像分析55... ======"); // // If an image is uploaded, start with image analysis @@ -1938,7 +1947,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // ].filter(Boolean), // }, // ]; - + // console.log("====== 开始图像分析666... ======"); // const completion = await client.chat.completions.create({ @@ -1949,7 +1958,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // }); // console.log("====== 开始图像分析... ======"); - + // for await (const chunk of completion) { // if (stopFlags.get(requestId)) { @@ -2147,7 +2156,7 @@ app.post("/refreshAiChatNum", async (req, res) => { // ); // console.log("生成的标题:", title); - + // await pool.query( // "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title,thinking) VALUES (?, ?, 'assistant', ?, ?,?)", // [sessId, userId || null, llmText, title,sceneText] @@ -2196,31 +2205,31 @@ app.post("/refreshAiChatNum", async (req, res) => { app.post("/analyze", upload.single("file"), async (req, res) => { - const { userId } = req.body; - try { - const exists = await hasQuota(userId); - if (!exists) { - const [userRemainChatNum] = await pool.query( - "SELECT ai_chat_num from sys_user where user_id= ?", - [userId] - ); - if (userRemainChatNum.length > 0) { - const quota = userRemainChatNum[0].ai_chat_num; - if (quota <= 0 && quota !== -1) { - return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); - } - await setQuota(userId, quota); - } - } else { - const quota = await getQuota(userId); - if (quota <= 0 && quota !== -1) { - return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); - } - } - - console.log("====== 开始图像分析11... ======"); + const { userId } = req.body; + try { + const exists = await hasQuota(userId); + if (!exists) { + const [userRemainChatNum] = await pool.query( + "SELECT ai_chat_num from sys_user where user_id= ?", + [userId] + ); + if (userRemainChatNum.length > 0) { + const quota = userRemainChatNum[0].ai_chat_num; + if (quota <= 0 && quota !== -1) { + return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); + } + await setQuota(userId, quota); + } + } else { + const quota = await getQuota(userId); + if (quota <= 0 && quota !== -1) { + return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); + } + } - const { requestId } = req.body; + console.log("====== 开始图像分析11... ======"); + + const { requestId } = req.body; /* TLS 加密缓冲,小块数据可能先缓存在服务器端,不立即发送,会导致图像分析模型的结果不能立即返回 */ res.writeHead(200, { @@ -2228,31 +2237,31 @@ app.post("/analyze", upload.single("file"), async (req, res) => { "Cache-Control": "no-cache, no-transform", "Connection": "keep-alive", "X-Accel-Buffering": "no", - }); + }); res.flushHeaders?.(); res.socket.setNoDelay(true); // 禁用缓冲,实时推送 - + let aborted = false; - req.on("close", () => { - console.log("🔴 前端断开连接,停止整个分析流程"); - aborted = true; - }); + req.on("close", () => { + console.log("🔴 前端断开连接,停止整个分析流程"); + aborted = true; + }); - console.time("analyze"); - const logBody = { ...req.body }; - if (req.body?.imageBase64) { - delete logBody.imageBase64; - } - console.log("analyze: ", logBody); + console.time("analyze"); + const logBody = { ...req.body }; + if (req.body?.imageBase64) { + delete logBody.imageBase64; + } + console.log("analyze: ", logBody); - if (!req.body?.userId || !req.body?.sessionId) { - console.log("缺少必要参数"); - return res.status(400).json({ error: "缺少必要参数" }); - } - const { userPrompt, imageBase64, sessionId } = req.body; - const [rows] = await pool.query( - ` + if (!req.body?.userId || !req.body?.sessionId) { + console.log("缺少必要参数"); + return res.status(400).json({ error: "缺少必要参数" }); + } + const { userPrompt, imageBase64, sessionId } = req.body; + const [rows] = await pool.query( + ` SELECT EXISTS( SELECT 1 FROM ai_chat_messages @@ -2262,67 +2271,66 @@ app.post("/analyze", upload.single("file"), async (req, res) => { AND title != '' ) AS hasTitle `, - [sessionId, userId] - ); - const existingTitle = rows[0].hasTitle === 1; // true / false - console.log(existingTitle, rows?.[0], 9999, rows); + [sessionId, userId] + ); + const existingTitle = rows[0].hasTitle === 1; // true / false + console.log(existingTitle, rows?.[0], 9999, rows); - const sessId = sessionId || Date.now().toString(); - const MAX_HISTORY = 6; - // ------------------ 获取历史对话(上下文) ------------------ - const [historyRows] = await pool.query( - "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", - [sessId, userId || null] - ); - - console.log("====== 开始图像分析22... ======"); + const sessId = sessionId || Date.now().toString(); + const MAX_HISTORY = 6; + // ------------------ 获取历史对话(上下文) ------------------ + const [historyRows] = await pool.query( + "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", + [sessId, userId || null] + ); - // 构建历史消息数组 - const historyMessages = historyRows.slice(-MAX_HISTORY).map(row => { - const message = { - role: row.role, - content: [] - }; + console.log("====== 开始图像分析22... ======"); - // 添加文本内容 - if (row.content) { - message.content.push({ type: "text", text: row.content }); - } + // 构建历史消息数组 + const historyMessages = historyRows.slice(-MAX_HISTORY).map(row => { + const message = { + role: row.role, + content: [] + }; - // 添加图片内容 - if (row.image_base64) { - message.content.push({ - type: "image_url", - image_url: { - url: row.image_base64.startsWith("data:") - ? row.image_base64 - : `data:image/png;base64,${row.image_base64}`, - } - }); - } + // 添加文本内容 + if (row.content) { + message.content.push({ type: "text", text: row.content }); + } - return message; - }); - - console.log("====== 开始图像分析33... ======"); + // 添加图片内容 + if (row.image_base64) { + message.content.push({ + type: "image_url", + image_url: { + url: row.image_base64.startsWith("data:") + ? row.image_base64 + : `data:image/png;base64,${row.image_base64}`, + } + }); + } - // ------------------ 保存用户提问 ------------------ - await pool.query( - "INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)", - [sessId, userId || null, userPrompt || "", imageBase64 || null] - ); + return message; + }); - // ------------------ 图像分析(核心修改:输出精准场景描述) ------------------ - let sceneText = ""; - - console.log("====== 开始图像分析55... ======"); + console.log("====== 开始图像分析33... ======"); - if (imageBase64) - { - const imageAnalysisMessages = [ - { - role: "system", - content: + // ------------------ 保存用户提问 ------------------ + await pool.query( + "INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)", + [sessId, userId || null, userPrompt || "", imageBase64 || null] + ); + + // ------------------ 图像分析(核心修改:输出精准场景描述) ------------------ + let sceneText = ""; + + console.log("====== 开始图像分析55... ======"); + + if (imageBase64) { + const imageAnalysisMessages = [ + { + role: "system", + content: // ` // 你是图像场景描述助手,严格按指定格式输出1次图片描述,绝不重复、无多余内容、无分析建议。 // 输出格式(固定话术,仅替换【】内容,必须1句完整文本): @@ -2333,7 +2341,7 @@ app.post("/analyze", upload.single("file"), async (req, res) => { // 3. 【标注特征】:方阵编号和绿色边界线/地块编号和边界标注(按图实际) // 强制要求:仅输出1次上述格式文本,无重复、无追加、无解释 // ` // 核心:引导精准场景描述,摒弃泛化内容 - ` + ` 你是图像场景描述助手,任务是根据用户上传的图片内容,输出一段自然且详细的描述。根据图片内容判断场景类型和任务需求,并结合图片的特征进行分析和推理。确保输出的描述自然流畅,符合图片中的实际情况。 输出格式(固定话术,仅替换【】内容,必须1句完整文本): 用户上传了一张【图片类型】的图片,从图片描述来看,这明显是一个【场景属性】的图片,有【标注特征】的特点。 @@ -2345,13 +2353,14 @@ app.post("/analyze", upload.single("file"), async (req, res) => { 强制要求:输出一次自然流畅、无死板格式的描述,并根据图片分析作业计划的可行性。要避免固定模板输出,更多地展现图片内容与作业任务的关联,并基于推理得出任务可能的实施方式。 ` - }, - ...historyMessages, - { - role: "user", - content: [ - { type: "text", text: - ` + }, + ...historyMessages, + { + role: "user", + content: [ + { + type: "text", text: + ` 输出格式要求(固定话术,仅替换【】内容,必须1句完整文本): 用户上传了一张【图片类型】的图片,从图片描述来看,这明显是一个【场景属性】的图片,有【标注特征】的特点。 1. 【图片类型】:根据图片内容,判断为如光伏场站、农业梯田、坡地、森林、生态保护区、矿区等(根据实际场景推断)。 @@ -2363,57 +2372,57 @@ app.post("/analyze", upload.single("file"), async (req, res) => { 强制要求:输出一次自然流畅、无死板格式的描述,并根据图片分析作业计划的可行性。要避免固定模板输出,更多地展现图片内容与作业任务的关联,并基于推理得出任务可能的实施方式。 ` }, // 贴合用户想要的描述风格 - imageBase64 ? { - type: "image_url", - image_url: { - url: imageBase64.startsWith("data:") ? imageBase64 : `data:image/png;base64,${imageBase64}`, - }, - } : null, - ].filter(Boolean), - }, - ]; - - // 去掉max_tokens限制,保证描述完整 - const completion = await client.chat.completions.create({ - model: "gpt-4.1", - stream: true, - messages: imageAnalysisMessages - }); + imageBase64 ? { + type: "image_url", + image_url: { + url: imageBase64.startsWith("data:") ? imageBase64 : `data:image/png;base64,${imageBase64}`, + }, + } : null, + ].filter(Boolean), + }, + ]; - console.log("====== 开始图像分析... ======"); - for await (const chunk of completion) { - if (stopFlags.get(requestId)) { - console.log("⛔ 图像分析被中断"); - res.end(); - stopFlags.delete(requestId); - return; - } - const delta = chunk.choices[0]?.delta?.content || ""; - sceneText += delta; - if (delta) { - res.write(`data: ${JSON.stringify({ type: "thinking", content: delta })}\n\n`); - } - } - console.log(sceneText); - console.log("====== 图像分析结束 ======"); - } + // 去掉max_tokens限制,保证描述完整 + const completion = await client.chat.completions.create({ + model: "gpt-5.6-luna", + stream: true, + messages: imageAnalysisMessages + }); - // ------------------ 思路阶段(核心修改:先呼应场景,再展开思考) ------------------ - const thinkingMessages = [ - { - role: "system", - content: - // ` - // 你是新能源领域专业分析人员,以第一人称模拟自然思考,基于图片场景和用户需求切入,开头不重复图片描述,直接顺延展开深度思考,不输出最终结论和具体方案。 - // 规则: - // 1. 思考开头直接承接图片场景(光伏场站/梯田+割草需求),不重复完整描述句,自然切入需求解读 - // 2. 思考逻辑:先解读用户要割草计划的表面诉求,再深挖背后核心顾虑,最后梳理需确认的基础信息,贴合场景属性 - // 3. 语气:自然口语化,连贯段落,无列表、无具体步骤/工具/人员安排、无泛化表述 - // 4. 必须结合图片标注特征(方阵编号/边界线)展开思考,体现场景关联性 - // 示例(参考风格,无重复描述): - // 用户要制定详细的割草计划,表面是想要一套可执行的作业流程,说白了就是希望在保障场站发电效率、作业安全的同时,平衡好长期运维成本,不过我得先琢磨他们真正担心的点。背后大概率在意割草会不会损伤光伏方阵或支架、作业时的安全风险管控,还有不同编号方阵和边界区域的作业能不能全覆盖、无死角,以及割草后植被反弹遮光影响发电收益的问题。再往深层想,他们还会关心草屑处理会不会堵塞排水或影响设备通风、是否符合环保监管要求、季节和天气对作业时机的影响,还有一次性清理和周期性维护的成本权衡。图里的方阵编号和绿色边界线,说明需要按区作业,还要关注场内通道和设施位置来规避风险,所以我会先梳理场站地形、植被类型、关键设施分布这些基础信息,明确作业优先级和潜在风险点。 - // ` // 给示例,锚定用户想要的风格 - ` + console.log("====== 开始图像分析... ======"); + for await (const chunk of completion) { + if (stopFlags.get(requestId)) { + console.log("⛔ 图像分析被中断"); + res.end(); + stopFlags.delete(requestId); + return; + } + const delta = chunk.choices[0]?.delta?.content || ""; + sceneText += delta; + if (delta) { + res.write(`data: ${JSON.stringify({ type: "thinking", content: delta })}\n\n`); + } + } + console.log(sceneText); + console.log("====== 图像分析结束 ======"); + } + + // ------------------ 思路阶段(核心修改:先呼应场景,再展开思考) ------------------ + const thinkingMessages = [ + { + role: "system", + content: + // ` + // 你是新能源领域专业分析人员,以第一人称模拟自然思考,基于图片场景和用户需求切入,开头不重复图片描述,直接顺延展开深度思考,不输出最终结论和具体方案。 + // 规则: + // 1. 思考开头直接承接图片场景(光伏场站/梯田+割草需求),不重复完整描述句,自然切入需求解读 + // 2. 思考逻辑:先解读用户要割草计划的表面诉求,再深挖背后核心顾虑,最后梳理需确认的基础信息,贴合场景属性 + // 3. 语气:自然口语化,连贯段落,无列表、无具体步骤/工具/人员安排、无泛化表述 + // 4. 必须结合图片标注特征(方阵编号/边界线)展开思考,体现场景关联性 + // 示例(参考风格,无重复描述): + // 用户要制定详细的割草计划,表面是想要一套可执行的作业流程,说白了就是希望在保障场站发电效率、作业安全的同时,平衡好长期运维成本,不过我得先琢磨他们真正担心的点。背后大概率在意割草会不会损伤光伏方阵或支架、作业时的安全风险管控,还有不同编号方阵和边界区域的作业能不能全覆盖、无死角,以及割草后植被反弹遮光影响发电收益的问题。再往深层想,他们还会关心草屑处理会不会堵塞排水或影响设备通风、是否符合环保监管要求、季节和天气对作业时机的影响,还有一次性清理和周期性维护的成本权衡。图里的方阵编号和绿色边界线,说明需要按区作业,还要关注场内通道和设施位置来规避风险,所以我会先梳理场站地形、植被类型、关键设施分布这些基础信息,明确作业优先级和潜在风险点。 + // ` // 给示例,锚定用户想要的风格 + ` 你是新能源领域的专业分析人员,以第一人称模拟自然思考。基于用户提供的图片场景,直奔用户需求,避免重复场景描述,直接通过场景细节展开深度思考,不输出最终结论和具体方案。 规则: 1. 思考开始时直接切入用户的实际需求,结合图片中的内容,避免不必要的描述,准确把握用户核心诉求。 @@ -2424,23 +2433,23 @@ app.post("/analyze", upload.single("file"), async (req, res) => { 示例: 用户希望制定一份割草计划,表面上看,是为了确保割草作业高效、安全,并控制运维成本。但从我对场站的理解来看,他们更深层次的顾虑是割草时是否会影响到光伏方阵的正常运作,或者是否会对设备的支架产生影响。再者,割草过程中需要特别注意的是,不同区域、不同编号方阵的处理,可能需要差异化的作业策略,确保每个区域都能得到充分覆盖,且不留死角。我还需要思考割草后的草屑是否会影响场站的排水系统、设备的通风散热,甚至环保合规问题。基于这些疑虑,我需要进一步确认场站的地形特点、植被情况、方阵分布,以及重要设施的位置,以便更好地评估潜在的作业风险与优先级。 ` - }, - ...historyMessages, - { role: "user", content: userPrompt || "请分析图片并制定计划" }, - { role: "assistant", content: sceneText } // 传入精准场景描述 - ]; - const thinkingStream = await client.chat.completions.create({ - model: "gpt-4", - stream: true, - messages: thinkingMessages, - max_completion_tokens: 1500, // 根据需要调整 - }); + }, + ...historyMessages, + { role: "user", content: userPrompt || "请分析图片并制定计划" }, + { role: "assistant", content: sceneText } // 传入精准场景描述 + ]; + const thinkingStream = await client.chat.completions.create({ + model: "gpt-5.6-luna", + stream: true, + messages: thinkingMessages, + max_completion_tokens: 1500, // 根据需要调整 + }); - res.write(`data: ${JSON.stringify({ type: "phase", content: "thinking" })}\n\n`); - let thinkingText = ""; + res.write(`data: ${JSON.stringify({ type: "phase", content: "thinking" })}\n\n`); + let thinkingText = ""; - console.log("====== 开始思路输出 ======"); - for await (const chunk of thinkingStream) { + console.log("====== 开始思路输出 ======"); + for await (const chunk of thinkingStream) { if (stopFlags.get(requestId)) { console.log("⛔ 思路阶段被中断"); res.end(); @@ -2463,14 +2472,14 @@ app.post("/analyze", upload.single("file"), async (req, res) => { } } } - console.log(thinkingText); - console.log("====== 思路输出结束 ======"); + console.log(thinkingText); + console.log("====== 思路输出结束 ======"); - // ------------------ 结论阶段(无修改,保持原有逻辑) ------------------ - const answerMessages = [ - { - role: "system", - content: ` + // ------------------ 结论阶段(无修改,保持原有逻辑) ------------------ + const answerMessages = [ + { + role: "system", + content: ` 你是一名新能源项目分析AI。 请基于【思路概览】,输出【最终结论】。 要求: @@ -2479,23 +2488,23 @@ app.post("/analyze", upload.single("file"), async (req, res) => { - 不描述图片 - 不出现“分析过程”“推理”"thinking""answer"等字眼 ` - }, - ...historyMessages, - { role: "user", content: userPrompt || "请给出最终分析建议" }, - { role: "assistant", content: thinkingText } - ]; + }, + ...historyMessages, + { role: "user", content: userPrompt || "请给出最终分析建议" }, + { role: "assistant", content: thinkingText } + ]; - const answerStream = await client.chat.completions.create({ - model: "gpt-4", - stream: true, - messages: answerMessages - }); + const answerStream = await client.chat.completions.create({ + model: "gpt-5.6-luna", + stream: true, + messages: answerMessages + }); - res.write(`data: ${JSON.stringify({ type: "phase", content: "answer" })}\n\n`); - let llmText = ""; + res.write(`data: ${JSON.stringify({ type: "phase", content: "answer" })}\n\n`); + let llmText = ""; - console.log("====== 开始最终分析 ======"); - for await (const chunk of answerStream) { + console.log("====== 开始最终分析 ======"); + for await (const chunk of answerStream) { if (stopFlags.get(requestId)) { console.log("⛔ 最终分析被中断"); res.end(); @@ -2504,7 +2513,7 @@ app.post("/analyze", upload.single("file"), async (req, res) => { } const delta = chunk.choices[0]?.delta?.content || ""; - + // 如果 delta 为空,不处理 if (delta) { // 逐字输出 @@ -2520,64 +2529,64 @@ app.post("/analyze", upload.single("file"), async (req, res) => { } } } - console.log(llmText); - console.log("====== 最终分析结束 ======"); + console.log(llmText); + console.log("====== 最终分析结束 ======"); - // ------------------ 保存结果到数据库(无修改) ------------------ - sceneText += thinkingText; - if (existingTitle) { - await pool.query( - "INSERT INTO ai_chat_messages (session_id, user_id, role, content,thinking) VALUES (?, ?, 'assistant', ?,?)", - [sessId, userId || null, llmText,sceneText] - ); - } else { - try { - const titlePrompt = `请根据以下对话内容生成一个简短的标题(不超过15字):\n用户问题: ${userPrompt}\nAI回答: ${llmText}`; - const titleResp = await client1.chat.completions.create({ - model: "gpt-5-mini", - messages: [ - { role: "system", content: "你是标题生成助手,只返回标题,不要解释。" }, - { role: "user", content: titlePrompt } - ] - }); - const title = titleResp.choices[0]?.message?.content?.trim() || "未命名会话"; - res.write(`data: ${JSON.stringify({ type: "title", content: title })}\n\n`); - console.log("生成的标题:", title); - await pool.query( - "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title,thinking) VALUES (?, ?, 'assistant', ?, ?,?)", - [sessId, userId || null, llmText, title,sceneText] - ); - } catch (err) { - console.error("生成标题失败:", err.message); - } - } + // ------------------ 保存结果到数据库(无修改) ------------------ + sceneText += thinkingText; + if (existingTitle) { + await pool.query( + "INSERT INTO ai_chat_messages (session_id, user_id, role, content,thinking) VALUES (?, ?, 'assistant', ?,?)", + [sessId, userId || null, llmText, sceneText] + ); + } else { + try { + const titlePrompt = `请根据以下对话内容生成一个简短的标题(不超过15字):\n用户问题: ${userPrompt}\nAI回答: ${llmText}`; + const titleResp = await client1.chat.completions.create({ + model: "gpt-5.6-luna", + messages: [ + { role: "system", content: "你是标题生成助手,只返回标题,不要解释。" }, + { role: "user", content: titlePrompt } + ] + }); + const title = titleResp.choices[0]?.message?.content?.trim() || "未命名会话"; + res.write(`data: ${JSON.stringify({ type: "title", content: title })}\n\n`); + console.log("生成的标题:", title); + await pool.query( + "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title,thinking) VALUES (?, ?, 'assistant', ?, ?,?)", + [sessId, userId || null, llmText, title, sceneText] + ); + } catch (err) { + console.error("生成标题失败:", err.message); + } + } - res.write("data: [DONE]\n\n"); - res.end(); + res.write("data: [DONE]\n\n"); + res.end(); - // 删除上传文件 - if (req.file && req.file.path) { - fs.unlinkSync(req.file.path); - } - console.log("====== 分析完成 ======"); + // 删除上传文件 + if (req.file && req.file.path) { + fs.unlinkSync(req.file.path); + } + console.log("====== 分析完成 ======"); - } catch (err) { + } catch (err) { console.error("❌ 调用失败:", err.message); if (!res.headersSent) { res.status(500).json({ error: err.message }); } else { - res.write(`data: ${JSON.stringify({type: "error",message: err.message})}\n\n`); + res.write(`data: ${JSON.stringify({ type: "error", message: err.message })}\n\n`); res.end(); } } finally { - let userChatNum = await getQuota(userId); - if (userChatNum !== -1 && userChatNum !== undefined) { - userChatNum = Math.max(0, userChatNum - 1); - await setQuota(userId, userChatNum); - await pool.query("update sys_user set ai_chat_num = ? where user_id= ?",[userChatNum, userId]); - console.log("更新用户对话次数:", userId, userChatNum); - } - } + let userChatNum = await getQuota(userId); + if (userChatNum !== -1 && userChatNum !== undefined) { + userChatNum = Math.max(0, userChatNum - 1); + await setQuota(userId, userChatNum); + await pool.query("update sys_user set ai_chat_num = ? where user_id= ?", [userChatNum, userId]); + console.log("更新用户对话次数:", userId, userChatNum); + } + } });