diff --git a/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java b/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java index b438256a..1462e5d1 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/AgentGraphBuilder.java @@ -194,7 +194,15 @@ public class AgentGraphBuilder { // 内置搜索作为首选,search 工具作为补充/兜底 log.info("内置搜索已开启 (provider={}),search 工具保留作为补充通道", provider.getProviderId()); } - int maxIter = entity.getMaxIterations() != null ? entity.getMaxIterations() : 25; + // Default 100 if DB row leaves max_iterations null; clamp per-agent overrides + // to the hard ceiling (BaseAgent.MAX_ITERATIONS_HARD_CEILING) so a misconfigured + // row can never push an unbounded loop. Aligned with QwenPaw's 1..100 range. + int rawMaxIter = entity.getMaxIterations() != null ? entity.getMaxIterations() : 100; + int maxIter = Math.max(1, Math.min(rawMaxIter, BaseAgent.MAX_ITERATIONS_HARD_CEILING)); + if (maxIter != rawMaxIter) { + log.warn("Agent {} max_iterations={} clamped to {} (1..{})", + entity.getId(), rawMaxIter, maxIter, BaseAgent.MAX_ITERATIONS_HARD_CEILING); + } String enhancedPrompt = buildEnhancedPrompt(entity, builtinSearchEnabled); diff --git a/mateclaw-server/src/main/java/vip/mate/agent/BaseAgent.java b/mateclaw-server/src/main/java/vip/mate/agent/BaseAgent.java index 5fd5058f..1d58915c 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/BaseAgent.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/BaseAgent.java @@ -43,8 +43,13 @@ public abstract class BaseAgent { /** 系统提示词 */ protected String systemPrompt; - /** 最大工具调用迭代次数 */ - protected int maxIterations = 25; + /** + * Max ReAct iterations (one reasoning + action + observation step counts as one). + * Default 100, hard ceiling 100 (enforced in AgentGraphBuilder so per-agent DB + * overrides cannot exceed it). Aligned with QwenPaw's _MAX_MAX_ITERATIONS. + */ + public static final int MAX_ITERATIONS_HARD_CEILING = 100; + protected int maxIterations = 100; /** 工作区活动目录(限制文件工具访问范围,为空不限制) */ protected String workspaceBasePath; diff --git a/mateclaw-server/src/main/java/vip/mate/tool/builtin/DocxRenderTool.java b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DocxRenderTool.java index 200c7c20..734646e2 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/builtin/DocxRenderTool.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DocxRenderTool.java @@ -70,7 +70,13 @@ public class DocxRenderTool { displayName, bytes.length, elapsed, id); String url = "/api/v1/files/generated/" + id; - return "文档已生成:[" + displayName + "](" + url + ")(链接 10 分钟内有效)"; + // Explicit instruction to suppress LLM hallucinating an absolute host. + // DeepSeek/Claude have been observed prepending placeholder domains + // (e.g. https://ai-tools-system.com) when echoing the URL back to the user, + // breaking the download link. Repeat the path verbatim with no host. + return "文档已生成:[" + displayName + "](" + url + ")(链接 10 分钟内有效)。\n" + + "重要:回答用户时**必须**使用上述相对路径 `" + url + "`," + + "**不要**添加任何 https://、http:// 域名前缀,前端会自动拼接当前主机。"; } catch (Exception e) { log.error("[DocxRender] render failed for {}: {}", displayName, e.getMessage(), e); return "渲染失败:" + e.getMessage(); diff --git a/mateclaw-server/src/main/resources/db/data-en.sql b/mateclaw-server/src/main/resources/db/data-en.sql index fa383730..656f7605 100644 --- a/mateclaw-server/src/main/resources/db/data-en.sql +++ b/mateclaw-server/src/main/resources/db/data-en.sql @@ -10,21 +10,21 @@ MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_n KEY (id) VALUES (1000000001, 'MateClaw Assistant', 'Default AI assistant with ReAct mode and tool calling', 'react', 'You are MateClaw, an intelligent AI assistant. You can help users answer questions, analyze data, and execute tasks. Please respond professionally and in a friendly manner.', - NULL, 25, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0); + NULL, 100, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0); -- Default Agent: Task Planner (Plan-Execute mode) MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) KEY (id) VALUES (1000000002, 'Task Planner', 'Task planning assistant for complex multi-step tasks', 'plan_execute', 'You are a professional task planning and execution assistant. You excel at breaking complex goals into executable steps and completing them systematically.', - NULL, 20, TRUE, '📋', 'planning,task', NOW(), NOW(), 0); + NULL, 100, TRUE, '📋', 'planning,task', NOW(), NOW(), 0); -- StateGraph ReAct Agent (StateGraph architecture) MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) KEY (id) VALUES (1000000003, 'StateGraph ReAct', 'StateGraph-based ReAct Agent with explicit reasoning loops and tool calling', 'react', 'You are an intelligent assistant based on the StateGraph architecture. You can use tools to help users solve problems. Please respond professionally and in a friendly manner.', - NULL, 25, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0); + NULL, 100, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0); -- ==================== Local Model Providers (displayed first) ==================== diff --git a/mateclaw-server/src/main/resources/db/data-mysql-en.sql b/mateclaw-server/src/main/resources/db/data-mysql-en.sql index 3ddd52ab..e820d4c1 100644 --- a/mateclaw-server/src/main/resources/db/data-mysql-en.sql +++ b/mateclaw-server/src/main/resources/db/data-mysql-en.sql @@ -9,21 +9,21 @@ ON DUPLICATE KEY UPDATE username=VALUES(username), password=VALUES(password), ni INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000001, 'MateClaw Assistant', 'Default AI assistant with ReAct mode and tool calling', 'react', 'You are MateClaw, an intelligent AI assistant. You can help users answer questions, analyze data, and execute tasks. Please respond professionally and in a friendly manner.', - NULL, 25, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0) + NULL, 100, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- Default Agent: Task Planner (Plan-Execute mode) INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000002, 'Task Planner', 'Task planning assistant for complex multi-step tasks', 'plan_execute', 'You are a professional task planning and execution assistant. You excel at breaking complex goals into executable steps and completing them systematically.', - NULL, 20, TRUE, '📋', 'planning,task', NOW(), NOW(), 0) + NULL, 100, TRUE, '📋', 'planning,task', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- StateGraph ReAct Agent (StateGraph architecture) INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000003, 'StateGraph ReAct', 'StateGraph-based ReAct Agent with explicit reasoning loops and tool calling', 'react', 'You are an intelligent assistant based on the StateGraph architecture. You can use tools to help users solve problems. Please respond professionally and in a friendly manner.', - NULL, 25, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0) + NULL, 100, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- ==================== Local Model Providers (displayed first) ==================== diff --git a/mateclaw-server/src/main/resources/db/data-mysql-zh.sql b/mateclaw-server/src/main/resources/db/data-mysql-zh.sql index 94afd8e3..494fcd77 100644 --- a/mateclaw-server/src/main/resources/db/data-mysql-zh.sql +++ b/mateclaw-server/src/main/resources/db/data-mysql-zh.sql @@ -9,21 +9,21 @@ ON DUPLICATE KEY UPDATE username=VALUES(username), password=VALUES(password), ni INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000001, 'MateClaw Assistant', '默认 AI 助手,基于 ReAct 模式,支持工具调用', 'react', '你是 MateClaw,一个智能 AI 助手。你可以帮助用户回答问题、分析数据、执行任务。请用中文回复,保持专业、友好的态度。', - NULL, 25, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0) + NULL, 100, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- 默认 Agent:任务规划助手(Plan-Execute 模式) INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000002, 'Task Planner', '任务规划助手,适合复杂多步骤任务', 'plan_execute', '你是一个专业的任务规划和执行助手。你擅长将复杂目标分解为可执行的步骤,并逐步完成。请用中文回复。', - NULL, 20, TRUE, '📋', 'planning,task', NOW(), NOW(), 0) + NULL, 100, TRUE, '📋', 'planning,task', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- StateGraph ReAct Agent(支持 StateGraph 架构) INSERT INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) VALUES (1000000003, 'StateGraph ReAct', '基于 StateGraph 的 ReAct Agent,支持显式推理循环和工具调用', 'react', '你是基于 StateGraph 架构的智能助手。你可以使用工具来帮助用户解决问题。请用中文回复,保持专业、友好的态度。', - NULL, 25, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0) + NULL, 100, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), agent_type=VALUES(agent_type), system_prompt=VALUES(system_prompt), model_name=VALUES(model_name), max_iterations=VALUES(max_iterations), enabled=VALUES(enabled), icon=VALUES(icon), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted); -- ==================== 本地模型 Provider(优先展示) ==================== diff --git a/mateclaw-server/src/main/resources/db/data-zh.sql b/mateclaw-server/src/main/resources/db/data-zh.sql index e5d13c91..17524c8a 100644 --- a/mateclaw-server/src/main/resources/db/data-zh.sql +++ b/mateclaw-server/src/main/resources/db/data-zh.sql @@ -10,21 +10,21 @@ MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_n KEY (id) VALUES (1000000001, 'MateClaw Assistant', '默认 AI 助手,基于 ReAct 模式,支持工具调用', 'react', '你是 MateClaw,一个智能 AI 助手。你可以帮助用户回答问题、分析数据、执行任务。请用中文回复,保持专业、友好的态度。', - NULL, 25, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0); + NULL, 100, TRUE, '🤖', 'default,assistant', NOW(), NOW(), 0); -- 默认 Agent:任务规划助手(Plan-Execute 模式) MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) KEY (id) VALUES (1000000002, 'Task Planner', '任务规划助手,适合复杂多步骤任务', 'plan_execute', '你是一个专业的任务规划和执行助手。你擅长将复杂目标分解为可执行的步骤,并逐步完成。请用中文回复。', - NULL, 20, TRUE, '📋', 'planning,task', NOW(), NOW(), 0); + NULL, 100, TRUE, '📋', 'planning,task', NOW(), NOW(), 0); -- StateGraph ReAct Agent(支持 StateGraph 架构) MERGE INTO mate_agent (id, name, description, agent_type, system_prompt, model_name, max_iterations, enabled, icon, tags, create_time, update_time, deleted) KEY (id) VALUES (1000000003, 'StateGraph ReAct', '基于 StateGraph 的 ReAct Agent,支持显式推理循环和工具调用', 'react', '你是基于 StateGraph 架构的智能助手。你可以使用工具来帮助用户解决问题。请用中文回复,保持专业、友好的态度。', - NULL, 25, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0); + NULL, 100, TRUE, '🔄', 'react,stategraph,tools', NOW(), NOW(), 0); -- ==================== 本地模型 Provider(优先展示) ==================== diff --git a/mateclaw-server/src/main/resources/db/migration/h2/V47__agent_max_iterations_100.sql b/mateclaw-server/src/main/resources/db/migration/h2/V47__agent_max_iterations_100.sql new file mode 100644 index 00000000..3175bb33 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/h2/V47__agent_max_iterations_100.sql @@ -0,0 +1,16 @@ +-- V47: Bump default agents' max_iterations to 100 (QwenPaw-style hard ceiling). +-- +-- The previous defaults (25 for ReAct, 20 for plan-execute) ran the LimitExceededNode +-- too eagerly on substantive multi-tool tasks (e.g. document generation with image +-- conversion). New default is 100, matching QwenPaw's _MAX_MAX_ITERATIONS upper bound. +-- AgentGraphBuilder still clamps any per-agent override to MAX_ITERATIONS_HARD_CEILING +-- at runtime, so a user-configured 200 will be silently capped to 100. +-- +-- Idempotent: only updates rows that still hold the old defaults, so user-customized +-- agents are not touched. + +UPDATE mate_agent SET max_iterations = 100 +WHERE id IN (1000000001, 1000000003) AND max_iterations = 25; + +UPDATE mate_agent SET max_iterations = 100 +WHERE id = 1000000002 AND max_iterations = 20; diff --git a/mateclaw-server/src/main/resources/db/migration/mysql/V47__agent_max_iterations_100.sql b/mateclaw-server/src/main/resources/db/migration/mysql/V47__agent_max_iterations_100.sql new file mode 100644 index 00000000..3175bb33 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/mysql/V47__agent_max_iterations_100.sql @@ -0,0 +1,16 @@ +-- V47: Bump default agents' max_iterations to 100 (QwenPaw-style hard ceiling). +-- +-- The previous defaults (25 for ReAct, 20 for plan-execute) ran the LimitExceededNode +-- too eagerly on substantive multi-tool tasks (e.g. document generation with image +-- conversion). New default is 100, matching QwenPaw's _MAX_MAX_ITERATIONS upper bound. +-- AgentGraphBuilder still clamps any per-agent override to MAX_ITERATIONS_HARD_CEILING +-- at runtime, so a user-configured 200 will be silently capped to 100. +-- +-- Idempotent: only updates rows that still hold the old defaults, so user-customized +-- agents are not touched. + +UPDATE mate_agent SET max_iterations = 100 +WHERE id IN (1000000001, 1000000003) AND max_iterations = 25; + +UPDATE mate_agent SET max_iterations = 100 +WHERE id = 1000000002 AND max_iterations = 20; diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue index 39f708bb..57dcc705 100644 --- a/mateclaw-ui/src/views/ChatConsole.vue +++ b/mateclaw-ui/src/views/ChatConsole.vue @@ -798,7 +798,11 @@ onBeforeUnmount(() => { clearInterval(activityPollTimer) activityPollTimer = null } - stopChatGeneration() + // Switching tabs / route changes / mouse-detach unmount this component, but the + // backend agent should keep running so the user can reconnect later. Use + // resetForNewConversation (front-end SSE disconnect only) instead of + // stopChatGeneration which would POST /stop and abort the in-flight turn. + resetForNewConversation() // 释放所有附件的 ObjectURL,防止内存泄漏 revokeAllPreviewUrls() })