diff --git a/app/composables/useAgentChat.ts b/app/composables/useAgentChat.ts index 4abd59d..a12b1ac 100644 --- a/app/composables/useAgentChat.ts +++ b/app/composables/useAgentChat.ts @@ -423,6 +423,14 @@ export function useAgentChat(options: UseAgentChatOptions) { type: "text", text: "(模型仅返回了思考过程,未生成正式回复。请尝试更换模型或调整问题后重试。)", }); + } else if (!hasText && hasToolCall) { + finalMsg.parts?.push({ + id: generateId(), + type: "text", + text: isStopped.value + ? "(对话已停止,以上是已完成的工具调用记录。)" + : "(已达到工具调用次数上限,模型未能生成最终回答。以上是工具调用的执行记录。)", + }); } } } @@ -533,6 +541,7 @@ export function useAgentChat(options: UseAgentChatOptions) { const msg = messages.value[assistantIdx]; if (msg) { cleanupInProgressToolCalls(msg); + validateAssistantContent(assistantIdx); } } else { errorMessage.value = err.message || "请求失败"; @@ -645,6 +654,7 @@ export function useAgentChat(options: UseAgentChatOptions) { const msg = messages.value[messages.value.indexOf(assistantMsg)]; if (msg) { cleanupInProgressToolCalls(msg); + validateAssistantContent(messages.value.indexOf(assistantMsg), true); } } else { errorMessage.value = err.message || "请求失败"; diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index 0b85290..31bd9e6 100644 Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ diff --git a/server/service/agent/chat-engine.ts b/server/service/agent/chat-engine.ts index 7315d06..ad0b09f 100644 --- a/server/service/agent/chat-engine.ts +++ b/server/service/agent/chat-engine.ts @@ -504,6 +504,7 @@ export async function executeChat(params: ChatEngineParams): Promise> = []; + let hasTextPart = false; for (const step of steps) { const toolResultMap = new Map(step.toolResults.map((tr) => [tr.toolCallId, tr.output])); const contentArr = step.content as Array>; @@ -518,6 +519,7 @@ export async function executeChat(params: ChatEngineParams): Promise p.type === "tool-call"); + const stepLimitReached = finishReason === "tool-calls" && !hasTextPart && hasToolCallPart; + let finalText = assistantText; + if (stepLimitReached) { + const noticeText = "(已达到工具调用次数上限,模型未能生成最终回答。以上是工具调用的执行记录。)"; + parts.push({ id: `p_${parts.length}`, type: "text", text: noticeText }); + finalText = (finalText || "") + noticeText; + logger.info("[%s] [CHAT-ENGINE] step limit reached, appended notice part", requestId); + } + if (isApprovalContinue) { const lastAssistant = historyMessages.filter((m) => m.role === "assistant").pop(); if (lastAssistant) { @@ -590,7 +602,7 @@ export async function executeChat(params: ChatEngineParams): Promise 0 ? JSON.stringify(parts) : null, modelId: resolvedModel.dbId, inputTokens: usage?.inputTokens ?? null, @@ -641,11 +653,13 @@ export async function executeChat(params: ChatEngineParams): Promise> = []; + let hasTextPart = false; if (streamedReasoning.trim()) { parts.push({ id: `p_${parts.length}`, type: "reasoning", text: streamedReasoning }); } if (partialText.trim()) { parts.push({ id: `p_${parts.length}`, type: "text", text: partialText }); + hasTextPart = true; } for (const tc of streamedToolCalls) { parts.push({ @@ -659,6 +673,15 @@ export async function executeChat(params: ChatEngineParams): Promise p.type === "tool-call"); + let finalText = partialText; + if (!hasTextPart && hasToolCallPart) { + const noticeText = "(对话已停止,以上是已完成的工具调用记录。)"; + parts.push({ id: `p_${parts.length}`, type: "text", text: noticeText }); + finalText = (finalText || "") + noticeText; + logger.info("[%s] [CHAT-ENGINE] aborted with tool calls but no text, appended notice", requestId); + } + if (isApprovalContinue) { const lastAssistant = historyMessages.filter((m) => m.role === "assistant").pop(); if (lastAssistant) { @@ -688,18 +711,18 @@ export async function executeChat(params: ChatEngineParams): Promise 0 || parts.length > 0; + const hasContent = finalText.trim().length > 0 || parts.length > 0; if (hasContent) { const assistantSortOrder = (await getMaxSortOrder(sessionId)) + 1; await saveMessage({ sessionId, role: "assistant", - content: partialText, + content: finalText, parts: parts.length > 0 ? JSON.stringify(parts) : null, modelId: resolvedModel.dbId, sortOrder: assistantSortOrder,