- Add core agent architecture with Command + Skill pattern - Implement Claude API integration for content analysis - Add Obsidian REST API integration for vault operations - Create conversational interface (v2.0) with natural language processing - Add comprehensive configuration management and validation - Include project documentation and developer guides - Set up testing framework with unit, integration, and property tests - Add Kiro specs for Claude API configuration and code quality improvements - Configure project steering files for development guidelines
355 lines
9.9 KiB
Python
355 lines
9.9 KiB
Python
"""
|
|
Agent 核心框架
|
|
定义 Command 和 Skill 的基础类和接口
|
|
支持 Claude Code 和 OpenCode
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass, field, asdict
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
|
|
|
|
class SkillType(Enum):
|
|
"""Skill 类型枚举"""
|
|
|
|
READ = "read" # 读取操作
|
|
WRITE = "write" # 写入操作
|
|
ANALYZE = "analyze" # 分析操作
|
|
TRANSFORM = "transform" # 转换操作
|
|
INTEGRATE = "integrate" # 集成操作
|
|
|
|
|
|
@dataclass
|
|
class SkillResult:
|
|
"""Skill 执行结果"""
|
|
|
|
success: bool
|
|
data: Optional[Any] = None
|
|
error: Optional[str] = None
|
|
message: str = ""
|
|
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Validate SkillResult consistency after initialization"""
|
|
if not self.success and not self.error:
|
|
raise ValueError("Failed results must include error message")
|
|
if self.success and self.error:
|
|
raise ValueError("Successful results should not include error message")
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return asdict(self)
|
|
|
|
def to_json(self) -> str:
|
|
"""转换为 JSON 字符串"""
|
|
return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
|
|
|
|
|
|
@dataclass
|
|
class CommandContext:
|
|
"""命令执行上下文"""
|
|
|
|
command_name: str
|
|
args: Dict[str, Any] = field(default_factory=dict)
|
|
options: Dict[str, Any] = field(default_factory=dict)
|
|
config: Optional[Dict[str, Any]] = None
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return asdict(self)
|
|
|
|
|
|
class Skill(ABC):
|
|
"""Skill 基础类"""
|
|
|
|
def __init__(self, name: str, skill_type: SkillType, description: str = "") -> None:
|
|
"""
|
|
初始化 Skill
|
|
|
|
Args:
|
|
name: Skill 名称
|
|
skill_type: Skill 类型
|
|
description: Skill 描述
|
|
"""
|
|
self.name = name
|
|
self.skill_type = skill_type
|
|
self.description = description
|
|
self.logger = logging.getLogger(f"Skill.{name}")
|
|
|
|
@abstractmethod
|
|
async def execute(self, context: CommandContext, **kwargs: Any) -> SkillResult:
|
|
"""
|
|
执行 Skill
|
|
|
|
Args:
|
|
context: 命令执行上下文
|
|
**kwargs: 额外参数
|
|
|
|
Returns:
|
|
SkillResult: 执行结果
|
|
"""
|
|
pass
|
|
|
|
def get_info(self) -> Dict[str, Any]:
|
|
"""获取 Skill 信息"""
|
|
return {
|
|
"name": self.name,
|
|
"type": self.skill_type.value,
|
|
"description": self.description,
|
|
}
|
|
|
|
|
|
class SkillChain:
|
|
"""Skill 链 - 用于按顺序执行多个 Skill"""
|
|
|
|
def __init__(self, name: str, description: str = "") -> None:
|
|
"""
|
|
初始化 Skill 链
|
|
|
|
Args:
|
|
name: 链名称
|
|
description: 链描述
|
|
"""
|
|
self.name = name
|
|
self.description = description
|
|
self.skills: List[Tuple[Skill, Dict[str, Any]]] = []
|
|
self.logger = logging.getLogger(f"SkillChain.{name}")
|
|
|
|
def add_skill(
|
|
self, skill: Skill, params: Optional[Dict[str, Any]] = None
|
|
) -> "SkillChain":
|
|
"""
|
|
添加 Skill 到链中
|
|
|
|
Args:
|
|
skill: 要添加的 Skill
|
|
params: Skill 参数
|
|
|
|
Returns:
|
|
self 用于链式调用
|
|
"""
|
|
self.skills.append((skill, params or {}))
|
|
return self
|
|
|
|
async def execute(self, context: CommandContext) -> SkillResult:
|
|
"""
|
|
执行 Skill 链
|
|
|
|
Args:
|
|
context: 命令执行上下文
|
|
|
|
Returns:
|
|
SkillResult: 最后一个 Skill 的结果
|
|
"""
|
|
result: Optional[SkillResult] = None
|
|
|
|
for skill, params in self.skills:
|
|
try:
|
|
self.logger.info(f"执行 Skill: {skill.name}")
|
|
result = await skill.execute(context, **params)
|
|
|
|
if not result.success:
|
|
self.logger.error(f"Skill {skill.name} 执行失败: {result.error}")
|
|
return result
|
|
|
|
# 将结果传递给下一个 Skill
|
|
if result.data:
|
|
context.metadata[f"{skill.name}_result"] = result.data
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"执行 Skill {skill.name} 时出错: {str(e)}")
|
|
return SkillResult(
|
|
success=False, error=str(e), message=f"Skill {skill.name} 执行异常"
|
|
)
|
|
|
|
return result or SkillResult(success=True, message="Skill 链执行完成")
|
|
|
|
def get_info(self) -> Dict[str, Any]:
|
|
"""获取 Skill 链信息"""
|
|
return {
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"skills": [skill.get_info() for skill, _ in self.skills],
|
|
}
|
|
|
|
|
|
class Command(ABC):
|
|
"""Command 基础类"""
|
|
|
|
def __init__(
|
|
self, name: str, description: str = "", aliases: Optional[List[str]] = None
|
|
) -> None:
|
|
"""
|
|
初始化 Command
|
|
|
|
Args:
|
|
name: 命令名称
|
|
description: 命令描述
|
|
aliases: 命令别名
|
|
"""
|
|
self.name = name
|
|
self.description = description
|
|
self.aliases = aliases or []
|
|
self.skills: Dict[str, Skill] = {}
|
|
self.skill_chains: Dict[str, SkillChain] = {}
|
|
self.logger = logging.getLogger(f"Command.{name}")
|
|
|
|
def register_skill(self, skill: Skill) -> "Command":
|
|
"""
|
|
注册 Skill
|
|
|
|
Args:
|
|
skill: 要注册的 Skill
|
|
|
|
Returns:
|
|
self 用于链式调用
|
|
"""
|
|
self.skills[skill.name] = skill
|
|
return self
|
|
|
|
def register_skill_chain(self, chain: SkillChain) -> "Command":
|
|
"""
|
|
注册 Skill 链
|
|
|
|
Args:
|
|
chain: 要注册的 Skill 链
|
|
|
|
Returns:
|
|
self 用于链式调用
|
|
"""
|
|
self.skill_chains[chain.name] = chain
|
|
return self
|
|
|
|
@abstractmethod
|
|
async def execute(self, context: CommandContext) -> SkillResult:
|
|
"""
|
|
执行命令
|
|
|
|
Args:
|
|
context: 命令执行上下文
|
|
|
|
Returns:
|
|
SkillResult: 执行结果
|
|
"""
|
|
pass
|
|
|
|
def get_info(self) -> Dict[str, Any]:
|
|
"""获取命令信息"""
|
|
return {
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"aliases": self.aliases,
|
|
"skills": {name: skill.get_info() for name, skill in self.skills.items()},
|
|
"skill_chains": {
|
|
name: chain.get_info() for name, chain in self.skill_chains.items()
|
|
},
|
|
}
|
|
|
|
|
|
class Agent:
|
|
"""Agent 核心类"""
|
|
|
|
def __init__(self, name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
"""
|
|
初始化 Agent
|
|
|
|
Args:
|
|
name: Agent 名称
|
|
config: 配置字典
|
|
"""
|
|
self.name = name
|
|
self.config = config or {}
|
|
self.commands: Dict[str, Command] = {}
|
|
self.command_aliases: Dict[str, str] = {}
|
|
self.logger = logging.getLogger(f"Agent.{name}")
|
|
self._setup_logging()
|
|
|
|
def _setup_logging(self) -> None:
|
|
"""设置日志"""
|
|
log_level = self.config.get("log_level", "INFO")
|
|
logging.basicConfig(
|
|
level=getattr(logging, log_level),
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
)
|
|
|
|
def register_command(self, command: Command) -> "Agent":
|
|
"""
|
|
注册命令
|
|
|
|
Args:
|
|
command: 要注册的命令
|
|
|
|
Returns:
|
|
self 用于链式调用
|
|
"""
|
|
self.commands[command.name] = command
|
|
|
|
# 注册别名
|
|
for alias in command.aliases:
|
|
self.command_aliases[alias] = command.name
|
|
|
|
self.logger.info(f"注册命令: {command.name}")
|
|
return self
|
|
|
|
async def execute_command(
|
|
self,
|
|
command_name: str,
|
|
args: Optional[Dict[str, Any]] = None,
|
|
options: Optional[Dict[str, Any]] = None,
|
|
) -> SkillResult:
|
|
"""
|
|
执行命令
|
|
|
|
Args:
|
|
command_name: 命令名称或别名
|
|
args: 命令参数
|
|
options: 命令选项
|
|
|
|
Returns:
|
|
SkillResult: 执行结果
|
|
"""
|
|
# 解析命令名称(处理别名)
|
|
actual_command_name = self.command_aliases.get(command_name, command_name)
|
|
|
|
if actual_command_name not in self.commands:
|
|
return SkillResult(
|
|
success=False,
|
|
error=f"未知命令: {command_name}",
|
|
message=f"命令 '{command_name}' 不存在",
|
|
)
|
|
|
|
command = self.commands[actual_command_name]
|
|
context = CommandContext(
|
|
command_name=actual_command_name,
|
|
args=args or {},
|
|
options=options or {},
|
|
config=self.config,
|
|
)
|
|
|
|
self.logger.info(f"执行命令: {actual_command_name}, 参数: {args}, 选项: {options}")
|
|
|
|
try:
|
|
result = await command.execute(context)
|
|
self.logger.info(f"命令 {actual_command_name} 执行完成: {result.success}")
|
|
return result
|
|
except Exception as e:
|
|
self.logger.error(f"执行命令 {actual_command_name} 时出错: {str(e)}")
|
|
return SkillResult(success=False, error=str(e), message=f"命令执行异常: {str(e)}")
|
|
|
|
def get_commands_info(self) -> Dict[str, Any]:
|
|
"""获取所有命令信息"""
|
|
return {
|
|
"agent_name": self.name,
|
|
"commands": {name: cmd.get_info() for name, cmd in self.commands.items()},
|
|
"aliases": self.command_aliases,
|
|
}
|
|
|
|
def list_commands(self) -> List[str]:
|
|
"""列出所有可用命令"""
|
|
return list(self.commands.keys())
|