Browse Source

feat: 增强聊天引擎,处理工具调用记录并优化最终回复内容

feat/agent-independence
npmrun 2 months ago
parent
commit
3deee59f2e
  1. 10
      app/composables/useAgentChat.ts
  2. BIN
      packages/drizzle-pkg/db.sqlite
  3. 33
      server/service/agent/chat-engine.ts

10
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 || "请求失败";

BIN
packages/drizzle-pkg/db.sqlite

Binary file not shown.

33
server/service/agent/chat-engine.ts

@ -504,6 +504,7 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
);
const parts: Array<Record<string, unknown>> = [];
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<Record<string, unknown>>;
@ -518,6 +519,7 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
parts.push({ id: `p_${parts.length}`, type: "reasoning", text: (cp as any).text });
} else if (cp.type === "text" && (cp as any).text) {
parts.push({ id: `p_${parts.length}`, type: "text", text: (cp as any).text });
hasTextPart = true;
} else if (cp.type === "tool-approval-request" && (cp as any).approvalId && (cp as any).toolCall) {
const tc = (cp as any).toolCall;
const toolResult = toolResultMap.get(tc.toolCallId);
@ -560,6 +562,16 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
}
}
const hasToolCallPart = parts.some((p) => 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<ChatEngineR
newParts[i]!.id = `p_${idOffset + i}`;
}
const mergedParts = [...existingParts, ...newParts];
const mergedContent = (lastAssistant.content || "") + assistantText;
const mergedContent = (lastAssistant.content || "") + finalText;
await updateMessageContent(lastAssistant.id, mergedContent);
await updateMessageParts(lastAssistant.id, JSON.stringify(mergedParts));
}
@ -599,7 +611,7 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
await saveMessage({
sessionId,
role: "assistant",
content: assistantText,
content: finalText,
parts: parts.length > 0 ? JSON.stringify(parts) : null,
modelId: resolvedModel.dbId,
inputTokens: usage?.inputTokens ?? null,
@ -641,11 +653,13 @@ export async function executeChat(params: ChatEngineParams): Promise<ChatEngineR
logger.info("[%s] [CHAT-ENGINE] aborted: streamedTextLen=%d streamedToolCalls=%d", requestId, partialText.length, streamedToolCalls.length);
const parts: Array<Record<string, unknown>> = [];
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<ChatEngineR
});
}
const hasToolCallPart = parts.some((p) => 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<ChatEngineR
newParts[i]!.id = `p_${idOffset + i}`;
}
const mergedParts = [...existingParts, ...newParts];
const mergedContent = (lastAssistant.content || "") + partialText;
const mergedContent = (lastAssistant.content || "") + finalText;
await updateMessageContent(lastAssistant.id, mergedContent);
await updateMessageParts(lastAssistant.id, JSON.stringify(mergedParts));
}
} else {
const hasContent = partialText.trim().length > 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,

Loading…
Cancel
Save