From 6c9ffa5135e5d294e931fd41e0bde1a13bbbd426 Mon Sep 17 00:00:00 2001 From: thzxx Date: Sat, 18 Apr 2026 10:00:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B6=88=E9=99=A4=E6=AD=BB=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=20=E2=80=94=20context-manager=20=E6=8E=A5=E5=85=A5=20?= =?UTF-8?q?+=20mcp-client=20=E6=8E=A5=E7=BA=BF=20+=20=E5=88=A0=E9=99=A4=20?= =?UTF-8?q?document-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - agent-engine.ts: 接入 context-manager buildContext(),长对话自动滑动窗口+摘要压缩+token 裁剪 - tool-registry.ts: 导入 mcp-client,getEnabledToolDefinitions() 合并 MCP 工具缓存 - main.ts: 启动时 refreshMCPTools() 注册已启用的 MCP 工具 - 删除 document-processor.ts(RAG 遗留,完全无引用) --- src/renderer/main.ts | 5 +- src/renderer/services/agent-engine.ts | 11 +++- src/renderer/services/document-processor.ts | 70 --------------------- src/renderer/services/tool-registry.ts | 20 +++++- 4 files changed, 32 insertions(+), 74 deletions(-) delete mode 100644 src/renderer/services/document-processor.ts diff --git a/src/renderer/main.ts b/src/renderer/main.ts index d5de0ba..fa8f2fb 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -24,7 +24,7 @@ import { initToolsModal, closeToolsModal } from './components/tools-modal.js'; import { initMemoryManager } from './services/memory-manager.js'; import { restoreHeartbeat } from './components/settings-modal.js'; import { restoreCronTasks } from './services/cron-manager.js'; -import { setToolEnabled, setRunCommandMode } from './services/tool-registry.js'; +import { setToolEnabled, setRunCommandMode, refreshMCPTools } from './services/tool-registry.js'; import { initToolConfirmModal } from './components/tool-confirm-modal.js'; import { initWorkspacePanel } from './components/workspace-panel.js'; import { initLogPanel, addLog } from './services/log-service.js'; @@ -361,6 +361,9 @@ async function init(): Promise { const toggleTC = document.querySelector('#toggleToolCalling'); if (toggleTC) toggleTC.checked = toolCallingEnabled; + // ── MCP 工具注册 ── + await refreshMCPTools(); + // ── Agent 记忆设置 ── const memoryEnabled = await db.getSetting('memoryEnabled', true); state.set('memoryEnabled', memoryEnabled); diff --git a/src/renderer/services/agent-engine.ts b/src/renderer/services/agent-engine.ts index 0c93f5a..ac48008 100644 --- a/src/renderer/services/agent-engine.ts +++ b/src/renderer/services/agent-engine.ts @@ -18,6 +18,7 @@ import { showToast } from '../components/toast.js'; import { logInfo, logWarn, logError, logToolStart, logToolResult, logThink, logAgentLoop, logModelResponse } from './log-service.js'; import { getWorkspaceDirPath } from '../components/workspace-panel.js'; import { generateId } from '../utils/utils.js'; +import { buildContext, estimateTokens } from './context-manager.js'; import type { OllamaMessage, OllamaStreamChunk, @@ -436,7 +437,7 @@ export async function runAgentLoop( messages.push({ role: 'system', content: fullSystemPrompt }); - // 添加历史消息(使用 context-manager 构建) + // 添加历史消息 for (const msg of historyMessages) { messages.push({ role: msg.role as 'user' | 'assistant', @@ -453,7 +454,13 @@ export async function runAgentLoop( }; messages.push(userMsg); - logInfo(`ReAct Agent Loop 启动: ${model}`, `工具: ${useTools ? '开启' : '关闭'}, 记忆: ${isMemoryEnabled() ? '开启' : '关闭'}`); + // 上下文窗口管理:滑动窗口 + 摘要压缩 + token 裁剪 + const numCtx = state.get(KEYS.NUM_CTX, 24576); + const contextResult = buildContext(messages, { maxTokens: numCtx, windowSize: 20 }); + messages.length = 0; + messages.push(...contextResult); + + logInfo(`ReAct Agent Loop 启动: ${model}`, `工具: ${useTools ? '开启' : '关闭'}, 记忆: ${isMemoryEnabled() ? '开启' : '关闭'}, tokens≈${estimateTokens(messages.map(m => m.content || '').join(''))}`); let loopCount = 0; const allToolRecords: ToolCallRecord[] = []; diff --git a/src/renderer/services/document-processor.ts b/src/renderer/services/document-processor.ts deleted file mode 100644 index ac24e85..0000000 --- a/src/renderer/services/document-processor.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * DocumentProcessor - 文档分块处理 - */ - -export function chunkText(text: string, { maxChunkSize = 1500, overlap = 200 } = {}): string[] { - if (!text || text.trim().length === 0) return []; - - const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim()); - const chunks: string[] = []; - let current = ''; - - for (const para of paragraphs) { - const trimmed = para.trim(); - if (!trimmed) continue; - - if (trimmed.length > maxChunkSize) { - if (current) { - chunks.push(current.trim()); - current = ''; - } - const sentences = splitSentences(trimmed); - let sentenceBuf = ''; - for (const sent of sentences) { - if ((sentenceBuf + sent).length > maxChunkSize && sentenceBuf) { - chunks.push(sentenceBuf.trim()); - sentenceBuf = overlap > 0 ? sentenceBuf.slice(-overlap) + sent : sent; - } else { - sentenceBuf += sent; - } - } - if (sentenceBuf.trim()) { - current = sentenceBuf; - } - continue; - } - - if ((current + '\n\n' + trimmed).length > maxChunkSize && current) { - chunks.push(current.trim()); - if (overlap > 0 && current.length > overlap) { - current = current.slice(-overlap) + '\n\n' + trimmed; - } else { - current = trimmed; - } - } else { - current = current ? current + '\n\n' + trimmed : trimmed; - } - } - - if (current.trim()) { - chunks.push(current.trim()); - } - - return chunks; -} - -function splitSentences(text: string): string[] { - const parts = text.split(/(?<=[。!?.!?])\s*/); - return parts.filter(p => p.trim()).map(p => p.trim() + ' '); -} - -export function createChunkMetadata(chunk: string, index: number, docId: string, filename: string) { - return { - id: `${docId}_chunk_${index}`, - docId, - filename, - chunkIndex: index, - text: chunk, - charCount: chunk.length - }; -} diff --git a/src/renderer/services/tool-registry.ts b/src/renderer/services/tool-registry.ts index 84c3eb0..0d32ec7 100644 --- a/src/renderer/services/tool-registry.ts +++ b/src/renderer/services/tool-registry.ts @@ -5,6 +5,7 @@ import type { ToolDefinition, ToolResult } from '../types.js'; import { logToolStart, logToolResult, logError, logInfo } from './log-service.js'; +import { getMCPToolDefinitions } from './mcp-client.js'; export const TOOL_DEFINITIONS: ToolDefinition[] = [ { @@ -576,7 +577,24 @@ export function isToolEnabled(toolName: string): boolean { } export function getEnabledToolDefinitions(): ToolDefinition[] { - return TOOL_DEFINITIONS.filter(def => enabledTools.has(def.function.name)); + const builtIn = TOOL_DEFINITIONS.filter(def => enabledTools.has(def.function.name)); + return [...builtIn, ..._mcpToolCache]; +} + +/** MCP 工具缓存 */ +let _mcpToolCache: ToolDefinition[] = []; + +/** 刷新 MCP 工具缓存(在 MCP 服务器配置变更时调用) */ +export async function refreshMCPTools(): Promise { + try { + _mcpToolCache = await getMCPToolDefinitions(); + if (_mcpToolCache.length > 0) { + logInfo(`MCP 工具已注册: ${_mcpToolCache.length} 个`, _mcpToolCache.map(t => t.function.name).join(', ')); + } + } catch (err) { + logError('MCP 工具刷新失败', (err as Error).message); + _mcpToolCache = []; + } } export async function executeTool(toolName: string, args: Record): Promise {