diff --git a/wiki/AI工程/ClaudeCode对话导出Obsidian.md b/wiki/AI工程/ClaudeCode对话导出Obsidian.md new file mode 100755 index 0000000..a98d9e8 --- /dev/null +++ b/wiki/AI工程/ClaudeCode对话导出Obsidian.md @@ -0,0 +1,266 @@ +--- +created: 2026-05-04 +type: guide +tags: [Claude Code, Obsidian, 数据导出, 对话存档, SQLite] +--- + +# Claude Code 对话导出到 Obsidian 完整方案 + +> 将所有 Claude Code 对话会话存档到 Obsidian 知识库 +> 归档时间:2026-05-04 + +--- + +## 📌 原理 + +Claude Code 的对话数据存储在本地 SQLite 数据库中: + +**数据库路径**: +- macOS/Linux:`~/.claude/CLAUDE.md` 同级目录下的 `~/.claude/conversations.db` +- 实际数据库文件:`~/.claude/*.db` 或 `~/.claude/PROJECT_ID/*.db` + +**数据库结构**(典型): +- `conversations` 表:会话元数据(ID、标题、创建时间等) +- `messages` 表:对话消息(角色、内容、时间戳) +- `attachments` 表:附件/文件引用 + +--- + +## 🔧 方案一:Python 脚本导出(推荐) + +### 1. 导出脚本 + +```python +#!/usr/bin/env python3 +""" +claude_to_obsidian.py +将 Claude Code 所有对话导出为 Obsidian Markdown 笔记 +""" + +import sqlite3 +import os +import json +from datetime import datetime +from pathlib import Path + +# 配置 +CLAUDE_DB_PATH = os.path.expanduser("~/.claude/conversations.db") +OBSIDIAN_PATH = "/obsidian/ClaudeCode对话/" # 你的 Obsidian vault 路径 + +def export_all(): + if not os.path.exists(CLAUDE_DB_PATH): + print(f"❌ 数据库不存在: {CLAUDE_DB_PATH}") + print("请确认 Claude Code 已安装并运行过") + return + + os.makedirs(OBSIDIAN_PATH, exist_ok=True) + + conn = sqlite3.connect(CLAUDE_DB_PATH) + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + + # 获取所有会话 + cursor.execute(""" + SELECT c.id, c.title, c.created_at, c.updated_at, + COUNT(m.id) as msg_count + FROM conversations c + LEFT JOIN messages m ON c.id = m.conversation_id + GROUP BY c.id + ORDER BY c.updated_at DESC + """) + + conversations = cursor.fetchall() + print(f"📊 找到 {len(conversations)} 个会话") + + exported = 0 + for conv in conversations: + title = conv['title'] or f"untitled-{conv['id'][:8]}" + # 文件名安全处理 + safe_title = "".join(c for c in title if c not in r'\/:*?"<>|') + safe_title = safe_title[:80] # 限制长度 + filename = f"{safe_title}.md" + filepath = os.path.join(OBSIDIAN_PATH, filename) + + # 获取该会话的所有消息 + cursor.execute(""" + SELECT role, content, created_at + FROM messages + WHERE conversation_id = ? + ORDER BY created_at ASC + """, (conv['id'],)) + + messages = cursor.fetchall() + + # 生成 Markdown + md = generate_markdown(conv, messages) + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(md) + + exported += 1 + print(f" ✅ {title} ({len(messages)} 条消息)") + + print(f"\n🎉 完成!导出 {exported} 个会话到 {OBSIDIAN_PATH}") + conn.close() + +def generate_markdown(conv, messages): + created = datetime.fromtimestamp(conv['created_at']).strftime('%Y-%m-%d %H:%M:%S') if conv['created_at'] else 'unknown' + updated = datetime.fromtimestamp(conv['updated_at']).strftime('%Y-%m-%d %H:%M:%S') if conv['updated_at'] else 'unknown' + + md = f"""--- +created: {created} +updated: {updated} +tags: [claude-code, 对话] +conversation_id: {conv['id']} +message_count: {len(messages)} +--- + +# {conv['title'] or 'Untitled'} + +> 创建时间:{created} +> 更新时间:{updated} + +--- + +## 对话记录 + +""" + + for msg in messages: + role = msg['role'] # 'user' or 'assistant' + content = msg['content'] or '' + ts = datetime.fromtimestamp(msg['created_at']).strftime('%H:%M:%S') if msg['created_at'] else '' + + if role == 'user': + md += f"\n### 👤 用户 `{ts}`\n\n" + else: + md += f"\n### 🤖 Claude `{ts}`\n\n" + + # 处理内容(可能是 JSON 字符串) + md += sanitize_content(content) + md += "\n\n---\n" + + return md + +def sanitize_content(content): + """清理内容,移除可能的工具调用元数据""" + try: + # 尝试解析 JSON + data = json.loads(content) + if isinstance(data, dict): + # 提取文本内容 + if 'content' in data: + return data['content'] + if 'text' in data: + return data['text'] + return json.dumps(data, indent=2, ensure_ascii=False) + except (json.JSONDecodeError, TypeError): + pass + + return str(content) + +if __name__ == "__main__": + export_all() +``` + +### 2. 使用 + +```bash +# 安装依赖(通常不需要) +pip install sqlite3 # Python 内置 + +# 运行导出 +python3 claude_to_obsidian.py +``` + +--- + +## 🔧 方案二:直接查看数据库结构 + +```bash +# 查看数据库表 +sqlite3 ~/.claude/conversations.db ".tables" + +# 查看表结构 +sqlite3 ~/.claude/conversations.db ".schema conversations" +sqlite3 ~/.claude/conversations.db ".schema messages" + +# 查看会话数量 +sqlite3 ~/.claude/conversations.db "SELECT COUNT(*) FROM conversations;" + +# 导出单个会话为 JSON +sqlite3 -json ~/.claude/conversations.db \ + "SELECT m.* FROM messages m JOIN conversations c ON m.conversation_id = c.id WHERE c.title LIKE '%关键词%';" +``` + +--- + +## 🔧 方案三:按项目/目录组织 + +Claude Code 为每个项目创建独立的数据库: + +```bash +# 查找所有项目数据库 +find ~/.claude -name "*.db" -type f + +# 输出示例: +# ~/.claude/projects/project-uuid-1/conversations.db +# ~/.claude/projects/project-uuid-2/conversations.db +``` + +**按项目导出脚本**: + +```python +import os +import glob + +def export_by_project(): + db_files = glob.glob(os.path.expanduser("~/.claude/**/conversations.db"), recursive=True) + + for db_path in db_files: + # 从路径提取项目名 + project_name = db_path.split(os.sep)[-2] # 倒数第二级是项目 UUID + project_dir = os.path.join(OBSIDIAN_PATH, project_name) + os.makedirs(project_dir, exist_ok=True) + + print(f"📁 处理项目: {project_name}") + # ... 使用上面的导出逻辑 +``` + +--- + +## 📂 Obsidian 目录结构建议 + +``` +/obsidian/ClaudeCode对话/ +├── _templates/ +│ └── Claude对话模板.md # Obsidian 模板 +├── 2026-05-04/ +│ ├── 修复API鉴权Bug.md +│ ├── 编写单元测试.md +│ └── 重构数据库模块.md +└── INDEX.md # 索引笔记(按日期/项目分类) +``` + +--- + +## ⚠️ 注意事项 + +1. **隐私安全**:对话可能包含敏感信息(API Key、代码等),导出后注意权限控制 +2. **大文件**:长对话可能生成大文件,Obsidian 打开会慢 +3. **编码问题**:确保以 UTF-8 编码写入 +4. **增量导出**:记录上次导出的时间戳,只导出新对话 +5. **工具调用**:Claude Code 的工具调用(文件读写、Shell 执行)元数据可能需要特殊处理 + +--- + +## 🔄 自动同步方案 + +```bash +# 加入 cron,每天凌晨自动导出 +0 2 * * * cd /path/to/scripts && python3 claude_to_obsidian.py >> /tmp/claude_export.log 2>&1 +``` + +--- + +*研究归档,2026-05-04 | Claude Code 对话导出到 Obsidian 完整指南*