Files
chill_notes/wiki/AI工程/ClaudeCode对话导出Obsidian.md
2026-05-04 10:39:03 +08:00

267 lines
7.1 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 完整指南*