refactor: remove run_python tool, consolidate all execution to run_command
This commit is contained in:
parent
4bf0733907
commit
78544cb205
@ -108,7 +108,7 @@ python3 -m server.app --path <当前目录> --port 8091 --thinking-mode
|
||||
- `list_files`: 列出目录内容
|
||||
- `search_files`: 搜索文件
|
||||
|
||||
> 说明:目录创建/重命名/删除等通用文件管理建议统一使用 `run_command` 或 `run_python`。
|
||||
> 说明:目录创建/重命名/删除等通用文件管理建议统一使用 `run_command`;需要 Python 时先探测并选择合适解释器。
|
||||
|
||||
#### 终端操作
|
||||
|
||||
@ -119,8 +119,7 @@ python3 -m server.app --path <当前目录> --port 8091 --thinking-mode
|
||||
- `sleep`: 等待命令执行完成
|
||||
|
||||
**快速命令执行**:
|
||||
- `run_command`: 执行 Shell 命令
|
||||
- `run_python`: 执行 Python 代码
|
||||
- `run_command`: 执行 Shell 命令;需要 Python 时通过命令探测并调用合适解释器
|
||||
|
||||
#### 网络检索
|
||||
- `web_search`: 网络搜索
|
||||
|
||||
@ -354,8 +354,8 @@ terminal_input(command="vim file.txt", output_wait=5) # vim 需要完整 TTY
|
||||
|
||||
✅ **正确:**
|
||||
```python
|
||||
# 使用 run_python 执行 Python 代码
|
||||
run_python(code="print('hello')", timeout=5)
|
||||
# 使用 run_command 探测并调用合适的 Python 解释器
|
||||
run_command(command="python3 -c 'print(\"hello\")'", timeout=5)
|
||||
|
||||
# 使用 edit_file 修改文件
|
||||
edit_file(file_path="file.txt", old_string="...", new_string="...")
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"label": "终端指令",
|
||||
"default_enabled": true,
|
||||
"silent_when_disabled": false,
|
||||
"tools": ["run_command", "run_python"]
|
||||
"tools": ["run_command"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -38,4 +38,3 @@
|
||||
- `categories[].label`:分类展示名
|
||||
- `categories[].tools`:该分类下具体工具名称列表(用于前端渲染/筛选/白名单提示)
|
||||
- `default_enabled/silent_when_disabled`:用于 UI/策略展示(并不等于“你一定能调用到该工具”,真实可用性仍取决于服务端策略与运行时环境)
|
||||
|
||||
|
||||
@ -158,7 +158,6 @@ export function reduceTaskEvent(items: TimelineItem[], state: EventRenderState,
|
||||
|
||||
function toolTitle(name: string, args: any, status: string): string {
|
||||
if (name === 'run_command') return `运行 ${args.command || ''}`.trim();
|
||||
if (name === 'run_python') return '运行 Python';
|
||||
if (name === 'write_file') return `Write ${args.file_path || args.path || ''} ${countWrite(args.content || '')}`.trim();
|
||||
if (name === 'edit_file') return `Edited ${args.file_path || args.path || ''} ${countEdit(args)}`.trim();
|
||||
if (name === 'read_file' || name === 'read_skill') return `阅读 ${args.path || args.file_path || ''}`.trim();
|
||||
@ -193,7 +192,7 @@ function toolInitialBody(name: string, args: any): string {
|
||||
function toolResultBody(name: string, args: any, result: any): string {
|
||||
if (typeof result === 'string') return summarizeText(result);
|
||||
if (!result) return '';
|
||||
if (name === 'run_command' || name === 'terminal_input' || name === 'run_python') {
|
||||
if (name === 'run_command' || name === 'terminal_input') {
|
||||
const exitCode = result.exit_code ?? result.returncode;
|
||||
const text = String(result?.output || result?.stdout || result?.stderr || '').trim();
|
||||
if (exitCode !== undefined && exitCode !== 0) return text ? `exit code ${exitCode}` : `exit code ${exitCode}\n(no output)`;
|
||||
|
||||
@ -131,7 +131,7 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
||||
broadcast_callback=None, # CLI模式不需要广播
|
||||
container_session=container_session,
|
||||
)
|
||||
# 让 run_command/run_python 复用终端容器,保持环境一致
|
||||
# 让 run_command 复用终端容器,保持环境一致
|
||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||
self._apply_container_session(container_session)
|
||||
|
||||
|
||||
@ -368,8 +368,6 @@ class MainTerminalCommandMixin:
|
||||
print(f"{OUTPUT_FORMATS['terminal']} 执行终端命令")
|
||||
elif tool_name == "web_search":
|
||||
print(f"{OUTPUT_FORMATS['search']} 网络搜索")
|
||||
elif tool_name == "run_python":
|
||||
print(f"{OUTPUT_FORMATS['code']} 执行Python代码")
|
||||
elif tool_name == "run_command":
|
||||
print(f"{OUTPUT_FORMATS['terminal']} 执行系统命令")
|
||||
elif tool_name == "update_memory":
|
||||
|
||||
@ -125,7 +125,7 @@ class MainTerminalContextMixin:
|
||||
detailed_rules = (
|
||||
"- 所有命令和工具均可直接使用,无需用户批准\n"
|
||||
"- run_command:支持任意命令,包括管道、重定向、子shell等\n"
|
||||
"- 文件操作:read_file / write_file / edit_file 可直接使用;其他文件管理请通过 run_command 或 run_python 执行"
|
||||
"- 文件操作:read_file / write_file / edit_file 可直接使用;其他文件管理请通过 run_command 执行;需要 Python 时先探测并选择合适解释器"
|
||||
)
|
||||
elif mode == "readonly":
|
||||
detailed_rules = (
|
||||
@ -138,7 +138,7 @@ class MainTerminalContextMixin:
|
||||
detailed_rules = (
|
||||
"- run_command:先在系统只读沙箱执行;仅当出现权限拒绝时才触发审批\n"
|
||||
"- 审批通过后:仅当前这一次命令会重试为可写沙箱执行\n"
|
||||
"- 需要用户批准:terminal_input、run_python、write_file、edit_file、save_webpage 及其他会修改工作区的操作\n"
|
||||
"- 需要用户批准:terminal_input、write_file、edit_file、save_webpage 及其他会修改工作区的操作\n"
|
||||
"- 被拒绝或超时:本次操作不会执行写入"
|
||||
)
|
||||
elif mode == "auto_approval":
|
||||
|
||||
@ -502,7 +502,7 @@ class MainTerminalToolsDefinitionMixin:
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"description": "读取/搜索/抽取 UTF-8 文本文件内容。通过 type 参数选择 read(阅读)、search(搜索)、extract(具体行段),支持限制返回字符数。若文件非 UTF-8 或过大,请改用 run_python。",
|
||||
"description": "读取/搜索/抽取 UTF-8 文本文件内容。通过 type 参数选择 read(阅读)、search(搜索)、extract(具体行段),支持限制返回字符数。若文件非 UTF-8 或过大,请改用 run_command 调用合适的解析工具或 Python 解释器。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
@ -809,24 +809,6 @@ class MainTerminalToolsDefinitionMixin:
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "run_python",
|
||||
"description": "执行一次性 Python 脚本,可用于处理二进制或非 UTF-8 文件(如 Excel、Word、PDF、图片),或进行数据分析与验证。必须提供 timeout(最长60秒);一旦超时,脚本会被打断且无法继续执行(需要重新运行),并返回已捕获输出。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
"code": {"type": "string", "description": "Python代码"},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"description": "超时时长(秒),必填,最大60"
|
||||
}
|
||||
}),
|
||||
"required": ["code", "timeout"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
|
||||
@ -195,7 +195,6 @@ class MainTerminalToolsExecutionMixin:
|
||||
}
|
||||
_APPROVAL_REQUIRED_TOOLS = {
|
||||
"run_command",
|
||||
"run_python",
|
||||
"terminal_input",
|
||||
"write_file",
|
||||
"edit_file",
|
||||
@ -1407,12 +1406,6 @@ class MainTerminalToolsExecutionMixin:
|
||||
"path": target_path
|
||||
}
|
||||
|
||||
elif tool_name == "run_python":
|
||||
result = await self.terminal_ops.run_python_code(
|
||||
arguments["code"],
|
||||
timeout=arguments.get("timeout")
|
||||
)
|
||||
|
||||
elif tool_name == "run_command":
|
||||
permission_mode = "unrestricted"
|
||||
try:
|
||||
|
||||
@ -66,7 +66,7 @@ TOOL_CATEGORIES: Dict[str, ToolCategory] = {
|
||||
),
|
||||
"terminal_command": ToolCategory(
|
||||
label="终端指令",
|
||||
tools=["run_command", "run_python"],
|
||||
tools=["run_command"],
|
||||
),
|
||||
"memory": ToolCategory(
|
||||
label="记忆",
|
||||
|
||||
@ -82,7 +82,7 @@ class WebTerminal(MainTerminal):
|
||||
broadcast_callback=message_callback,
|
||||
container_session=self.container_session
|
||||
)
|
||||
# 让 run_command/run_python 与实时终端共享同一容器环境
|
||||
# 让 run_command 与实时终端共享同一容器环境
|
||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||
|
||||
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||
@ -477,12 +477,6 @@ class WebTerminal(MainTerminal):
|
||||
'status': 'saving_webpage',
|
||||
'detail': f'保存网页: {arguments.get("url", "")}'
|
||||
})
|
||||
elif tool_name == "run_python":
|
||||
self.broadcast('tool_status', {
|
||||
'tool': tool_name,
|
||||
'status': 'running_code',
|
||||
'detail': '执行Python代码'
|
||||
})
|
||||
elif tool_name == "run_command":
|
||||
self.broadcast('tool_status', {
|
||||
'tool': tool_name,
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
- 执行环境(Execution Mode)
|
||||
- 权限模式(Permission Mode)
|
||||
- 路径授权(Path Authorization)
|
||||
- `run_command` / `run_python` / `terminal` / `sub_agent` 的实际行为
|
||||
- `run_command` / `terminal` / `sub_agent` 的实际行为
|
||||
|
||||
---
|
||||
|
||||
@ -136,25 +136,20 @@
|
||||
- 在 approval 模式可触发“权限拒绝 -> 审批 -> 单次可写重试”
|
||||
- 在 auto_approval 模式可触发“权限拒绝 -> 自动审批 -> 单次可写重试”
|
||||
|
||||
### 7.2 run_python
|
||||
|
||||
- 复用 `run_command` 执行路径与执行环境策略
|
||||
- 与 `run_command` 一致受 sandbox/direct 与权限模式影响
|
||||
|
||||
### 7.3 terminal 系列
|
||||
### 7.2 terminal 系列
|
||||
|
||||
- 终端会话从启动时就在沙箱里(host+sandbox)
|
||||
- 同一会话中的后续输入继承该会话沙箱上下文
|
||||
- 若路径授权更新,需要新开终端会话才能让该会话使用新策略
|
||||
|
||||
### 7.4 子智能体(sub_agent)
|
||||
### 7.3 子智能体(sub_agent)
|
||||
|
||||
- 宿主机下已接入执行环境策略:
|
||||
- `sandbox`:子智能体进程经宿主机沙箱启动
|
||||
- `direct`:直接宿主机启动
|
||||
- Docker 模式维持容器执行
|
||||
|
||||
### 7.5 自动审批(approval agent)
|
||||
### 7.4 自动审批(approval agent)
|
||||
|
||||
- 自动审批由独立审批智能体执行(与主智能体分离)
|
||||
- 配置文件:`config/auto_approval.json`
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shlex
|
||||
import string
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
@ -62,8 +63,11 @@ class CustomToolExecutor:
|
||||
except Exception as exc:
|
||||
return {"success": False, "error": f"模板渲染失败: {exc}", "tool_id": tool_id}
|
||||
|
||||
# 执行 python 代码
|
||||
result = await self.terminal_ops.run_python_code(rendered, timeout=timeout)
|
||||
# 通过统一的 run_command 链路执行 Python,复用同一套解释器探测、沙箱与审批语义。
|
||||
result = await self.terminal_ops.run_command(
|
||||
f"python3 -u -c {shlex.quote(rendered)}",
|
||||
timeout=timeout,
|
||||
)
|
||||
result["custom_tool"] = True
|
||||
result["tool_id"] = tool_id
|
||||
result["code_rendered"] = rendered
|
||||
|
||||
@ -445,7 +445,7 @@ class FileManager:
|
||||
except UnicodeDecodeError:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "文件不是 UTF-8 文本,无法直接读取,请改用 run_python 解析。"
|
||||
"error": "文件不是 UTF-8 文本,无法直接读取,请改用 run_command 调用合适的解析工具或 Python 解释器。"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"读取文件失败: {e}"}
|
||||
|
||||
@ -13,7 +13,6 @@ from typing import Dict, Optional, Tuple, TYPE_CHECKING
|
||||
from types import SimpleNamespace
|
||||
try:
|
||||
from config import (
|
||||
CODE_EXECUTION_TIMEOUT,
|
||||
TERMINAL_COMMAND_TIMEOUT,
|
||||
FORBIDDEN_COMMANDS,
|
||||
OUTPUT_FORMATS,
|
||||
@ -25,7 +24,6 @@ except ImportError:
|
||||
if str(project_root) not in sys.path:
|
||||
sys.path.insert(0, str(project_root))
|
||||
from config import (
|
||||
CODE_EXECUTION_TIMEOUT,
|
||||
TERMINAL_COMMAND_TIMEOUT,
|
||||
FORBIDDEN_COMMANDS,
|
||||
OUTPUT_FORMATS,
|
||||
@ -111,7 +109,7 @@ class TerminalOperator:
|
||||
def _resolve_active_container_session(self) -> Optional[SimpleNamespace]:
|
||||
"""
|
||||
若已存在活动终端且在容器内运行,返回一个临时的容器句柄,
|
||||
以便 run_command/run_python 复用同一个容器环境。
|
||||
以便 run_command 复用同一个容器环境。
|
||||
"""
|
||||
manager = self._terminal_manager
|
||||
if not manager:
|
||||
@ -734,129 +732,6 @@ class TerminalOperator:
|
||||
"elapsed_ms": int((time.time() - start_ts) * 1000)
|
||||
}
|
||||
|
||||
async def run_python_code(
|
||||
self,
|
||||
code: str,
|
||||
timeout: int = None
|
||||
) -> Dict:
|
||||
"""
|
||||
执行Python代码
|
||||
|
||||
Args:
|
||||
code: Python代码
|
||||
timeout: 超时时间(秒)
|
||||
|
||||
Returns:
|
||||
执行结果字典
|
||||
"""
|
||||
if timeout is None or timeout <= 0:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "timeout 参数必填且需大于0",
|
||||
"status": "error",
|
||||
"output": "timeout 参数缺失",
|
||||
"return_code": -1
|
||||
}
|
||||
timeout = self._clamp_timeout(timeout, default=timeout, max_limit=CODE_EXECUTION_TIMEOUT)
|
||||
|
||||
# 强制重置工具容器,避免上一段代码仍在运行时输出混入
|
||||
self._reset_toolbox()
|
||||
|
||||
# 创建临时Python文件
|
||||
temp_file = self.project_path / ".temp_code.py"
|
||||
|
||||
try:
|
||||
# 写入代码
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
f.write(code)
|
||||
|
||||
print(f"{OUTPUT_FORMATS['code']} 执行Python代码")
|
||||
|
||||
try:
|
||||
relative_temp = temp_file.relative_to(self.project_path).as_posix()
|
||||
except ValueError:
|
||||
relative_temp = temp_file.as_posix()
|
||||
|
||||
# 使用通用 python3 占位,由 run_command 根据执行环境重写
|
||||
result = await self.run_command(
|
||||
f'python3 -u "{relative_temp}"',
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
# 添加代码到结果
|
||||
result["code"] = code
|
||||
|
||||
return result
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if temp_file.exists():
|
||||
temp_file.unlink()
|
||||
|
||||
async def run_python_file(
|
||||
self,
|
||||
file_path: str,
|
||||
args: str = "",
|
||||
timeout: int = None
|
||||
) -> Dict:
|
||||
"""
|
||||
执行Python文件
|
||||
|
||||
Args:
|
||||
file_path: Python文件路径
|
||||
args: 命令行参数
|
||||
timeout: 超时时间(秒)
|
||||
|
||||
Returns:
|
||||
执行结果字典
|
||||
"""
|
||||
# 构建完整路径
|
||||
full_path = (self.project_path / file_path).resolve()
|
||||
|
||||
# 验证文件存在
|
||||
if not full_path.exists():
|
||||
return {
|
||||
"success": False,
|
||||
"error": "文件不存在",
|
||||
"output": "",
|
||||
"return_code": -1
|
||||
}
|
||||
|
||||
# 验证是Python文件
|
||||
if not full_path.suffix == '.py':
|
||||
return {
|
||||
"success": False,
|
||||
"error": "不是Python文件",
|
||||
"output": "",
|
||||
"return_code": -1
|
||||
}
|
||||
|
||||
# 验证文件在项目内
|
||||
try:
|
||||
full_path.relative_to(self.project_path)
|
||||
except ValueError:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "文件必须在项目文件夹内",
|
||||
"output": "",
|
||||
"return_code": -1
|
||||
}
|
||||
|
||||
print(f"{OUTPUT_FORMATS['code']} 执行Python文件: {file_path}")
|
||||
|
||||
try:
|
||||
relative_path = full_path.relative_to(self.project_path).as_posix()
|
||||
except ValueError:
|
||||
relative_path = full_path.as_posix()
|
||||
|
||||
# 使用通用 python3 占位,由 run_command 根据执行环境重写
|
||||
command = f'python3 "{relative_path}"'
|
||||
if args:
|
||||
command += f" {args}"
|
||||
|
||||
# 执行命令
|
||||
return await self.run_command(command, timeout=timeout)
|
||||
|
||||
async def install_package(self, package: str) -> Dict:
|
||||
"""
|
||||
安装Python包
|
||||
|
||||
@ -55,7 +55,7 @@ def _build_sandbox_options(container_session: Optional["ContainerHandle"] = None
|
||||
|
||||
|
||||
class ToolboxContainer:
|
||||
"""为 run_command/run_python 提供的专用容器."""
|
||||
"""为 run_command 提供的专用容器."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
||||
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
||||
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
||||
| **终端操作** | 执行命令(`run_command`)、运行Python代码(`run_python`)、持久终端会话(`terminal_session` 系列) |
|
||||
| **终端操作** | 执行命令(`run_command`,需要 Python 时先探测并选择合适解释器)、持久终端会话(`terminal_session` 系列) |
|
||||
| **视觉理解** | 分析图片内容、识别文字/物体/表格(`vlm_analyze`/`view_image`) |
|
||||
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
||||
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
||||
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command` / `run_python`
|
||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command`
|
||||
|
||||
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
||||
|
||||
@ -105,8 +105,7 @@
|
||||
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
||||
|
||||
**快速执行**:
|
||||
- `run_command`:一次性命令(最长30s,输出限10000字符)
|
||||
- `run_python`:执行 Python 脚本(最长60s)
|
||||
- `run_command`:一次性命令(最长30s,输出限10000字符);需要 Python 时先用命令探测可用解释器(如 `python3 --version` / `python --version`)再执行
|
||||
|
||||
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
||||
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
||||
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
||||
| **终端操作** | 执行命令(`run_command`)、运行Python代码(`run_python`)、持久终端会话(`terminal_session` 系列) |
|
||||
| **终端操作** | 执行命令(`run_command`,需要 Python 时先探测并选择合适解释器)、持久终端会话(`terminal_session` 系列) |
|
||||
| **视觉理解** | 自带多模态能力,可直接分析图片内容、识别文字/物体/表格/场景 |
|
||||
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
||||
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
||||
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command` / `run_python`
|
||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command`
|
||||
|
||||
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
|
||||
#### 细节增强与"切图放大"方法(推荐)
|
||||
当图片文字很小、细节密集、或需要逐块检查时:
|
||||
1. **用 `run_python` 切图**:把原图按区域裁切成若干张更小的局部图(例如:左上/右上/左下/右下;或按表格/按钮/车标/铭牌所在区域裁切)
|
||||
1. **用 `run_command` 调用合适的 Python 解释器切图**:先探测可用解释器,再把原图按区域裁切成若干张更小的局部图(例如:左上/右上/左下/右下;或按表格/按钮/车标/铭牌所在区域裁切)
|
||||
2. **必要时做多版本增强**:对同一区域输出多张增强版本(例如:对比度增强、锐化、灰度、二值化)用于读字/看边缘
|
||||
3. **再次查看局部图**:对每张局部图分别观察并给出结论,再把结论汇总回原图任务
|
||||
|
||||
@ -123,8 +123,7 @@
|
||||
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
||||
|
||||
**快速执行**:
|
||||
- `run_command`:一次性命令(最长30s,输出限10000字符)
|
||||
- `run_python`:执行 Python 脚本(最长60s,切图处理必备)
|
||||
- `run_command`:一次性命令(最长30s,输出限10000字符);需要 Python/切图处理时先用命令探测可用解释器(如 `python3 --version` / `python --version`)再执行
|
||||
|
||||
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
||||
|
||||
|
||||
@ -332,7 +332,7 @@ def detect_malformed_tool_call(text):
|
||||
'create_file', 'read_file', 'write_file', 'edit_file', 'delete_file',
|
||||
'terminal_session', 'terminal_input', 'web_search',
|
||||
'extract_webpage', 'save_webpage',
|
||||
'run_python', 'run_command', 'sleep',
|
||||
'run_command', 'sleep',
|
||||
]
|
||||
for tool in tool_names:
|
||||
if tool in text and '{' in text:
|
||||
|
||||
@ -120,16 +120,6 @@ def _build_tool_approval_preview(web_terminal, function_name: str, arguments: Di
|
||||
preview["summary"] = f"执行命令: {args.get('command') or ''}"
|
||||
return preview
|
||||
|
||||
if function_name in {"run_python", "runpython"}:
|
||||
code = str(args.get("code") or "")
|
||||
timeout = args.get("timeout")
|
||||
preview["code"] = code
|
||||
preview["timeout"] = timeout
|
||||
# 兼容前端已有 command 展示分支
|
||||
preview["command"] = code
|
||||
preview["summary"] = f"执行 Python 代码(timeout={timeout if timeout is not None else '未提供'}s)"
|
||||
return preview
|
||||
|
||||
if function_name in {"create_file", "create_folder", "delete_file"}:
|
||||
preview["path"] = args.get("path")
|
||||
preview["summary"] = f"{function_name}: {args.get('path') or ''}"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>虚拟显示器 - 单次终端指令(run_python)演示</title>
|
||||
<title>虚拟显示器 - 单次终端指令(run_command 调 Python)演示</title>
|
||||
<style>
|
||||
:root {
|
||||
--panel: #0b0d14;
|
||||
@ -80,7 +80,7 @@
|
||||
<div class="monitor">
|
||||
<div class="monitor-shell">
|
||||
<div class="monitor-top">
|
||||
<div>Agent Display Surface · run_python</div>
|
||||
<div>Agent Display Surface · run_command Python</div>
|
||||
<div class="status-pill" id="statusPill">待机</div>
|
||||
</div>
|
||||
<div class="monitor-screen" id="monitorScreen">
|
||||
@ -318,7 +318,7 @@
|
||||
helpers.setStatus('正在准备');
|
||||
await helpers.sleep(600);
|
||||
helpers.setStatus('正在规划');
|
||||
await helpers.showBubble('run_python:打开独立的 Python 窗口。', { duration: 2200 });
|
||||
await helpers.showBubble('run_command:调用合适的 Python 解释器。', { duration: 2200 });
|
||||
|
||||
const pythonIcon = helpers.getAppIcon('pythonApp');
|
||||
await helpers.moveMouseTo(pythonIcon);
|
||||
@ -352,7 +352,7 @@
|
||||
await helpers.sleep(1500);
|
||||
|
||||
helpers.setStatus('正在输出');
|
||||
await helpers.showBubble('run_python 完成,输出已写入结果面板。', { duration: 2000 });
|
||||
await helpers.showBubble('run_command 完成,输出已写入结果面板。', { duration: 2000 });
|
||||
helpers.setStatus('已完成');
|
||||
replayBtn.disabled = false;
|
||||
}
|
||||
|
||||
@ -133,16 +133,6 @@
|
||||
</div>
|
||||
<div v-else class="search-empty">未返回详细的搜索结果。</div>
|
||||
</div>
|
||||
<div v-else-if="action.tool?.name === 'run_python' && action.tool?.result">
|
||||
<div class="code-block">
|
||||
<div class="code-label">代码:</div>
|
||||
<pre><code class="language-python">{{ action.tool.result.code || action.tool.arguments?.code }}</code></pre>
|
||||
</div>
|
||||
<div v-if="action.tool.result.output" class="output-block">
|
||||
<div class="output-label">输出:</div>
|
||||
<pre>{{ action.tool.result.output }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<pre>{{
|
||||
JSON.stringify(action.tool?.result || action.tool?.arguments, null, 2)
|
||||
|
||||
@ -159,8 +159,6 @@ function renderEnhancedToolResult(): string {
|
||||
// 终端指令类
|
||||
else if (name === 'run_command') {
|
||||
return renderRunCommand(result, args);
|
||||
} else if (name === 'run_python') {
|
||||
return renderRunPython(result, args);
|
||||
}
|
||||
|
||||
// 记忆类
|
||||
@ -733,25 +731,6 @@ function renderRunCommand(result: any, args: any): string {
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderRunPython(result: any, args: any): string {
|
||||
const code = result.code || args.code || '';
|
||||
const output = result.output || '';
|
||||
|
||||
let html = '<div class="code-block">';
|
||||
html += '<div class="code-label">代码:</div>';
|
||||
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
|
||||
html += '</div>';
|
||||
|
||||
if (output) {
|
||||
html += '<div class="output-block">';
|
||||
html += '<div class="output-label">输出:</div>';
|
||||
html += `<pre>${escapeHtml(output)}</pre>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// ===== 记忆类 =====
|
||||
function renderUpdateMemory(result: any, args: any): string {
|
||||
const operations = args.operations || [];
|
||||
@ -1339,7 +1318,7 @@ function renderEasterEgg(result: any, args: any): string {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 暗色模式:run_python 代码/输出统一白字,去除任何描边/阴影观感 */
|
||||
/* 暗色模式:代码/输出统一白字,去除任何描边/阴影观感 */
|
||||
:root[data-theme='dark'] .code-block pre,
|
||||
:root[data-theme='dark'] .output-block pre {
|
||||
color: var(--text-primary) !important;
|
||||
|
||||
@ -95,8 +95,6 @@ export function renderEnhancedToolResult(
|
||||
// 终端指令类
|
||||
else if (name === 'run_command') {
|
||||
return renderRunCommand(result, args);
|
||||
} else if (name === 'run_python') {
|
||||
return renderRunPython(result, args);
|
||||
}
|
||||
|
||||
// 记忆类
|
||||
@ -673,25 +671,6 @@ function renderRunCommand(result: any, args: any): string {
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderRunPython(result: any, args: any): string {
|
||||
const code = result.code || args.code || '';
|
||||
const output = result.output || '';
|
||||
|
||||
let html = '<div class="code-block">';
|
||||
html += '<div class="code-label">代码:</div>';
|
||||
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
|
||||
html += '</div>';
|
||||
|
||||
if (output) {
|
||||
html += '<div class="output-block">';
|
||||
html += '<div class="output-label">输出:</div>';
|
||||
html += `<pre>${escapeHtml(output)}</pre>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// 记忆类渲染函数
|
||||
function renderUpdateMemory(result: any, args: any): string {
|
||||
const operation = args.operation || result.operation || '';
|
||||
|
||||
@ -3202,59 +3202,6 @@ export class MonitorDirector implements MonitorDriver {
|
||||
await sleep(500);
|
||||
};
|
||||
|
||||
this.sceneHandlers.runPython = async (payload, runtime) => {
|
||||
const toolLabel = payload?.name || payload?.tool || 'run_python';
|
||||
this.applySceneStatus(runtime, 'runPython', `调用 ${toolLabel}`);
|
||||
const runId =
|
||||
payload?.executionId ||
|
||||
payload?.execution_id ||
|
||||
payload?.id ||
|
||||
`${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
||||
// 丢弃过期的回放任务
|
||||
if (this.latestPythonExecutionId && this.latestPythonExecutionId !== runId) {
|
||||
const older =
|
||||
typeof runId === 'number' && typeof this.latestPythonExecutionId === 'number'
|
||||
? runId < this.latestPythonExecutionId
|
||||
: false;
|
||||
if (older) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.latestPythonExecutionId = runId;
|
||||
const code = payload?.arguments?.code || 'print("Hello")';
|
||||
const codeLines = this.normalizeLines(code);
|
||||
const animate = codeLines.length <= 15;
|
||||
const runToken = ++this.pythonRunToken;
|
||||
const reuse = this.isWindowVisible(this.elements.pythonWindow);
|
||||
if (reuse) {
|
||||
this.showWindow(this.elements.pythonWindow);
|
||||
this.scrollPythonToTop();
|
||||
await this.focusPythonInput();
|
||||
this.elements.pythonOutput.innerHTML = '';
|
||||
} else {
|
||||
await this.revealPythonWindow('Python');
|
||||
}
|
||||
await this.typePythonCode(code, { deletePrevious: true, animate });
|
||||
const completion = await runtime.waitForResult(payload.executionId || payload.id);
|
||||
if (!this.ensureSuccessOrErrorBubble(completion, payload, 'Python 执行失败')) {
|
||||
return;
|
||||
}
|
||||
if (runToken !== this.pythonRunToken) {
|
||||
// 有新的 Python 运行已启动,本次结果丢弃
|
||||
return;
|
||||
}
|
||||
const output = completion?.result?.output || completion?.result?.stdout || '>>> 执行完成';
|
||||
const lines = this.sanitizeTerminalOutput(
|
||||
typeof output === 'string'
|
||||
? output.split('\n')
|
||||
: Array.isArray(output)
|
||||
? output.map(String)
|
||||
: [String(output || '')]
|
||||
);
|
||||
this.appendPythonOutput('输出', lines.join('\n') || '>>> 执行完成');
|
||||
await sleep(500);
|
||||
};
|
||||
|
||||
this.sceneHandlers.reader = async (payload, runtime) => {
|
||||
const targetPath = payload?.arguments?.path || payload?.result?.path || '文档';
|
||||
const readMode = String(
|
||||
|
||||
@ -12,7 +12,6 @@ export const SCENE_PROGRESS_LABELS: Record<string, string> = {
|
||||
unfocus: '正在处理',
|
||||
// 运行类工具显示具体工具名,由运行时传入
|
||||
runCommand: '运行命令',
|
||||
runPython: '运行 Python',
|
||||
terminalSession: '打开终端',
|
||||
terminalInput: '终端输入',
|
||||
terminalSnapshot: '获取终端输出',
|
||||
|
||||
@ -187,7 +187,7 @@ onMounted(() => {
|
||||
|
||||
const isEditPreview = (item: any) => item?.tool_name === 'edit_file' && item?.preview?.edit_context;
|
||||
const isCommandPreview = (item: any) =>
|
||||
['run_command', 'terminal_input', 'run_python', 'runpython'].includes(item?.tool_name);
|
||||
['run_command', 'terminal_input'].includes(item?.tool_name);
|
||||
const isWritePreview = (item: any) => item?.tool_name === 'write_file';
|
||||
const isRenamePreview = (item: any) => item?.tool_name === 'rename_file';
|
||||
const isPathOnlyPreview = (item: any) =>
|
||||
@ -210,9 +210,7 @@ const getToolLabel = (toolName: string) => {
|
||||
delete_file: '删除文件',
|
||||
rename_file: '重命名文件',
|
||||
write_file: '写入文件',
|
||||
edit_file: '编辑文件',
|
||||
run_python: '执行 Python',
|
||||
runpython: '执行 Python'
|
||||
edit_file: '编辑文件'
|
||||
};
|
||||
return map[name] || name || '待审批操作';
|
||||
};
|
||||
|
||||
@ -108,7 +108,6 @@ const TOOL_SCENE_MAP: Record<string, string> = {
|
||||
write_file: 'modifyFile',
|
||||
edit_file: 'modifyFile',
|
||||
run_command: 'runCommand',
|
||||
run_python: 'runPython',
|
||||
terminal_session: 'terminalSession',
|
||||
terminal_input: 'terminalInput',
|
||||
terminal_snapshot: 'terminalSnapshot',
|
||||
|
||||
@ -16,7 +16,6 @@ const RUNNING_ANIMATIONS: Record<string, string> = {
|
||||
extract_webpage: 'search-animation',
|
||||
save_webpage: 'file-animation',
|
||||
vlm_analyze: 'file-animation',
|
||||
run_python: 'code-animation',
|
||||
run_command: 'terminal-animation',
|
||||
update_memory: 'memory-animation',
|
||||
sleep: 'wait-animation',
|
||||
@ -40,7 +39,6 @@ const RUNNING_STATUS_TEXTS: Record<string, string> = {
|
||||
web_search: '正在搜索网络...',
|
||||
extract_webpage: '正在提取网页...',
|
||||
save_webpage: '正在保存网页...',
|
||||
run_python: '调用 run_python',
|
||||
run_command: '调用 run_command',
|
||||
update_memory: '正在更新记忆...',
|
||||
terminal_session: '正在管理终端会话...',
|
||||
@ -63,7 +61,6 @@ const COMPLETED_STATUS_TEXTS: Record<string, string> = {
|
||||
extract_webpage: '网页提取完成',
|
||||
save_webpage: '网页保存完成(纯文本)',
|
||||
vlm_analyze: '图片解析完成',
|
||||
run_python: '代码执行完成',
|
||||
run_command: '命令执行完成',
|
||||
update_memory: '记忆更新成功',
|
||||
terminal_session: '终端操作完成',
|
||||
|
||||
@ -67,7 +67,6 @@ export const TOOL_ICON_MAP = Object.freeze({
|
||||
read_skill: 'book',
|
||||
rename_file: 'pencil',
|
||||
run_command: 'terminal',
|
||||
run_python: 'python',
|
||||
save_webpage: 'save',
|
||||
sleep: 'clock',
|
||||
todo_create: 'stickyNote',
|
||||
|
||||
@ -68,7 +68,6 @@ AUTO_SHALLOW_TOOL_WHITELIST = {
|
||||
"terminal_snapshot",
|
||||
"web_search",
|
||||
"extract_webpage",
|
||||
"run_python",
|
||||
"run_command",
|
||||
"view_image",
|
||||
"view_video",
|
||||
|
||||
@ -816,14 +816,6 @@ def _format_run_command(result_data: Dict[str, Any]) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def _format_run_python(result_data: Dict[str, Any]) -> str:
|
||||
text = _plain_command_output(result_data)
|
||||
if (result_data.get("status") or "").lower() == "timeout":
|
||||
suggestion = "建议:将代码保存为脚本后,在持久终端中执行(terminal_session + terminal_input),或拆分/优化代码以缩短运行时间。"
|
||||
text = f"{text}\n{suggestion}" if text else suggestion
|
||||
return text
|
||||
|
||||
|
||||
def _format_todo_create(result_data: Dict[str, Any]) -> str:
|
||||
if not result_data.get("success"):
|
||||
return _format_failure("todo_create", result_data)
|
||||
@ -1191,7 +1183,6 @@ TOOL_FORMATTERS = {
|
||||
"terminal_input": _format_terminal_input,
|
||||
"sleep": _format_sleep,
|
||||
"run_command": _format_run_command,
|
||||
"run_python": _format_run_python,
|
||||
"extract_webpage": _format_extract_webpage,
|
||||
"vlm_analyze": _format_vlm_analyze,
|
||||
"ocr_image": _format_ocr_image,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user