본문으로 이동

도움말:S3 연구 메모리/아키텍처: 두 판 사이의 차이

S3 연구 메모리
S3 연구 메모리 저장소 문서 게시
 
S3 연구 메모리 저장소 문서 동기화
 
(같은 사용자의 중간 판 7개는 보이지 않습니다)
9번째 줄: 9번째 줄:
flowchart LR
flowchart LR
     H["브라우저 사용자<br/>읽기는 공개, 쓰기는 이름이 있는 계정"] -->|"https://s3wiki.yonsei.ac.kr/"| P["Caddy HTTPS 진입점"]
     H["브라우저 사용자<br/>읽기는 공개, 쓰기는 이름이 있는 계정"] -->|"https://s3wiki.yonsei.ac.kr/"| P["Caddy HTTPS 진입점"]
     C["모든 MCP 클라이언트"] -->|"https://s3wiki.yonsei.ac.kr/mcp<br/>읽기는 공개, 쓰기는 공유 Bearer 토큰"]| P
     C["모든 MCP 클라이언트"] -->|"https://s3wiki.yonsei.ac.kr/mcp<br/>읽기는 공개, 변경은 운영 모드로 인증"]| P
     P -->|"/mcp 이외의 모든 경로"| W["Semantic MediaWiki + Page Forms"]
     P -->|"/mcp 이외의 모든 경로"| W["Semantic MediaWiki + Page Forms"]
     P -->|"/mcp 및 /mcp/*"| M["중앙 mcp-http"]
     P -->|"/mcp 및 /mcp/*"| M["중앙 mcp-http"]
15번째 줄: 15번째 줄:
     W --> D[("MariaDB: 기준 페이지, 의미 속성, 리비전")]
     W --> D[("MariaDB: 기준 페이지, 의미 속성, 리비전")]
     W --> U[("업로드 볼륨")]
     W --> U[("업로드 볼륨")]
     M --> S["교체 가능한 검색 어댑터"]
     M --> S["검색 조정 계층"]
     S -->|"파일럿"| W
     S -->|"전체 텍스트·relation·최신 snapshot"| W
     S -. "추후 선택 사항" .-> V["벡터 인덱스: 파생 데이터이며 기준 저장소가 아님"]
     S -. "선택한 hybrid_v1" .-> V["프로세스 메모리 exact vector index"]
    W -. "시작 프리웜·dirty 즉시·due 백그라운드 대조" .-> V
</nowiki></pre>
</nowiki></pre>


Lesson 내용은 MediaWiki에만 저장합니다. 중앙 MCP 서비스는 데이터를 저장하지 않고 정책만 적용하는 어댑터입니다. 별도의 지식 저장소가 아닙니다. 따라서 MariaDB와 업로드 볼륨을 함께 백업하며, MCP 전송 상태는 복구 대상에 포함하지 않습니다. Caddy는 TLS 종료와 경로 전달만 맡습니다. 인증서 상태는 Docker 볼륨에 남지만 기준 Lesson 데이터에는 속하지 않습니다.
Semantic MediaWiki는 유일한 기준 저장소입니다. Lesson 내용은 MediaWiki에만 저장합니다. 중앙 MCP 서비스는 데이터를 저장하지 않고 정책만 적용하는 어댑터이며 별도의 지식 저장소가 아닙니다. 따라서 MariaDB와 업로드 볼륨을 함께 백업하며, MCP 전송 상태는 복구 대상에 포함하지 않습니다. Caddy는 TLS 종료와 경로 전달만 맡습니다. 인증서 상태는 Docker 볼륨에 남지만 기준 Lesson 데이터에는 속하지 않습니다.


<code><nowiki>S3RM_WIKI_DIRECT_URL</nowiki></code>을 명시한 서버는 같은 MediaWiki 화면을 직접 HTTP 포트로도 열 수 있습니다. 정확히 설정한 Host만 이 주소를 선택하고 정식 링크와 백그라운드 작업은 <code><nowiki>MW_SERVER_URL</nowiki></code>의 HTTPS 도메인을 계속 사용합니다. 이 입구는 위키 프런트엔드 하나를 추가하는 것이 아니라 동일 프로세스의 요청 URL 선택이며, MCP나 Lesson 저장소를 하나 더 만들지 않습니다.
<code><nowiki>S3RM_WIKI_DIRECT_URL</nowiki></code>을 명시한 서버는 같은 MediaWiki 화면을 직접 HTTP 포트로도 열 수 있습니다. 정확히 설정한 Host만 이 주소를 선택하고 정식 링크와 백그라운드 작업은 <code><nowiki>MW_SERVER_URL</nowiki></code>의 HTTPS 도메인을 계속 사용합니다. 이 입구는 위키 프런트엔드 하나를 추가하는 것이 아니라 동일 프로세스의 요청 URL 선택이며, MCP나 Lesson 저장소를 하나 더 만들지 않습니다.
31번째 줄: 32번째 줄:


# <code><nowiki>S3RM_MCP_PUBLIC_URL</nowiki></code>로 외부에 공개할 <code><nowiki>/mcp</nowiki></code> 주소를 고정하고 정확한 Host 및 Origin 허용 목록을 만듭니다.
# <code><nowiki>S3RM_MCP_PUBLIC_URL</nowiki></code>로 외부에 공개할 <code><nowiki>/mcp</nowiki></code> 주소를 고정하고 정확한 Host 및 Origin 허용 목록을 만듭니다.
# 초기화, 도구 목록 조회, 검색 도구 네 개는 인증 없이 쓸 수 있습니다. 변경 도구 세 개에는 <code><nowiki>S3RM_MCP_TOKEN_SHA256</nowiki></code>만 저장합니다. 서버는 받은 Bearer 값을 해시한 뒤 정확한 다이제스트와 상수 시간으로 비교합니다.
# 기본 <code><nowiki>token</nowiki></code> 모드에서는 초기화, 도구 목록 조회, 읽기 도구 다섯 개를 인증 없이 쓸 수 있습니다. 생성·관계·근거 추가·검증 기록과 기존 Lesson의 조건부 수정은 같은 공유 Bearer token을 확인합니다. <code><nowiki>open</nowiki></code> 모드는 다섯 변경 도구를 공개합니다. 선택 사항인 <code><nowiki>github</nowiki></code> 모드는 GitHub 로그인을 상류 인증으로 사용하고 MCP 연결 전체에 자체 OAuth token을 요구합니다.
# 공개 URL의 정확한 Host와 Origin은 자동으로 허용합니다. 명시적인 역방향 프록시 값이 더 필요하면 쉼표로 구분한 <code><nowiki>S3RM_MCP_ALLOWED_HOSTS</nowiki></code>와 <code><nowiki>S3RM_MCP_ALLOWED_ORIGINS</nowiki></code>에 추가합니다. 와일드카드는 허용하지 않습니다.
# 공개 URL의 정확한 Host와 Origin은 자동으로 허용합니다. 명시적인 역방향 프록시 값이 더 필요하면 쉼표로 구분한 <code><nowiki>S3RM_MCP_ALLOWED_HOSTS</nowiki></code>와 <code><nowiki>S3RM_MCP_ALLOWED_ORIGINS</nowiki></code>에 추가합니다. 와일드카드는 허용하지 않습니다.
# MCP 프로세스는 최소 권한 <code><nowiki>agent</nowiki></code> 계정으로 MediaWiki에 로그인합니다. 유효한 쓰기 토큰이 있어도 변경 도구 세 개와 이 계정에 부여된 위키 권한을 넘을 수 없습니다.
# MCP 프로세스는 최소 권한 <code><nowiki>agent</nowiki></code> 계정으로 MediaWiki에 로그인합니다. 유효한 토큰이 있어도 제한된 도메인 작업과 이 계정에 부여된 위키 권한을 넘을 수 없습니다. 조건부 수정은 MCP와 MediaWiki 훅만 공유하는 HMAC 키로 서명하므로 위키 계정 비밀번호만 가진 직접 Action API 요청은 revision 표식을 만들 수 없습니다.
<code><nowiki>./scripts/configure-public-domain.sh s3wiki.yonsei.ac.kr --expected-ip 165.132.118.220</nowiki></code>을 실행하면 MCP의 호스트 직접 연결 포트가 <code><nowiki>127.0.0.1</nowiki></code>에 묶이고 HTTPS가 필수가 되며 Compose <code><nowiki>domain</nowiki></code> 프로필이 켜집니다. Caddy는 80번과 443번 포트를 열어 TLS를 종료하고, 경로를 삭제하거나 바꾸지 않은 채 <code><nowiki>/mcp</nowiki></code>를 전달합니다. 토큰이 없는 클라이언트는 공개 검색을 쓸 수 있습니다. 토큰을 헤더에 넣을 수 있는 클라이언트는 제한된 변경 도구 세 개도 쓸 수 있습니다. 고정 헤더를 보낼 수 없는 호스팅 커넥터는 현재 공개 검색만 사용합니다. 추후 OAuth/OIDC 진입점을 추가하면 어댑터나 기준 저장소를 바꾸지 않고 쓰기를 지원할 수 있습니다.
<code><nowiki>./scripts/configure-public-domain.sh s3wiki.yonsei.ac.kr --expected-ip 165.132.118.220</nowiki></code>을 실행하면 MCP의 호스트 직접 연결 포트가 <code><nowiki>127.0.0.1</nowiki></code>에 묶이고 HTTPS가 필수가 되며 Compose <code><nowiki>domain</nowiki></code> 프로필이 켜집니다. Caddy는 80번과 443번 포트를 열어 TLS를 종료하고, 경로를 삭제하거나 바꾸지 않은 채 <code><nowiki>/mcp</nowiki></code>를 전달합니다. Caddy는 caller가 보낸 client IP·proxy secret 헤더를 덮어쓰고, <code><nowiki>mcp-http</nowiki></code>는 별도 서버 비밀값이 맞을 때만 전달된 IP를 <code><nowiki>open</nowiki></code> 모드 rate principal로 사용합니다. <code><nowiki>token</nowiki></code> 모드에서 토큰이 없는 클라이언트는 공개 조회를 쓸 수 있고, 공용 토큰을 헤더에 넣을 수 있는 클라이언트는 다섯 변경 도구를 쓸 수 있습니다. <code><nowiki>open</nowiki></code> 모드는 호스팅 커넥터에도 다섯 변경 도구를 공개합니다. <code><nowiki>github</nowiki></code> 모드는 같은 프로세스의 OAuth discovery, 동적 client 등록, GitHub callback, token·refresh·revoke 경로를 사용합니다. Lesson 저장소는 계속 MediaWiki 하나뿐입니다.


<code><nowiki>GET /healthz</nowiki></code>는 컨테이너 상태 확인에 쓰는 작은 비인증 경로입니다. 위키 상태나 설정값을 내보내지 않으며 에이전트 도구도 아닙니다. 현재 <code><nowiki>/mcp</nowiki></code>는 인증 없이 초기화와 도구 조회를 처리하고 <code><nowiki>search_lessons</nowiki></code>, <code><nowiki>get_lesson</nowiki></code>, <code><nowiki>find_related_lessons</nowiki></code>, <code><nowiki>find_analogies</nowiki></code>를 실행합니다. <code><nowiki>create_lesson_draft</nowiki></code>, <code><nowiki>link_lessons</nowiki></code>, <code><nowiki>attach_evidence</nowiki></code>에는 Bearer 토큰이 필요합니다.
<code><nowiki>GET /livez</nowiki></code>는 MCP 프로세스만 확인하는 작은 비인증 경로입니다. <code><nowiki>GET /healthz</nowiki></code>와 <code><nowiki>GET /readyz</nowiki></code>는 canonical MediaWiki 로그인 가능 여부와 공유 상태 파일에서 확인한 bounded job runner freshness를 모두 readiness 조건으로 사용합니다. 응답에는 bounded admission·semantic 갱신 상태도 내보내지만 설정값이나 Lesson 내용은 내보내지 않습니다. 세 경로 모두 에이전트 도구는 아닙니다. 현재 <code><nowiki>/mcp</nowiki></code>는 인증 없이 초기화와 도구 조회를 처리하고 <code><nowiki>search_lessons</nowiki></code>, <code><nowiki>get_lesson</nowiki></code>, <code><nowiki>audit_lessons</nowiki></code>, <code><nowiki>find_related_lessons</nowiki></code>, <code><nowiki>find_analogies</nowiki></code>를 실행합니다. <code><nowiki>token</nowiki></code> 모드의 <code><nowiki>create_lesson_draft</nowiki></code>, <code><nowiki>link_lessons</nowiki></code>, <code><nowiki>attach_evidence</nowiki></code>, <code><nowiki>record_evidence_verification</nowiki></code>, <code><nowiki>revise_lesson</nowiki></code>에는 같은 공용 Bearer 토큰이 필요합니다. GitHub 모드에서는 다섯 도구에 <code><nowiki>mcp:write</nowiki></code> scope가 필요합니다.


파일럿은 공유 토큰 다이제스트 한 개와 공용 MediaWiki 에이전트 계정 한 개를 사용합니다. MediaWiki에는 서비스 계정과 모든 페이지 리비전이 기록됩니다. 토큰을 사용한 컴퓨터, 모델, 사람은 구분하지 않습니다. 파일럿에서 협업하고 변경을 되돌리는 데는 이 기록으로 충분합니다. 개인별 토큰 폐기나 확인된 작성자 구분이 필요해지면 제한된 도구 권한은 그대로 두고 OAuth/OIDC 또는 별도 전송 ID를 추가할 수 있습니다. 클라이언트가 보낸 author 필드나 메모는 참고 정보일 뿐, 확인된 신원은 아닙니다.
파일럿은 공용 쓰기 토큰 다이제스트와 공용 MediaWiki 에이전트 계정 한 개를 사용합니다. MediaWiki에는 서비스 계정과 모든 페이지 리비전이 기록됩니다. 토큰을 사용한 컴퓨터, 모델, 사람은 구분하지 않습니다. 개인별 토큰 폐기나 확인된 작성자 구분이 필요해지면 제한된 도구 권한은 그대로 두고 OAuth/OIDC 또는 별도 전송 ID를 추가할 수 있습니다. 클라이언트가 보낸 author 필드나 메모는 참고 정보일 뿐, 확인된 신원은 아닙니다.


=== 기준 페이지 계약 ===
=== 기준 페이지 계약 ===
84번째 줄: 85번째 줄:
|}
|}


근거 첨부는 반복 가능한 <code><nowiki>Lesson evidence</nowiki></code> 템플릿으로 저장합니다. 필드는 <code><nowiki>id</nowiki></code>, <code><nowiki>citation</nowiki></code>, 선택 항목인 <code><nowiki>url</nowiki></code>, <code><nowiki>kind</nowiki></code>, <code><nowiki>note</nowiki></code>, <code><nowiki>added_by</nowiki></code>, <code><nowiki>added_at</nowiki></code>입니다. URL이 없어도 citation은 반드시 입력합니다. 브라우저에서 새 Lesson을 작성할 때는 구조화된 근거 한 건 이상이 필요합니다. MCP에서는 Lesson 생성과 근거 첨부를 별도의 제한된 작업으로 처리합니다.
근거 첨부는 반복 가능한 <code><nowiki>Lesson evidence</nowiki></code> 템플릿으로 저장합니다. 필드는 <code><nowiki>id</nowiki></code>, <code><nowiki>citation</nowiki></code>, 선택 항목인 <code><nowiki>url</nowiki></code>, <code><nowiki>kind</nowiki></code>, <code><nowiki>verification_basis</nowiki></code>, <code><nowiki>note</nowiki></code>, <code><nowiki>added_by</nowiki></code>, <code><nowiki>added_at</nowiki></code>입니다. <code><nowiki>verification_basis</nowiki></code>는 <code><nowiki>full_text</nowiki></code>, <code><nowiki>official_abstract</nowiki></code>, <code><nowiki>partial_source</nowiki></code>, <code><nowiki>metadata_only</nowiki></code>, <code><nowiki>not_recorded</nowiki></code> 중 하나이며, 자료에서 실제로 확인한 범위를 뜻합니다. 이는 주장의 종합 신뢰도인 <code><nowiki>confidence</nowiki></code>와 다릅니다. URL이 없어도 citation은 반드시 입력합니다. <code><nowiki>not_recorded</nowiki></code>는 이전 자료의 확인 범위가 없음을 나타내는 호환값이며 MCP의 새 evidence에는 허용하지 않습니다. 브라우저 양식에도 기본값이 없고 새 항목에서 선택할 수 없습니다. 브라우저에서 새 Lesson을 작성할 때는 구조화된 근거 한 건 이상이 필요합니다. MCP는 초기 evidence 한 건을 Lesson과 같은 revision에 원자적으로 만들고, 이후 추가 evidence만 별도 제한 작업으로 처리합니다. high confidence 생성에는 아래 검증 기록이 초기 evidence와 같은 revision에 필요합니다. 이 기록은 <code><nowiki>full_text</nowiki></code> 또는 <code><nowiki>official_abstract</nowiki></code>, <code><nowiki>supports</nowiki></code>이고 observation, interpretation, reusable_lesson을 모두 <code><nowiki>claim_fields</nowiki></code>로 연결해야 합니다.
 
근거 검증은 반복 가능한 <code><nowiki>Lesson evidence verification</nowiki></code> 템플릿으로 저장합니다. 필드는 <code><nowiki>id</nowiki></code>, <code><nowiki>evidence_id</nowiki></code>, <code><nowiki>evidence_digest</nowiki></code>, <code><nowiki>verification_basis</nowiki></code>, <code><nowiki>source_identity</nowiki></code>, <code><nowiki>source_sha256</nowiki></code>, <code><nowiki>source_locator</nowiki></code>, <code><nowiki>coverage</nowiki></code>, <code><nowiki>outcome</nowiki></code>, <code><nowiki>claim_fields</nowiki></code>, <code><nowiki>verified_by</nowiki></code>, <code><nowiki>verified_at</nowiki></code>입니다. <code><nowiki>outcome</nowiki></code>은 <code><nowiki>supports</nowiki></code>, <code><nowiki>contradicts</nowiki></code>, <code><nowiki>inconclusive</nowiki></code> 중 하나입니다. <code><nowiki>claim_fields</nowiki></code>는 <code><nowiki>question</nowiki></code>, <code><nowiki>attempt</nowiki></code>, <code><nowiki>context</nowiki></code>, <code><nowiki>observation</nowiki></code>, <code><nowiki>interpretation</nowiki></code>, <code><nowiki>reusable_lesson</nowiki></code>, <code><nowiki>applicability</nowiki></code> 중 자료가 실제로 검사한 주장만 나열합니다.
 
<code><nowiki>source_sha256</nowiki></code>은 실제로 검사한 원본 파일 바이트의 SHA-256입니다. 원본 대신 저장·정규화한 텍스트를 검사했다면 그 정확한 텍스트의 UTF-8 바이트를 해시합니다. 이 경우 변환 방법과 보존한 산출물을 <code><nowiki>source_identity</nowiki></code>와 <code><nowiki>coverage</nowiki></code>에 명시해 다시 동일한 바이트를 만들 수 있게 합니다. <code><nowiki>source_locator</nowiki></code>에는 페이지·절·라인·실험 구간 등 실제 위치를, <code><nowiki>coverage</nowiki></code>에는 읽은 범위와 제외 범위를 적습니다.
 
검증 기록은 추가 후 수정·삭제하지 않습니다. 같은 evidence를 다시 확인하면 새 기록을 append-only로 붙이고 마지막 기록을 현재 판정으로 사용합니다. <code><nowiki>verified_at</nowiki></code>은 이전 Lesson <code><nowiki>updated_at</nowiki></code>과 이전 검증 시각보다 반드시 늦고, 새 Lesson <code><nowiki>updated_at</nowiki></code>보다 늦을 수 없습니다. 시각이 같거나 거꾸로인 기록은 현재 판정을 추측하지 않고 감사 대상으로 남깁니다.
 
MediaWiki revision의 actor와 timestamp가 브라우저·raw 저장의 권위 출처입니다. 저장 훅은 새 Lesson의 <code><nowiki>author</nowiki></code>와 새 하위 항목의 <code><nowiki>added_by</nowiki></code>, <code><nowiki>created_by</nowiki></code>, <code><nowiki>verified_by</nowiki></code>가 현재 저장 계정과 정확히 같은지 확인합니다. 새 <code><nowiki>created_at</nowiki></code>, <code><nowiki>updated_at</nowiki></code>, <code><nowiki>added_at</nowiki></code>, relation의 <code><nowiki>created_at</nowiki></code>, <code><nowiki>verified_at</nowiki></code>은 revision 저장 시각 전후 5분 안이어야 합니다. 기존 <code><nowiki>created_at</nowiki></code>은 변경할 수 없고, <code><nowiki>updated_at</nowiki></code>과 새로 append하는 하위 시각은 이전 revision·기록보다 늦어야 하는 monotonic 보조 필드입니다. 양식의 검증 출처·시각 필드는 숨김 표시되며, 기존 evidence·relation·검증 기록과 그 출처는 변경할 수 없습니다. 새 Lesson은 <code><nowiki>created_at=updated_at</nowiki></code>으로 저장하고, 초기 evidence와 초기 검증의 <code><nowiki>verification_basis</nowiki></code>가 다르면 거부합니다.


관계는 반복 가능한 <code><nowiki>Lesson relation</nowiki></code> 템플릿으로 저장합니다. 필드는 <code><nowiki>from</nowiki></code>, <code><nowiki>relation</nowiki></code>, <code><nowiki>to</nowiki></code>, 선택 항목인 <code><nowiki>note</nowiki></code>, <code><nowiki>created_by</nowiki></code>, <code><nowiki>created_at</nowiki></code>입니다. 사용할 수 있는 relation 값은 다음으로 제한합니다.
관계는 반복 가능한 <code><nowiki>Lesson relation</nowiki></code> 템플릿으로 저장합니다. 필드는 <code><nowiki>from</nowiki></code>, <code><nowiki>relation</nowiki></code>, <code><nowiki>to</nowiki></code>, 선택 항목인 <code><nowiki>note</nowiki></code>, <code><nowiki>created_by</nowiki></code>, <code><nowiki>created_at</nowiki></code>입니다. 사용할 수 있는 relation 값은 다음으로 제한합니다.
106번째 줄: 115번째 줄:
* 형식이 정해진 관계로 지지, 반박, 유사점, 적용 조건, 대체 관계를 표시합니다.
* 형식이 정해진 관계로 지지, 반박, 유사점, 적용 조건, 대체 관계를 표시합니다.
* Page Forms, MCP 모델, 도메인 계층에서 같은 스키마를 검사합니다.
* Page Forms, MCP 모델, 도메인 계층에서 같은 스키마를 검사합니다.
같은 주장을 고칠 때는 기존 Lesson의 리비전을 추가합니다. 주장이 크게 달라졌다면 새 Lesson을 만들고 <code><nowiki>contradicts</nowiki></code><code><nowiki>supersedes</nowiki></code>처럼 알맞은 관계로 연결합니다. 이전 판단도 검색할 수 있도록 삭제하지 않습니다.
같은 주장이나 같은 원천 논문의 내용을 정정·보강할 때는 같은 Lesson ID에 새 리비전을 추가합니다. evidence note나 별도 <code><nowiki>Technical Review</nowiki></code> Lesson은 본문 수정 수단이 아닙니다. 독립된 주장이 기존 판단을 실제로 대신할 때만 새 Lesson을 만들고 <code><nowiki>supersedes</nowiki></code>로 연결합니다. 충돌하거나 범위를 제한하는 별도 주장은 <code><nowiki>contradicts</nowiki></code>나 다른 알맞은 관계로 연결합니다. 이전 판단은 삭제하지 않습니다.
 
다섯 쓰기 도구는 <code><nowiki>operation_id</nowiki></code>와 요청 digest를 사용합니다. 생성 시 자동 Lesson·evidence ID도 이 요청에서 결정되므로 응답이 유실돼도 같은 요청이 중복 레코드를 만들지 않습니다. 생성에는 구조화된 초기 evidence가 필수이며 Lesson과 같은 revision에 저장됩니다. 같은 Lesson 안에서 S3W 일반 쓰기, S3R revision, S3V 검증 기록은 <code><nowiki>operation_id</nowiki></code> 충돌 범위를 공유합니다. 같은 ID·종류·요청 digest의 정확한 재시도만 <code><nowiki>replayed</nowiki></code>이고, 다른 종류나 digest면 <code><nowiki>idempotency_conflict</nowiki></code>입니다. 다른 Lesson은 같은 ID를 쓰더라도 서로 간섭하지 않습니다.
 
<code><nowiki>record_evidence_verification</nowiki></code>은 현재 <code><nowiki>expected_revision_id</nowiki></code>와 대상 <code><nowiki>expected_evidence_digest</nowiki></code>가 모두 맞을 때만 새 검증 기록을 추가합니다. 검증은 evidence, Lesson 주장, relation을 다시 쓰지 않습니다.
 
<code><nowiki>revise_lesson</nowiki></code>은 현재 <code><nowiki>expected_revision_id</nowiki></code>가 맞을 때만 허용된 핵심 필드를 바꾸고, 이미 해당 Lesson에 붙은 <code><nowiki>basis_evidence_ids</nowiki></code> 1~8개를 요구합니다. ID, 출처, 최초 작성자·생성 시각, 호환 필드, relation·evidence·검증 기록 목록은 바꾸지 않습니다. MediaWiki 편집 충돌은 자동 병합하지 않습니다. 요청 digest, 모든 basis evidence, 실제 변경 필드와 본문 digest를 HMAC으로 봉인해 revision 요약에 남깁니다. 서명·작성자·부모 revision·본문 digest가 모두 맞는 이력만 replay로 인정합니다. 최대 10,000개 revision을 페이지 단위로 확인하며 같은 요청은 <code><nowiki>replayed</nowiki></code>, 같은 ID의 다른 요청은 <code><nowiki>idempotency_conflict</nowiki></code>입니다. replay 응답의 적용 revision과 현재 최신 revision은 별도 필드로 반환합니다. 최신 검증 기록 중 <code><nowiki>full_text</nowiki></code> 또는 <code><nowiki>official_abstract</nowiki></code>, <code><nowiki>supports</nowiki></code>이며 실제 변경 필드를 <code><nowiki>claim_fields</nowiki></code>로 연결한 근거만 저장 후 결과 confidence가 <code><nowiki>medium</nowiki></code> 또는 <code><nowiki>high</nowiki></code>인 observation, interpretation, reusable_lesson 수정을 뒷받침합니다. confidence를 <code><nowiki>high</nowiki></code>로 하면 세 필드 전부를 뒷받침해야 합니다. <code><nowiki>contradicts</nowiki></code>와 <code><nowiki>inconclusive</nowiki></code>는 지지 근거가 아닙니다. 결과 confidence가 <code><nowiki>low</nowiki></code>인 교정은 기존 <code><nowiki>medium</nowiki></code>/<code><nowiki>high</nowiki></code>를 낮추는 경우와 <code><nowiki>low</nowiki></code>에서 유지하는 경우 모두 <code><nowiki>not_recorded</nowiki></code>가 아닌 basis evidence로 할 수 있습니다.


<code><nowiki>reviewer</nowiki></code>와 <code><nowiki>review_state</nowiki></code>는 이전 리비전을 읽기 위한 고정 호환 필드입니다. 화면, 검색 순서, 공개 여부에는 쓰지 않습니다.
<code><nowiki>reviewer</nowiki></code>와 <code><nowiki>review_state</nowiki></code>는 이전 리비전을 읽기 위한 고정 호환 필드입니다. 화면, 검색 순서, 공개 여부에는 쓰지 않습니다.


MCP 검색 도구는 인증 없이 쓸 수 있습니다. 공유 쓰기 자격 증명이 있으면 다음 작업만 할 수 있습니다.
MCP 읽기 도구는 인증 없이 쓸 수 있습니다. 공유 쓰기 자격 증명이 있으면 다음 작업을 할 수 있습니다.


* <code><nowiki>lab</nowiki></code> Lesson 생성
* <code><nowiki>lab</nowiki></code> Lesson 생성
* 기존 Lesson에 검증된 관계 한 건 추가
* 기존 Lesson에 검증된 관계 한 건 추가
* 기존 Lesson에 인용이 있는 근거 한 건 추가
* 기존 Lesson에 인용이 있는 근거 한 건 추가
* 기존 evidence에 append-only 검증 기록 한 건 추가
* 기준 revision과 기존 근거를 전제로 같은 Lesson의 허용된 핵심 필드를 새 revision으로 수정
서버 관리 필드는 클라이언트가 선택하거나 바꿀 수 없습니다. 페이지 삭제, 이동, 보호, 관리도 허용하지 않습니다. <code><nowiki>supersedes</nowiki></code> 관계를 추가해도 대상 Lesson은 변경되지 않습니다. 클라이언트가 예상하지 않은 필드를 보내면 도메인 계층이 거부하고, MediaWiki 계정에도 관리 권한을 부여하지 않습니다.
서버 관리 필드는 클라이언트가 선택하거나 바꿀 수 없습니다. 페이지 삭제, 이동, 보호, 관리도 허용하지 않습니다. <code><nowiki>supersedes</nowiki></code> 관계를 추가해도 대상 Lesson은 변경되지 않습니다. 클라이언트가 예상하지 않은 필드를 보내면 도메인 계층이 거부하고, MediaWiki 계정에도 관리 권한을 부여하지 않습니다.


Lesson이 바뀔 때마다 <code><nowiki>updated_at</nowiki></code>은 이전 값보다 반드시 늦어야 합니다. 에이전트가 근거나 관계를 추가하면 새 항목의 출처 시각에도 같은 값을 사용합니다.
Lesson이 바뀔 때마다 <code><nowiki>updated_at</nowiki></code>은 이전 값보다 반드시 늦어야 합니다. 에이전트가 근거, 관계나 검증 기록을 추가하면 새 항목의 출처 시각에도 같은 값을 사용합니다. 생성·관계·근거·검증 추가 응답도 적용 뒤 현재 revision ID를 반환하므로 다음 조건부 수정을 이어 갈 수 있습니다.


=== 검색 구현 ===
=== 검색 구현 ===


파일럿은 MediaWiki 전체 텍스트 검색 결과를 어댑터에서 구조화 필터로 거릅니다. Action API에는 <code><nowiki>srwhat=text</nowiki></code>를 명시합니다. 이를 생략하면 MediaWiki의 레거시 기본값인 페이지명 검색이 적용되어, <code><nowiki>Lesson:&lt;slug&gt;</nowiki></code> 본문에 저장된 한국어 필드를 찾지 못합니다. MySQL 검색 엔진은 색인과 질의에 같은 UTF-8 정규화 및 짧은 단어 길이 보정을 적용하므로 MariaDB의 최소 단어 길이는 기본값을 사용합니다. 유사점 검색에는 Unicode NFKC 정규화, Unicode 단어, 한국어 2글자 및 3글자 n-gram을 사용합니다. MediaWiki 리비전 내용은 한 번에 최대 50페이지씩 가져옵니다. 초기 데이터 규모에서 운영하기에 충분하며 구성이 단순합니다. MCP 서비스는 작은 검색 인터페이스만 외부에 제공합니다. 나중에 벡터 또는 하이브리드 검색을 붙여도 후보 페이지 ID만 반환하게 하여 별도 기준 저장소가 생기지 않도록 합니다. 파생 인덱스는 위키 리비전만으로 다시 만들 수 있어야 합니다. 필드 및 출처 필터와 이전 버전 호환 <code><nowiki>review_state</nowiki></code> 필터를 보존하고 현재 응답 제한도 그대로 지켜야 합니다.
기본 <code><nowiki>retrieval_mode=legacy</nowiki></code>는 MediaWiki 전체 텍스트 검색 결과를 어댑터에서 구조화 필터로 거릅니다. Action API에는 <code><nowiki>srwhat=text</nowiki></code>를 명시합니다. 이를 생략하면 MediaWiki의 레거시 기본값인 페이지명 검색이 적용되어, <code><nowiki>Lesson:&lt;slug&gt;</nowiki></code> 본문에 저장된 한국어 필드를 찾지 못합니다. MySQL 검색 엔진은 색인과 질의에 같은 UTF-8 정규화 및 짧은 단어 길이 보정을 적용하므로 MariaDB의 최소 단어 길이는 기본값을 사용합니다. 유사점 검색에는 Unicode NFKC 정규화, Unicode 단어, 한국어 2글자 및 3글자 n-gram을 사용합니다. MediaWiki 리비전 내용은 한 번에 최대 50페이지씩 가져옵니다. 초기 데이터 규모에서 운영하기에 충분하며 구성이 단순합니다.
 
의미 검색을 켠 서버에서 호출자가 <code><nowiki>retrieval_mode=hybrid_v1</nowiki></code>을 명시하면 세 가지 facet을 사용합니다. <code><nowiki>situation</nowiki></code>은 질문·조건·관찰, <code><nowiki>approach</nowiki></code>는 시도, <code><nowiki>mechanism</nowiki></code>은 해석·재사용 가능한 결론·적용 조건으로 구성합니다. 전체 텍스트, 저장된 <code><nowiki>analogous_to</nowiki></code>, 의미 후보는 versioned RRF 규칙으로 결합합니다. <code><nowiki>find_analogies</nowiki></code>의 결합 점수는 raw cosine이 아니며 의미 후보를 relation으로 저장하지 않습니다.
 
<code><nowiki>S3RM_SEARCH_SCOPES_JSON</nowiki></code>으로 서버 담당자가 이름 있는 검색 scope를 최대 16개까지 설정할 수 있습니다. scope는 기존 <code><nowiki>SearchFilters</nowiki></code>의 bounded 묶음이며 호출자의 필터와 교집합으로 적용됩니다. Lesson 필드나 별도 저장소가 아니고, 현재는 relevance 범위만 정하며 접근 제어를 대신하지 않습니다. 응답과 cursor fingerprint에는 실제 scope가 포함됩니다.
 
<code><nowiki>S3RM_SEARCH_SCOPE_PRINCIPALS_JSON</nowiki></code>을 함께 설정하면 scope 사용 자체에 optional principal ACL을 적용합니다. GitHub subject 또는 토큰의 짧은 hash 식별자만 저장하며 원문 자격 증명은 저장하지 않습니다. ACL이 설정된 scope는 익명 호출을 거부하고 <code><nowiki>search_lessons</nowiki></code>·<code><nowiki>find_analogies</nowiki></code>에서 먼저 확인합니다.
 
외부 자료 연결은 <code><nowiki>mcp/src/s3_research_memory_mcp/connectors.py</nowiki></code>의 읽기 전용 candidate connector와 <code><nowiki>scripts/collect-candidates.py</nowiki></code>로 수행합니다. code/docs 파일과 Slack export를 bounded 후보로 만들 뿐 Lesson이나 evidence를 자동 저장하지 않습니다. <code><nowiki>scripts/research-query.py</nowiki></code>는 세 facet을 병렬 검색하고 RRF로 결합하며, 선택적으로 OpenAI-compatible reranker와 인용 제한 synthesis를 실행합니다.
 
파생 exact index는 <code><nowiki>mcp-http</nowiki></code> 프로세스 메모리에만 있습니다. HTTP transport와 <code><nowiki>EmbeddingProfile</nowiki></code>을 분리하며, profile은 모델 artifact digest, 차원, pooling, query/document 형식, facet별 instruction과 입력 상한을 고정합니다. 이 profile과 projection 구성을 함께 해시해 generation을 만듭니다. 외부 Streamable HTTP 요청은 stateless로 처리하지만 위키 어댑터와 exact index는 ASGI 프로세스 lifespan에서 한 번만 만들고 모든 요청이 공유합니다. 서비스 시작 시 단 하나의 백그라운드 작업이 MediaWiki snapshot 전체를 읽어 인덱스를 프리웜합니다. MCP로 새 Lesson을 저장하면 dirty hint가 즉시 같은 단일 갱신을 시작합니다. 대조 주기가 지나면 다음 hybrid 요청이 갱신을 시작합니다. 대조한 Lesson 수와 projection hash가 모두 같으면 재임베딩 없이 대조 시각만 갱신합니다. 어느 경우에도 공개 요청은 전체 재구축을 기다리지 않습니다. 사용할 snapshot이 있으면 갱신 중에도 그 snapshot을 사용하고 <code><nowiki>stale</nowiki></code> 또는 <code><nowiki>degraded</nowiki></code> 상태를 표시합니다. 아직 snapshot이 없으면 <code><nowiki>building</nowiki></code> 또는 <code><nowiki>unavailable</nowiki></code> 상태로 즉시 legacy 검색을 반환합니다. 새 인덱스는 옆에서 완성한 뒤 원자적으로 교체합니다.
 
후보는 <code><nowiki>Lesson:</nowiki></code> ID와 retrieval hash만 제공하며, 최종 필터와 응답 본문은 최신 MediaWiki snapshot으로 다시 확인합니다. hash가 달라진 의미 기여는 제거하고 <code><nowiki>semantic_status=stale</nowiki></code>로 표시합니다. 제공자 장애나 과부하 시 첫 페이지는 legacy로 대체하며, 이미 발급한 hybrid cursor의 순서를 중간에 바꾸지 않습니다.
 
MCP 서비스는 같은 열 도구만 외부에 제공합니다. exact index의 재구축 시간, 메모리 또는 지연 시간이 실제 목표를 넘을 때만 같은 비공개 backend 경계를 내부 sidecar나 공유 검색 서비스로 교체합니다. 필드 및 출처 필터와 이전 버전 호환 <code><nowiki>review_state</nowiki></code> 필터, 현재 응답 제한은 계속 보존합니다. 내부 경계, 색인 세대, 장애 fallback과 단계별 확장안은 [[Help:S3 연구 메모리/벡터·하이브리드 검색 설계|벡터·하이브리드 검색 설계]]에 정리합니다.
 
현재 서버의 선택적 <code><nowiki>embedding</nowiki></code> Compose profile은 고정된 Qwen3 F16 GGUF를 <code><nowiki>llama.cpp</nowiki></code> Vulkan으로 실행합니다. 모델 downloader만 별도 egress 네트워크를 쓰고, 추론 서비스는 호스트 포트 없이 <code><nowiki>mcp-http</nowiki></code>와 공유하는 전용 내부 <code><nowiki>embedding-backend</nowiki></code>에만 연결합니다. 이 서비스와 <code><nowiki>embedding_models</nowiki></code> 볼륨은 파생 계산 계층이며 Semantic MediaWiki를 대신하지 않습니다.


=== 출력 제한 ===
=== 출력 제한 ===


모든 MCP 목록 작업에는 작은 기본 페이지 크기, 엄격한 최댓값, 커서 기반 페이지 나누기, 필드 선택, UTF-8 직렬화 응답의 바이트 상한이 있습니다. 스캔 응답에는 <code><nowiki>partial</nowiki></code>, <code><nowiki>scanned</nowiki></code>, <code><nowiki>scan_limit</nowiki></code>가 들어갑니다. <code><nowiki>partial=true</nowiki></code>이면 검색 결과가 완전하지 않은 것으로 처리해야 합니다. 기본 결과에는 ID, 제목, 한 줄짜리 <code><nowiki>reusable_lesson</nowiki></code>, 출처, confidence, 짧은 근거 인용이 들어갑니다. <code><nowiki>get_lesson</nowiki></code>도 같은 간단한 기본값을 쓰며 인용과 관계를 각각 나누어 반환합니다. 본문 필드는 필요할 때만 직접 선택합니다.
모든 MCP 목록 작업에는 작은 기본 페이지 크기, 엄격한 최댓값, 커서 기반 페이지 나누기, 필드 선택, UTF-8 직렬화 응답의 바이트 상한이 있습니다. 스캔 응답에는 <code><nowiki>partial</nowiki></code>, <code><nowiki>scanned</nowiki></code>, <code><nowiki>scan_limit</nowiki></code>가 들어갑니다. <code><nowiki>partial=true</nowiki></code>이면 검색 결과가 완전하지 않은 것으로 처리해야 합니다. 기본 결과에는 ID, 제목, 한 줄짜리 <code><nowiki>reusable_lesson</nowiki></code>, 출처, confidence, 짧은 근거 인용이 들어갑니다. <code><nowiki>get_lesson</nowiki></code>은 현재 revision ID를 포함하고 인용, 검증 기록과 관계를 각각 나누어 반환합니다. 각 하위 목록은 기본 5개, 최대 20개의 독립 cursor 페이지입니다. 기본 <code><nowiki>citation_detail=compact</nowiki></code>는 짧은 인용만, <code><nowiki>full</nowiki></code>은 note와 추가한 주체·시각까지 반환합니다. full 인용은 한 항목도 조용히 자르지 않고 페이지 크기를 줄이며, 한 항목도 출력 한도에 맞지 않으면 명시적인 오류를 반환합니다. 본문 필드는 필요할 때만 직접 선택합니다.
 
<code><nowiki>audit_lessons</nowiki></code>는 정확한 ID 기대값을 최대 50개 읽고 결과를 최대 20개씩 반환합니다. revision, 내용 digest, 최대 8개 evidence와 최대 8개 relation을 확인하며, 페이지 사이 어느 대상 revision이라도 바뀌면 이전 cursor를 거부합니다. 긴 본문을 반복 반환하지 않고 <code><nowiki>ok</nowiki></code>, <code><nowiki>missing</nowiki></code>, <code><nowiki>mismatch</nowiki></code>와 실패한 기대값 위치만 반환합니다.
 
열 도구는 모두 닫힌 <code><nowiki>outputSchema</nowiki></code>를 공개합니다. 성공 응답의 <code><nowiki>structuredContent</nowiki></code>는 이 스키마로 검증하며, 기존 클라이언트가 읽는 compact JSON text도 같은 내용으로 유지합니다.

2026년 7월 20일 (월) 14:08 기준 최신판

이 페이지는 저장소 문서에서 자동으로 동기화됩니다. 위키에서 직접 편집하지 마세요.

용도와 범위

S3 연구 메모리는 프로젝트가 끝난 뒤에도 다시 쓸 수 있는 Lesson을 저장합니다. 작업 추적기, 실행 로그, 산출물 저장소, 원시 지표 데이터베이스로 쓰지 않습니다. 원본 근거는 알맞은 저장소나 객체 스토리지에 두고, Lesson에는 주장과 적용 조건, 해석, 원본을 다시 찾는 데 필요한 인용을 기록합니다.

flowchart LR
    H["브라우저 사용자<br/>읽기는 공개, 쓰기는 이름이 있는 계정"] -->|"https://s3wiki.yonsei.ac.kr/"| P["Caddy HTTPS 진입점"]
    C["모든 MCP 클라이언트"] -->|"https://s3wiki.yonsei.ac.kr/mcp<br/>읽기는 공개, 변경은 운영 모드로 인증"]| P
    P -->|"/mcp 이외의 모든 경로"| W["Semantic MediaWiki + Page Forms"]
    P -->|"/mcp 및 /mcp/*"| M["중앙 mcp-http"]
    M -->|"인증된 제한형 MediaWiki API 어댑터"| W
    W --> D[("MariaDB: 기준 페이지, 의미 속성, 리비전")]
    W --> U[("업로드 볼륨")]
    M --> S["검색 조정 계층"]
    S -->|"전체 텍스트·relation·최신 snapshot"| W
    S -. "선택한 hybrid_v1" .-> V["프로세스 메모리 exact vector index"]
    W -. "시작 프리웜·dirty 즉시·due 백그라운드 대조" .-> V

Semantic MediaWiki는 유일한 기준 저장소입니다. Lesson 내용은 MediaWiki에만 저장합니다. 중앙 MCP 서비스는 데이터를 저장하지 않고 정책만 적용하는 어댑터이며 별도의 지식 저장소가 아닙니다. 따라서 MariaDB와 업로드 볼륨을 함께 백업하며, MCP 전송 상태는 복구 대상에 포함하지 않습니다. Caddy는 TLS 종료와 경로 전달만 맡습니다. 인증서 상태는 Docker 볼륨에 남지만 기준 Lesson 데이터에는 속하지 않습니다.

S3RM_WIKI_DIRECT_URL을 명시한 서버는 같은 MediaWiki 화면을 직접 HTTP 포트로도 열 수 있습니다. 정확히 설정한 Host만 이 주소를 선택하고 정식 링크와 백그라운드 작업은 MW_SERVER_URL의 HTTPS 도메인을 계속 사용합니다. 이 입구는 위키 프런트엔드 하나를 추가하는 것이 아니라 동일 프로세스의 요청 URL 선택이며, MCP나 Lesson 저장소를 하나 더 만들지 않습니다.

연구실 공용 MCP 연결

에이전트는 장기 실행 서비스 mcp-http 한 개와 Streamable HTTP 엔드포인트 https://s3wiki.yonsei.ac.kr/mcp 한 곳만 사용합니다. 각 컴퓨터에서 별도 어댑터나 지식 사본을 실행하지 않습니다. 모든 클라이언트는 같은 어댑터를 거쳐 내부 http://mediawiki/api.php에 접근합니다. 한 클라이언트가 저장한 Lesson은 복제나 내보내기 없이 다음 검색부터 다른 클라이언트에도 보입니다. 동시에 저장할 때는 MediaWiki 리비전과 편집 충돌 검사가 쓰기 순서를 정합니다.

원격 연결에는 다음 네 가지 제한을 둡니다.

  1. S3RM_MCP_PUBLIC_URL로 외부에 공개할 /mcp 주소를 고정하고 정확한 Host 및 Origin 허용 목록을 만듭니다.
  2. 기본 token 모드에서는 초기화, 도구 목록 조회, 읽기 도구 다섯 개를 인증 없이 쓸 수 있습니다. 생성·관계·근거 추가·검증 기록과 기존 Lesson의 조건부 수정은 같은 공유 Bearer token을 확인합니다. open 모드는 다섯 변경 도구를 공개합니다. 선택 사항인 github 모드는 GitHub 로그인을 상류 인증으로 사용하고 MCP 연결 전체에 자체 OAuth token을 요구합니다.
  3. 공개 URL의 정확한 Host와 Origin은 자동으로 허용합니다. 명시적인 역방향 프록시 값이 더 필요하면 쉼표로 구분한 S3RM_MCP_ALLOWED_HOSTSS3RM_MCP_ALLOWED_ORIGINS에 추가합니다. 와일드카드는 허용하지 않습니다.
  4. MCP 프로세스는 최소 권한 agent 계정으로 MediaWiki에 로그인합니다. 유효한 토큰이 있어도 제한된 도메인 작업과 이 계정에 부여된 위키 권한을 넘을 수 없습니다. 조건부 수정은 MCP와 MediaWiki 훅만 공유하는 HMAC 키로 서명하므로 위키 계정 비밀번호만 가진 직접 Action API 요청은 revision 표식을 만들 수 없습니다.

./scripts/configure-public-domain.sh s3wiki.yonsei.ac.kr --expected-ip 165.132.118.220을 실행하면 MCP의 호스트 직접 연결 포트가 127.0.0.1에 묶이고 HTTPS가 필수가 되며 Compose domain 프로필이 켜집니다. Caddy는 80번과 443번 포트를 열어 TLS를 종료하고, 경로를 삭제하거나 바꾸지 않은 채 /mcp를 전달합니다. Caddy는 caller가 보낸 client IP·proxy secret 헤더를 덮어쓰고, mcp-http는 별도 서버 비밀값이 맞을 때만 전달된 IP를 open 모드 rate principal로 사용합니다. token 모드에서 토큰이 없는 클라이언트는 공개 조회를 쓸 수 있고, 공용 토큰을 헤더에 넣을 수 있는 클라이언트는 다섯 변경 도구를 쓸 수 있습니다. open 모드는 호스팅 커넥터에도 다섯 변경 도구를 공개합니다. github 모드는 같은 프로세스의 OAuth discovery, 동적 client 등록, GitHub callback, token·refresh·revoke 경로를 사용합니다. Lesson 저장소는 계속 MediaWiki 하나뿐입니다.

GET /livez는 MCP 프로세스만 확인하는 작은 비인증 경로입니다. GET /healthzGET /readyz는 canonical MediaWiki 로그인 가능 여부와 공유 상태 파일에서 확인한 bounded job runner freshness를 모두 readiness 조건으로 사용합니다. 응답에는 bounded admission·semantic 갱신 상태도 내보내지만 설정값이나 Lesson 내용은 내보내지 않습니다. 세 경로 모두 에이전트 도구는 아닙니다. 현재 /mcp는 인증 없이 초기화와 도구 조회를 처리하고 search_lessons, get_lesson, audit_lessons, find_related_lessons, find_analogies를 실행합니다. token 모드의 create_lesson_draft, link_lessons, attach_evidence, record_evidence_verification, revise_lesson에는 같은 공용 Bearer 토큰이 필요합니다. GitHub 모드에서는 다섯 도구에 mcp:write scope가 필요합니다.

파일럿은 공용 쓰기 토큰 다이제스트와 공용 MediaWiki 에이전트 계정 한 개를 사용합니다. MediaWiki에는 서비스 계정과 모든 페이지 리비전이 기록됩니다. 토큰을 사용한 컴퓨터, 모델, 사람은 구분하지 않습니다. 개인별 토큰 폐기나 확인된 작성자 구분이 필요해지면 제한된 도구 권한은 그대로 두고 OAuth/OIDC 또는 별도 전송 ID를 추가할 수 있습니다. 클라이언트가 보낸 author 필드나 메모는 참고 정보일 뿐, 확인된 신원은 아닙니다.

기준 페이지 계약

브라우저 화면은 한국어로 표시합니다. 기준 식별자, 템플릿 매개변수 이름, 속성 이름, 이전 버전 호환 상태값, 근거 종류, 관계 토큰, Lesson: 이름공간은 영문 기계 계약을 그대로 씁니다. 한국어 Page Forms 라벨과 매핑 템플릿은 화면에 보일 때만 이 값을 번역합니다. MediaWiki의 기본 이름공간과 화면 문구가 한국어로 바뀌어도 기존 MCP 클라이언트와 저장된 리비전은 그대로 작동합니다.

각 Lesson은 Lesson: 이름공간의 페이지 한 건입니다. 전체 페이지 제목을 고정 식별자로 씁니다. 예를 들면 Lesson:gpu_allocator_fragmentation입니다. 화면에 보이는 제목은 식별자를 바꾸지 않고 수정할 수 있습니다.

페이지에는 다음 매개변수를 정확히 사용하는 Lesson 템플릿 한 개가 들어갑니다.

필드
title 화면에 표시할 Lesson 제목
question 작업을 시작하게 된 질문
attempt 시도한 방법
context 하드웨어, 워크로드, 버전, 규모, 제약 조건
observation 직접 관찰한 내용
interpretation 관찰한 결과가 나온 이유에 대한 해석
reusable_lesson 짧게 정리한 재사용 가능한 내용
applicability 다시 적용할 수 있는 조건과 범위
confidence low, medium, high 중 하나
evidence 사람이 읽을 수 있는 근거 개요
record_origin 바꿀 수 없는 출처 분류인 lab, imported, synthetic 중 하나
author 처음 작성한 사람 또는 계정
reviewer 이전 버전 호환 필드. 일반 작성에서는 비워 둠
review_state 이전 버전 호환 필드. 값은 Draft, Reviewed, Stable, Superseded이며 일반 작성에서는 Draft를 사용
created_at UTC ISO-8601 생성 시각
updated_at UTC ISO-8601 마지막 수정 시각

근거 첨부는 반복 가능한 Lesson evidence 템플릿으로 저장합니다. 필드는 id, citation, 선택 항목인 url, kind, verification_basis, note, added_by, added_at입니다. verification_basisfull_text, official_abstract, partial_source, metadata_only, not_recorded 중 하나이며, 자료에서 실제로 확인한 범위를 뜻합니다. 이는 주장의 종합 신뢰도인 confidence와 다릅니다. URL이 없어도 citation은 반드시 입력합니다. not_recorded는 이전 자료의 확인 범위가 없음을 나타내는 호환값이며 MCP의 새 evidence에는 허용하지 않습니다. 브라우저 양식에도 기본값이 없고 새 항목에서 선택할 수 없습니다. 브라우저에서 새 Lesson을 작성할 때는 구조화된 근거 한 건 이상이 필요합니다. MCP는 초기 evidence 한 건을 Lesson과 같은 revision에 원자적으로 만들고, 이후 추가 evidence만 별도 제한 작업으로 처리합니다. high confidence 생성에는 아래 검증 기록이 초기 evidence와 같은 revision에 필요합니다. 이 기록은 full_text 또는 official_abstract, supports이고 observation, interpretation, reusable_lesson을 모두 claim_fields로 연결해야 합니다.

근거 검증은 반복 가능한 Lesson evidence verification 템플릿으로 저장합니다. 필드는 id, evidence_id, evidence_digest, verification_basis, source_identity, source_sha256, source_locator, coverage, outcome, claim_fields, verified_by, verified_at입니다. outcomesupports, contradicts, inconclusive 중 하나입니다. claim_fieldsquestion, attempt, context, observation, interpretation, reusable_lesson, applicability 중 자료가 실제로 검사한 주장만 나열합니다.

source_sha256은 실제로 검사한 원본 파일 바이트의 SHA-256입니다. 원본 대신 저장·정규화한 텍스트를 검사했다면 그 정확한 텍스트의 UTF-8 바이트를 해시합니다. 이 경우 변환 방법과 보존한 산출물을 source_identitycoverage에 명시해 다시 동일한 바이트를 만들 수 있게 합니다. source_locator에는 페이지·절·라인·실험 구간 등 실제 위치를, coverage에는 읽은 범위와 제외 범위를 적습니다.

검증 기록은 추가 후 수정·삭제하지 않습니다. 같은 evidence를 다시 확인하면 새 기록을 append-only로 붙이고 마지막 기록을 현재 판정으로 사용합니다. verified_at은 이전 Lesson updated_at과 이전 검증 시각보다 반드시 늦고, 새 Lesson updated_at보다 늦을 수 없습니다. 시각이 같거나 거꾸로인 기록은 현재 판정을 추측하지 않고 감사 대상으로 남깁니다.

MediaWiki revision의 actor와 timestamp가 브라우저·raw 저장의 권위 출처입니다. 저장 훅은 새 Lesson의 author와 새 하위 항목의 added_by, created_by, verified_by가 현재 저장 계정과 정확히 같은지 확인합니다. 새 created_at, updated_at, added_at, relation의 created_at, verified_at은 revision 저장 시각 전후 5분 안이어야 합니다. 기존 created_at은 변경할 수 없고, updated_at과 새로 append하는 하위 시각은 이전 revision·기록보다 늦어야 하는 monotonic 보조 필드입니다. 양식의 검증 출처·시각 필드는 숨김 표시되며, 기존 evidence·relation·검증 기록과 그 출처는 변경할 수 없습니다. 새 Lesson은 created_at=updated_at으로 저장하고, 초기 evidence와 초기 검증의 verification_basis가 다르면 거부합니다.

관계는 반복 가능한 Lesson relation 템플릿으로 저장합니다. 필드는 from, relation, to, 선택 항목인 note, created_by, created_at입니다. 사용할 수 있는 relation 값은 다음으로 제한합니다.

  • supports
  • contradicts
  • analogous_to
  • failed_because
  • works_when
  • suggests
  • supersedes

관계에는 방향이 있습니다. 조회할 때 analogous_to는 대칭 관계처럼 보여 주지만, 출처를 남기기 위해 저장된 양쪽 끝점은 명시적으로 유지합니다. 같은 간선을 중복해서 저장하면 MCP 도메인 계층이 거부합니다. 브라우저와 에이전트 모두 이미 존재하는 Lesson만 관계 대상으로 지정할 수 있습니다.

저장과 수정

저장에 성공한 Lesson은 기준 위키에 바로 반영되며 브라우저와 MCP에서 즉시 검색할 수 있습니다. 수정 이력과 근거는 다음과 같이 관리합니다.

  • MediaWiki 리비전으로 모든 변경을 남기고 되돌릴 수 있습니다.
  • 구조화된 근거로 주장의 출처를 확인할 수 있습니다.
  • confidence에 현재 근거의 수준을 표시합니다.
  • 형식이 정해진 관계로 지지, 반박, 유사점, 적용 조건, 대체 관계를 표시합니다.
  • Page Forms, MCP 모델, 도메인 계층에서 같은 스키마를 검사합니다.

같은 주장이나 같은 원천 논문의 내용을 정정·보강할 때는 같은 Lesson ID에 새 리비전을 추가합니다. evidence note나 별도 Technical Review Lesson은 본문 수정 수단이 아닙니다. 독립된 주장이 기존 판단을 실제로 대신할 때만 새 Lesson을 만들고 supersedes로 연결합니다. 충돌하거나 범위를 제한하는 별도 주장은 contradicts나 다른 알맞은 관계로 연결합니다. 이전 판단은 삭제하지 않습니다.

다섯 쓰기 도구는 operation_id와 요청 digest를 사용합니다. 생성 시 자동 Lesson·evidence ID도 이 요청에서 결정되므로 응답이 유실돼도 같은 요청이 중복 레코드를 만들지 않습니다. 생성에는 구조화된 초기 evidence가 필수이며 Lesson과 같은 revision에 저장됩니다. 같은 Lesson 안에서 S3W 일반 쓰기, S3R revision, S3V 검증 기록은 operation_id 충돌 범위를 공유합니다. 같은 ID·종류·요청 digest의 정확한 재시도만 replayed이고, 다른 종류나 digest면 idempotency_conflict입니다. 다른 Lesson은 같은 ID를 쓰더라도 서로 간섭하지 않습니다.

record_evidence_verification은 현재 expected_revision_id와 대상 expected_evidence_digest가 모두 맞을 때만 새 검증 기록을 추가합니다. 검증은 evidence, Lesson 주장, relation을 다시 쓰지 않습니다.

revise_lesson은 현재 expected_revision_id가 맞을 때만 허용된 핵심 필드를 바꾸고, 이미 해당 Lesson에 붙은 basis_evidence_ids 1~8개를 요구합니다. ID, 출처, 최초 작성자·생성 시각, 호환 필드, relation·evidence·검증 기록 목록은 바꾸지 않습니다. MediaWiki 편집 충돌은 자동 병합하지 않습니다. 요청 digest, 모든 basis evidence, 실제 변경 필드와 본문 digest를 HMAC으로 봉인해 revision 요약에 남깁니다. 서명·작성자·부모 revision·본문 digest가 모두 맞는 이력만 replay로 인정합니다. 최대 10,000개 revision을 페이지 단위로 확인하며 같은 요청은 replayed, 같은 ID의 다른 요청은 idempotency_conflict입니다. replay 응답의 적용 revision과 현재 최신 revision은 별도 필드로 반환합니다. 최신 검증 기록 중 full_text 또는 official_abstract, supports이며 실제 변경 필드를 claim_fields로 연결한 근거만 저장 후 결과 confidence가 medium 또는 high인 observation, interpretation, reusable_lesson 수정을 뒷받침합니다. confidence를 high로 하면 세 필드 전부를 뒷받침해야 합니다. contradictsinconclusive는 지지 근거가 아닙니다. 결과 confidence가 low인 교정은 기존 medium/high를 낮추는 경우와 low에서 유지하는 경우 모두 not_recorded가 아닌 basis evidence로 할 수 있습니다.

reviewerreview_state는 이전 리비전을 읽기 위한 고정 호환 필드입니다. 화면, 검색 순서, 공개 여부에는 쓰지 않습니다.

MCP 읽기 도구는 인증 없이 쓸 수 있습니다. 공유 쓰기 자격 증명이 있으면 다음 작업을 할 수 있습니다.

  • lab Lesson 생성
  • 기존 Lesson에 검증된 관계 한 건 추가
  • 기존 Lesson에 인용이 있는 근거 한 건 추가
  • 기존 evidence에 append-only 검증 기록 한 건 추가
  • 기준 revision과 기존 근거를 전제로 같은 Lesson의 허용된 핵심 필드를 새 revision으로 수정

서버 관리 필드는 클라이언트가 선택하거나 바꿀 수 없습니다. 페이지 삭제, 이동, 보호, 관리도 허용하지 않습니다. supersedes 관계를 추가해도 대상 Lesson은 변경되지 않습니다. 클라이언트가 예상하지 않은 필드를 보내면 도메인 계층이 거부하고, MediaWiki 계정에도 관리 권한을 부여하지 않습니다.

Lesson이 바뀔 때마다 updated_at은 이전 값보다 반드시 늦어야 합니다. 에이전트가 근거, 관계나 검증 기록을 추가하면 새 항목의 출처 시각에도 같은 값을 사용합니다. 생성·관계·근거·검증 추가 응답도 적용 뒤 현재 revision ID를 반환하므로 다음 조건부 수정을 이어 갈 수 있습니다.

검색 구현

기본 retrieval_mode=legacy는 MediaWiki 전체 텍스트 검색 결과를 어댑터에서 구조화 필터로 거릅니다. Action API에는 srwhat=text를 명시합니다. 이를 생략하면 MediaWiki의 레거시 기본값인 페이지명 검색이 적용되어, Lesson:<slug> 본문에 저장된 한국어 필드를 찾지 못합니다. MySQL 검색 엔진은 색인과 질의에 같은 UTF-8 정규화 및 짧은 단어 길이 보정을 적용하므로 MariaDB의 최소 단어 길이는 기본값을 사용합니다. 유사점 검색에는 Unicode NFKC 정규화, Unicode 단어, 한국어 2글자 및 3글자 n-gram을 사용합니다. MediaWiki 리비전 내용은 한 번에 최대 50페이지씩 가져옵니다. 초기 데이터 규모에서 운영하기에 충분하며 구성이 단순합니다.

의미 검색을 켠 서버에서 호출자가 retrieval_mode=hybrid_v1을 명시하면 세 가지 facet을 사용합니다. situation은 질문·조건·관찰, approach는 시도, mechanism은 해석·재사용 가능한 결론·적용 조건으로 구성합니다. 전체 텍스트, 저장된 analogous_to, 의미 후보는 versioned RRF 규칙으로 결합합니다. find_analogies의 결합 점수는 raw cosine이 아니며 의미 후보를 relation으로 저장하지 않습니다.

S3RM_SEARCH_SCOPES_JSON으로 서버 담당자가 이름 있는 검색 scope를 최대 16개까지 설정할 수 있습니다. scope는 기존 SearchFilters의 bounded 묶음이며 호출자의 필터와 교집합으로 적용됩니다. Lesson 필드나 별도 저장소가 아니고, 현재는 relevance 범위만 정하며 접근 제어를 대신하지 않습니다. 응답과 cursor fingerprint에는 실제 scope가 포함됩니다.

S3RM_SEARCH_SCOPE_PRINCIPALS_JSON을 함께 설정하면 scope 사용 자체에 optional principal ACL을 적용합니다. GitHub subject 또는 토큰의 짧은 hash 식별자만 저장하며 원문 자격 증명은 저장하지 않습니다. ACL이 설정된 scope는 익명 호출을 거부하고 search_lessons·find_analogies에서 먼저 확인합니다.

외부 자료 연결은 mcp/src/s3_research_memory_mcp/connectors.py의 읽기 전용 candidate connector와 scripts/collect-candidates.py로 수행합니다. code/docs 파일과 Slack export를 bounded 후보로 만들 뿐 Lesson이나 evidence를 자동 저장하지 않습니다. scripts/research-query.py는 세 facet을 병렬 검색하고 RRF로 결합하며, 선택적으로 OpenAI-compatible reranker와 인용 제한 synthesis를 실행합니다.

파생 exact index는 mcp-http 프로세스 메모리에만 있습니다. HTTP transport와 EmbeddingProfile을 분리하며, profile은 모델 artifact digest, 차원, pooling, query/document 형식, facet별 instruction과 입력 상한을 고정합니다. 이 profile과 projection 구성을 함께 해시해 generation을 만듭니다. 외부 Streamable HTTP 요청은 stateless로 처리하지만 위키 어댑터와 exact index는 ASGI 프로세스 lifespan에서 한 번만 만들고 모든 요청이 공유합니다. 서비스 시작 시 단 하나의 백그라운드 작업이 MediaWiki snapshot 전체를 읽어 인덱스를 프리웜합니다. MCP로 새 Lesson을 저장하면 dirty hint가 즉시 같은 단일 갱신을 시작합니다. 대조 주기가 지나면 다음 hybrid 요청이 갱신을 시작합니다. 대조한 Lesson 수와 projection hash가 모두 같으면 재임베딩 없이 대조 시각만 갱신합니다. 어느 경우에도 공개 요청은 전체 재구축을 기다리지 않습니다. 사용할 snapshot이 있으면 갱신 중에도 그 snapshot을 사용하고 stale 또는 degraded 상태를 표시합니다. 아직 snapshot이 없으면 building 또는 unavailable 상태로 즉시 legacy 검색을 반환합니다. 새 인덱스는 옆에서 완성한 뒤 원자적으로 교체합니다.

후보는 Lesson: ID와 retrieval hash만 제공하며, 최종 필터와 응답 본문은 최신 MediaWiki snapshot으로 다시 확인합니다. hash가 달라진 의미 기여는 제거하고 semantic_status=stale로 표시합니다. 제공자 장애나 과부하 시 첫 페이지는 legacy로 대체하며, 이미 발급한 hybrid cursor의 순서를 중간에 바꾸지 않습니다.

MCP 서비스는 같은 열 도구만 외부에 제공합니다. exact index의 재구축 시간, 메모리 또는 지연 시간이 실제 목표를 넘을 때만 같은 비공개 backend 경계를 내부 sidecar나 공유 검색 서비스로 교체합니다. 필드 및 출처 필터와 이전 버전 호환 review_state 필터, 현재 응답 제한은 계속 보존합니다. 내부 경계, 색인 세대, 장애 fallback과 단계별 확장안은 벡터·하이브리드 검색 설계에 정리합니다.

현재 서버의 선택적 embedding Compose profile은 고정된 Qwen3 F16 GGUF를 llama.cpp Vulkan으로 실행합니다. 모델 downloader만 별도 egress 네트워크를 쓰고, 추론 서비스는 호스트 포트 없이 mcp-http와 공유하는 전용 내부 embedding-backend에만 연결합니다. 이 서비스와 embedding_models 볼륨은 파생 계산 계층이며 Semantic MediaWiki를 대신하지 않습니다.

출력 제한

모든 MCP 목록 작업에는 작은 기본 페이지 크기, 엄격한 최댓값, 커서 기반 페이지 나누기, 필드 선택, UTF-8 직렬화 응답의 바이트 상한이 있습니다. 스캔 응답에는 partial, scanned, scan_limit가 들어갑니다. partial=true이면 검색 결과가 완전하지 않은 것으로 처리해야 합니다. 기본 결과에는 ID, 제목, 한 줄짜리 reusable_lesson, 출처, confidence, 짧은 근거 인용이 들어갑니다. get_lesson은 현재 revision ID를 포함하고 인용, 검증 기록과 관계를 각각 나누어 반환합니다. 각 하위 목록은 기본 5개, 최대 20개의 독립 cursor 페이지입니다. 기본 citation_detail=compact는 짧은 인용만, full은 note와 추가한 주체·시각까지 반환합니다. full 인용은 한 항목도 조용히 자르지 않고 페이지 크기를 줄이며, 한 항목도 출력 한도에 맞지 않으면 명시적인 오류를 반환합니다. 본문 필드는 필요할 때만 직접 선택합니다.

audit_lessons는 정확한 ID 기대값을 최대 50개 읽고 결과를 최대 20개씩 반환합니다. revision, 내용 digest, 최대 8개 evidence와 최대 8개 relation을 확인하며, 페이지 사이 어느 대상 revision이라도 바뀌면 이전 cursor를 거부합니다. 긴 본문을 반복 반환하지 않고 ok, missing, mismatch와 실패한 기대값 위치만 반환합니다.

열 도구는 모두 닫힌 outputSchema를 공개합니다. 성공 응답의 structuredContent는 이 스키마로 검증하며, 기존 클라이언트가 읽는 compact JSON text도 같은 내용으로 유지합니다.