From 0efb8a2c39f5bf9b7507fc39d21c0665d52fce63 Mon Sep 17 00:00:00 2001 From: ageerle Date: Mon, 4 May 2026 14:44:23 +0000 Subject: [PATCH] feat: improve thinking mode portability and add continuous runner Co-authored-by: monkeycode-ai --- .monkeycode/MEMORY.md | 32 ++++++++++++ .../service/chat/impl/ChatServiceFacade.java | 21 ++++++-- scripts/continuous_runner.py | 52 +++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 .monkeycode/MEMORY.md create mode 100644 scripts/continuous_runner.py diff --git a/.monkeycode/MEMORY.md b/.monkeycode/MEMORY.md new file mode 100644 index 00000000..f9b80a7f --- /dev/null +++ b/.monkeycode/MEMORY.md @@ -0,0 +1,32 @@ +# 用户指令记忆 + +本文件记录了用户的指令、偏好和教导,用于在未来的交互中提供参考。 + +## 格式 + +### 用户指令条目 +用户指令条目应遵循以下格式: + +[用户指令摘要] +- Date: [YYYY-MM-DD] +- Context: [提及的场景或时间] +- Instructions: + - [用户教导或指示的内容,逐行描述] + +### 项目知识条目 +Agent 在任务执行过程中发现的条目应遵循以下格式: + +[项目知识摘要] +- Date: [YYYY-MM-DD] +- Context: Agent 在执行 [具体任务描述] 时发现 +- Category: [代码结构|代码模式|代码生成|构建方法|测试方法|依赖关系|环境配置] +- Instructions: + - [具体的知识点,逐行描述] + +## 去重策略 +- 添加新条目前,检查是否存在相似或相同的指令 +- 若发现重复,跳过新条目或与已有条目合并 +- 合并时,更新上下文或日期信息 +- 这有助于避免冗余条目,保持记忆文件整洁 + +## 条目 diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java index 16e0750d..273a8ac9 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java @@ -219,6 +219,8 @@ public class ChatServiceFacade implements IChatService { */ private SseEmitter handleThinkingMode(ChatRequest chatRequest) { + String npxCommand = resolveNpxCommand(); + // 配置监督者模型 OpenAiChatModel plannerModel = OpenAiChatModel.builder() .baseUrl(chatRequest.getChatModelVo().getApiHost()) @@ -228,7 +230,7 @@ public class ChatServiceFacade implements IChatService { // Bing 搜索 MCP 客户端 McpTransport bingTransport = new StdioMcpTransport.Builder() - .command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "bing-cn-mcp")) + .command(List.of(npxCommand, "-y", "bing-cn-mcp")) .logEvents(true) .build(); @@ -240,7 +242,7 @@ public class ChatServiceFacade implements IChatService { // Playwright MCP 客户端 - 浏览器自动化工具 McpTransport playwrightTransport = new StdioMcpTransport.Builder() - .command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "@playwright/mcp@latest")) + .command(List.of(npxCommand, "-y", "@playwright/mcp@latest")) .logEvents(true) .build(); @@ -253,7 +255,7 @@ public class ChatServiceFacade implements IChatService { // 允许 AI 读取、写入、搜索文件(基于当前项目根目录) String userDir = System.getProperty("user.dir"); McpTransport filesystemTransport = new StdioMcpTransport.Builder() - .command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", + .command(List.of(npxCommand, "-y", "@modelcontextprotocol/server-filesystem", userDir)) .logEvents(true) @@ -344,6 +346,18 @@ public class ChatServiceFacade implements IChatService { return chatRequest.getEmitter(); } + /** + * 根据运行平台选择 npx 命令,避免 Linux/macOS 环境下固定使用 npx.cmd 导致失败。 + */ + private String resolveNpxCommand() { + String customNpxCommand = System.getenv("MCP_NPX_COMMAND"); + if (StringUtils.isNotBlank(customNpxCommand)) { + return customNpxCommand; + } + String osName = System.getProperty("os.name", "").toLowerCase(); + return osName.contains("win") ? "npx.cmd" : "npx"; + } + /** * 支持外部 handler 的对话接口(跨模块调用) * 同时发送到 SSE 和外部 handler @@ -618,4 +632,3 @@ public class ChatServiceFacade implements IChatService { }; } } - diff --git a/scripts/continuous_runner.py b/scripts/continuous_runner.py new file mode 100644 index 00000000..ff1b9eb0 --- /dev/null +++ b/scripts/continuous_runner.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Run a shell command continuously for a fixed duration. + +Example: + python3 scripts/continuous_runner.py --cmd "mvn -pl ruoyi-modules/ruoyi-chat -DskipTests compile" +""" + +from __future__ import annotations + +import argparse +import subprocess +import time +from datetime import datetime, timedelta + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Run command continuously with retry") + parser.add_argument("--cmd", required=True, help="Command to run each round") + parser.add_argument("--minutes", type=int, default=30, help="Total running minutes") + parser.add_argument("--interval", type=int, default=5, help="Seconds between rounds") + parser.add_argument("--stop-on-success", action="store_true", help="Stop after first success") + return parser.parse_args() + + +def main() -> int: + args = parse_args() + deadline = datetime.now() + timedelta(minutes=args.minutes) + + print(f"start={datetime.now().isoformat(timespec='seconds')}") + print(f"end={deadline.isoformat(timespec='seconds')}") + print(f"cmd={args.cmd}") + + round_no = 0 + while datetime.now() < deadline: + round_no += 1 + print(f"\n[{datetime.now().isoformat(timespec='seconds')}] round={round_no}") + result = subprocess.run(args.cmd, shell=True) + print(f"exit_code={result.returncode}") + + if result.returncode == 0 and args.stop_on_success: + print("stop_on_success=true, exiting") + return 0 + + if datetime.now() < deadline: + time.sleep(max(0, args.interval)) + + print(f"done={datetime.now().isoformat(timespec='seconds')}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())