로그인 상태를 확인하는 중입니다…

contextBio 내부 문서 · 전체관리자 · 앱관리자

관리자 로그인이 필요합니다

계정 페이지로 이동합니다. 로그인하면 보던 자리로 그대로 돌아옵니다.

로그인하러 가기

계정이 없으신가요? 같은 화면에서 가입할 수 있습니다.

접근 권한 없음

열 수 없습니다

로그인 계정:

연결 실패

확인하지 못했습니다

contextBio BioWrit 구현

논문 작성 플랫폼의 구현 스펙 — Claude 마스터 + 워커 브릿지 구조, 대시보드 백엔드, 출력 경로 단일 출처, 실행 상한과 CORS.

기준 2026-10-05 · main
79037d1

한눈에

Claude 기반 마스터 에이전트(harness)가 입력 하나를 받아 문헌 수집 → 초안 → 심사 → 문서 생성까지 지휘한다. 워커는 의견만 내고 문서를 쓰지 않는다.

항목 값
저장소 contextBio/BioWrit (bare ContextBio/BioWrit.git)
호스트 c1.sysmed.kr — 백엔드·워커 CLI 모두 (2026-08-29 g2 에서 이전)
운영 워크트리 ContextBio/BioWrit — main, 127.0.0.1:8765, systemd user biowrit-backend
개발 워크트리 dev/BioWrit — dev, 127.0.0.1:8766, systemd user biowrit-backend-dev
백엔드 주소 Apache c1.sysmed.kr/biowrit/ → 8765 · /biowrit-dev/ → 8766
프론트 개발 미러에만 있다 — dev-contextbio.web.app/biowrit (아래 참조)
백엔드 코드 dashboard/server.py — 표준 라이브러리만 쓴다(의존성 없음)
로그인 contextBio 통합계정 ID 토큰 (contextbio_auth.py)
릴리즈 deploy/release.sh (scripts/ 가 아니다)
사용자 자료 CONTEXTBIO_USERDATA — 운영 /mnt/S1/sdata/contextbio/users, dev 는 contextbio-dev/users
산출물 <CONTEXTBIO_USERDATA>/<사용자>/biowrit/artifact/<타입>/<폴더명>/ (저장소 밖)
실행 기록 저장소 안 runs/<user_slug>/<rid>/{meta.json,output.log} (.gitignore)
로그 journalctl --user -u biowrit-backend(-dev)

화면은 지금 개발 미러에서만 나온다

2026-09-12 에 공개 사이트를 AURORA·PepDesigner 로 좁혔다(content.py 의 RELEASE_APPS). 그래서 운영 빌드(deploy.yml)는 BioWrit 을 굽지 않고, contextbio.ai/biowrit 과 biowrit.contextbio.ai 는 /home 으로 302 된다(2026-09-29 확인).

화면은 사이트 저장소의 deploy-dev.yml 이 BioWrit dev 브랜치의 dashboard/ 를 build_subapp.py 로 구워 dev-contextbio.web.app/biowrit 에 싣는다. 이때 백엔드 주소는 SUBAPP_BACKEND_BIOWRIT=https://c1.sysmed.kr/biowrit-dev 로 dev 백엔드를 가리킨다. 화면을 고쳤으면 BioWrit 저장소에 푸시한 뒤 사이트 워크플로가 돌아야 반영된다.

운영 백엔드(:8765)는 계속 떠 있고 main 릴리즈도 나간다(최근 release-20260927-0344). 운영 빌드의 레시피(RECIPES["biowrit"])는 https://c1.sysmed.kr/biowrit 를 가리킨 채로 남아 있어, 공개 목록에 다시 넣으면 그쪽에 붙는다.

에이전트 구조

입력(URL · PMID · 키워드 · 원고 · 리뷰어 코멘트 · 계획서 요청)
   │
   ▼  harness (Claude 마스터) — .claude/agents/harness.md
   ├ Stage 0~1  hermes            수집 라우터
   ├ Stage 2    출력 에이전트 8종  research · review · critic · rebuttal ·
   │                              editorial · editorial_comment · wiki · proposal
   ├ Stage 2.5  codex-bridge      코드 생성 (gpt-5.3-codex) → 패치 → 승인 후 적용
   ├ Stage 2-C+ codex/gemini-bridge  동료심사 2nd 의견 → critic 재호출
   └ Stage 3~4  품질 검사 · 리포트
  • 통신 규약: 서브에이전트 간 직접 호출 금지. 모든 데이터는 harness 를 지난다. 필요할 때는 critic_required · cross_validation_required 같은 신호만 올리고 분기는 harness 가 한다. 예외는 보조 워커 둘 — pubmed-notebook(PubMed→NotebookLM)과 hwp2docs(HWP→docx)는 hermes 가 직접 부를 수 있다.
  • 워커 선택은 .agents/rules/triangle.md §2 — 코드는 Codex 고정, 동료심사 2nd 의견은 사용자 지정 > ChatGPT > 실패 시 Gemini 승계. 둘을 동시에 부르지 않는다.
  • 워커가 없어도 파이프라인은 끝까지 간다 — cross-validation 과 Stage 2.5 만 꺼진다. 그래서 워커 설치는 기능 추가이지 복구 절차가 아니다(deploy/workers.md).
  • 워커 CLI 는 c1 의 ~/.npm-global/bin/{codex,gemini} 에 있다. claude -p 가 c1 에서 돌므로 g2 에 남은 설치분은 쓰이지 않는다. start-backend.sh 가 이 경로를 PATH 에 넣는데, 빠지면 브릿지가 not_installed 로만 보고하고 조용히 Claude 단독으로 돈다.
  • Gemini 브릿지는 항상 --approval-mode plan 이라 파일을 쓸 수 없다 — 코드 패치 경로가 없다는 뜻이다.

규칙 파일이 정본이다

에이전트는 매 호출마다 자기 스펙을 다시 읽는다. 그래서 동작을 바꾸는 자리는 코드가 아니라 마크다운이다.

파일 정하는 것
.agents/rules/setPath.md 출력 경로 단일 출처 — 사용자 자료 뿌리·운영자 지정 루트·타입별 경로 패턴
.agents/rules/source_policy.md 논문 소스 단일 출처 — PubMed 는 pubmed_fetch.py(E-utilities)만, 최근 5년·IF≥5 하한·IF순→최신순 상위 10, 출처 원장 필수
.agents/rules/triangle.md 워커 거버넌스 — 모델 매트릭스·폴백·안전 가드·승격 기준
.agents/rules/llmwiki.md 문서 구조·파일명·프론트매터·내비 동기화
.agents/rules/tool_mapping.md 스킬의 추상 도구명 → 실행 환경 도구명
.agents/rules/karpathy-guidelines.md 코딩 태도(단순함·수술적 변경·검증 가능한 목표)
.agents/skill/*.md 실행 워크플로 20개 (수집·출력·공통·검사·교정)

출력 경로를 바꾸려면 setPath.md 만 고친다 — 대시보드 서버도 그 규칙을 따라간다. 소스 규칙은 source_policy.md 가 스킬·에이전트 문서보다 우선한다.

산출물 경로 — 저장소 밖이다

<CONTEXTBIO_USERDATA>/<사용자>/biowrit/artifact/<타입>/<폴더명>/
   운영  /mnt/S1/sdata/contextbio/users/…        개발  /mnt/S1/sdata/contextbio-dev/users/…
   <폴더명> = NotebookLM 노트북 이름(snake_case)
   타입 = wiki · editorial_comment · editorial · review · research ·
          critic · rebuttal · proposal

절대경로 하나로 통일돼 있다 — 그 폴더 자체가 이미 그 사람 것이므로 경로에 <사용자> 세그먼트를 다시 두지 않는다. 대시보드가 프롬프트에 <output_root> 로 이 값을 그대로 넘기고, 결과보기·다운로드도 같은 자리를 본다.

쓰는 자리의 우선순위는 setPath.md §1 — ① 운영자 지정 루트 → ② (로컬 모드) 사용자 커스텀 루트 → ③ 사용자 자료 뿌리 → ④ 사용자 미지정이면 레포 안 artifact/ 다.

운영자 지정 루트(BIOWRIT_PINNED_ARTIFACT_ROOTS) 는 특정 사용자의 쓰는 자리를 서버 환경변수로 고정한다("<slug>=<절대경로>,…"). 배포별 정책이라 레포가 아니라 systemd 유닛의 Environment= 에 둔다. 지금은 운영 hyun_goo_woo, dev hyungoowoo 가 c1 의 구글 드라이브 rclone 마운트 ~/GoogleDrive/03_manuscript/artifact/ 에 쓴다(2026-09-23). c1 에만 있는 마운트이므로 다른 호스트에서는 무효다. 경로가 없으면 경고를 남기고 ③ 으로 돌아가고, 결과보기는 지정 경로·원래 자리·레거시 트리를 모두 읽는다.

2026-09-07 에 저장소 밖으로 옮겼다. 그전까지 신규 출력이 레포 안 artifact/ 로 떨어졌는데, 읽는 자리는 이미 사용자 자료 뿌리로 옮겨져 있어 읽기와 쓰기가 어긋난 상태였다. 그 사이 산출물 73개가 저장소에 추적돼 두 워크트리가 늘 더러웠고, 관계없는 커밋에 산출물이 실려 나갔다. 지금 레포 안 artifact/ 는 .gitignore 에 있고 이관 전 산출물을 결과보기에 계속 노출하는 레거시 읽기전용 트리로만 남는다.

끊는 순서가 중요하다. git rm --cached 로 추적만 끊으면 그 커밋을 다른 워크트리가 체크아웃할 때 디스크의 연구 산출물이 지워진다. 먼저 사용자 자료 뿌리로 복사해 해시로 확인하고, 그다음에 추적을 끊는다.

하나의 노트북에 속한 산출물이 모두 같은 <폴더명> 아래로 모인다. 그림은 .docx 에 박히지 않고 같은 폴더에 별도 TIFF(600 dpi 이상) 로 떨어지고 본문에는 legend 만 들어간다 — 저널 제출 형식에 맞춘 것이다.

더 오래된 루트(03_manuscript/artifact)도 읽기전용 스캔 대상으로 남아 있다. BIOWRIT_LEGACY_ARTIFACT_ROOT → 레포 상대 → ~/GoogleDrive/… → ~/gdrive/… 순으로 실존하는 곳을 쓴다. 상대경로 계산이 실제 트리를 못 찾은 적이 있고(2026-08-08), 드라이브 마운트 이름이 ~/gdrive 에서 ~/GoogleDrive 로 바뀌어 다시 안 보인 적이 있다(2026-09-09).

사용자 폴더명은 이메일 로컬파트를 그대로 쓴다(hyun.goo.woo, 점 유지). API 의 user slug(hyun_goo_woo, 밑줄)와 다르다 — 둘을 헷갈리면 남의 폴더를 찾는다.

대시보드 백엔드

dashboard/server.py 는 표준 라이브러리만 쓴다. 화면이 문서를 만들지는 않는다 — harness 에 건넬 지침을 짜고(dashboard/tasks/pending/<timestamp>.json) 실행은 claude -p --dangerously-skip-permissions 를 백그라운드로 띄워 결과가 나왔는지 지켜본다.

deploy/start-backend.sh 가 dashboard/.env(시크릿)를 읽고 원격 모드 (BIOWRIT_DEPLOY_MODE=remote)·127.0.0.1 바인드로 띄운다. 외부 노출·HTTPS 종단은 Apache 몫이다 — 백엔드를 직접 열면 프록시를 우회한 평문 접근이 생긴다.

환경변수 기본값 무엇
BIOWRIT_DASH_PORT 8765 포트 — dev 유닛은 8766
BIOWRIT_ALLOWED_ORIGINS (start-backend.sh 가 채운다) CORS 허용 오리진. dev 유닛은 dev-contextbio.web.app·localhost 만 — 실서비스 오리진은 일부러 뺐다
BIOWRIT_ALLOWED_USERS 관리자만 실행(/api/run) 허용 계정. 운영·dev 유닛 모두 * = 이메일 인증된 모든 사용자
BIOWRIT_RUN_MAX_CONCURRENT 1 사용자당 동시 실행
BIOWRIT_RUN_LIMIT_PER_HOUR 20 사용자당 시간당 실행
BIOWRIT_DEFAULT_TOKEN_QUOTA 50M 새 계정 기본 토큰 한도(생성 약 2회 분량). 기존 레코드에 소급하지 않는다
BIOWRIT_DEFAULT_STORAGE_BYTES 50 MB 계정 기본 저장 한도(0 이하 = 무제한)
BIOWRIT_PINNED_ARTIFACT_ROOTS (없음) 운영자 지정 산출물 루트 (위 참조)
BIOWRIT_PRIMARY_USER (start-backend.sh 가 채운다) 레거시 평면 산출물 트리의 소유자
CONTEXTBIO_USERDATA /mnt/S1/sdata/contextbio/users 사용자 자료 루트 — dev 유닛은 contextbio-dev/users

원격 모드는 기동 전 검사에서 fail closed 다(_preflight_remote). Firebase 웹 apiKey· BIOWRIT_ALLOWED_ORIGINS·BIOWRIT_PRIMARY_USER·실행 허용 계정 중 하나라도 비면 기동을 거부한다 — 인증이 꺼진 채 뜨는 사고를 막는다.

실행 허용 판정(can_run)은 이메일 인증 → 관리자는 항상 허용 → 관리 화면에서 정한 계정별 run_allowed → BIOWRIT_ALLOWED_USERS 순이다. 토큰 한도와 저장 한도는 따로 잰다 — 자원이 달라 합치면 엉뚱한 사람이 막힌다.

인증과 관리자

  • 원격 모드에서는 모든 /api/* 와 /download 가 Authorization: Bearer <ID 토큰> 을 요구한다. 예외는 /api/config·/api/info·/api/version 셋뿐이다. 이메일 미인증 계정은 403 이다. 클라이언트가 보낸 user 값은 무시하고 토큰에서 신원을 정한다.
  • 토큰 검증은 공용 모듈 contextbio_auth.py 가 한다(폐기·클레임 포함, 5분 캐시). 레포의 파일은 사본이고 정본은 사이트 저장소다 — 갱신은 사이트 저장소에서 python tools/sync_auth.py --write.
  • 전체관리자의 정본은 커스텀 클레임 admin 이다. BIOWRIT_ADMIN_EMAILS(없으면 js/firebase-config.js 의 ADMIN_EMAILS)는 아무에게도 클레임이 없는 최초 상태를 넘기는 부트스트랩일 뿐이다.
  • 클레임 apps 가 목록이면서 biowrit 이 없으면 앱을 쓸 수 없다(management.py).
  • 앱 역할은 user·admin 둘이다. 앱관리자는 사용자 목록·토큰 한도·잡 관리를 쓸 수 있지만, 역할 변경과 앱관리자의 권한 변경은 전체관리자만 한다(app_management.py).

실행 중인 작업 보기·중지

누가 API 비고
본인 GET /api/runs · POST /api/run/stop {id} 대시보드 "Running jobs" 구획(8초 폴링)
관리자 GET /api/management/jobs · POST /api/management/kill {id, owner} 사이트 공용 관리 패널 jobsPanel(). 끝난 잡은 409

관리자 목록은 사용자 자료 뿌리 전체를 훑고 PID 가 살아 있는지 확인한다. 킬은 PID 가 사는 호스트에서만 유효하다.

유닛은 KillMode=process 다 — 백엔드를 재기동해도 claude -p 자식은 계속 돈다. 기본값(control-group)이면 재기동이 수십 분짜리 생성을 죽인다(2026-08-08 실측).

NotebookLM

NotebookLM 은 MCP 로 붙는다. 주요 변경·릴리즈·재기동 뒤에는 /api/nlm/health 로 authenticated 를 확인한다 — 서버가 헤드리스라 브라우저 로그인을 화면에서 끝낼 수 없고, 끊긴 상태로도 다른 기능은 멀쩡해 보이기 때문이다.

  • 주소는 운영 https://c1.sysmed.kr/biowrit/api/nlm/health, dev 는 /biowrit-dev/…. 인증 게이트 뒤라 토큰 없이 부르면 401 이 정상이다 — NLM 장애가 아니다.
  • 서버가 실제로 믿는 신호는 쿠키 DB 다. ~/.local/share/notebooklm-mcp/chrome_profile/**/Cookies 를 읽기 전용으로 열어 .google.com 의 SID·__Secure-1PSID·__Secure-3PSID·SSID·HSID 중 하나라도 미만료면 로그인 상태다. MCP get_health 는 호출마다 새 프로세스라 늘 false 로 나오므로 그것만 보고 판단하지 않는다.
  • 쿠키가 만료됐으면 관리자가 setup_auth 를 한 번 수행한다.

문서 생성

python .scripts/generate_docx.py <content> <out.docx> "<title>" "<citation>" "" <doc_type>
python .scripts/generate_docx.py <content> <out.docx> "<title>" "" <prev_content> research
#                                                                  └ 5번째 인자를 주면 변경내용 추적

doc_type(6번째 인자)은 반드시 명시한다. editorial_comment 만 고정 byline 과 citation 라벨을 쓰고, 나머지 타입에서는 citation 을 무시한다. proposal 은 review/research 와 같은 학술 렌더링을 물려받되 본문이 한국어다. 변경내용 추적은 5번째 인자를 준 개정 모드에서만 켜진다.

wiki 타입만 .md 로 나가고 MkDocs(Material)가 굽는다. 내비게이션 정본은 mkdocs.yml 이라 위키 파일과 어긋나지 않게 함께 고친다.

배포

./deploy/release.sh              # main 에 dev 머지 → 태그 → 푸시 → 재기동 → /api/version 검증
./deploy/release.sh --dry-run    # 무엇이 나가는지만

운영 워크트리(main)에서 dev 를 --no-ff 머지하고 release-YYYYMMDD-HHMM 태그를 남긴 뒤 systemctl --user restart biowrit-backend 한다. 배포 확인은 /api/version 이다 — 응답의 commit·branch·started_at 이 새 것이어야 새 코드가 뜬 것이다. 릴리즈 뒤에는 위 NotebookLM 확인까지 마친다.

dev 는 릴리즈가 없다. dev 워크트리에 커밋하고 systemctl --user restart biowrit-backend-dev 로 반영한다.

실제로 도는 유닛은 ~/.config/systemd/user/biowrit-backend(-dev).service 다. 저장소 deploy/ 의 사본은 경로가 맞지만(ExecStart 가 각 워크트리의 deploy/start-backend.sh), 운영 설치본에만 BIOWRIT_ALLOWED_USERS=* 가 더 있다(2026-09-29 확인). 사본으로 다시 설치하면 실행이 관리자에게만 열린다.

Node 는 /opt/node-v24.19.0-linux-x64/bin 을 쓴다 — 셸 기본 /usr/bin/node 는 v12 라 워커 CLI 설치·실행이 실패한다(Cannot find module 'node:path').

지켜야 할 것

  • 원고 전문은 NotebookLM 에 올리지 않는다. 기밀·저작권·미발표 데이터 때문에 일부러 막은 동작이다. 근거로 쓸 문헌만 노트북에 넣는다.
  • 인용 형식은 Vancouver/NLM 하나뿐이고 Word 에는 EndNote 필드로 들어간다.
  • 실행은 서버에서 도구를 자동 승인한 채 돈다 — 파일을 쓰고 외부 도구를 부르며 토큰을 쓴다.
  • runs/ 로그에는 프롬프트와 원고 본문이 그대로 남는다 — 커밋하지 않는다.
  • 시크릿(Claude·Gemini·NCBI 키 등)은 dashboard/.env 에만 둔다 — 유닛이나 레포에 적지 않는다.