- 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
964 lines
23 KiB
Markdown
964 lines
23 KiB
Markdown
# 开发者指南
|
||
|
||
本指南说明如何扩展和自定义日记整理 Agent,包括新的错误处理模式、配置验证功能和故障排除指南。
|
||
|
||
## 架构概览
|
||
|
||
Agent 系统基于以下核心概念:
|
||
|
||
- **Agent**:主控制器,负责管理 Commands 和 Skills
|
||
- **Command**:用户可执行的命令,编排多个 Skills
|
||
- **Skill**:原子化的功能单元,执行具体任务
|
||
- **SkillChain**:多个 Skills 的有序执行链
|
||
- **ErrorHandler**:集中式错误处理和日志记录
|
||
- **ConfigurationValidator**:配置验证和环境变量扩展
|
||
|
||
## 核心类
|
||
|
||
### Agent
|
||
|
||
```python
|
||
from journal_organizer.agent_core import Agent
|
||
|
||
# 创建 Agent
|
||
agent = Agent("MyAgent", config={})
|
||
|
||
# 注册命令
|
||
agent.register_command(my_command)
|
||
|
||
# 执行命令
|
||
result = await agent.execute_command("command_name", args={})
|
||
```
|
||
|
||
### Skill
|
||
|
||
所有 Skill 都继承自 `Skill` 基类:
|
||
|
||
```python
|
||
from journal_organizer.agent_core import Skill, SkillType, SkillResult, CommandContext
|
||
|
||
class MySkill(Skill):
|
||
def __init__(self):
|
||
super().__init__(
|
||
name="my_skill",
|
||
skill_type=SkillType.ANALYZE,
|
||
description="我的自定义 Skill"
|
||
)
|
||
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
# 实现您的逻辑
|
||
try:
|
||
result = do_something(**kwargs)
|
||
return SkillResult(
|
||
success=True,
|
||
data=result,
|
||
message="执行成功"
|
||
)
|
||
except Exception as e:
|
||
return SkillResult(
|
||
success=False,
|
||
error=str(e),
|
||
message="执行失败"
|
||
)
|
||
```
|
||
|
||
### Command
|
||
|
||
所有 Command 都继承自 `Command` 基类:
|
||
|
||
```python
|
||
from journal_organizer.agent_core import Command, SkillResult, CommandContext
|
||
|
||
class MyCommand(Command):
|
||
def __init__(self):
|
||
super().__init__(
|
||
name="my_command",
|
||
description="我的自定义命令",
|
||
aliases=["mc"]
|
||
)
|
||
# 注册 Skills
|
||
self.register_skill(MySkill())
|
||
|
||
async def execute(self, context: CommandContext) -> SkillResult:
|
||
# 获取参数
|
||
param1 = context.args.get('param1')
|
||
|
||
# 执行 Skill
|
||
skill = self.skills['my_skill']
|
||
result = await skill.execute(context, param1=param1)
|
||
|
||
return result
|
||
```
|
||
|
||
## 添加新的 Skill
|
||
|
||
### 步骤 1: 创建 Skill 类
|
||
|
||
在 `skills/` 目录下创建一个新文件,例如 `my_skill.py`:
|
||
|
||
```python
|
||
from ..agent_core import Skill, SkillType, SkillResult, CommandContext
|
||
|
||
class MyCustomSkill(Skill):
|
||
def __init__(self):
|
||
super().__init__(
|
||
name="my_custom_skill",
|
||
skill_type=SkillType.TRANSFORM,
|
||
description="执行自定义转换"
|
||
)
|
||
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
try:
|
||
input_data = kwargs.get('input_data')
|
||
|
||
# 您的自定义逻辑
|
||
output_data = self._process(input_data)
|
||
|
||
return SkillResult(
|
||
success=True,
|
||
data=output_data,
|
||
message="处理完成"
|
||
)
|
||
except Exception as e:
|
||
return SkillResult(
|
||
success=False,
|
||
error=str(e),
|
||
message="处理失败"
|
||
)
|
||
|
||
def _process(self, data):
|
||
# 实现处理逻辑
|
||
return data
|
||
```
|
||
|
||
### 步骤 2: 在 Command 中使用 Skill
|
||
|
||
```python
|
||
from ..skills.my_skill import MyCustomSkill
|
||
|
||
class MyCommand(Command):
|
||
def __init__(self):
|
||
super().__init__(name="my_command")
|
||
self.register_skill(MyCustomSkill())
|
||
|
||
async def execute(self, context: CommandContext) -> SkillResult:
|
||
skill = self.skills['my_custom_skill']
|
||
return await skill.execute(context, input_data="test")
|
||
```
|
||
|
||
## 添加新的 Command
|
||
|
||
### 步骤 1: 创建 Command 类
|
||
|
||
在 `commands/` 目录下创建一个新文件,例如 `my_command.py`:
|
||
|
||
```python
|
||
from ..agent_core import Command, SkillResult, CommandContext
|
||
from ..skills.my_skill import MyCustomSkill
|
||
|
||
class MyCommand(Command):
|
||
def __init__(self):
|
||
super().__init__(
|
||
name="my_command",
|
||
description="我的自定义命令",
|
||
aliases=["mc", "my-cmd"]
|
||
)
|
||
self.register_skill(MyCustomSkill())
|
||
|
||
async def execute(self, context: CommandContext) -> SkillResult:
|
||
# 获取参数
|
||
param1 = context.args.get('param1')
|
||
param2 = context.args.get('param2', 'default')
|
||
|
||
# 执行 Skill
|
||
skill = self.skills['my_custom_skill']
|
||
result = await skill.execute(context, input_data=param1)
|
||
|
||
return result
|
||
```
|
||
|
||
### 步骤 2: 在 Agent 中注册 Command
|
||
|
||
编辑 `main.py` 的 `_register_commands` 方法:
|
||
|
||
```python
|
||
def _register_commands(self) -> None:
|
||
"""注册所有命令"""
|
||
self.agent.register_command(OrganizeCommand())
|
||
self.agent.register_command(MyCommand()) # 添加新命令
|
||
```
|
||
|
||
### 步骤 3: 测试新命令
|
||
|
||
```bash
|
||
python -m journal_organizer my_command --param1 "value1"
|
||
```
|
||
|
||
## 使用 SkillChain
|
||
|
||
SkillChain 允许您按顺序执行多个 Skills:
|
||
|
||
```python
|
||
from journal_organizer.agent_core import SkillChain
|
||
|
||
class MyCommand(Command):
|
||
def __init__(self):
|
||
super().__init__(name="my_command")
|
||
|
||
# 创建 Skill 链
|
||
chain = SkillChain("my_chain", "执行一系列操作")
|
||
chain.add_skill(Skill1(), {"param1": "value1"})
|
||
chain.add_skill(Skill2(), {"param2": "value2"})
|
||
|
||
self.register_skill_chain(chain)
|
||
|
||
async def execute(self, context: CommandContext) -> SkillResult:
|
||
chain = self.skill_chains['my_chain']
|
||
return await chain.execute(context)
|
||
```
|
||
|
||
## 异步编程
|
||
|
||
所有 Skills 和 Commands 都使用异步编程(async/await)。这允许并发执行多个操作。
|
||
|
||
### 基本示例
|
||
|
||
```python
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
# 异步调用外部 API
|
||
result = await self.call_external_api()
|
||
return SkillResult(success=True, data=result)
|
||
|
||
async def call_external_api(self):
|
||
# 使用 aiohttp 进行异步 HTTP 请求
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.get('https://api.example.com/data') as resp:
|
||
return await resp.json()
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
### 新的错误处理框架
|
||
|
||
系统现在使用集中式错误处理框架,提供一致的错误管理和日志记录:
|
||
|
||
```python
|
||
from journal_organizer.error_handling import (
|
||
ErrorHandler,
|
||
JournalOrganizerError,
|
||
ConfigurationError,
|
||
APIError,
|
||
ValidationError
|
||
)
|
||
|
||
# 创建错误处理器
|
||
import logging
|
||
logger = logging.getLogger("MySkill")
|
||
error_handler = ErrorHandler(logger)
|
||
|
||
# 处理 API 错误
|
||
try:
|
||
result = await api_call()
|
||
except Exception as e:
|
||
error_result = error_handler.handle_api_error(e, "claude", "analyze_text")
|
||
return SkillResult(
|
||
success=False,
|
||
error=error_result["error"],
|
||
message=error_result["message"]
|
||
)
|
||
```
|
||
|
||
### 自定义异常类型
|
||
|
||
使用专门的异常类型来处理不同类型的错误:
|
||
|
||
```python
|
||
from journal_organizer.error_handling import (
|
||
JournalOrganizerError,
|
||
ConfigurationError,
|
||
APIError,
|
||
ValidationError
|
||
)
|
||
|
||
# 配置错误
|
||
if not api_key:
|
||
raise ConfigurationError("API key is required", context={"service": "claude"})
|
||
|
||
# API 错误
|
||
if response.status_code != 200:
|
||
raise APIError(
|
||
"API request failed",
|
||
api_name="obsidian",
|
||
status_code=response.status_code
|
||
)
|
||
|
||
# 验证错误
|
||
if not validate_input(data):
|
||
raise ValidationError("Invalid input format", field="date")
|
||
```
|
||
|
||
### Skill 中的错误处理模式
|
||
|
||
在 Skill 中实现标准化的错误处理:
|
||
|
||
```python
|
||
from journal_organizer.agent_core import Skill, SkillResult, CommandContext
|
||
from journal_organizer.error_handling import ErrorHandler, APIError, ValidationError
|
||
|
||
class MySkill(Skill):
|
||
def __init__(self):
|
||
super().__init__(name="my_skill")
|
||
self.error_handler = ErrorHandler(self.logger)
|
||
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
try:
|
||
# 输入验证
|
||
self._validate_inputs(**kwargs)
|
||
|
||
# 执行主要逻辑
|
||
result = await self._perform_operation(**kwargs)
|
||
|
||
return SkillResult(success=True, data=result, message="操作成功")
|
||
|
||
except ValidationError as e:
|
||
error_result = self.error_handler.handle_validation_error(e, "input_data")
|
||
return SkillResult(
|
||
success=False,
|
||
error=error_result["error"],
|
||
message=error_result["message"]
|
||
)
|
||
except APIError as e:
|
||
error_result = self.error_handler.handle_api_error(e, "external_service", "operation")
|
||
return SkillResult(
|
||
success=False,
|
||
error=error_result["error"],
|
||
message=error_result["message"]
|
||
)
|
||
except Exception as e:
|
||
# 处理未预期的错误
|
||
self.logger.error(f"Unexpected error in {self.name}: {str(e)}", exc_info=True)
|
||
return SkillResult(
|
||
success=False,
|
||
error="Internal error occurred",
|
||
message="操作失败,请检查日志"
|
||
)
|
||
|
||
def _validate_inputs(self, **kwargs):
|
||
"""验证输入参数"""
|
||
required_params = ['param1', 'param2']
|
||
for param in required_params:
|
||
if param not in kwargs:
|
||
raise ValidationError(f"Missing required parameter: {param}", field=param)
|
||
|
||
async def _perform_operation(self, **kwargs):
|
||
"""执行主要操作"""
|
||
# 实现您的逻辑
|
||
pass
|
||
```
|
||
|
||
## 日志记录
|
||
|
||
使用内置的 logger 记录信息:
|
||
|
||
```python
|
||
class MySkill(Skill):
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
self.logger.debug("开始执行")
|
||
self.logger.info("处理数据")
|
||
self.logger.warning("可能的问题")
|
||
self.logger.error("发生错误")
|
||
|
||
return SkillResult(success=True)
|
||
```
|
||
|
||
## 配置管理
|
||
|
||
### 新的配置验证系统
|
||
|
||
系统现在包含强大的配置验证功能,支持类型检查、环境变量扩展和路径验证:
|
||
|
||
```python
|
||
from journal_organizer.config_validation import (
|
||
SystemConfig,
|
||
ObsidianConfig,
|
||
ClaudeConfig,
|
||
validate_system_config
|
||
)
|
||
|
||
# 验证配置
|
||
try:
|
||
config = validate_system_config(raw_config)
|
||
print("配置验证成功")
|
||
except ValidationError as e:
|
||
print(f"配置验证失败: {e}")
|
||
```
|
||
|
||
### 环境变量扩展
|
||
|
||
配置文件支持环境变量扩展:
|
||
|
||
```yaml
|
||
# config.yaml
|
||
claude:
|
||
api_key: "${ANTHROPIC_API_KEY}" # 从环境变量读取
|
||
model: "${CLAUDE_MODEL:-claude-3-5-sonnet-20241022}" # 带默认值
|
||
|
||
obsidian:
|
||
vault_path: "${OBSIDIAN_VAULT_PATH}"
|
||
rest_api:
|
||
api_key: "${OBSIDIAN_API_KEY}"
|
||
```
|
||
|
||
### 配置验证示例
|
||
|
||
```python
|
||
from journal_organizer.config_validation import validate_obsidian_config, validate_claude_config
|
||
|
||
# 验证 Obsidian 配置
|
||
obsidian_config = {
|
||
"vault_path": "/path/to/vault",
|
||
"rest_api": {
|
||
"url": "https://localhost:27123",
|
||
"api_key": "your-key",
|
||
"verify_ssl": False
|
||
}
|
||
}
|
||
|
||
try:
|
||
validated_config = validate_obsidian_config(obsidian_config)
|
||
print("Obsidian 配置有效")
|
||
except ValidationError as e:
|
||
print(f"Obsidian 配置错误: {e}")
|
||
|
||
# 验证 Claude 配置
|
||
claude_config = {
|
||
"api_key": "sk-ant-...",
|
||
"model": "claude-3-5-sonnet-20241022",
|
||
"max_tokens": 4096
|
||
}
|
||
|
||
try:
|
||
validated_config = validate_claude_config(claude_config)
|
||
print("Claude 配置有效")
|
||
except ValidationError as e:
|
||
print(f"Claude 配置错误: {e}")
|
||
```
|
||
|
||
### 在 Skill 中访问配置
|
||
|
||
```python
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
config = context.config or {}
|
||
|
||
# 安全地访问配置
|
||
claude_config = config.get('claude', {})
|
||
api_key = claude_config.get('api_key')
|
||
|
||
if not api_key:
|
||
raise ConfigurationError("Claude API key not configured")
|
||
|
||
# 使用配置
|
||
return SkillResult(success=True)
|
||
```
|
||
|
||
## 测试
|
||
|
||
### 单元测试示例
|
||
|
||
```python
|
||
import pytest
|
||
from journal_organizer.agent_core import CommandContext
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_my_skill():
|
||
skill = MySkill()
|
||
context = CommandContext(command_name="test")
|
||
|
||
result = await skill.execute(context, input_data="test")
|
||
|
||
assert result.success == True
|
||
assert result.data is not None
|
||
```
|
||
|
||
### 运行测试
|
||
|
||
```bash
|
||
pytest tests/
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 并发执行
|
||
|
||
```python
|
||
import asyncio
|
||
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
# 并发执行多个操作
|
||
results = await asyncio.gather(
|
||
self.operation1(),
|
||
self.operation2(),
|
||
self.operation3()
|
||
)
|
||
|
||
return SkillResult(success=True, data=results)
|
||
```
|
||
|
||
### 缓存
|
||
|
||
```python
|
||
from functools import lru_cache
|
||
|
||
class MySkill(Skill):
|
||
@lru_cache(maxsize=128)
|
||
def expensive_operation(self, key):
|
||
# 缓存昂贵的操作
|
||
return process(key)
|
||
```
|
||
|
||
## 最佳实践
|
||
|
||
### 现代 Python 模式
|
||
|
||
系统现在遵循现代 Python 最佳实践:
|
||
|
||
#### 1. 使用 f-strings 进行字符串格式化
|
||
|
||
```python
|
||
# ✅ 推荐:使用 f-strings
|
||
name = "用户"
|
||
message = f"欢迎 {name},当前时间是 {datetime.now()}"
|
||
|
||
# ❌ 避免:字符串连接
|
||
message = "欢迎 " + name + ",当前时间是 " + str(datetime.now())
|
||
```
|
||
|
||
#### 2. 使用 pathlib 进行文件路径操作
|
||
|
||
```python
|
||
from pathlib import Path
|
||
|
||
# ✅ 推荐:使用 pathlib
|
||
vault_path = Path(config['obsidian']['vault_path'])
|
||
daily_folder = vault_path / "Daily"
|
||
note_file = daily_folder / f"{date}.md"
|
||
|
||
# 检查文件是否存在
|
||
if note_file.exists():
|
||
content = note_file.read_text(encoding='utf-8')
|
||
|
||
# ❌ 避免:使用 os.path
|
||
import os
|
||
note_file = os.path.join(vault_path, "Daily", f"{date}.md")
|
||
```
|
||
|
||
#### 3. 使用 dataclasses 定义数据结构
|
||
|
||
```python
|
||
from dataclasses import dataclass, field
|
||
from typing import Optional, List
|
||
from datetime import datetime
|
||
|
||
@dataclass
|
||
class SkillResult:
|
||
success: bool
|
||
data: Optional[Dict[str, Any]] = None
|
||
error: Optional[str] = None
|
||
message: str = ""
|
||
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
||
|
||
def to_dict(self) -> Dict[str, Any]:
|
||
return asdict(self)
|
||
```
|
||
|
||
#### 4. 使用类型提示
|
||
|
||
```python
|
||
from typing import Dict, Any, Optional, List, Union
|
||
|
||
async def execute_skill(
|
||
skill_name: str,
|
||
context: CommandContext,
|
||
**kwargs: Any
|
||
) -> SkillResult:
|
||
"""
|
||
执行指定的 Skill
|
||
|
||
Args:
|
||
skill_name: Skill 名称
|
||
context: 命令执行上下文
|
||
**kwargs: Skill 参数
|
||
|
||
Returns:
|
||
SkillResult: 执行结果
|
||
"""
|
||
pass
|
||
```
|
||
|
||
#### 5. 使用异步上下文管理器
|
||
|
||
```python
|
||
from contextlib import asynccontextmanager
|
||
import aiohttp
|
||
|
||
@asynccontextmanager
|
||
async def http_client(config: Dict[str, Any]):
|
||
"""HTTP 客户端上下文管理器"""
|
||
connector = aiohttp.TCPConnector(
|
||
ssl=False if not config.get('verify_ssl', True) else None
|
||
)
|
||
|
||
async with aiohttp.ClientSession(connector=connector) as session:
|
||
try:
|
||
yield session
|
||
finally:
|
||
await session.close()
|
||
|
||
# 使用示例
|
||
async def call_api():
|
||
async with http_client(api_config) as client:
|
||
async with client.get(url) as response:
|
||
return await response.json()
|
||
```
|
||
|
||
### 通用最佳实践
|
||
|
||
1. **单一职责**:每个 Skill 只负责一个任务
|
||
2. **错误处理**:使用新的错误处理框架
|
||
3. **日志记录**:使用 logger 记录重要信息
|
||
4. **配置驱动**:使用配置验证系统
|
||
5. **异步编程**:充分利用异步特性提高性能
|
||
6. **类型安全**:使用类型提示和验证
|
||
7. **文档**:为您的 Skills 和 Commands 编写清晰的文档
|
||
8. **测试**:编写单元测试和属性测试确保代码质量
|
||
9. **版本控制**:使用 git 管理代码版本
|
||
10. **代码格式化**:使用 black 和 isort 保持代码风格一致
|
||
|
||
## 示例:完整的自定义 Skill
|
||
|
||
```python
|
||
"""
|
||
自定义 Skill 示例:文本统计
|
||
"""
|
||
|
||
from ..agent_core import Skill, SkillType, SkillResult, CommandContext
|
||
|
||
class TextStatisticsSkill(Skill):
|
||
"""计算文本统计信息的 Skill"""
|
||
|
||
def __init__(self):
|
||
super().__init__(
|
||
name="text_statistics",
|
||
skill_type=SkillType.ANALYZE,
|
||
description="计算文本的字数、词数、句数等统计信息"
|
||
)
|
||
|
||
async def execute(self, context: CommandContext, **kwargs) -> SkillResult:
|
||
"""
|
||
执行文本统计
|
||
|
||
Args:
|
||
context: 命令执行上下文
|
||
**kwargs: 包含 text 参数
|
||
|
||
Returns:
|
||
SkillResult: 包含统计结果的结果
|
||
"""
|
||
try:
|
||
text = kwargs.get('text', '')
|
||
|
||
if not text:
|
||
return SkillResult(
|
||
success=False,
|
||
error="缺少文本参数",
|
||
message="未提供要统计的文本"
|
||
)
|
||
|
||
# 计算统计信息
|
||
stats = {
|
||
'char_count': len(text),
|
||
'word_count': len(text.split()),
|
||
'sentence_count': len(text.split('。')),
|
||
'line_count': len(text.split('\n')),
|
||
'avg_word_length': len(text) / len(text.split()) if text.split() else 0
|
||
}
|
||
|
||
self.logger.info(f"文本统计完成: {stats}")
|
||
|
||
return SkillResult(
|
||
success=True,
|
||
data=stats,
|
||
message="文本统计完成"
|
||
)
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"文本统计失败: {str(e)}")
|
||
return SkillResult(
|
||
success=False,
|
||
error=str(e),
|
||
message="文本统计异常"
|
||
)
|
||
```
|
||
|
||
## 资源
|
||
|
||
- [Python 异步编程](https://docs.python.org/3/library/asyncio.html)
|
||
- [Anthropic API 文档](https://docs.anthropic.com/)
|
||
- [Obsidian API 文档](https://docs.obsidian.md/Obsidian+API)
|
||
|
||
---
|
||
|
||
# 故障排除指南
|
||
|
||
本节提供常见问题的解决方案和调试技巧。
|
||
|
||
## 常见问题
|
||
|
||
### 1. 导入错误 (ImportError)
|
||
|
||
**问题**: `ImportError: attempted relative import with no known parent package`
|
||
|
||
**解决方案**:
|
||
```bash
|
||
# 确保以模块方式运行
|
||
python -m journal_organizer --help
|
||
|
||
# 而不是直接运行
|
||
python main.py # ❌ 错误方式
|
||
```
|
||
|
||
**原因**: 项目使用相对导入,需要作为包运行。
|
||
|
||
### 2. 配置文件问题
|
||
|
||
**问题**: `ConfigurationError: Missing required configuration`
|
||
|
||
**解决方案**:
|
||
1. 检查配置文件是否存在:
|
||
```bash
|
||
ls -la config.yaml
|
||
```
|
||
|
||
2. 验证配置格式:
|
||
```bash
|
||
python -c "
|
||
import yaml
|
||
with open('config.yaml', 'r') as f:
|
||
config = yaml.safe_load(f)
|
||
print('配置文件格式正确')
|
||
"
|
||
```
|
||
|
||
3. 检查环境变量:
|
||
```bash
|
||
echo $ANTHROPIC_API_KEY
|
||
echo $OBSIDIAN_API_KEY
|
||
```
|
||
|
||
### 3. API 连接问题
|
||
|
||
**问题**: `APIError: Failed to connect to Claude/Obsidian API`
|
||
|
||
**解决方案**:
|
||
|
||
**Claude API**:
|
||
```bash
|
||
# 测试 API 密钥
|
||
curl -H "Authorization: Bearer $ANTHROPIC_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
https://api.anthropic.com/v1/messages
|
||
```
|
||
|
||
**Obsidian API**:
|
||
```bash
|
||
# 检查 Obsidian Local REST API 插件状态
|
||
curl -k -H "Authorization: Bearer $OBSIDIAN_API_KEY" \
|
||
https://localhost:27123/
|
||
```
|
||
|
||
### 4. 依赖项问题
|
||
|
||
**问题**: `ModuleNotFoundError: No module named 'xxx'`
|
||
|
||
**解决方案**:
|
||
```bash
|
||
# 检查依赖项状态
|
||
python -m journal_organizer check-deps
|
||
|
||
# 安装缺失的依赖项
|
||
pip install -r requirements.txt
|
||
|
||
# 安装可选依赖项
|
||
pip install pyyaml aiohttp anthropic
|
||
```
|
||
|
||
### 5. 权限问题
|
||
|
||
**问题**: `PermissionError: [Errno 13] Permission denied`
|
||
|
||
**解决方案**:
|
||
```bash
|
||
# 检查文件权限
|
||
ls -la config.yaml
|
||
ls -la /path/to/obsidian/vault
|
||
|
||
# 修复权限
|
||
chmod 644 config.yaml
|
||
chmod -R 755 /path/to/obsidian/vault
|
||
```
|
||
|
||
### 6. SSL 证书问题
|
||
|
||
**问题**: `SSL: CERTIFICATE_VERIFY_FAILED`
|
||
|
||
**解决方案**:
|
||
在配置文件中禁用 SSL 验证(仅用于本地开发):
|
||
```yaml
|
||
obsidian:
|
||
rest_api:
|
||
verify_ssl: false
|
||
```
|
||
|
||
## 调试技巧
|
||
|
||
### 1. 启用详细日志
|
||
|
||
```bash
|
||
# 设置调试级别日志
|
||
python -m journal_organizer --log-level DEBUG organize
|
||
```
|
||
|
||
### 2. 使用 Python 调试器
|
||
|
||
```python
|
||
# 在 Skill 中添加断点
|
||
import pdb; pdb.set_trace()
|
||
|
||
# 或使用 ipdb(更友好的界面)
|
||
import ipdb; ipdb.set_trace()
|
||
```
|
||
|
||
### 3. 检查配置加载
|
||
|
||
```python
|
||
# 测试配置加载
|
||
from journal_organizer.main import JournalOrganizerAgent
|
||
|
||
agent = JournalOrganizerAgent("config.yaml")
|
||
print(f"配置: {agent.config}")
|
||
```
|
||
|
||
### 4. 测试单个 Skill
|
||
|
||
```python
|
||
# 单独测试 Skill
|
||
import asyncio
|
||
from journal_organizer.skills.claude_skill import ClaudeAnalyzeSkill
|
||
from journal_organizer.agent_core import CommandContext
|
||
|
||
async def test_skill():
|
||
skill = ClaudeAnalyzeSkill()
|
||
context = CommandContext(command_name="test")
|
||
result = await skill.execute(context, text="测试文本")
|
||
print(f"结果: {result}")
|
||
|
||
asyncio.run(test_skill())
|
||
```
|
||
|
||
## 性能问题
|
||
|
||
### 1. 内存使用过高
|
||
|
||
**诊断**:
|
||
```python
|
||
import psutil
|
||
import os
|
||
|
||
process = psutil.Process(os.getpid())
|
||
print(f"内存使用: {process.memory_info().rss / 1024 / 1024:.2f} MB")
|
||
```
|
||
|
||
**解决方案**:
|
||
- 检查是否有内存泄漏
|
||
- 使用 `gc.collect()` 强制垃圾回收
|
||
- 限制并发操作数量
|
||
|
||
### 2. API 调用缓慢
|
||
|
||
**诊断**:
|
||
```python
|
||
import time
|
||
|
||
start_time = time.time()
|
||
result = await api_call()
|
||
duration = time.time() - start_time
|
||
print(f"API 调用耗时: {duration:.2f} 秒")
|
||
```
|
||
|
||
**解决方案**:
|
||
- 检查网络连接
|
||
- 增加超时设置
|
||
- 使用连接池
|
||
- 实现重试机制
|
||
|
||
## 错误代码参考
|
||
|
||
| 错误代码 | 描述 | 解决方案 |
|
||
|---------|------|----------|
|
||
| CONFIG_001 | 配置文件不存在 | 创建 config.yaml 文件 |
|
||
| CONFIG_002 | 配置格式错误 | 检查 YAML/JSON 语法 |
|
||
| CONFIG_003 | 缺少必需配置项 | 添加缺失的配置项 |
|
||
| API_001 | API 密钥无效 | 检查并更新 API 密钥 |
|
||
| API_002 | API 连接超时 | 检查网络连接和服务状态 |
|
||
| API_003 | API 限流 | 减少请求频率或升级 API 计划 |
|
||
| SKILL_001 | Skill 执行失败 | 检查 Skill 输入参数和依赖项 |
|
||
| SKILL_002 | Skill 超时 | 增加超时设置或优化 Skill 逻辑 |
|
||
|
||
## 获取帮助
|
||
|
||
如果问题仍然存在:
|
||
|
||
1. **检查日志文件**: `logs/journal_organizer.log`
|
||
2. **运行诊断命令**: `python -m journal_organizer check-deps`
|
||
3. **查看详细错误**: 使用 `--log-level DEBUG`
|
||
4. **测试基本功能**: 运行简单的命令如 `list` 或 `help`
|
||
|
||
## 开发环境设置
|
||
|
||
### 推荐的开发工具
|
||
|
||
```bash
|
||
# 安装开发依赖
|
||
pip install pytest pytest-asyncio black isort mypy
|
||
|
||
# 代码格式化
|
||
black .
|
||
isort .
|
||
|
||
# 类型检查
|
||
mypy journal_organizer/
|
||
|
||
# 运行测试
|
||
pytest tests/
|
||
```
|
||
|
||
### 调试配置 (VS Code)
|
||
|
||
创建 `.vscode/launch.json`:
|
||
```json
|
||
{
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{
|
||
"name": "Debug Journal Organizer",
|
||
"type": "python",
|
||
"request": "launch",
|
||
"module": "journal_organizer",
|
||
"args": ["--log-level", "DEBUG", "organize"],
|
||
"console": "integratedTerminal",
|
||
"cwd": "${workspaceFolder}"
|
||
}
|
||
]
|
||
}
|
||
```
|