AI chat 로그를 Obsidian vault와 SQLite 기반 지식 그래프로 변환하는 Python 배치 처리기입니다.
assistant 응답 로그를 JSONL에서 읽고, Ollama로 문서 제목/본문/엔티티를 추출한 뒤 markdown 문서로 저장합니다. 동시에 선택한 embedding provider로 벡터를 만들고, SQLite에 문서와 엔티티 노드 및 sqlite-vec 임베딩 테이블을 관리해 이후 로그가 기존 문서에 append될지 새 문서가 될지 판단합니다.
- JSONL chat 로그에서 assistant 메시지만 추출
- 여러 text fragment를 하나의 답변 본문으로 결합
- Ollama 기반 요약, 문서 본문, append 본문, 엔티티 추출
- Ollama/Voyage 기반 embedding provider 선택
- Obsidian markdown 문서 생성 및 기존 문서 append
- frontmatter에
created,modified,aliases,entities,source_file,source_line기록 - 엔티티를
[[Entity]]wikilink로 기록 - SQLite
Nodes,Edges,ProcessedLogs와sqlite-vec기반vec_docs관리 ProcessedLogs.line_hash전역 unique 기준으로 동일한 로그 라인 내용 중복 처리 방지UNIQUE(type, name), document path unique index,Edgesforeign key 기반 graph schema- vector similarity와 graph relationship을 결합한 hybrid search
- markdown 파일 변경 후 DB 작업 실패 시 파일 rollback
src/secondbrain/main.py: CLI 진입점src/secondbrain/commands.py: CLI/API 요청을 실제 작업 단위로 실행하는 command servicesrc/secondbrain/config/settings.py: 환경변수 기반 런타임 설정src/secondbrain/ingestion/logs.py: JSONL 로그 해석과 assistant 메시지 추출src/secondbrain/pipeline/: 로그 한 줄 처리 파이프라인src/secondbrain/services/llm_client.py: Ollama generate client와 Ollama/Voyage embedding clientsrc/secondbrain/core/search.py: hybrid scoring과 문서 검색src/secondbrain/db/: SQLite 연결, schema, repository querysrc/secondbrain/storage/: Obsidian markdown 생성, 파일 변경, vault/graph 갱신src/secondbrain/api/: FastAPI job API, job store, worker, callback 처리tests/: pytest 테스트
이 프로젝트는 Python 3.13만 대상으로 합니다.
uv를 사용할 수 있으면 다음 명령으로 의존성을 설치하고 테스트를 실행합니다.
uv sync
uv run pytestuv가 없으면 Python 3.13 환경에서 editable install 후 pytest를 실행합니다.
python -m pip install -e . pytest
python -m pytest.env.example을 참고해 .env를 만들고 필요한 값을 채운 뒤 실행합니다.
uv run --env-file .env secondbrain --log "/path/to/logs/*.jsonl"의존성이 이미 설치되어 있고 src가 import 가능한 환경이라면 다음처럼 실행할 수도 있습니다.
python -m secondbrain --log "/path/to/logs/*.jsonl"기존 markdown vault를 DB/graph에 색인하려면:
uv run --env-file .env secondbrain --index-vault현재 markdown 본문 기준으로 문서 임베딩을 다시 만들려면:
uv run --env-file .env secondbrain --refresh-embeddingsLangGraph 처리 파이프라인을 PNG로 저장하려면:
uv run --env-file .env secondbrain --draw-pipeline-graph "./docs/mermaid-graph.png"이 명령은 app.get_graph().draw_mermaid_png()를 호출하며, Mermaid.ink API를 사용해 PNG를 생성합니다. 따라서 실행 환경에서 Mermaid.ink에 접근할 수 있어야 합니다.
CLI와 API는 최종적으로 src/secondbrain/commands.py의 run_secondbrain_command()를 호출합니다. 이 함수는 SecondbrainCommandOptions를 받아 아래 작업들을 순서대로 실행하고, 실행 결과를 operations 배열로 반환합니다.
SecondbrainCommandOptions 필드는 다음과 같습니다.
log_patterns: 처리할 JSONL 파일 또는 glob pattern 목록index_vault: 기존 markdown vault를 DB/graph에 색인할지 여부refresh_embeddings: 색인된 document embedding을 현재 markdown 본문 기준으로 갱신할지 여부markdown_dir:index_vault실행 시 스캔할 markdown 디렉터리draw_pipeline_graph: LangGraph 처리 파이프라인 PNG 출력 경로
주요 내부 operation:
| Operation | 실행 조건 | 역할 |
|---|---|---|
draw_pipeline_graph |
draw_pipeline_graph 값이 있을 때 |
DB를 초기화하지 않고 LangGraph processor graph를 PNG로 렌더링한 뒤 종료 |
process_logs |
log_patterns가 있거나, 다른 작업 옵션이 없을 때 |
JSONL assistant 로그를 처리해 vault markdown, SQLite graph, vector index를 갱신 |
index_vault |
index_vault=True |
실제 markdown 파일이 없는 document row와 embedding을 먼저 정리한 뒤, 아직 DB에 색인되지 않은 markdown 파일을 document node, embedding, entity edge로 추가 |
refresh_embeddings |
refresh_embeddings=True |
DB에 색인된 document의 현재 markdown 본문을 읽어 vec_docs embedding을 교체 |
draw_pipeline_graph는 단독 작업입니다. 이 값이 있으면 로그 처리, vault 색인, embedding refresh는 실행하지 않습니다.
process_logs는 기본 작업입니다. log_patterns, index_vault, refresh_embeddings가 모두 비어 있으면 LOG_PATH를 사용해 로그 처리를 실행합니다. 반대로 --index-vault만 실행하거나 --refresh-embeddings만 실행할 때는 로그 처리를 건너뜁니다.
index_vault는 시작 시 DB의 document path를 확인합니다. Nodes(type='document') row의 path가 비어 있거나 실제 파일이 없으면 해당 document row와 vec_docs embedding을 삭제합니다. 연결된 Edges는 foreign key cascade로 정리됩니다.
index_vault와 refresh_embeddings에서 사용할 document embedding provider는 EMBEDDING_MODEL_PROVIDER로 결정됩니다. 값이 voyage이면 Voyage embedding을 input_type="document"로 호출하고, 그 외에는 Ollama embedding을 사용합니다.
작업 조합 예시:
# 기본 LOG_PATH로 로그 처리
uv run --env-file .env secondbrain
# 로그 처리 후 vault 색인과 embedding refresh까지 순차 실행
uv run --env-file .env secondbrain \
--log "./logs/*.jsonl" \
--index-vault \
--refresh-embeddings \
--markdown-dir "./vault"FastAPI 서버를 실행하면 HTTP로 secondbrain 작업을 enqueue할 수 있습니다.
uv run --env-file .env uvicorn secondbrain.api.main:app --host 0.0.0.0 --port 8000주요 endpoint:
GET /health: 서버 상태 확인POST /jobs: secondbrain 작업 생성GET /jobs/{job_id}: 단일 job 상태와 결과 조회GET /jobs?limit=20: 최근 job 목록 조회
POST /jobs 요청 필드는 command service 옵션과 대응됩니다. process_logs, log_patterns, index_vault, refresh_embeddings, markdown_dir, draw_pipeline_graph, callback_url, metadata를 사용할 수 있습니다.
이미지 빌드:
docker build -f secondbrain.DockerFile -t secondbrain .worker 컨테이너 실행:
docker run -d --name secondbrain-worker --env-file .env \
-v "$PWD/vault:/app/vault" \
-v "$PWD/logs:/app/logs" \
-v "$PWD/data:/app/data" \
secondbrain실행 중인 컨테이너에서 작업 수행:
docker exec secondbrain-worker secondbrain --log "/app/logs/*.jsonl"
docker exec secondbrain-worker secondbrain --index-vault
docker exec secondbrain-worker secondbrain --refresh-embeddings일회성 배치 실행:
docker run --rm --env-file .env \
-v "$PWD/vault:/app/vault" \
-v "$PWD/logs:/app/logs" \
-v "$PWD/data:/app/data" \
secondbrain secondbrain --log "/app/logs/*.jsonl"Docker에서 Ollama를 사용할 때는 OLLAMA_ENDPOINT=localhost가 컨테이너 자기 자신을 가리킨다는 점에 주의해야 합니다. Ollama가 호스트나 다른 컨테이너에서 실행 중이면 해당 환경에서 접근 가능한 주소를 사용해야 합니다.
주요 설정값은 secondbrain.config.settings에서 import 시점에 읽습니다.
OLLAMA_ENDPOINT: Ollama base URLOLLAMA_API_KEY: Ollama API key가 필요한 endpoint를 사용할 때의 optional bearer tokenOLLAMA_TIMEOUT: Ollama HTTP timeoutSECONDBRAIN_API_TOKEN: FastAPI job API 인증에 사용할 optional bearer tokenSECONDBRAIN_JOB_DB_PATH: API job 상태를 저장할 SQLite DB 경로N8N_CALLBACK_URL: job 완료 후 결과를 보낼 기본 callback URLN8N_CALLBACK_TIMEOUT: callback HTTP timeoutN8N_CALLBACK_RETRIES: callback 실패 시 추가 retry 횟수VAULT_PATH: Obsidian markdown 출력 디렉터리DB_PATH: SQLite database 경로LOG_PATH:--log가 없을 때 사용할 기본 JSONL globTHRESHOLD: 기존 문서 append 여부를 판단하는 hybrid score 기준값LLM_MODEL: summary/entity extraction 모델EMBEDDING_MODEL_PROVIDER: embedding provider.ollama또는voyage를 사용할 수 있으며 대소문자는 구분하지 않습니다.EMBEDDING_MODEL: embedding 모델VOYAGE_API_KEY:EMBEDDING_MODEL_PROVIDER=voyage일 때 사용할 Voyage AI API keyVOYAGE_ENDPOINT: Voyage AI API endpointEMBEDDING_DIMENSION: embedding vector 차원과vec_docs차원VECTOR_SEARCH_LIMIT: vector search 후보 개수VECTOR_SCORE_WEIGHT: hybrid score에서 vector score 가중치GRAPH_SCORE_WEIGHT: hybrid score에서 graph score 가중치EDGE_WEIGHT_INCREMENT: 같은 entity/document 관계가 반복될 때 edge weight 증가량MAX_FILENAME_STEM_LENGTH: markdown 파일명 stem 최대 길이MARKDOWN_SUMMARY_MAX_CHARS: markdown 색인 시 summary 최대 길이SUMMARY_INSTRUCTION: 초기 문서화 prompt instructionAPPEND_DECISION_INSTRUCTION: 기존 문서 append 판단 prompt instruction
새 로그가 기존 문서에 이어 붙을 수 있는지는 hybrid_search가 계산한 점수로 판단합니다. 점수는 vector score와 graph score를 가중 평균한 값입니다.
vector 결과만 있으면 vector score만으로 평균을 내고, graph 결과만 있으면 graph score만으로 평균을 냅니다. 둘 다 없으면 점수는 0.0입니다.
sqlite-vec에서 반환한 distance를 다음 방식으로 0.0 ~ 1.0 범위의 similarity score로 변환합니다.
distance가 0이면 score는 1.0입니다. distance가 커질수록 score는 완만하게 낮아집니다. 이 방식은 1.0 - distance처럼 distance 범위가 항상 0~1이라고 가정하지 않습니다.
graph score는 새 문서의 entity 및 alias와 기존 문서가 공유하는 entity/document edge weight를 기반으로 합니다. alias는 별도 DB 컬럼이 아니라 Nodes(type='entity')와 Edges를 통해 graph 신호로 저장됩니다.
먼저 DB에서 공유 엔티티를 통해 연결된 문서별 edge weight 합을 구합니다.
그 다음 로그 스케일 정규화를 적용합니다.
이 정책은 graph score를 0.0 ~ 1.0에 묶어 vector score와 안정적으로 결합하면서도, 반복적으로 같은 entity와 연결된 문서가 약간 더 높은 점수를 받도록 합니다. 선형 정규화처럼 edge weight가 급격히 커져 vector score를 압도하지 않고, 반복 연결의 효과는 로그 스케일로 완만하게 증가하다가 1.0에서 포화됩니다.
처리 흐름은 다음과 같습니다.
- 로그 한 줄이 이미 처리된
line_hash인지 확인합니다. 같은 내용의 로그 라인은 파일이 달라도 전역 중복으로 보고 처리하지 않습니다. - assistant message가 아니면 무시합니다.
- Ollama로 제목, aliases, summary, body, append_body, entities를 추출합니다.
- 원문 assistant content로 document embedding을 생성합니다.
- vector score와 graph score를 결합해 기존 문서 후보를 찾습니다. graph search에는
entities와aliases를 함께 사용합니다. - score가 낮아도 title 또는 aliases가 기존 markdown의 title/aliases와 겹치면 같은 중심 개념의 문서 후보로 봅니다.
- 후보 문서가 있으면 Ollama가 기존 문서 본문과 summary를 보고 append 여부를 다시 판단합니다.
THRESHOLD이상이거나 title/alias 기반 append가 허용되면 기존 문서에 추가 기록을 붙입니다.- 새 문서가 더 적절하면 unique markdown 파일을 만들고 graph에 새 document node를 추가합니다.
- 새 문서 생성, 기존 문서 append, vault 색인 과정에서 aliases도 graph entity edge로 연결합니다.
- 처리가 끝나면
ProcessedLogs에 로그 line hash를 기록합니다.
실제 chat 로그, 생성된 vault, SQLite DB, .env 파일, secret은 source로 커밋하지 않습니다.
logs/, vault/, database 파일은 로컬 런타임 데이터로 취급합니다. vault 파일을 쓰는 동작은 DB 갱신 실패 시 rollback을 시도하지만, 중요한 vault를 대상으로 실행하기 전에는 별도 백업을 권장합니다.
