285 lines
8.5 KiB
Python
285 lines
8.5 KiB
Python
"""
|
|||
|
|
日记整理 Agent 主入口
|
||
|
|
支持命令行调用和外部集成
|
||
|
|
"""
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import asyncio
|
||
|
|
import json
|
||
|
|
import logging
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Dict, Any, Optional
|
||
|
|
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Dict, Any, Optional
|
||
|
|
|
||
|
|
# Handle imports with both relative and absolute paths
|
||
|
|
try:
|
||
|
|
from .dependency_manager import get_dependency_manager
|
||
|
|
from .agent_core import Agent, SkillResult
|
||
|
|
from .commands.organize_command import OrganizeCommand
|
||
|
|
except ImportError:
|
||
|
|
# Fallback to absolute imports when running as script
|
||
|
|
from dependency_manager import get_dependency_manager
|
||
|
|
from agent_core import Agent, SkillResult
|
||
|
|
from commands.organize_command import OrganizeCommand
|
||
|
|
|
||
|
|
# Try to import yaml with graceful degradation
|
||
|
|
dependency_manager = get_dependency_manager()
|
||
|
|
yaml = dependency_manager.get_module('yaml')
|
||
|
|
|
||
|
|
|
||
|
|
class JournalOrganizerAgent:
|
||
|
|
"""日记整理 Agent"""
|
||
|
|
|
||
|
|
def __init__(self, config_file: Optional[str] = None):
|
||
|
|
"""
|
||
|
|
初始化 Agent
|
||
|
|
|
||
|
|
Args:
|
||
|
|
config_file: 配置文件路径
|
||
|
|
"""
|
||
|
|
self.logger = logging.getLogger("JournalOrganizerAgent")
|
||
|
|
self.config = self._load_config(config_file)
|
||
|
|
self.agent = Agent("JournalOrganizer", self.config)
|
||
|
|
self._register_commands()
|
||
|
|
|
||
|
|
def _load_config(self, config_file: Optional[str] = None) -> Dict[str, Any]:
|
||
|
|
"""
|
||
|
|
加载配置文件
|
||
|
|
|
||
|
|
Args:
|
||
|
|
config_file: 配置文件路径
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
配置字典
|
||
|
|
"""
|
||
|
|
if config_file and Path(config_file).exists():
|
||
|
|
config_path = Path(config_file)
|
||
|
|
with config_path.open("r", encoding="utf-8") as f:
|
||
|
|
if config_path.suffix in [".yaml", ".yml"]:
|
||
|
|
return yaml.safe_load(f) or {}
|
||
|
|
elif config_path.suffix == ".json":
|
||
|
|
return json.load(f)
|
||
|
|
|
||
|
|
# 尝试从默认位置加载
|
||
|
|
default_paths = [
|
||
|
|
Path.home() / ".journal_organizer" / "config.yaml",
|
||
|
|
Path.home() / ".journal_organizer" / "config.json",
|
||
|
|
Path.cwd() / "config.yaml",
|
||
|
|
Path.cwd() / "config.json",
|
||
|
|
]
|
||
|
|
|
||
|
|
for path in default_paths:
|
||
|
|
if path.exists():
|
||
|
|
self.logger.info(f"从 {path} 加载配置")
|
||
|
|
with path.open("r", encoding="utf-8") as f:
|
||
|
|
if path.suffix in [".yaml", ".yml"]:
|
||
|
|
return yaml.safe_load(f) or {}
|
||
|
|
else:
|
||
|
|
return json.load(f)
|
||
|
|
|
||
|
|
self.logger.warning("未找到配置文件,使用默认配置")
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def _register_commands(self) -> None:
|
||
|
|
"""注册所有命令"""
|
||
|
|
self.agent.register_command(OrganizeCommand())
|
||
|
|
|
||
|
|
async def run_command(
|
||
|
|
self,
|
||
|
|
command: str,
|
||
|
|
args: Optional[Dict[str, Any]] = None,
|
||
|
|
options: Optional[Dict[str, Any]] = None,
|
||
|
|
) -> SkillResult:
|
||
|
|
"""
|
||
|
|
运行命令
|
||
|
|
|
||
|
|
Args:
|
||
|
|
command: 命令名称
|
||
|
|
args: 命令参数
|
||
|
|
options: 命令选项
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
SkillResult: 执行结果
|
||
|
|
"""
|
||
|
|
return await self.agent.execute_command(command, args, options)
|
||
|
|
|
||
|
|
def list_commands(self) -> list:
|
||
|
|
"""列出所有可用命令"""
|
||
|
|
return self.agent.list_commands()
|
||
|
|
|
||
|
|
def get_command_info(self, command: str) -> Dict[str, Any]:
|
||
|
|
"""获取命令信息"""
|
||
|
|
if command in self.agent.commands:
|
||
|
|
return self.agent.commands[command].get_info()
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def get_all_commands_info(self) -> Dict[str, Any]:
|
||
|
|
"""获取所有命令信息"""
|
||
|
|
return self.agent.get_commands_info()
|
||
|
|
|
||
|
|
|
||
|
|
async def main():
|
||
|
|
"""命令行主函数"""
|
||
|
|
parser = argparse.ArgumentParser(
|
||
|
|
description="Obsidian 智能日记整理 Agent",
|
||
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
|
|
epilog="""
|
||
|
|
示例:
|
||
|
|
# 整理今天的日记
|
||
|
|
python -m journal_organizer organize
|
||
|
|
|
||
|
|
# 整理指定日期的日记
|
||
|
|
python -m journal_organizer organize --date 2025-12-31
|
||
|
|
|
||
|
|
# 列出所有可用命令
|
||
|
|
python -m journal_organizer list
|
||
|
|
|
||
|
|
# 显示命令帮助
|
||
|
|
python -m journal_organizer help organize
|
||
|
|
|
||
|
|
# 使用指定配置文件
|
||
|
|
python -m journal_organizer --config /path/to/config.yaml organize
|
||
|
|
""",
|
||
|
|
)
|
||
|
|
|
||
|
|
parser.add_argument("--config", type=str, help="配置文件路径")
|
||
|
|
|
||
|
|
parser.add_argument(
|
||
|
|
"--log-level",
|
||
|
|
type=str,
|
||
|
|
default="INFO",
|
||
|
|
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||
|
|
help="日志级别",
|
||
|
|
)
|
||
|
|
|
||
|
|
subparsers = parser.add_subparsers(dest="command", help="命令")
|
||
|
|
|
||
|
|
# organize 命令
|
||
|
|
organize_parser = subparsers.add_parser("organize", help="整理日记")
|
||
|
|
organize_parser.add_argument("--date", type=str, help="日期 (YYYY-MM-DD)")
|
||
|
|
organize_parser.add_argument("--vault-path", type=str, help="Obsidian vault 路径")
|
||
|
|
organize_parser.add_argument(
|
||
|
|
"--daily-folder", type=str, default="Daily", help="日记文件夹"
|
||
|
|
)
|
||
|
|
|
||
|
|
# list 命令
|
||
|
|
subparsers.add_parser("list", help="列出所有可用命令")
|
||
|
|
|
||
|
|
# help 命令
|
||
|
|
help_parser = subparsers.add_parser("help", help="显示命令帮助")
|
||
|
|
help_parser.add_argument("help_command", nargs="?", help="要查看帮助的命令")
|
||
|
|
|
||
|
|
# info 命令
|
||
|
|
subparsers.add_parser("info", help="显示 Agent 信息")
|
||
|
|
|
||
|
|
# check-deps 命令
|
||
|
|
subparsers.add_parser("check-deps", help="检查依赖项状态")
|
||
|
|
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
# 设置日志
|
||
|
|
logging.basicConfig(
|
||
|
|
level=getattr(logging, args.log_level),
|
||
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||
|
|
)
|
||
|
|
|
||
|
|
# 初始化 Agent
|
||
|
|
agent = JournalOrganizerAgent(args.config)
|
||
|
|
|
||
|
|
# 处理命令
|
||
|
|
if not args.command:
|
||
|
|
parser.print_help()
|
||
|
|
return
|
||
|
|
|
||
|
|
if args.command == "organize":
|
||
|
|
# 构建参数
|
||
|
|
organize_args = {}
|
||
|
|
if args.date:
|
||
|
|
organize_args["date"] = args.date
|
||
|
|
if args.vault_path:
|
||
|
|
organize_args["vault_path"] = args.vault_path
|
||
|
|
if args.daily_folder:
|
||
|
|
organize_args["daily_folder"] = args.daily_folder
|
||
|
|
|
||
|
|
result = await agent.run_command("organize", organize_args)
|
||
|
|
|
||
|
|
print(f"\n{'='*50}")
|
||
|
|
print("执行结果")
|
||
|
|
print("=" * 50)
|
||
|
|
print(result.to_json())
|
||
|
|
|
||
|
|
return 0 if result.success else 1
|
||
|
|
|
||
|
|
elif args.command == "list":
|
||
|
|
commands = agent.list_commands()
|
||
|
|
print("\n可用命令:")
|
||
|
|
for cmd in commands:
|
||
|
|
print(f" - {cmd}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
elif args.command == "help":
|
||
|
|
if args.help_command:
|
||
|
|
info = agent.get_command_info(args.help_command)
|
||
|
|
if info:
|
||
|
|
print(f"\n命令: {info['name']}")
|
||
|
|
print(f"描述: {info['description']}")
|
||
|
|
if info.get("aliases"):
|
||
|
|
print(f"别名: {', '.join(info['aliases'])}")
|
||
|
|
print("\nSkills:")
|
||
|
|
for skill_name, skill_info in info.get("skills", {}).items():
|
||
|
|
print(f" - {skill_name}: {skill_info['description']}")
|
||
|
|
else:
|
||
|
|
print(f"未找到命令: {args.help_command}")
|
||
|
|
return 1
|
||
|
|
else:
|
||
|
|
parser.print_help()
|
||
|
|
return 0
|
||
|
|
|
||
|
|
elif args.command == "info":
|
||
|
|
info = agent.get_all_commands_info()
|
||
|
|
print(f"\n{json.dumps(info, ensure_ascii=False, indent=2)}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
elif args.command == "check-deps":
|
||
|
|
print("\n🔍 检查依赖项状态...")
|
||
|
|
print(dependency_manager.get_dependency_status_report())
|
||
|
|
|
||
|
|
missing_deps = dependency_manager.get_missing_dependencies()
|
||
|
|
if missing_deps:
|
||
|
|
print(f"\n📋 安装说明:")
|
||
|
|
print(dependency_manager.get_installation_instructions(missing_only=True))
|
||
|
|
return 1
|
||
|
|
else:
|
||
|
|
print(f"\n✅ 所有依赖项都已正确安装!")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
def run_command_sync(
|
||
|
|
command: str,
|
||
|
|
args: Optional[Dict[str, Any]] = None,
|
||
|
|
config_file: Optional[str] = None,
|
||
|
|
) -> Dict[str, Any]:
|
||
|
|
"""
|
||
|
|
同步运行命令(用于外部调用)
|
||
|
|
|
||
|
|
Args:
|
||
|
|
command: 命令名称
|
||
|
|
args: 命令参数
|
||
|
|
config_file: 配置文件路径
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
执行结果字典
|
||
|
|
"""
|
||
|
|
agent = JournalOrganizerAgent(config_file)
|
||
|
|
result = asyncio.run(agent.run_command(command, args))
|
||
|
|
return result.to_dict()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
exit_code = asyncio.run(main())
|
||
|
|
sys.exit(exit_code or 0)
|