Files
journal_organizer/agent_core.py
T

355 lines
9.9 KiB
Python
Raw Normal View History

"""
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())