feat: 新增 core/agents 模块和 nanobot
- 新增 agents 模块,包含 agent、api、skills 等子模块 - 新增 nanobot 项目,支持多渠道集成 - 添加启动脚本 start-all.bat 和 start-all.sh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
202
core/agents/tools.py
Normal file
202
core/agents/tools.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""Tool system for agent capabilities."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Tool(ABC):
|
||||
"""Abstract base class for agent tools."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""Tool name used in function calls."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def description(self) -> str:
|
||||
"""Description of what the tool does."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def parameters(self) -> dict[str, Any]:
|
||||
"""JSON Schema for tool parameters."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self, **kwargs: Any) -> str:
|
||||
"""Execute the tool with given parameters.
|
||||
|
||||
Returns:
|
||||
String result of the tool execution.
|
||||
"""
|
||||
pass
|
||||
|
||||
def to_schema(self) -> dict[str, Any]:
|
||||
"""Convert tool to function schema format."""
|
||||
return {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"parameters": self.parameters,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ToolRegistry:
|
||||
"""Registry for managing agent tools."""
|
||||
|
||||
def __init__(self):
|
||||
self._tools: dict[str, Tool] = {}
|
||||
|
||||
def register(self, tool: Tool) -> None:
|
||||
"""Register a tool.
|
||||
|
||||
Args:
|
||||
tool: Tool instance to register
|
||||
"""
|
||||
self._tools[tool.name] = tool
|
||||
logger.info(f"Registered tool: {tool.name}")
|
||||
|
||||
def unregister(self, name: str) -> None:
|
||||
"""Unregister a tool.
|
||||
|
||||
Args:
|
||||
name: Tool name to unregister
|
||||
"""
|
||||
if name in self._tools:
|
||||
del self._tools[name]
|
||||
logger.info(f"Unregistered tool: {name}")
|
||||
|
||||
def get(self, name: str) -> Tool | None:
|
||||
"""Get a tool by name.
|
||||
|
||||
Args:
|
||||
name: Tool name
|
||||
|
||||
Returns:
|
||||
Tool instance or None
|
||||
"""
|
||||
return self._tools.get(name)
|
||||
|
||||
def get_definitions(self) -> list[dict[str, Any]]:
|
||||
"""Get all tool definitions for LLM.
|
||||
|
||||
Returns:
|
||||
List of tool schemas
|
||||
"""
|
||||
return [tool.to_schema() for tool in self._tools.values()]
|
||||
|
||||
async def execute(self, name: str, arguments: dict[str, Any]) -> str:
|
||||
"""Execute a tool.
|
||||
|
||||
Args:
|
||||
name: Tool name
|
||||
arguments: Tool arguments
|
||||
|
||||
Returns:
|
||||
Tool execution result
|
||||
"""
|
||||
tool = self.get(name)
|
||||
if not tool:
|
||||
return f'{{"error": "Unknown tool: {name}"}}'
|
||||
|
||||
try:
|
||||
# Validate parameters
|
||||
validated = tool.cast_params(arguments)
|
||||
errors = tool.validate_params(validated)
|
||||
if errors:
|
||||
return f'{{"error": "Parameter validation failed: {errors}"}}'
|
||||
|
||||
# Execute with timeout
|
||||
result = await asyncio.wait_for(
|
||||
tool.execute(**validated),
|
||||
timeout=60.0,
|
||||
)
|
||||
return result
|
||||
except asyncio.TimeoutError:
|
||||
return f'{{"error": "Tool execution timed out: {name}"}}'
|
||||
except Exception as exc:
|
||||
logger.exception(f"Tool execution error: {name}")
|
||||
return f'{{"error": "Tool execution failed: {exc}"}}'
|
||||
|
||||
def list_tools(self) -> list[str]:
|
||||
"""List all registered tool names.
|
||||
|
||||
Returns:
|
||||
List of tool names
|
||||
"""
|
||||
return list(self._tools.keys())
|
||||
|
||||
|
||||
# Built-in placeholder tools
|
||||
class EchoTool(Tool):
|
||||
"""Echo tool for testing."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "echo"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return "Echo back the input text. Useful for testing."
|
||||
|
||||
@property
|
||||
def parameters(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "Text to echo back",
|
||||
}
|
||||
},
|
||||
"required": ["text"],
|
||||
}
|
||||
|
||||
async def execute(self, **kwargs: Any) -> str:
|
||||
text = kwargs.get("text", "")
|
||||
return f'{{"echo": "{text}"}}'
|
||||
|
||||
|
||||
class TimeTool(Tool):
|
||||
"""Get current time tool."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "get_time"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return "Get the current date and time."
|
||||
|
||||
@property
|
||||
def parameters(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
}
|
||||
|
||||
async def execute(self, **kwargs: Any) -> str:
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
return f'{{"time": "{now.isoformat()}"}}'
|
||||
|
||||
|
||||
def create_default_registry() -> ToolRegistry:
|
||||
"""Create a tool registry with default tools.
|
||||
|
||||
Returns:
|
||||
Tool registry with built-in tools
|
||||
"""
|
||||
registry = ToolRegistry()
|
||||
registry.register(EchoTool())
|
||||
registry.register(TimeTool())
|
||||
return registry
|
||||
Reference in New Issue
Block a user