fix: Agent Loop 注入工具路径上下文引导,避免连续操作时路径丢失

This commit is contained in:
thzxx
2026-04-07 11:05:52 +08:00
parent 4e11bd549c
commit 06e1900435
4 changed files with 21 additions and 65 deletions
+6 -20
View File
@@ -18,23 +18,9 @@ let panelEl: HTMLElement;
let isOpen = false; let isOpen = false;
export function initMemoryPanel(): void { export function initMemoryPanel(): void {
const modelBar = document.querySelector('.model-bar'); // 绑定 header 中的记忆按钮
if (!modelBar) return; const btnMemory = document.querySelector('#btnMemory');
if (!btnMemory) return;
// 创建记忆状态栏
const bar = document.createElement('div');
bar.className = 'memory-bar';
bar.innerHTML = `
<button class="memory-bar-btn" id="btnMemoryPanel" title="Agent 记忆">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
<line x1="9" y1="21" x2="15" y2="21"/>
</svg>
<span class="memory-bar-label">记忆</span>
<span class="memory-bar-count" id="memoryCount">0</span>
</button>
`;
modelBar.after(bar);
// 创建记忆面板 // 创建记忆面板
const panel = document.createElement('div'); const panel = document.createElement('div');
@@ -77,7 +63,7 @@ export function initMemoryPanel(): void {
panelEl = panel; panelEl = panel;
// 绑定事件 // 绑定事件
bar.querySelector('#btnMemoryPanel')!.addEventListener('click', togglePanel); btnMemory.addEventListener('click', togglePanel);
panel.querySelector('#btnCloseMemoryPanel')!.addEventListener('click', () => setPanelOpen(false)); panel.querySelector('#btnCloseMemoryPanel')!.addEventListener('click', () => setPanelOpen(false));
const toggleMemory = panel.querySelector('#toggleMemoryPanelEnabled') as HTMLInputElement; const toggleMemory = panel.querySelector('#toggleMemoryPanelEnabled') as HTMLInputElement;
@@ -125,8 +111,8 @@ function setPanelOpen(open: boolean): void {
function updateMemoryCount(): void { function updateMemoryCount(): void {
const count = getMemoryCache().length; const count = getMemoryCache().length;
const countEl = document.querySelector('#memoryCount'); const btn = document.querySelector('#btnMemory');
if (countEl) countEl.textContent = String(count); if (btn) btn.setAttribute('title', count > 0 ? `Agent 记忆 (${count})` : 'Agent 记忆');
} }
function renderMemoryList(): void { function renderMemoryList(): void {
+6
View File
@@ -54,6 +54,12 @@
<line x1="8" y1="11" x2="14" y2="11"/> <line x1="8" y1="11" x2="14" y2="11"/>
</svg> </svg>
</button> </button>
<button class="icon-btn" id="btnMemory" title="Agent 记忆">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
<line x1="9" y1="21" x2="15" y2="21"/>
</svg>
</button>
<button class="icon-btn" id="btnHistory" title="历史记录"> <button class="icon-btn" id="btnHistory" title="历史记录">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/> <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
+9 -1
View File
@@ -26,6 +26,12 @@ const MAX_LOOP_TIME = 300000; // 5 分钟总超时
const TOOL_EXEC_TIMEOUT = 30000; // 工具执行超时 30 秒 const TOOL_EXEC_TIMEOUT = 30000; // 工具执行超时 30 秒
const STREAM_TIMEOUT = 120000; // 单次流式调用超时 2 分钟 const STREAM_TIMEOUT = 120000; // 单次流式调用超时 2 分钟
const TOOL_USAGE_GUIDE = `【工具使用规则】
1. 路径上下文:当用户说"修改这个文件"、"删除它"、"查看里面"等未指定路径时,必须从本次对话中最近的工具调用结果中提取路径。例如用户之前让你读取了 /home/user/project/config.json,后续说"修改配置"就应继续使用该路径,而不是推测新路径。
2. 工具调用结果中的 path 字段是权威的文件路径,优先使用它。
3. 如果对话中从未出现过相关路径,应先用 search_files 或 list_directory 定位,不要凭空猜测路径。
4. 对同一文件的连续操作(读取→修改、读取→删除),路径必须一致。`;
export interface AgentCallbacks { export interface AgentCallbacks {
onThinking: (text: string) => void; onThinking: (text: string) => void;
onContent: (text: string) => void; onContent: (text: string) => void;
@@ -77,7 +83,9 @@ export async function runAgentLoop(
} }
if (systemPromptParts.length > 0) { if (systemPromptParts.length > 0) {
messages.push({ role: 'system', content: systemPromptParts.join('\n\n') }); messages.push({ role: 'system', content: systemPromptParts.join('\n\n') + '\n\n' + TOOL_USAGE_GUIDE });
} else {
messages.push({ role: 'system', content: TOOL_USAGE_GUIDE });
} }
for (const msg of historyMessages) { for (const msg of historyMessages) {
-44
View File
@@ -2421,50 +2421,6 @@ html, body {
Agent 记忆系统 Agent 记忆系统
═══════════════════════════════════════════════════════════════ */ ═══════════════════════════════════════════════════════════════ */
/* ── 记忆状态栏 ── */
.memory-bar {
display: flex;
align-items: center;
padding: 2px 16px;
background: rgba(255, 255, 255, 0.02);
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.memory-bar-btn {
display: flex;
align-items: center;
gap: 6px;
background: none;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 4px 12px;
color: var(--text-secondary, rgba(255, 255, 255, 0.6));
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.memory-bar-btn:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(0, 245, 212, 0.3);
color: var(--text-primary, #fff);
}
.memory-bar-btn svg {
width: 14px;
height: 14px;
}
.memory-bar-count {
background: rgba(0, 245, 212, 0.15);
color: var(--accent-cyan, #00f5d4);
padding: 0 6px;
border-radius: 8px;
font-size: 11px;
min-width: 18px;
text-align: center;
}
/* ── 记忆面板 ── */ /* ── 记忆面板 ── */
.memory-panel { .memory-panel {
position: absolute; position: absolute;