Initial project setup: Obsidian intelligent journal organizer
- 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
This commit is contained in:
+354
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
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())
|
||||
Reference in New Issue
Block a user