diff --git a/.gitignore b/.gitignore index 8658050..20d816a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ sub_agent/tasks/ sub_agent/data/ sub_agent/logs/ sub_agent/project/ +sub_agent_results/ # Misc *.pid @@ -32,6 +33,7 @@ static/dist/ *.tsbuildinfo user_upload/ skills/ +ax_probe_chrome.py # Ignore docs and runtime pid webapp.pid diff --git a/config/custom_models.json b/config/custom_models.json index 009dc41..ceee03f 100644 --- a/config/custom_models.json +++ b/config/custom_models.json @@ -25,21 +25,22 @@ "enable_thinking": true } }, - "extra_parameter": {} + "extra_parameter": {}, + "model_description": "你的基础模型是 Kimi-k2.6(测试别名),底层与 Kimi-k2.5 一致,并通过 thinking 参数开启/关闭思考能力。" }, { - "model_name": "kimi-k2.7", - "description": "Kimi-k2.7(测试模型),仅快速模式", + "model_name": "DeepSeek-V4-Flash Max", + "description": "DeepSeek V4 Flash(reasoning_effort=max),支持快速/思考/深度思考", "visible": true, - "url": "${API_BASE_KIMI_OFFICIAL}", - "apikey": "${API_KEY_KIMI_OFFICIAL}", + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", "multimodal": "none", - "reasoning_capability": "fast", - "context_window": 256000, - "max_output_tokens": 32768, + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, "thinkmode_status": { "type": "param_toggle", - "model_id": "kimi-k2.5", + "model_id": "deepseek-v4-flash", "fast_extra_parameter": { "thinking": { "type": "disabled" @@ -47,11 +48,97 @@ }, "thinking_extra_parameter": { "thinking": { - "type": "disabled" - } + "type": "enabled" + }, + "reasoning_effort": "max" } }, - "extra_parameter": {} + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Flash,一个快捷、高效的通用模型,具备接近旗舰级的推理能力,在简单到中等复杂度任务中表现出色,并支持 1M 上下文,适合追求响应速度与成本效率的使用场景。" + }, + { + "model_name": "DeepSeek-V4-Flah High", + "description": "DeepSeek V4 Flash(reasoning_effort=high),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-flash", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "high" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Flash,一个快捷、高效的通用模型,具备接近旗舰级的推理能力,在简单到中等复杂度任务中表现出色,并支持 1M 上下文,适合追求响应速度与成本效率的使用场景。" + }, + { + "model_name": "DeepSeek-V4-Pro Max", + "description": "DeepSeek V4 Pro(reasoning_effort=max),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-pro", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "max" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Pro,一个面向复杂任务的高性能通用模型,具备突出的 Agent 能力、丰富的世界知识和顶级推理表现,在编程、数学、STEM 与复杂问题分析场景中表现尤为出色,并支持 1M 上下文。" + }, + { + "model_name": "DeepSeek-V4-Pro High", + "description": "DeepSeek V4 Pro(reasoning_effort=high),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-pro", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "high" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Pro,一个面向复杂任务的高性能通用模型,具备突出的 Agent 能力、丰富的世界知识和顶级推理表现,在编程、数学、STEM 与复杂问题分析场景中表现尤为出色,并支持 1M 上下文。" } ] } diff --git a/config/custom_models.json.example b/config/custom_models.json.example index 009dc41..ceee03f 100644 --- a/config/custom_models.json.example +++ b/config/custom_models.json.example @@ -25,21 +25,22 @@ "enable_thinking": true } }, - "extra_parameter": {} + "extra_parameter": {}, + "model_description": "你的基础模型是 Kimi-k2.6(测试别名),底层与 Kimi-k2.5 一致,并通过 thinking 参数开启/关闭思考能力。" }, { - "model_name": "kimi-k2.7", - "description": "Kimi-k2.7(测试模型),仅快速模式", + "model_name": "DeepSeek-V4-Flash Max", + "description": "DeepSeek V4 Flash(reasoning_effort=max),支持快速/思考/深度思考", "visible": true, - "url": "${API_BASE_KIMI_OFFICIAL}", - "apikey": "${API_KEY_KIMI_OFFICIAL}", + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", "multimodal": "none", - "reasoning_capability": "fast", - "context_window": 256000, - "max_output_tokens": 32768, + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, "thinkmode_status": { "type": "param_toggle", - "model_id": "kimi-k2.5", + "model_id": "deepseek-v4-flash", "fast_extra_parameter": { "thinking": { "type": "disabled" @@ -47,11 +48,97 @@ }, "thinking_extra_parameter": { "thinking": { - "type": "disabled" - } + "type": "enabled" + }, + "reasoning_effort": "max" } }, - "extra_parameter": {} + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Flash,一个快捷、高效的通用模型,具备接近旗舰级的推理能力,在简单到中等复杂度任务中表现出色,并支持 1M 上下文,适合追求响应速度与成本效率的使用场景。" + }, + { + "model_name": "DeepSeek-V4-Flah High", + "description": "DeepSeek V4 Flash(reasoning_effort=high),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-flash", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "high" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Flash,一个快捷、高效的通用模型,具备接近旗舰级的推理能力,在简单到中等复杂度任务中表现出色,并支持 1M 上下文,适合追求响应速度与成本效率的使用场景。" + }, + { + "model_name": "DeepSeek-V4-Pro Max", + "description": "DeepSeek V4 Pro(reasoning_effort=max),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-pro", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "max" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Pro,一个面向复杂任务的高性能通用模型,具备突出的 Agent 能力、丰富的世界知识和顶级推理表现,在编程、数学、STEM 与复杂问题分析场景中表现尤为出色,并支持 1M 上下文。" + }, + { + "model_name": "DeepSeek-V4-Pro High", + "description": "DeepSeek V4 Pro(reasoning_effort=high),支持快速/思考/深度思考", + "visible": true, + "url": "https://api.deepseek.com", + "apikey": "${API_KEY_DEEPSEEK}", + "multimodal": "none", + "reasoning_capability": "fast,thinking", + "context_window": 1000000, + "max_output_tokens": 384000, + "thinkmode_status": { + "type": "param_toggle", + "model_id": "deepseek-v4-pro", + "fast_extra_parameter": { + "thinking": { + "type": "disabled" + } + }, + "thinking_extra_parameter": { + "thinking": { + "type": "enabled" + }, + "reasoning_effort": "high" + } + }, + "extra_parameter": {}, + "model_description": "你是DeepSeek-V4-Pro,一个面向复杂任务的高性能通用模型,具备突出的 Agent 能力、丰富的世界知识和顶级推理表现,在编程、数学、STEM 与复杂问题分析场景中表现尤为出色,并支持 1M 上下文。" } ] } diff --git a/config/model_profiles.py b/config/model_profiles.py index c7cc113..694b152 100644 --- a/config/model_profiles.py +++ b/config/model_profiles.py @@ -400,6 +400,8 @@ def _build_custom_profile(item: Dict[str, Any]) -> Optional[Dict[str, Any]]: profile: Dict[str, Any] = { "name": key, "description": str(item.get("description") or ""), + "model_description": str(item.get("model_description") or ""), + "is_custom_model": True, "hidden": not bool(item.get("visible", True)), "multimodal": _normalize_multimodal(item.get("multimodal")), "context_window": context_window, @@ -496,8 +498,23 @@ def get_model_prompt_replacements(key: str) -> dict: """获取模型相关的提示词替换字段,若缺失则回退到 Kimi 版本。""" fallback = MODEL_PROMPT_OVERRIDES.get("kimi", {}) overrides = MODEL_PROMPT_OVERRIDES.get(key) or {} + custom_model_description = "" + is_custom_model = False + try: + custom_profile = get_registered_model_profiles().get(key) or {} + custom_model_description = str(custom_profile.get("model_description") or "") + is_custom_model = bool(custom_profile.get("is_custom_model")) + except Exception: + custom_model_description = "" + is_custom_model = False + if is_custom_model: + return { + "model_description": custom_model_description or overrides.get("model_description") or fallback.get("model_description") or "", + "thinking_model_line": "", + "deep_thinking_line": "", + } return { - "model_description": overrides.get("model_description") or fallback.get("model_description") or "", + "model_description": custom_model_description or overrides.get("model_description") or fallback.get("model_description") or "", "thinking_model_line": overrides.get("thinking_model_line") or fallback.get("thinking_model_line") or "", "deep_thinking_line": overrides.get("deep_thinking_line") or fallback.get("deep_thinking_line") or "" } diff --git a/prompts/main_system.txt b/prompts/main_system.txt index 0551444..2cd0b46 100644 --- a/prompts/main_system.txt +++ b/prompts/main_system.txt @@ -143,6 +143,7 @@ 3. **诚实守信**:做不到的事情坦白说,不编造 4. **用户友好**:用简单的语言解释复杂的操作 5. **正确执行**:主动确认细节,获得明确许可后再开始 +6. **沟通原则**:使用用户使用的语言进行思考和交流 ## 8. 个性化配置 diff --git a/prompts/main_system_qwenvl.txt b/prompts/main_system_qwenvl.txt index 2f30cbe..76cf585 100644 --- a/prompts/main_system_qwenvl.txt +++ b/prompts/main_system_qwenvl.txt @@ -161,6 +161,7 @@ 3. **诚实守信**:做不到的事情坦白说,不编造 4. **用户友好**:用简单的语言解释复杂的操作 5. **正确执行**:主动确认细节,获得明确许可后再开始 +6. **沟通原则**:使用用户使用的语言进行思考和交流 ## 8. 个性化配置 diff --git a/server/chat_flow_tool_loop.py b/server/chat_flow_tool_loop.py index 3d2c5f0..7bc718b 100644 --- a/server/chat_flow_tool_loop.py +++ b/server/chat_flow_tool_loop.py @@ -151,6 +151,16 @@ async def _wait_for_tool_approval(*, approval_id: str, username: str, timeout_se async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, client_sid: str, username: str, iteration: int, conversation_id: Optional[str], last_tool_call_time: float, process_sub_agent_updates, process_background_command_updates, maybe_mark_failure_from_message, mark_force_thinking, get_stop_flag, clear_stop_flag, workspace=None): previous_tool_loop_active = getattr(web_terminal, "_tool_loop_active", False) web_terminal._tool_loop_active = True + allowed_tool_names = set() + try: + defined_tools = web_terminal.define_tools() or [] + for tool in defined_tools: + name = ((tool or {}).get("function") or {}).get("name") + if isinstance(name, str) and name: + allowed_tool_names.add(name) + except Exception as exc: + debug_log(f"构建工具白名单失败(降级继续): {exc}") + # 执行每个工具 for tool_call in tool_calls: # 检查停止标志 @@ -310,6 +320,49 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie }) continue + # 严格校验:只允许执行当前回合明确下发给模型的工具。 + # 某些模型会“幻觉”调用未下发工具(如 view_image),必须在执行层拦截。 + if allowed_tool_names and function_name not in allowed_tool_names: + if function_name == "view_image" and "vlm_analyze" in allowed_tool_names: + if not arguments.get("prompt"): + arguments["prompt"] = "请详细分析这张图片的内容,包括关键文字、主体对象与场景信息。" + debug_log( + "工具名自动纠正: view_image -> vlm_analyze (模型未获授权 view_image)" + ) + function_name = "vlm_analyze" + else: + denied_message = ( + f"工具 {function_name} 不在当前模型可用工具列表中,已拒绝执行。" + ) + denied_payload = { + "success": False, + "status": "denied", + "code": "tool_not_allowed", + "tool": function_name, + "message": denied_message, + } + sender('update_action', { + 'preparing_id': tool_call_id, + 'status': 'completed', + 'result': denied_payload, + 'message': denied_message, + 'conversation_id': conversation_id + }) + denied_content = json.dumps(denied_payload, ensure_ascii=False) + web_terminal.context_manager.add_conversation( + "tool", + denied_content, + tool_call_id=tool_call_id, + name=function_name + ) + messages.append({ + "role": "tool", + "tool_call_id": tool_call_id, + "name": function_name, + "content": denied_content + }) + continue + debug_log(f"执行工具: {function_name} (ID: {tool_call_id})") permission_eval = web_terminal.evaluate_tool_permission(function_name, arguments) diff --git a/skills/terminal-guide/SKILL.md b/skills/terminal-guide/SKILL.md index 3d83625..cce7fe7 100644 --- a/skills/terminal-guide/SKILL.md +++ b/skills/terminal-guide/SKILL.md @@ -65,6 +65,8 @@ Terminal 工具提供了一个可以持续存在的终端会话,命令在其 - top、htop - 任何需要完整 TTY 的程序 +# 简单的交互程序 如只需要输入输出内容的py,node程序等可以使用 + ## 三步工作流 使用 terminal 工具的标准流程: diff --git a/static/src/app/lifecycle.ts b/static/src/app/lifecycle.ts index 4d28471..165e242 100644 --- a/static/src/app/lifecycle.ts +++ b/static/src/app/lifecycle.ts @@ -91,6 +91,11 @@ export async function mounted() { } export function beforeUnmount() { + if (this._todoRefreshTimer) { + clearTimeout(this._todoRefreshTimer); + this._todoRefreshTimer = null; + } + // 停止任务轮询 try { const { useTaskStore } = require('../stores/task'); diff --git a/static/src/app/methods/taskPolling.ts b/static/src/app/methods/taskPolling.ts index 5f618a8..75bd7d4 100644 --- a/static/src/app/methods/taskPolling.ts +++ b/static/src/app/methods/taskPolling.ts @@ -44,6 +44,20 @@ function isEmptyAssistantPlaceholderMessage(message: any): boolean { * 将从 REST API 轮询获取的事件转换为前端状态更新 */ export const taskPollingMethods = { + scheduleTodoListRefresh(delayMs = 120) { + const delay = Number.isFinite(delayMs) ? Math.max(0, Number(delayMs)) : 120; + if (this._todoRefreshTimer) { + clearTimeout(this._todoRefreshTimer); + this._todoRefreshTimer = null; + } + this._todoRefreshTimer = setTimeout(() => { + this._todoRefreshTimer = null; + if (typeof this.fetchTodoList === 'function') { + Promise.resolve(this.fetchTodoList()).catch(() => {}); + } + }, delay); + }, + cleanupTrailingEmptyAssistantPlaceholder(reason = 'unspecified') { if (!Array.isArray(this.messages) || !this.messages.length) { return false; @@ -912,6 +926,13 @@ export const taskPollingMethods = { targetAction.tool.content = data.content; } + // 待办工具执行完成后主动刷新左侧待办列表(网页端不再依赖 websocket todo_updated) + if (data.status === 'completed') { + const toolName = String(targetAction?.tool?.name || '').toLowerCase(); + if (toolName.startsWith('todo_') || toolName === 'todo_create' || toolName === 'todo_update_task') { + this.scheduleTodoListRefresh(80); + } + } this.$forceUpdate(); this.conditionalScrollToBottom(); @@ -1063,6 +1084,7 @@ export const taskPollingMethods = { this.updateCurrentContextTokens(); }, 500); } + this.scheduleTodoListRefresh(100); const pendingToolsAfter = typeof this.hasPendingToolActions === 'function' ? this.hasPendingToolActions() : null; jsonDebug('handleTaskComplete:after', { @@ -1099,6 +1121,7 @@ export const taskPollingMethods = { if (typeof this.clearPendingTools === 'function') { this.clearPendingTools('task_stopped'); } + this.scheduleTodoListRefresh(100); this.$forceUpdate(); this.clearTaskState(); diff --git a/static/src/app/methods/ui.ts b/static/src/app/methods/ui.ts index 0841658..a2c6539 100644 --- a/static/src/app/methods/ui.ts +++ b/static/src/app/methods/ui.ts @@ -1600,7 +1600,6 @@ export const uiMethods = { } const focusPromise = this.focusFetchFiles(); - const todoPromise = this.fileFetchTodoList(); let treePromise: Promise | null = null; const isHostMode = statusData?.container?.mode === 'host'; this.versioningHostMode = !!isHostMode; @@ -1672,6 +1671,9 @@ export const uiMethods = { } } + // 待办数据依赖当前会话上下文,放在会话恢复/切换之后拉取,避免初始化早期拿到空快照 + const todoPromise = this.fileFetchTodoList(); + // 等待其他加载项完成(允许部分失败不阻塞模式切换) const pendingPromises = [focusPromise, todoPromise]; if (treePromise) {