Files
journal_organizer/commands/organize_command.py
T

423 lines
17 KiB
Python
Raw Normal View History

"""
日记整理命令
负责协调各个 Skill 完成日记的分析和整理
"""
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Optional, List
# Handle imports with both relative and absolute paths
try:
from ..agent_core import Command, SkillResult, CommandContext
from ..date_validation import validate_date_input
from ..input_validation import command_input_validator
from ..config_validation import ClaudeAPIConfig
from ..skills.claude_skill import ClaudeAnalyzeSkill, ClaudeTransformSkill
from ..skills.obsidian_skill import (
ObsidianReadSkill,
ObsidianWriteSkill,
ObsidianAppendSkill,
ObsidianListFilesSkill,
)
except ImportError:
# Fallback to absolute imports when running as script
from agent_core import Command, SkillResult, CommandContext
from date_validation import validate_date_input
from input_validation import command_input_validator
from config_validation import ClaudeAPIConfig
from skills.claude_skill import ClaudeAnalyzeSkill, ClaudeTransformSkill
from skills.obsidian_skill import (
ObsidianReadSkill,
ObsidianWriteSkill,
ObsidianAppendSkill,
ObsidianListFilesSkill,
)
class OrganizeCommand(Command):
"""日记整理命令"""
def __init__(self) -> None:
super().__init__(
name="organize",
description="分析和整理日记内容,提取经验和要点",
aliases=["org", "organize-journal"],
)
# 注册 Skills
self.register_skill(ObsidianReadSkill())
self.register_skill(ObsidianWriteSkill())
self.register_skill(ObsidianAppendSkill())
self.register_skill(ClaudeAnalyzeSkill())
self.register_skill(ClaudeTransformSkill())
async def execute(self, context: CommandContext) -> SkillResult:
"""
执行日记整理命令
Args:
context: 命令执行上下文
- args:
- date: 日期(格式: YYYY-MM-DD,默认今天)
- vault_path: Obsidian vault 路径
- daily_folder: 日记文件夹(默认: Daily
- config: 系统配置
Returns:
SkillResult: 执行结果
"""
try:
# Validate and sanitize input arguments
validated_args = command_input_validator.validate_organize_command_input(
context.args
)
# 获取参数
date_str: Optional[str] = validated_args.get("date")
vault_path: Optional[str] = validated_args.get("vault_path")
daily_folder: str = validated_args.get("daily_folder", "Daily")
# 从配置中获取信息
config: Dict[str, Any] = context.config or {}
obsidian_config: Dict[str, Any] = config.get("obsidian", {})
claude_config: Dict[str, Any] = config.get("claude", {})
output_config: Dict[str, Any] = config.get("output", {})
analysis_config: Dict[str, Any] = config.get("analysis", {})
# 如果没有提供日期,使用今天
if not date_str:
date_str = datetime.now().strftime("%Y-%m-%d")
else:
# Validate the provided date
validated_date = validate_date_input(
date_str,
field_name="date",
required=True,
format_hint="iso_date"
)
date_str = validated_date.strftime("%Y-%m-%d")
# 获取必要的配置
api_url: str = obsidian_config.get("rest_api", {}).get(
"url", "https://localhost:27123"
)
api_key: Optional[str] = obsidian_config.get("rest_api", {}).get("api_key")
# Create enhanced Claude API configuration
try:
claude_api_config = ClaudeAPIConfig(**claude_config)
except Exception as e:
return SkillResult(
success=False,
error=f"Claude API 配置无效: {str(e)}",
message="请检查 Claude API 配置",
)
if not api_key or not claude_api_config.api_key:
return SkillResult(
success=False,
error="缺少必要的配置",
message="请配置 Obsidian API 密钥和 Claude API 密钥",
)
self.logger.info(f"开始整理日期 {date_str} 的日记")
# 步骤 1: 读取日记
daily_note_path = str(Path(daily_folder) / f"{date_str}.md")
self.logger.info(f"步骤 1: 读取日记 {daily_note_path}")
read_skill = self.skills["obsidian_read"]
read_result: SkillResult = await read_skill.execute(
context,
file_path=daily_note_path,
vault_path=vault_path,
api_url=api_url,
api_key=api_key,
)
if not read_result.success:
return read_result
journal_content: str = read_result.data["content"]
# 步骤 2: 使用 Claude 分析日记
self.logger.info("步骤 2: 使用 Claude 分析日记内容")
analyze_skill = self.skills["claude_analyze"]
categories: List[str] = analysis_config.get("categories", [])
analyze_result: SkillResult = await analyze_skill.execute(
context,
journal_content=journal_content,
claude_config=claude_api_config,
categories=categories,
)
if not analyze_result.success:
return analyze_result
analysis_data: Dict[str, Any] = analyze_result.data["analysis"]
# 步骤 3: 整理分析结果到各个位置
self.logger.info("步骤 3: 整理分析结果")
write_results: Dict[str, SkillResult] = {}
write_skill = self.skills["obsidian_write"]
# 处理经验
if "experiences" in analysis_data and analysis_data["experiences"]:
experiences_content: str = self._format_experiences(
analysis_data["experiences"], date_str
)
experiences_path = str(
Path(
output_config.get("experiences_folder", "Knowledge/Experiences")
)
/ f"{date_str}.md"
)
exp_result: SkillResult = await write_skill.execute(
context,
file_path=experiences_path,
content=experiences_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["experiences"] = exp_result
# 处理经验教训
if "lessons_learned" in analysis_data and analysis_data["lessons_learned"]:
lessons_content: str = self._format_lessons(
analysis_data["lessons_learned"], date_str
)
lessons_path = str(
Path(output_config.get("lessons_folder", "Knowledge/Lessons"))
/ f"{date_str}.md"
)
lessons_result: SkillResult = await write_skill.execute(
context,
file_path=lessons_path,
content=lessons_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["lessons"] = lessons_result
# 处理待办事项
if "action_items" in analysis_data and analysis_data["action_items"]:
tasks_content: str = self._format_tasks(
analysis_data["action_items"], date_str
)
tasks_path = str(
Path(output_config.get("tasks_folder", "Tasks/Daily"))
/ f"{date_str}.md"
)
tasks_result: SkillResult = await write_skill.execute(
context,
file_path=tasks_path,
content=tasks_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["tasks"] = tasks_result
# 处理问题
if "problems" in analysis_data and analysis_data["problems"]:
problems_content: str = self._format_problems(
analysis_data["problems"], date_str
)
problems_path = str(
Path(output_config.get("problems_folder", "Knowledge/Problems"))
/ f"{date_str}.md"
)
problems_result: SkillResult = await write_skill.execute(
context,
file_path=problems_path,
content=problems_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["problems"] = problems_result
# 处理成就
if "achievements" in analysis_data and analysis_data["achievements"]:
achievements_content: str = self._format_achievements(
analysis_data["achievements"], date_str
)
achievements_path = str(
Path(
output_config.get(
"achievements_folder", "Knowledge/Achievements"
)
)
/ f"{date_str}.md"
)
achievements_result: SkillResult = await write_skill.execute(
context,
file_path=achievements_path,
content=achievements_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["achievements"] = achievements_result
# 处理改进建议
if "improvements" in analysis_data and analysis_data["improvements"]:
improvements_content: str = self._format_improvements(
analysis_data["improvements"], date_str
)
improvements_path = str(
Path(
output_config.get(
"improvements_folder", "Knowledge/Improvements"
)
)
/ f"{date_str}.md"
)
improvements_result: SkillResult = await write_skill.execute(
context,
file_path=improvements_path,
content=improvements_content,
api_url=api_url,
api_key=api_key,
overwrite=True,
)
write_results["improvements"] = improvements_result
# 统计结果
successful_writes: int = sum(1 for r in write_results.values() if r.success)
return SkillResult(
success=True,
data={
"date": date_str,
"journal_file": daily_note_path,
"analysis": analysis_data,
"write_results": {k: v.success for k, v in write_results.items()},
"successful_writes": successful_writes,
"total_writes": len(write_results),
"organized_at": datetime.now().isoformat(),
},
message=f"成功整理日记 {date_str},已生成 {successful_writes} 个文件",
)
except Exception as e:
self.logger.error(f"执行日记整理命令时出错: {str(e)}")
return SkillResult(success=False, error=str(e), message="日记整理异常")
def _format_experiences(
self, experiences: List[Dict[str, Any]], date_str: str
) -> str:
"""格式化经验内容"""
content: str = f"# 经验总结 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
for i, exp in enumerate(experiences, 1):
content += f"## {i}. {exp.get('title', '经验')}\n\n"
content += f"**分类**: {exp.get('category', '未分类')}\n"
content += f"**优先级**: {exp.get('priority', 'medium')}\n\n"
content += f"{exp.get('content', '')}\n\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content
def _format_lessons(self, lessons: List[Dict[str, Any]], date_str: str) -> str:
"""格式化经验教训内容"""
content: str = f"# 经验教训 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
for i, lesson in enumerate(lessons, 1):
content += f"## {i}. {lesson.get('lesson', '教训')}\n\n"
content += f"**背景**: {lesson.get('context', '')}\n\n"
content += f"**应用**: {lesson.get('application', '')}\n\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content
def _format_tasks(self, tasks: List[Dict[str, Any]], date_str: str) -> str:
"""格式化待办事项内容"""
content: str = f"# 待办事项 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
# 按优先级分组
by_priority: Dict[str, List[Dict[str, Any]]] = {
"high": [],
"medium": [],
"low": [],
}
for task in tasks:
priority: str = task.get("priority", "medium")
by_priority[priority].append(task)
for priority in ["high", "medium", "low"]:
if by_priority[priority]:
priority_text: Dict[str, str] = {
"high": "🔴 高",
"medium": " 中",
r"low": "🟢 低",
}
content += f"## {priority_text[priority]} 优先级\n\n"
for task in by_priority[priority]:
content += f"- [ ] {task.get('task', '任务')}\n"
if task.get("deadline"):
content += f" - 截止: {task.get('deadline')}\n"
content += "\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content
def _format_problems(self, problems: List[Dict[str, Any]], date_str: str) -> str:
"""格式化问题内容"""
content: str = f"# 问题记录 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
for i, problem in enumerate(problems, 1):
content += f"## {i}. {problem.get('problem', '问题')}\n\n"
content += f"**影响**: {problem.get('impact', '')}\n\n"
content += f"**建议方案**: {problem.get('proposed_solution', '')}\n\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content
def _format_achievements(
self, achievements: List[Dict[str, Any]], date_str: str
) -> str:
"""格式化成就内容"""
content: str = f"# 成就记录 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
for i, achievement in enumerate(achievements, 1):
content += f"## {i}. {achievement.get('achievement', '成就')}\n\n"
content += f"**重要性**: {achievement.get('significance', '')}\n\n"
content += f"**证据**: {achievement.get('evidence', '')}\n\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content
def _format_improvements(
self, improvements: List[Dict[str, Any]], date_str: str
) -> str:
"""格式化改进建议内容"""
content: str = f"# 改进建议 - {date_str}\n\n"
content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
for i, improvement in enumerate(improvements, 1):
content += f"## {i}. {improvement.get('area', '改进领域')}\n\n"
content += f"**当前状态**: {improvement.get('current_state', '')}\n\n"
content += f"**建议改进**: {improvement.get('suggested_change', '')}\n\n"
content += f"**预期收益**: {improvement.get('expected_benefit', '')}\n\n"
content += f"\n---\n*来源: [[{date_str}]]*\n"
return content