diff --git a/app/agent/prompt/__init__.py b/app/agent/prompt/__init__.py index 1de0129d..3e9de3e6 100644 --- a/app/agent/prompt/__init__.py +++ b/app/agent/prompt/__init__.py @@ -1,5 +1,6 @@ """提示词管理器""" +import shutil import socket from dataclasses import dataclass, field from pathlib import Path @@ -22,6 +23,82 @@ from app.utils.system import SystemUtils SYSTEM_TASKS_FILE = "System Tasks.yaml" SYSTEM_TASKS_SCHEMA_VERSION = 2 +COMMON_SHELL_COMMANDS = ( + "ssh", + "scp", + "sftp", + "rsync", + "git", + "gh", + "rg", + "fd", + "find", + "grep", + "sed", + "awk", + "jq", + "yq", + "curl", + "wget", + "tar", + "gzip", + "gunzip", + "zip", + "unzip", + "xz", + "7z", + "docker", + "docker-compose", + "kubectl", + "helm", + "sqlite3", + "psql", + "mysql", + "redis-cli", + "python", + "python3", + "pip", + "pip3", + "uv", + "node", + "npm", + "yarn", + "pnpm", + "bun", + "ffmpeg", + "ffprobe", + "mediainfo", + "rclone", + "aria2c", + "yt-dlp", + "openssl", + "base64", + "sha256sum", + "shasum", + "du", + "df", + "ps", + "top", + "lsof", + "netstat", + "ss", + "ping", + "traceroute", + "dig", + "nslookup", + "nc", + "telnet", + "crontab", + "systemctl", + "service", + "journalctl", + "launchctl", + "brew", + "apt", + "apk", + "yum", + "dnf", +) class PromptConfigError(ValueError): @@ -305,8 +382,30 @@ class PromptManager: f"- 系统安装目录: {settings.ROOT_PATH}", ] + available_commands = PromptManager._get_available_shell_commands() + if available_commands: + info_lines.append("- 可用系统命令(可通过 `execute_command` 调用):") + info_lines.extend( + f" - {command}: {path}" for command, path in available_commands + ) + return "\n".join(info_lines) + @staticmethod + def _get_available_shell_commands() -> list[tuple[str, str]]: + """ + 探测 PATH 中已经安装的常用命令。 + + 这里只使用 shutil.which 做无副作用查找,不实际执行命令;执行权限、 + 高风险操作确认和输出限制仍由 execute_command 工具负责。 + """ + available_commands: list[tuple[str, str]] = [] + for command in COMMON_SHELL_COMMANDS: + command_path = shutil.which(command) + if command_path: + available_commands.append((command, command_path)) + return available_commands + @staticmethod def _generate_formatting_instructions(caps: ChannelCapabilities) -> str: """ diff --git a/tests/test_agent_prompt_style.py b/tests/test_agent_prompt_style.py index fe0afa0a..10058dfd 100644 --- a/tests/test_agent_prompt_style.py +++ b/tests/test_agent_prompt_style.py @@ -38,6 +38,30 @@ class TestAgentPromptStyle(unittest.TestCase): self.assertIn("当前日期", prompt) self.assertNotIn("当前时间", prompt) + def test_base_prompt_injects_available_shell_commands(self): + """系统信息应注入 PATH 中已安装的常用命令,帮助 Agent 选择 execute_command。""" + command_paths = { + "ssh": "/usr/bin/ssh", + "rg": "/opt/homebrew/bin/rg", + } + with patch( + "app.agent.prompt.shutil.which", + side_effect=lambda command: command_paths.get(command), + ): + prompt = prompt_manager.get_agent_prompt() + + self.assertIn("- 可用系统命令(可通过 `execute_command` 调用):", prompt) + self.assertIn(" - ssh: /usr/bin/ssh", prompt) + self.assertIn(" - rg: /opt/homebrew/bin/rg", prompt) + self.assertNotIn(" - git:", prompt) + + def test_base_prompt_omits_shell_command_section_when_none_available(self): + """PATH 中没有命中白名单命令时,不注入空的系统命令段落。""" + with patch("app.agent.prompt.shutil.which", return_value=None): + prompt = prompt_manager.get_agent_prompt() + + self.assertNotIn("可用系统命令", prompt) + def test_runtime_config_middleware_injects_persona_only(self): middleware = RuntimeConfigMiddleware() updated_request = middleware.modify_request(_FakeRequest())