fix: 修复system消息渲染到聊天界面+AI说了做但没调用工具直接停止的问题

This commit is contained in:
thzxx
2026-07-11 18:50:05 +08:00
parent cf96afad1e
commit e04320d454
3 changed files with 30 additions and 20 deletions
+2
View File
@@ -136,6 +136,8 @@ export function renderMessages(): void {
for (let i = 0; i < msgs.length; i++) {
const msg = msgs[i];
// 跳过 system 消息(Plan Mode 进度等内部状态消息不展示给用户)
if (msg.role === 'system') continue;
// 只跳过既无内容也无 think 也无工具的空消息
if (msg.role === 'assistant' && !msg.content && !msg.think && (!msg.toolCalls || msg.toolCalls.length === 0)) continue;
appendMessageDOM(msgs[i], i);
+2 -18
View File
@@ -634,16 +634,8 @@ async function handleRetry(): Promise<void> {
// renderMessages 统一渲染 session 数据,先清掉流式 placeholder 避免 DOM 重复
clearMessagesDOM();
renderMessages();
// ── 保存 Plan Mode 最终进度到会话(供下一轮 Loop 注入)──
const lastPlanStatus = state.get<string>('_lastPlanStatus', '');
if (lastPlanStatus) {
// ── 消费 _lastPlanStatus(不再保存为 session 消息,避免 system 消息渲染到聊天界面)──
state.set('_lastPlanStatus', '');
state.update(KEYS.CURRENT_SESSION, (s: any) => ({
...s,
messages: [...s.messages, { role: 'system', content: lastPlanStatus, timestamp: Date.now() }],
updatedAt: Date.now()
}));
}
await saveCurrentSession();
}
});
@@ -1352,16 +1344,8 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio
renderMessages();
// ── 保存 Plan Mode 最终进度到会话(供下一轮 Loop 注入)──
const lastPlanStatus2 = state.get<string>('_lastPlanStatus', '');
if (lastPlanStatus2) {
// ── 消费 _lastPlanStatus(不再保存为 session 消息,避免 system 消息渲染到聊天界面)──
state.set('_lastPlanStatus', '');
state.update(KEYS.CURRENT_SESSION, (session: any) => ({
...session,
messages: [...session.messages, { role: 'system', content: lastPlanStatus2, timestamp: Date.now() }],
updatedAt: Date.now()
}));
}
await saveCurrentSession();
}
});
+24
View File
@@ -2035,6 +2035,30 @@ async function handleReflecting(
return;
}
// ── 异常3: 模型说了要做但没调用工具 → 注入提醒再给一次机会 ──
// 场景:AI 回复"我来写入文件"但未发出 write_file 工具调用
if (ctx.toolCalls.length === 0 && ctx.content.trim() && ctx.loopCount <= 3) {
const text = ctx.content.toLowerCase();
const actionHints: Array<{ pattern: RegExp; tool: string; hint: string }> = [
{ pattern: /写入|写文件|写入文件|保存.*文件|创建.*文件|生成.*文件|输出.*文件|write.*file/i, tool: 'write_file', hint: '你提到了写入/创建文件,但没有调用 write_file 工具。请直接调用 write_file 工具执行写入操作。' },
{ pattern: /搜索|查找|搜一下|查一下|检索|search/i, tool: 'web_search', hint: '你提到了搜索,但没有调用 web_search 工具。请直接调用 web_search 工具执行搜索。' },
{ pattern: /抓取|获取.*内容|fetch|获取.*网页/i, tool: 'web_fetch', hint: '你提到了抓取网页,但没有调用 web_fetch 工具。请直接调用 web_fetch 工具获取网页内容。' },
{ pattern: /运行.*命令|执行.*命令|run.*command/i, tool: 'run_command', hint: '你提到了运行命令,但没有调用 run_command 工具。请直接调用 run_command 工具执行命令。' },
{ pattern: /下载/i, tool: 'download_file', hint: '你提到了下载,但没有调用 download_file 工具。请直接调用 download_file 工具执行下载。' },
];
const matched = actionHints.find(h => h.pattern.test(text) && !ctx.allToolRecords.some(r => r.name === h.tool));
if (matched) {
logWarn(`Reflecting: 检测到"说了做但没做" → ${matched.tool},注入提醒`);
ctx.messages.push({
role: 'user',
content: matched.hint,
ephemeral: true,
});
transition(ctx, S.THINKING);
return;
}
}
// ── 本轮调用了工具 → 模型需要处理工具结果 → 继续循环 ──
if (ctx.toolCalls.length > 0) {
transition(ctx, S.THINKING);