Files
JARVIS/development-doc/plan/tool-update/phase-t-4-advanced.md

643 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase T.4:高级特性
日期2026-04-04
状态:待开始
依赖T.3(待完成)
---
## 1. 本阶段目的
实现 Jarvis 工具系统的高级特性:
- 多运行时支持Python/JS/原生)
- Agent 间协作
- 定时任务
---
## 2. 多运行时支持
### 2.1 运行时架构
```
┌─────────────────────────────────────────────────────────────┐
│ Runtime Manager │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Python │ │ JS │ │ Native │ │ WASM │ │
│ │ Runtime │ │ Runtime │ │ Runtime │ │ Runtime │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └─────────────┴──────┬──────┴─────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ Tool Executor │ │
│ │ (统一接口) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 运行时基类
```python
# tools/runtime/base.py
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional
class BaseRuntime(ABC):
"""运行时基类"""
@abstractmethod
async def execute(
self,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
"""执行工具"""
pass
@abstractmethod
async def validate(self, entry: str) -> bool:
"""验证工具是否可用"""
pass
@abstractmethod
def get_name(self) -> str:
"""获取运行时名称"""
pass
```
### 2.3 Python 运行时
```python
# tools/runtime/python_runtime.py
import asyncio
from pathlib import Path
from tools.runtime.base import BaseRuntime
class PythonRuntime(BaseRuntime):
"""Python 运行时"""
def __init__(self):
self._executors: Dict[str, Callable] = {}
def get_name(self) -> str:
return "python"
async def validate(self, entry: str) -> bool:
path = Path(entry)
return path.exists() and path.suffix == ".py"
async def execute(
self,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
# 动态加载并执行
# 或者通过 subprocess 调用
pass
```
### 2.4 JavaScript 运行时
```python
# tools/runtime/js_runtime.py
import asyncio
import subprocess
import json
from tools.runtime.base import BaseRuntime
class JavaScriptRuntime(BaseRuntime):
"""JavaScript 运行时"""
def __init__(self):
self.node_path = "node" # 可配置
def get_name(self) -> str:
return "javascript"
async def validate(self, entry: str) -> bool:
# 检查 node 是否可用
try:
result = await asyncio.create_subprocess_exec(
self.node_path, "--version",
stdout=asyncio.subprocess.PIPE,
)
return result.returncode == 0
except:
return False
async def execute(
self,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
# 通过 stdio 调用 Node.js 脚本
input_data = json.dumps({
"command": command,
"parameters": parameters,
})
process = await asyncio.create_subprocess_exec(
self.node_path, entry,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await asyncio.wait_for(
process.communicate(input=input_data.encode()),
timeout=timeout / 1000,
)
if process.returncode != 0:
return {
"status": "error",
"error": stderr.decode(),
}
return json.loads(stdout.decode())
```
### 2.5 原生运行时
```python
# tools/runtime/native_runtime.py
import asyncio
import subprocess
from tools.runtime.base import BaseRuntime
class NativeRuntime(BaseRuntime):
"""原生二进制运行时"""
def get_name(self) -> str:
return "native"
async def validate(self, entry: str) -> bool:
from pathlib import Path
path = Path(entry)
return path.exists() and path.stat().st_mode & 0o111
async def execute(
self,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
# 调用原生可执行文件
args = [entry, command] + self._format_args(parameters)
process = await asyncio.create_subprocess_exec(
*args,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await asyncio.wait_for(
process.communicate(),
timeout=timeout / 1000,
)
if process.returncode != 0:
return {
"status": "error",
"error": stderr.decode(),
}
return {
"status": "success",
"result": stdout.decode(),
}
def _format_args(self, parameters: Dict[str, Any]) -> list:
"""格式化参数"""
args = []
for key, value in parameters.items():
args.extend([f"--{key}", str(value)])
return args
```
### 2.6 运行时管理器
```python
# tools/runtime/manager.py
from tools.runtime.base import BaseRuntime
from tools.runtime.python_runtime import PythonRuntime
from tools.runtime.js_runtime import JavaScriptRuntime
from tools.runtime.native_runtime import NativeRuntime
class RuntimeManager:
"""运行时管理器"""
def __init__(self):
self._runtimes: Dict[str, BaseRuntime] = {
"python": PythonRuntime(),
"javascript": JavaScriptRuntime(),
"native": NativeRuntime(),
}
def get_runtime(self, name: str) -> BaseRuntime:
return self._runtimes.get(name)
async def execute(
self,
runtime_name: str,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
runtime = self.get_runtime(runtime_name)
if not runtime:
return {
"status": "error",
"error": f"未知运行时: {runtime_name}",
}
return await runtime.execute(entry, command, parameters, timeout)
```
---
## 3. Agent 间协作
### 3.1 协作协议
```python
# agents/tools/collaboration.py
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
class MessageType(str, Enum):
REQUEST = "request" # 请求协作
RESPONSE = "response" # 响应结果
PROGRESS = "progress" # 进度更新
CANCEL = "cancel" # 取消请求
@dataclass
class CollaborationMessage:
"""协作消息"""
id: str
type: MessageType
from_agent: str
to_agent: str
content: Any
metadata: Dict[str, Any]
timestamp: datetime = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.utcnow()
class CollaborationProtocol:
"""Agent 协作协议"""
def __init__(self):
self._pending_requests: Dict[str, CollaborationMessage] = {}
self._handlers: Dict[str, callable] = {}
def register_handler(self, tool_name: str, handler: callable) -> None:
"""注册工具处理器"""
self._handlers[tool_name] = handler
async def request_collaboration(
self,
from_agent: str,
to_agent: str,
tool_name: str,
parameters: Dict[str, Any],
timeout: int = 30000,
) -> Dict[str, Any]:
"""请求协作"""
import uuid
request_id = str(uuid.uuid4())
message = CollaborationMessage(
id=request_id,
type=MessageType.REQUEST,
from_agent=from_agent,
to_agent=to_agent,
content={
"tool": tool_name,
"parameters": parameters,
},
metadata={"timeout": timeout},
)
self._pending_requests[request_id] = message
# 发送请求
await self._send_message(message)
# 等待响应
try:
response = await self._wait_for_response(
request_id,
timeout,
)
return response
except TimeoutError:
return {
"status": "error",
"error": "协作请求超时",
}
async def handle_request(
self,
message: CollaborationMessage,
) -> CollaborationMessage:
"""处理协作请求"""
tool_name = message.content["tool"]
parameters = message.content["parameters"]
handler = self._handlers.get(tool_name)
if not handler:
return CollaborationMessage(
id=str(uuid.uuid4()),
type=MessageType.RESPONSE,
from_agent=message.to_agent,
to_agent=message.from_agent,
content={
"status": "error",
"error": f"未知工具: {tool_name}",
},
metadata={},
)
try:
result = await handler(**parameters)
return CollaborationMessage(
id=str(uuid.uuid4()),
type=MessageType.RESPONSE,
from_agent=message.to_agent,
to_agent=message.from_agent,
content={"status": "success", "result": result},
metadata={},
)
except Exception as e:
return CollaborationMessage(
id=str(uuid.uuid4()),
type=MessageType.RESPONSE,
from_agent=message.to_agent,
to_agent=message.from_agent,
content={"status": "error", "error": str(e)},
metadata={},
)
async def _send_message(self, message: CollaborationMessage) -> None:
"""发送消息"""
# TODO: 实现消息发送WebSocket/消息队列)
pass
async def _wait_for_response(
self,
request_id: str,
timeout: int,
) -> Dict[str, Any]:
"""等待响应"""
# TODO: 实现等待逻辑
pass
```
---
## 4. 定时任务
### 4.1 定时任务服务
```python
# tools/scheduler.py
import asyncio
from typing import Dict, Any, Callable, Optional
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
class ScheduleType(str, Enum):
ONCE = "once" # 单次
INTERVAL = "interval" # 间隔
CRON = "cron" # Cron 表达式
@dataclass
class ScheduledTask:
"""定时任务"""
id: str
name: str
schedule_type: ScheduleType
schedule_value: str # 时间/间隔/cron
tool_name: str
parameters: Dict[str, Any]
enabled: bool = True
last_run: Optional[datetime] = None
next_run: Optional[datetime] = None
run_count: int = 0
callback: Optional[Callable] = field(default=None)
class ToolScheduler:
"""工具定时调度器"""
def __init__(self):
self._tasks: Dict[str, ScheduledTask] = {}
self._running = False
self._loop_task = None
async def schedule(
self,
name: str,
schedule_type: ScheduleType,
schedule_value: str,
tool_name: str,
parameters: Dict[str, Any],
callback: Optional[Callable] = None,
) -> str:
"""创建定时任务"""
import uuid
task_id = str(uuid.uuid4())[:8]
task = ScheduledTask(
id=task_id,
name=name,
schedule_type=schedule_type,
schedule_value=schedule_value,
tool_name=tool_name,
parameters=parameters,
callback=callback,
)
task.next_run = self._calculate_next_run(task)
self._tasks[task_id] = task
# 启动调度器
if not self._running:
await self.start()
return task_id
def _calculate_next_run(self, task: ScheduledTask) -> datetime:
"""计算下次运行时间"""
now = datetime.utcnow()
if task.schedule_type == ScheduleType.ONCE:
return datetime.fromisoformat(task.schedule_value)
elif task.schedule_type == ScheduleType.INTERVAL:
seconds = int(task.schedule_value)
return now + timedelta(seconds=seconds)
elif task.schedule_type == ScheduleType.CRON:
# 解析 cron 表达式
# TODO: 实现 cron 解析
return now + timedelta(hours=1)
return now
async def start(self) -> None:
"""启动调度器"""
self._running = True
self._loop_task = asyncio.create_task(self._run_loop())
async def stop(self) -> None:
"""停止调度器"""
self._running = False
if self._loop_task:
self._loop_task.cancel()
async def _run_loop(self) -> None:
"""调度循环"""
while self._running:
now = datetime.utcnow()
for task in self._tasks.values():
if not task.enabled:
continue
if task.next_run and task.next_run <= now:
await self._execute_task(task)
await asyncio.sleep(1) # 每秒检查一次
async def _execute_task(self, task: ScheduledTask) -> None:
"""执行任务"""
# 调用工具
executor = get_executor(task.tool_name)
result = await executor(
command=task.parameters.get("command"),
parameters=task.parameters,
)
# 更新状态
task.last_run = datetime.utcnow()
task.run_count += 1
# 计算下次运行
if task.schedule_type != ScheduleType.ONCE:
task.next_run = self._calculate_next_run(task)
else:
task.enabled = False
# 调用回调
if task.callback:
await task.callback(task, result)
async def cancel(self, task_id: str) -> bool:
"""取消任务"""
if task_id in self._tasks:
del self._tasks[task_id]
return True
return False
async def list_tasks(self) -> list:
"""列出所有任务"""
return [
{
"id": t.id,
"name": t.name,
"type": t.schedule_type.value,
"enabled": t.enabled,
"next_run": t.next_run.isoformat() if t.next_run else None,
"run_count": t.run_count,
}
for t in self._tasks.values()
]
```
---
## 5. 实现步骤
| 步骤 | 任务 | 优先级 |
|------|------|--------|
| 1 | 实现运行时基类 | 🟢 高 |
| 2 | 实现 Python 运行时 | 🟢 高 |
| 3 | 实现 JS 运行时 | 🟡 中 |
| 4 | 实现原生运行时 | 🟡 中 |
| 5 | 实现运行时管理器 | 🟢 高 |
| 6 | 实现协作协议 | 🟡 中 |
| 7 | 实现定时调度器 | 🟡 中 |
| 8 | 单元测试 | 🟡 中 |
---
## 6. 核心文件变更
| 文件 | 变更 |
|------|------|
| `tools/runtime/__init__.py` | 新增 |
| `tools/runtime/base.py` | 新增 |
| `tools/runtime/python_runtime.py` | 新增 |
| `tools/runtime/js_runtime.py` | 新增 |
| `tools/runtime/native_runtime.py` | 新增 |
| `tools/runtime/manager.py` | 新增 |
| `agents/tools/collaboration.py` | 新增 |
| `tools/scheduler.py` | 新增 |
---
## 7. 工作量估算
| 任务 | 工作量 |
|------|--------|
| 运行时基类 | 0.5 天 |
| Python 运行时 | 0.5 天 |
| JS 运行时 | 0.5 天 |
| 原生运行时 | 0.5 天 |
| 运行时管理器 | 0.5 天 |
| 协作协议 | 1 天 |
| 定时调度器 | 0.5 天 |
| 单元测试 | 0.5 天 |
| **总计** | **4 天** |
---
## 8. 验收标准
- [ ] Python 运行时可正常执行工具
- [ ] JS 运行时可通过 stdio 调用
- [ ] 原生运行时可执行二进制
- [ ] 运行时管理器正确路由
- [ ] 协作协议可正常请求/响应
- [ ] 定时调度器可按计划执行任务
- [ ] 单元测试通过