优化输出内容
This commit is contained in:
727
server2.js
727
server2.js
@@ -1370,454 +1370,361 @@ 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: "对话次数已用完,请完成账号注册" });
|
||||
}
|
||||
}
|
||||
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;
|
||||
const { requestId } = req.body;
|
||||
|
||||
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
res.setHeader("Transfer-Encoding", "chunked");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
let aborted = false;
|
||||
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.log("analyze: ",req.body)
|
||||
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
|
||||
WHERE session_id = ?
|
||||
AND user_id = ?
|
||||
AND title IS NOT NULL
|
||||
AND title != ''
|
||||
) AS hasTitle
|
||||
`,
|
||||
[sessionId, userId]
|
||||
);
|
||||
const existingTitle = rows[0].hasTitle === 1; // true / false
|
||||
console.log(existingTitle, rows?.[0], 9999, rows)
|
||||
|
||||
// 如果前端没有传 sessionId,生成一个
|
||||
const sessId = sessionId || Date.now().toString();
|
||||
|
||||
// ------------------ 获取历史对话(上下文) ------------------
|
||||
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.map(row => {
|
||||
const message = {
|
||||
role: row.role,
|
||||
content: []
|
||||
};
|
||||
|
||||
// 添加文本内容
|
||||
if (row.content) {
|
||||
message.content.push({ type: "text", text: row.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}`,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
});
|
||||
|
||||
|
||||
// ------------------ 保存用户提问 ------------------
|
||||
await pool.query(
|
||||
"INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)",
|
||||
[sessId, userId || null, userPrompt || "", imageBase64 || null]
|
||||
);
|
||||
|
||||
// console.log("userPrompt: ",userPrompt);
|
||||
// console.log("imageBase64: ",imageBase64);
|
||||
// console.log("userId: ",userId);
|
||||
// console.log("sessionId: ",sessionId);
|
||||
|
||||
// ------------------ 图像分析 ------------------
|
||||
// 构建包含上下文的消息数组
|
||||
const imageAnalysisMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: "你是一名图像分析专家,需要结合历史对话内容分析当前图像。"
|
||||
},
|
||||
...historyMessages, // 添加历史对话作为上下文
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: userPrompt || "请描述图片内容,并给出分析思考过程" },
|
||||
imageBase64
|
||||
? {
|
||||
type: "image_url",
|
||||
image_url: {
|
||||
url: imageBase64.startsWith("data:")
|
||||
? imageBase64
|
||||
: `data:image/png;base64,${imageBase64}`,
|
||||
},
|
||||
}
|
||||
: null,
|
||||
].filter(Boolean),
|
||||
},
|
||||
];
|
||||
|
||||
const completion = await client.chat.completions.create({
|
||||
model: "gpt-4.1",
|
||||
stream: true,
|
||||
messages: imageAnalysisMessages
|
||||
});
|
||||
|
||||
console.log("====== 开始图像分析... ======")
|
||||
let sceneText = "";
|
||||
|
||||
/* TLS 加密缓冲,小块数据可能先缓存在服务器端,不立即发送,会导致图像分析模型的结果不能立即返回 */
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": "text/event-stream; charset=utf-8",
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"Connection": "keep-alive",
|
||||
});
|
||||
res.socket.setNoDelay(true);
|
||||
"X-Accel-Buffering": "no",
|
||||
});
|
||||
res.flushHeaders?.();
|
||||
let aborted = false;
|
||||
req.on("close", () => {
|
||||
console.log("🔴 前端断开连接,停止整个分析流程");
|
||||
aborted = true;
|
||||
});
|
||||
|
||||
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;
|
||||
console.time("analyze");
|
||||
const logBody = { ...req.body };
|
||||
if (req.body?.imageBase64) {
|
||||
delete logBody.imageBase64;
|
||||
}
|
||||
console.log("analyze: ", logBody);
|
||||
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "scene", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
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
|
||||
WHERE session_id = ?
|
||||
AND user_id = ?
|
||||
AND title IS NOT NULL
|
||||
AND title != ''
|
||||
) AS hasTitle
|
||||
`,
|
||||
[sessionId, userId]
|
||||
);
|
||||
const existingTitle = rows[0].hasTitle === 1; // true / false
|
||||
console.log(existingTitle, rows?.[0], 9999, rows);
|
||||
|
||||
// if (delta)
|
||||
// {
|
||||
// for (const ch of delta)
|
||||
// {
|
||||
// res.write(`data: ${JSON.stringify({ type: "scene", content: ch })}\n\n`);
|
||||
// await new Promise(r => setTimeout(r, 0)); // 控制渲染节奏
|
||||
// }
|
||||
// }
|
||||
}
|
||||
console.log(sceneText);
|
||||
console.log("====== 图像分析结束 ======")
|
||||
// ------------------ ① 思路阶段(模拟 ChatGPT 思考) ------------------
|
||||
const thinkingMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一名新能源项目AI分析师。
|
||||
请输出【思路概览】,模拟人类分析问题的过程,但不要暴露内部推理细节。
|
||||
const sessId = sessionId || Date.now().toString();
|
||||
|
||||
要求:
|
||||
- 使用项目符号
|
||||
- 只输出思路,不给结论
|
||||
`
|
||||
},
|
||||
...historyMessages,
|
||||
{
|
||||
role: "user",
|
||||
content: userPrompt || "请分析图片。"
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: sceneText
|
||||
}
|
||||
];
|
||||
// ------------------ 获取历史对话(上下文) ------------------
|
||||
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 thinkingStream = await client1.chat.completions.create({
|
||||
model: "gpt-5-mini",
|
||||
stream: true,
|
||||
messages: thinkingMessages
|
||||
});
|
||||
// 构建历史消息数组
|
||||
const historyMessages = historyRows.map(row => {
|
||||
const message = {
|
||||
role: row.role,
|
||||
content: []
|
||||
};
|
||||
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "phase", content: "thinking" })}\n\n`
|
||||
);
|
||||
// 添加文本内容
|
||||
if (row.content) {
|
||||
message.content.push({ type: "text", text: row.content });
|
||||
}
|
||||
|
||||
let thinkingText = "";
|
||||
// 添加图片内容
|
||||
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}`,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log("====== 开始思路输出 ======");
|
||||
for await (const chunk of thinkingStream) {
|
||||
if (stopFlags.get(requestId)) {
|
||||
console.log("⛔ 思路阶段被中断");
|
||||
res.end();
|
||||
stopFlags.delete(requestId);
|
||||
return;
|
||||
}
|
||||
return message;
|
||||
});
|
||||
|
||||
const delta = chunk.choices[0]?.delta?.content || "";
|
||||
thinkingText += delta;
|
||||
// ------------------ 保存用户提问 ------------------
|
||||
await pool.query(
|
||||
"INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)",
|
||||
[sessId, userId || null, userPrompt || "", imageBase64 || null]
|
||||
);
|
||||
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "thinking", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(thinkingText);
|
||||
console.log("====== 思路输出结束 ======");
|
||||
// ------------------ 图像分析 ------------------
|
||||
let sceneText = "";
|
||||
|
||||
// If an image is uploaded, start with image analysis
|
||||
if (imageBase64) {
|
||||
const imageAnalysisMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: "你是一名图像分析专家,需要结合历史对话内容分析当前图像。"
|
||||
},
|
||||
...historyMessages, // 添加历史对话作为上下文
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: userPrompt || `请描述图片内容, 请输出【思路概览】,模拟人类分析问题的过程,但不要暴露内部推理细节。
|
||||
|
||||
// 保存图像分析结果到数据库
|
||||
// await pool.query(
|
||||
// "INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)",
|
||||
// [sessId, userId || null, sceneText]
|
||||
// );
|
||||
要求:
|
||||
- 使用项目符号
|
||||
- 只输出思路,不给结论
|
||||
` },
|
||||
imageBase64
|
||||
? {
|
||||
type: "image_url",
|
||||
image_url: {
|
||||
url: imageBase64.startsWith("data:")
|
||||
? imageBase64
|
||||
: `data:image/png;base64,${imageBase64}`,
|
||||
},
|
||||
}
|
||||
: null,
|
||||
].filter(Boolean),
|
||||
},
|
||||
];
|
||||
|
||||
// ------------------ 3️⃣ LLM 分析 ------------------
|
||||
// console.log("LLM 分析 sceneText: ", sceneText)
|
||||
// const preText = "详细分析这个光伏场站,根据图片给出详细的运维方案"
|
||||
// + "如果这个光伏场站需要割草怎么做,给出一份详细的计划"
|
||||
// + "如果这个光伏场站需要用无人机清洗怎么做,给出一份详细的计划"
|
||||
// + "如果这个光伏场站需要巡检怎么做,给出一份详细的计划"
|
||||
const completion = await client.chat.completions.create({
|
||||
model: "gpt-4.1",
|
||||
stream: true,
|
||||
messages: imageAnalysisMessages
|
||||
});
|
||||
|
||||
// 构建包含完整上下文的LLM消息数组
|
||||
const llmMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: "你是一名新能源项目的图像分析AI分析师,需要结合历史对话提供专业建议。"
|
||||
},
|
||||
...historyMessages, // 添加历史对话作为上下文
|
||||
{
|
||||
role: "user",
|
||||
content: userPrompt || "请基于图片分析提供建议。"
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: sceneText // 添加当前图像分析结果
|
||||
}
|
||||
];
|
||||
console.log("====== 开始图像分析... ======");
|
||||
/* TLS 加密缓冲 */
|
||||
|
||||
|
||||
// const llmStream = await client1.chat.completions.create({
|
||||
// model: "gpt-5-mini",
|
||||
// stream: true,
|
||||
// messages: llmMessages
|
||||
// });
|
||||
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;
|
||||
|
||||
// ------------------ ② 结论阶段(最终分析) ------------------
|
||||
const answerMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一名新能源项目图像分析AI。
|
||||
请基于以下信息,输出【最终结论】:
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "scene", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(sceneText);
|
||||
console.log("====== 图像分析结束 ======");
|
||||
}
|
||||
|
||||
要求:
|
||||
- 内容完整、专业、可执行
|
||||
- 不重复思路概览
|
||||
- 可以分段、分点说明
|
||||
`
|
||||
},
|
||||
...historyMessages,
|
||||
{
|
||||
role: "user",
|
||||
content: userPrompt || "请给出最终分析建议。"
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: sceneText + thinkingText
|
||||
}
|
||||
];
|
||||
// ------------------ ① 思路阶段(模拟 ChatGPT 思考) ------------------
|
||||
const thinkingMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一名新能源项目AI分析师。
|
||||
请输出【思路概览】,模拟人类分析问题的过程,但不要暴露内部推理细节。
|
||||
|
||||
const answerStream = await client1.chat.completions.create({
|
||||
model: "gpt-5-mini",
|
||||
stream: true,
|
||||
messages: answerMessages
|
||||
});
|
||||
要求:
|
||||
- 使用项目符号
|
||||
- 只输出思路,不给结论
|
||||
`
|
||||
},
|
||||
...historyMessages,
|
||||
{
|
||||
role: "user",
|
||||
content: userPrompt || "请分析图片。"
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: sceneText
|
||||
}
|
||||
];
|
||||
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "phase", content: "answer" })}\n\n`
|
||||
);
|
||||
const thinkingStream = await client1.chat.completions.create({
|
||||
model: "gpt-5-mini",
|
||||
stream: true,
|
||||
messages: thinkingMessages
|
||||
});
|
||||
|
||||
let llmText = "";
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "phase", content: "thinking" })}\n\n`
|
||||
);
|
||||
|
||||
console.log("====== 开始最终分析 ======");
|
||||
for await (const chunk of answerStream) {
|
||||
if (stopFlags.get(requestId)) {
|
||||
console.log("⛔ 最终分析被中断");
|
||||
res.end();
|
||||
stopFlags.delete(requestId);
|
||||
return;
|
||||
}
|
||||
let thinkingText = "";
|
||||
|
||||
const delta = chunk.choices[0]?.delta?.content || "";
|
||||
llmText += delta;
|
||||
console.log("====== 开始思路输出 ======");
|
||||
for await (const chunk of thinkingStream) {
|
||||
if (stopFlags.get(requestId)) {
|
||||
console.log("⛔ 思路阶段被中断");
|
||||
res.end();
|
||||
stopFlags.delete(requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "answer", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(llmText);
|
||||
console.log("====== 最终分析结束 ======");
|
||||
const delta = chunk.choices[0]?.delta?.content || "";
|
||||
thinkingText += delta;
|
||||
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "thinking", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(thinkingText);
|
||||
console.log("====== 思路输出结束 ======");
|
||||
|
||||
// console.log("====== 开始深度思考... ======")
|
||||
// let llmText = "";
|
||||
// for await (const chunk of llmStream) {
|
||||
// if (stopFlags.get(requestId)) {
|
||||
// console.log("⛔ 深度分析被前端中断");
|
||||
// res.end();
|
||||
// stopFlags.delete(requestId);
|
||||
// return;
|
||||
// }
|
||||
// const delta = chunk.choices[0]?.delta?.content || "";
|
||||
// llmText += delta;
|
||||
// ------------------ ② 结论阶段(最终分析) ------------------
|
||||
const answerMessages = [
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一名新能源项目图像分析AI。
|
||||
请基于以下信息,输出【最终结论】:
|
||||
|
||||
// if (delta)
|
||||
// res.write(
|
||||
// `data: ${JSON.stringify({ type: "llm", content: delta })}\n\n`
|
||||
// );
|
||||
要求:
|
||||
- 内容完整、专业、可执行
|
||||
- 不重复思路概览
|
||||
- 可以分段、分点说明
|
||||
`
|
||||
},
|
||||
...historyMessages,
|
||||
{
|
||||
role: "user",
|
||||
content: userPrompt || "请给出最终分析建议。"
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: sceneText + thinkingText
|
||||
}
|
||||
];
|
||||
|
||||
const answerStream = await client1.chat.completions.create({
|
||||
model: "gpt-5-mini",
|
||||
stream: true,
|
||||
messages: answerMessages
|
||||
});
|
||||
|
||||
// /* delta 很长,这会 人为拉长处理时间。 */
|
||||
// // if (delta)
|
||||
// // {
|
||||
// // for (const ch of delta)
|
||||
// // {
|
||||
// // res.write(`data: ${JSON.stringify({ type: "llm", content: ch })}\n\n`);
|
||||
// // await new Promise(r => setTimeout(r, 0)); // 控制渲染节奏
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "phase", content: "answer" })}\n\n`
|
||||
);
|
||||
|
||||
let llmText = "";
|
||||
|
||||
// 保存 LLM 输出到数据库
|
||||
/* 加标题需要异步,且在会话开始时就进行 */
|
||||
if (existingTitle) {
|
||||
console.log("uidh")
|
||||
await pool.query(
|
||||
"INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)",
|
||||
[sessId, userId || null, llmText]
|
||||
);
|
||||
console.log("====== 开始最终分析 ======");
|
||||
for await (const chunk of answerStream) {
|
||||
if (stopFlags.get(requestId)) {
|
||||
console.log("⛔ 最终分析被中断");
|
||||
res.end();
|
||||
stopFlags.delete(requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = chunk.choices[0]?.delta?.content || "";
|
||||
llmText += delta;
|
||||
|
||||
if (delta) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({ type: "answer", content: delta })}\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(llmText);
|
||||
console.log("====== 最终分析结束 ======");
|
||||
|
||||
// ------------------ Save Results to Database ------------------
|
||||
if (existingTitle) {
|
||||
await pool.query(
|
||||
"INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)",
|
||||
[sessId, userId || null, llmText]
|
||||
);
|
||||
} 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) VALUES (?, ?, 'assistant', ?, ?)",
|
||||
[sessId, userId || null, llmText, title]
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("生成标题失败:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
res.write("data: [DONE]\n\n");
|
||||
res.end();
|
||||
|
||||
// 删除上传文件
|
||||
if (req.file && req.file.path) {
|
||||
fs.unlinkSync(req.file.path);
|
||||
}
|
||||
console.log("====== 分析完成 ======");
|
||||
console.timeEnd("analyze");
|
||||
|
||||
} catch (err) {
|
||||
console.error("❌ 调用失败:", err.message);
|
||||
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({ error: err.message });
|
||||
} else {
|
||||
console.log("uidh1111")
|
||||
|
||||
try {
|
||||
const titlePrompt = `请根据以下对话内容生成一个简短的标题(不超过15字):
|
||||
用户问题: ${userPrompt}
|
||||
AI回答: ${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);
|
||||
|
||||
// 保存到数据库(建议你给 ai_chat_messages 表新增一列 title,或者单独建一张会话表)
|
||||
await pool.query(
|
||||
"INSERT INTO ai_chat_messages (session_id, user_id, role, content,title) VALUES (?, ?, 'assistant', ?,?)",
|
||||
[sessId, userId || null, llmText, title]
|
||||
);
|
||||
}
|
||||
catch (err) {
|
||||
console.error("生成标题失败:", err.message);
|
||||
}
|
||||
res.write(`data: ${JSON.stringify({
|
||||
type: "error",
|
||||
message: err.message
|
||||
})}\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("====== 分析完成 ======");
|
||||
console.timeEnd("analyze");
|
||||
|
||||
// // 更新用户剩余对话次数
|
||||
// let userChatNum = await getQuota(userId);
|
||||
// if(userChatNum!==-1){
|
||||
// userChatNum = Math.max(0, userChatNum - 1); // 防止变成负数
|
||||
// await setQuota(userId,userChatNum);
|
||||
// console.log(`用户剩余AI 对话次数: ${userChatNum}`);
|
||||
// }
|
||||
}
|
||||
catch (err) {
|
||||
console.error("❌ 调用失败:", err.response?.data || err.message);
|
||||
res.status(500).json({ code: 1, error: err.response?.data || err.message });
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.post("/stop", express.json(), (req, res) => {
|
||||
const { requestId } = req.body;
|
||||
console.log("🔴 收到停止信号:", requestId);
|
||||
|
||||
Reference in New Issue
Block a user