지식 플랫폼
WeKnora는 문서를 검색 가능한 지식으로 바꾸고, 그 지식을 RAG·Agent·Wiki 세 방식으로 사용하는 플랫폼입니다. 보고서와 매뉴얼을 읽어 텍스트·이미지를 추출하고, 작은 조각으로 나눠 색인합니다. 사용자가 질문하면 관련 조각을 찾아 언어 모델에 전달하고, 답변에 문서 근거를 연결합니다.
이 저장소에서 배울 핵심은 모델을 만드는 법보다, 모델이 믿을 만한 자료를 찾고 사용하는 제품을 만드는 법입니다. 검색·비동기 작업·권한·화면 갱신·배포가 모두 이 목적을 위해 연결됩니다.
RAG는 질문에 맞는 책과 페이지를 찾아 주는 사서입니다. Agent는 검색만으로 해결되지 않으면 다른 도구도 쓰는 조사 담당자입니다.
Wiki는 여러 문서의 개념을 연결해 읽기 좋은 지식 문서를 만드는 편집자입니다. 세 역할은 원문이라는 같은 재료를 사용하지만, 실행 흐름과 결과물은 다릅니다.
| 사용 방식 | 사용자 질문 / 요청 | 소스에서 찾아볼 입구 |
|---|---|---|
| 지식 질의응답 | “이 매뉴얼의 환불 조건은?” — 근거를 검색해 답변 | internal/application/service/session_knowledge_qa.go |
| 에이전트 | “관련 자료를 더 읽고 비교해 줘” — 모델 판단에 따라 도구 호출 반복 | internal/agent/engine.go |
| 지식 위키 | 문서에서 개념을 추출하고 서로 연결된 페이지로 정리 | internal/application/service/wiki_ingest_batch.go |
자료를 넣는 시간
문서 업로드 → 원문 저장 → 파싱 → 청크 분할 → 임베딩·색인 → 검색 준비
질문에 답하는 시간
질문 → 관련 청크 검색 → 순서 재평가 → 문맥 구성 → 모델 답변 → 출처 표시
앞의 경로는 오래 걸려도 재시도할 수 있어야 하고, 뒤의 경로는 사용자가 기다리므로 진행 상황을 빨리 보여줘야 합니다. 이 차이가 작업 큐와 스트리밍 화면을 따로 설계하는 이유입니다. 파일을 받았다는 응답만으로 “이제 검색할 수 있다”고 판단하면 안 됩니다.
처음부터 모든 모델 공급자와 저장소 구현을 읽을 필요는 없습니다. 업로드 함수 → 문서 처리 함수 → 질의응답 파이프라인을 한 번 연결하면, 나머지는 어떤 부품을 교체하는 코드인지 보이기 시작합니다.
2026-10-23 분석 대상은 GitHub Trending Go Monthly #8입니다. 스타 35,175개와 포크 4,724개는 관심의 크기를 보여 주지만, 정확도나 운영 안정성을 증명하는 수치는 아닙니다. 다음은 기능과 코드에서 읽어 낸 주목할 이유이며, 순위 상승의 인과관계를 입증한 분석은 아닙니다.
단순한 PDF 채팅 예제는 문서를 읽고 답하는 순간에 집중합니다.
WeKnora에는 문서 교체·재파싱·폴더·외부 데이터 소스·공간 권한·위키 변경 이력까지 이어지는 코드가 있습니다.
internal/application/service/knowledge_create.go의 중복 검사와 internal/application/service/knowledge_process.go의 처리 상태만 읽어도,
“한 번 성공하는 기능”을 “반복해서 쓰는 기능”으로 만드는 차이가 드러납니다.
v0.8.3(2026-10-14)의 README 릴리스 설명은 이미지 갤러리, 텍스트·이미지를 함께 다루는 임베딩,
이미지 인식 재정렬, 위키 그래프, 원문 인용 위치 이동을 강조합니다.
기능 소개와 별개로 코드에서는 internal/application/service/chat_pipeline/rerank.go가 이미지 로더를 재정렬 옵션에 연결하고,
internal/sourceloc/가 원문 위치를 다루는 것을 확인할 수 있습니다.
각 모델과 파일 형식에서 같은 품질이 나온다는 의미는 아닙니다.
표준 구성은 PostgreSQL 계열 저장소와 Redis를 사용하고, Lite는 SQLite와 메모리 기반 실행을 사용합니다.
파서는 Go 내부·Python 서비스·원격 API로 나뉘어도 types.ReadResult로 결과를 모읍니다.
부품을 갈아 끼워도 문서 처리와 질의응답의 중심 흐름이 유지되는 구조가 이 저장소의 중요한 학습 가치입니다.
아래는 공식 프로젝트 설명과 WeKnora의 코드 범위를 바탕으로 한 학습 관점의 비교입니다. 기능이 서로 겹치므로 “어느 제품에 이 기능이 전혀 없다”는 표로 읽으면 안 됩니다.
| 대상 | 공식 설명의 중심 | WeKnora에서 특히 볼 지점 |
|---|---|---|
| Dify | 시각적 워크플로, RAG, Agent를 결합한 LLM 앱 개발 플랫폼 | 지식 문서의 입력·검색·위키화를 Go 서비스 계층에서 이어 읽기 좋습니다. |
| RAGFlow | 복잡한 문서 이해와 템플릿 기반 분할을 강조하는 RAG 엔진 | 파서 경계뿐 아니라 Agent 루프·공간 권한·Lite 실행을 함께 학습할 수 있습니다. |
| LangChain | 모델·검색·도구를 조립해 앱과 Agent를 만드는 프레임워크 | 이미 연결된 UI·계정·문서 상태·배포 설정을 보며 완성된 서비스의 연결 비용을 배웁니다. |
| 직접 만든 RAG 예제 | 필요한 기능만 빠르게 이해하고 실험하기 좋음 | 실패 복구·동시성·데이터 경계가 추가되는 과정을 비교할 수 있습니다. |
프레임워크는 부품 상자에 가깝고, WeKnora는 부품이 연결된 작업실에 가깝습니다. 자유롭게 새 구조를 설계할 때와, 이미 돌아가는 서비스의 연결부를 배울 때 필요한 재료가 다릅니다.
장점에는 비용이 따릅니다. 표준 배포에는 여러 서비스가 필요하고, 설정과 소스 규모도 큽니다. 문서 질의응답만 필요하다면 작은 예제가 학습의 출발점으로 더 쉬울 수 있습니다. WeKnora의 강점은 모든 상황의 최저 비용보다 지식 서비스의 여러 층을 한 번에 추적할 수 있다는 점입니다.
버전은 커밋 314a6fa의 의존성 파일 기준입니다.
Go 모듈의 버전, npm의 허용 범위, Python의 최소 버전은 서로 다른 의미입니다.
특히 ^와 >=가 붙은 선언을 실제 설치 버전처럼 읽지 마세요.
| 기술 / 실제 선언 | 역할 | 확인 파일 |
|---|---|---|
Go 1.26.0 | HTTP API, 문서 처리 조정, 검색, Agent 실행의 주 언어 | go.mod |
Gin v1.12.0 | 라우팅과 HTTP 미들웨어 | go.mod, internal/router/router.go |
GORM v1.31.1 | 문서·세션·공간 등 관계형 데이터 접근 | go.mod, internal/application/repository/ |
Dig v1.19.0 | 생성자와 인터페이스를 연결하는 의존성 주입 | internal/container/container.go |
Asynq v0.26.0 | Redis 기반 문서 처리·후처리 작업 큐 | go.mod, internal/router/ |
go-redis v9.14.1 | 큐 주변 상태·스트림·분산 실행 조정 | go.mod |
gRPC v1.81.0Protobuf v1.36.11 | Go 앱과 문서 파서 사이의 타입이 정해진 통신 | docreader/proto/docreader.proto |
pgvector-go v0.3.0Qdrant client v1.18.1 | 벡터 저장소 연결의 일부; 이 버전은 서버 버전이 아닌 클라이언트 버전 | go.mod |
mcp-go v1.1.1Ollama v0.23.2 | MCP와 로컬 모델 서비스 연동 | go.mod |
OpenTelemetry v1.43.0 | 실행 구간 추적; Langfuse 연동 코드와 함께 읽기 | internal/tracing/langfuse/ |
| 패키지 | frontend/package.json | 잠금 버전 / 역할 |
|---|---|---|
| Vue | ^3.5.34 | 3.5.34 — 화면 컴포넌트와 반응형 상태 |
| TypeScript | ~6.0.3 | 6.0.3 — 화면 데이터의 타입 |
| Vite | ^7.3.5 | 7.3.6 — 개발·정적 자산 빌드 |
| Pinia | ^3.0.4 | 3.0.4 — 화면 간 공유 상태 |
| TDesign Vue Next | ^1.19.2 | 1.19.2 — UI 컴포넌트 |
| Axios | ^1.16.0 | 1.16.0 — 일반 HTTP API 호출 |
| fetch-event-source | ^2.0.1 | 2.0.1 — 답변 이벤트 스트림 수신 |
잠금 버전은 frontend/package-lock.json에서 확인할 수 있습니다.
문서 미리보기를 위한 pdfjs-dist ^6.3.289, docx-preview ^0.3.7와,
Markdown을 위한 marked ^17.0.5, HTML 정제를 위한 dompurify ^3.4.11도 선언되어 있습니다.
의존성이 있다는 사실만으로 모든 렌더링 경로가 안전하다고 단정할 수는 없습니다.
docreader/pyproject.toml은 Python >=3.10.18을 요구합니다.
grpcio >=1.78.0, markitdown[docx,pdf,xls,xlsx,pptx] >=0.1.3,
opendataloader-pdf >=2.4.7, rapidocr >=3.0,
playwright >=1.55.0, pypdf >=6.13.3 등이 선언되어 있습니다.
이는 최소 요구 버전이며, docreader/uv.lock에는 선택된 의존성 버전이 기록되어 있습니다.
모든 문서가 Python으로 가는 것은 아닙니다.
internal/infrastructure/docparser/engine_registry.go의 NewReader는 단순 형식을 Go에서 처리할 수 있고,
anydoc 엔진은 빌드 태그와 네이티브 라이브러리가 갖춰졌을 때 프로세스 안에서 동작합니다.
go.mod에는 third_party/anydoc-go를 가리키는 로컬 교체 설정이 있습니다.
| 구성 | 실제 값 | 읽어야 할 의미 |
|---|---|---|
| 기본 검색 DB | paradedb/paradedb:v0.25.11-pg17 | 표준 Compose의 PostgreSQL 17 계열 이미지 |
| 기본 큐 | redis:7.0-alpine | 기본 구성의 Redis 이미지 태그 |
| 선택 벡터 DB | qdrant/qdrant:v1.16.2milvusdb/milvus:v2.6.11 | 선택 profile; 모두 기본으로 켜지는 것은 아님 |
| 선택 그래프 DB | neo4j:2025.10.1 | 그래프 기능용 선택 서비스 |
| 앱 이미지 | golang:1.26-bookworm → debian:12.12-slim | 빌드 도구와 실행 환경 분리; BrowserSkill 빌드 단계도 있음 |
| UI 이미지 | Node 24 → Nginx 1.30.3-alpine | UI 빌드 산출물과 Nginx 설정·시작 스크립트를 실행 이미지에 복사; 기반 이미지 digest 고정 |
| 문서 파서 이미지 | python:3.10.18-bookworm | uv 잠금 설치, LibreOffice·Java·Playwright 등 포함 |
| Helm | chart 0.1.0, app v0.8.3 | helm/Chart.yaml의 Kubernetes 조건은 >=1.25.0-0 |
표의 이미지 값은 docker-compose.yml, 빌드 단계는 docker/Dockerfile.app,
docker/Dockerfile.docreader, frontend/Dockerfile 기준입니다.
앱 Dockerfile의 WITH_ANYDOC=1 기본값은 Rust 도구 체인과 CGO까지 빌드 과정에 들어오는 이유를 설명합니다.
“Go 프로젝트니까 실행 파일만 빌드하면 된다”는 예상과 실제 배포 구성은 다릅니다.
frontend/package.json은 어떤 버전을 허용할지, frontend/package-lock.json은 어떤 버전을 선택했는지 보여 줍니다.
Python도 docreader/pyproject.toml의 하한과 docreader/uv.lock을 구분해야 합니다.
재현 가능한 빌드를 이해하려면 두 종류의 파일을 함께 읽으세요..github/workflows/app.yml은 Go 1.26 환경에서 형식 검사·go vet·go test·서버 빌드를 정의합니다.
.github/workflows/frontend.yml은 Node 24에서 테스트·타입 검사·빌드를 수행하도록 구성되어 있습니다.
.github/workflows/docreader.yml은 Python 테스트와 Go 클라이언트를, .github/workflows/anydoc.yml은 Rust 엔진 경로를 별도로 다룹니다.
여러 언어를 쓰는 서비스의 품질 경계는 저장소 하나가 아니라, 각 언어의 검사와 서로 만나는 인터페이스에 놓입니다. CI 파일에 검사가 정의되어 있다는 사실과 해당 커밋에서 모든 검사가 통과했다는 사실은 구분해야 합니다.
이 그림의 상자는 모두 별도 마이크로서비스를 뜻하지 않습니다. Handler·Service·Repository와 기본 작업 처리기는 Go 앱 내부의 역할 구분입니다. Redis 큐가 있다고 작업 처리기가 반드시 별도 배포 단위가 되는 것은 아닙니다. 모델 서버는 배포 선택에 따라 로컬일 수도 외부 API일 수도 있습니다.
Lite에서는 핵심 저장소가 SQLite로 바뀌고 Redis 없는 작업 실행 및 메모리 스트림을 사용합니다.
이를 “표준 배포의 모든 기능이 한 실행 파일로 동일하게 압축된다”는 뜻으로 해석하면 안 됩니다.
docs/LITE.md는 공유 공간과 기본 파서 범위의 차이도 명시합니다.
cmd/server/main.go의 main은
container.BuildContainer(runtime.GetContainer())로 부품을 연결합니다.
internal/container/container.go의 BuildContainer는
DB·파일 서비스·검색 엔진·저장소·서비스·핸들러 생성자를 Dig에 등록합니다.
// internal/types/interfaces/retriever.go의 실제 인터페이스 발췌
type RetrieveEngine interface {
EngineType() types.RetrieverEngineType
Retrieve(ctx context.Context, params types.RetrieveParams) ([]*types.RetrieveResult, error)
Support() []types.RetrieverType
}
상위 코드는 PostgreSQL의 질의 문법이나 Qdrant 호출 형식을 모두 알 필요가 없습니다.
Retrieve와 Support 같은 공통 약속을 사용하고,
구체적인 연결 차이는 internal/application/repository/retriever/가 맡습니다.
새 엔진을 추가할 때도 지원 검색 방식과 오류 계약을 맞춰야 한다는 뜻입니다.
문서 파서도 같은 방향입니다. docparser.NewReader는 이름으로 등록된 엔진을 찾고,
엔진을 명시하지 않은 단순 형식의 업로드 파일에는 SimpleFormatReader를 사용합니다.
그 밖의 경로는 연결된 docreader로 갑니다. 등록된 엔진의 초기화 실패는 오류로 반환합니다.
다만 anydoc은 OCR이 필요한 PDF를 연결된 docreader로 넘길 수 있습니다.
학습할 때는 인터페이스 → 생성자 → 등록 위치 → 실제 구현 순서로 읽으세요. 인터페이스만 보면 가능한 기능을, 컨테이너까지 보면 현재 실행 환경에서 어떤 구현이 선택되는지를 알 수 있습니다.
internal/application/service/knowledge_create.go의
CreateKnowledgeFromFile을 보면 업로드의 책임이 분명해집니다.
확장자·중복·할당량 등을 검사하고 원문을 저장한 뒤, 문서 레코드를 만들고 처리 작업을 넣습니다.
ParseStatus: "pending", EnableStatus: "disabled"로 준비합니다.fileSvc.SaveFile로 파일을 저장한 후 repo.CreateKnowledge를 호출합니다.types.DocumentProcessPayload에 문서 ID·공간 ID·파일 정보를 담습니다.asynq.NewTask(types.TypeDocumentProcess, ...)를 만들고 큐에 넣습니다.
이 업로드 경로는 asynq.MaxRetry(3)을 지정합니다.internal/application/service/knowledge_process.go의 ProcessDocument가 파싱과 분할을 조정하고,
processChunks가 청크 저장과 색인을 진행합니다.| 실패 지점 | 코드에서 보이는 동작 | 배울 점 |
|---|---|---|
| 원문 저장 실패 | 문서 레코드 생성 전에 오류 반환 | 존재하지 않는 파일을 가리키는 행을 줄입니다. |
| 문서 행 생성 실패 | 이미 저장한 파일을 삭제하려 시도 | DB 트랜잭션 밖 자원에는 보상 정리가 필요합니다. |
| 작업 직렬화·등록 실패 | 실패 상태를 기록하고 문서 객체를 반환할 수 있음 | 요청 성공과 후속 처리 성공은 서로 다른 상태입니다. |
| Redis 미설정 | BuildContainer가 SyncTaskExecutor를 주입 | 이름만 보고 내구성 있는 외부 큐라고 가정하면 안 됩니다. |
파일 저장·문서 등록·색인 완료는 서로 다른 성공이며, 서비스는 그 사이의 상태와 실패를 표현해야 합니다. 사용자에게는 “업로드됨”과 “검색 준비 완료”를 구분해 보여 줄 필요가 있습니다.
표준 모드에서는 core·post-process·enrichment·maintenance·shared·wiki 작업 처리 풀을 등록합니다. 큰 문서의 부가 처리 때문에 기본 처리가 모두 밀리지 않도록 용량을 나누는 설계입니다. 실제 처리량은 풀 크기뿐 아니라 파서·모델 API·DB 속도에도 제한됩니다.
internal/application/service/wiki_ingest.go의 EnqueueWikiIngest와
internal/application/service/wiki_ingest_batch.go의 ProcessWikiIngest를 연결해 보세요.
mapOneDocument는 문서별 재료를 만들고,
reduceSlugUpdates는 같은 페이지 식별자로 모인 갱신을 다룹니다.
이는 문서마다 얻은 내용을 공통 페이지로 합치는 Map/Reduce식 분해로 읽을 수 있습니다.
여러 문서가 같은 위키 페이지를 수정할 수 있으므로 단순한 병렬 실행만으로는 충분하지 않습니다.
internal/application/service/wiki_ingest.go에는 Redis 모드의 페이지별 잠금과 지식베이스별 동시 실행 상한,
업로드를 모으는 30 * time.Second 지연이 나타납니다.
internal/application/service/session_knowledge_qa.go는 검색이 필요한지 판단한 뒤 단계를 조립합니다.
아래는 RAG 경로의 실제 구성 부분입니다. 각 AddIf가 어떤 기능을 선택적으로 켜는지 읽어 보세요.
// internal/application/service/session_knowledge_qa.go 발췌
pipeline = types.NewPipelineBuilder().
AddIf(hasHistory, types.LOAD_HISTORY).
Add(types.MEMORY_RECALL).
Add(types.QUERY_UNDERSTAND).
Add(types.CHUNK_SEARCH_PARALLEL).
Add(types.CHUNK_RERANK).
AddIf(webSearchEnabled, types.WEB_FETCH).
Add(types.CHUNK_MERGE).
Add(types.FILTER_TOP_K).
AddIf(chatManage.DataAnalysisEnabled, types.DATA_ANALYSIS).
Add(types.INTO_CHAT_MESSAGE).
Add(types.CHAT_COMPLETION_STREAM).
Build()
internal/application/service/chat_pipeline/chat_pipeline.go의
Plugin은 ActivationEvents와 OnEvent를 요구합니다.
EventManager.Register는 이벤트별 플러그인을 등록하고,
buildHandler는 next를 이어 호출하는 체인을 만듭니다.
세션 서비스의 KnowledgeQAByEvent는 조립된 이벤트 목록을 순서대로 실행합니다.
| 단계 | 실제 코드 | 주의해서 볼 행동 |
|---|---|---|
| 병렬 검색 | internal/application/service/chat_pipeline/search_parallel.go의 PluginSearchParallel.OnEvent |
청크 검색과 엔티티 검색에 각각 ChatManage.Clone()을 주고, 끝난 뒤 결과를 합칩니다. |
| 청크·웹 검색 | internal/application/service/chat_pipeline/search.go의 PluginSearch.OnEvent |
지식베이스와 선택적 웹 검색을 병렬 실행하며, 남은 결과가 있으면 일부 실패를 경고로 다룰 수 있습니다. |
| 재정렬 | internal/application/service/chat_pipeline/rerank.go의 PluginRerank.OnEvent |
모델이 없거나 로드·호출에 실패하면 검색 순서를 살리는 경로가 있습니다. 정상 평가 후 모두 걸러진 경우와 다릅니다. |
| 결과 제한 | internal/application/service/chat_pipeline/filter_top_k.go의 PluginFilterTopK.OnEvent |
병합·재정렬·검색 결과 중 사용할 목록을 고르고, 동점에서도 재현 가능한 순서로 정렬합니다. |
RAG는 답변 원고가 지나가는 편집 공정입니다. 자료 수집, 중요도 판단, 중복 정리, 분량 조절을 차례로 거칩니다. 어느 공정이 비었는지 알아야 고칠 수 있습니다. 최종 답변만 보고 검색 실패와 글쓰기 실패를 구분하기는 어렵습니다.
오류 정책은 계층마다 다릅니다.
internal/application/service/retriever/composite.go의 CompositeRetrieveEngine.Retrieve는 엔진 수준 오류가 나면 모은 결과를 버리는 반면,
결과 객체가 보고하는 부분 실패는 결과와 오류를 함께 반환합니다.
상위 파이프라인의 “남은 결과 사용” 정책을 모든 저장소 호출에 그대로 적용하면 소스를 잘못 읽게 됩니다.
internal/agent/engine.go의 AgentEngine.Execute는 실행 상태를 만들고,
executeLoop와 runReActIteration으로 반복을 진행합니다.
검색 질의응답의 고정 공정과 달리 모델 응답에 따라 다음 도구와 행동이 달라집니다.
// 이해를 위한 의사코드 — 원본 그대로의 Go 코드는 아닙니다.
상태 = 실행 시작 시 전달받은 대화 기록
반복 가능한 동안:
취소·진행 정체·문맥 크기를 점검
모델에 현재 상태와 도구 정의 전달
도구 호출이 있으면 실행하고 결과를 상태에 추가
최종 답변이 완성되면 종료
사용자 응답이 필요하면 실행을 중단·보류할 수 있음
withinIterationBudget는 반복 예산을 확인합니다.
음수 MaxIterations는 횟수 제한을 없애지만, 자연 종료·취소·빈 응답·진행 정체 같은 제어는 별개입니다.
기본값을 확인하지 않고 “항상 N번만 돈다”고 설명해서는 안 됩니다.
internal/agent/act.go의 executeToolCalls는
ParallelToolCalls가 켜지고 도구 호출이 둘 이상일 때 병렬 경로를 선택합니다.
병렬 경로에서도 동시 실행을 허용하지 않는 도구는 앞선 호출이 끝난 뒤 순서대로 실행합니다.
응답이 출력 길이 제한으로 잘렸다면 인자가 불완전할 수 있어 도구 호출을 거부합니다.
“JSON처럼 보인다”와 “실행해도 되는 완전한 명령이다” 사이의 경계를 보여 주는 사례입니다.
검색 도구의 실제 구현은 internal/agent/tools/search_knowledge.go와
internal/agent/tools/read_document.go에서 볼 수 있습니다.
search_knowledge로 후보를 찾고 read_document로 필요한 범위를 읽는 구조는,
모든 문서를 한 번에 모델 문맥에 넣지 않도록 역할을 나눈 예입니다.
엔진은 다음 질문까지의 대화 기록을 자체 캐시에 계속 쌓는 구조가 아닙니다.
AgentEngine의 설명대로 호출자가 매 턴 DB에서 기록을 복원해 전달합니다.
한 턴 안의 반복 상태, 턴 사이의 영속 기록, 장기 메모리는 서로 다른 수명입니다.
frontend/src/api/chat/streame.ts의 useStream은
fetchEventSource로 이벤트를 받고, AbortController와 실행 세대 번호로 이전 요청의 늦은 이벤트를 구분합니다.
이미 접수된 POST를 단순 반복하는 대신, 받은 메시지 ID로 기존 실행에 다시 붙는 조건도 확인할 수 있습니다.
frontend/nginx-api-proxy.conf의 proxy_buffering off는
프록시가 답변 조각을 모아 뒀다가 한꺼번에 보내는 일을 막는 설정입니다.
모델의 스트리밍만 켠다고 화면까지 곧바로 조금씩 표시되는 것은 아닙니다.
internal/application/repository/knowledge.go의 GetKnowledgeByID는
tenant_id와 id를 함께 조건으로 사용합니다.
검색 저장소도 internal/application/service/retriever/factory.go의 소유권 검증을 거쳐야 합니다.
공간 선택 헤더는 접근 요청의 문맥일 뿐입니다. 서버가 권한을 확인해야 합니다. 코드에 이런 경계가 있다는 사실만으로 모든 경로의 안전성이 검증되었다는 뜻은 아닙니다.
| 알고 싶은 것 | 먼저 읽을 곳 | 그다음 이동 |
|---|---|---|
| 서버는 어디서 시작하나 | cmd/server/main.go | container.BuildContainer → router.NewRouter |
| 파일이 왜 아직 검색되지 않나 | internal/application/service/knowledge_create.go | internal/application/service/knowledge_process.go의 처리 상태·색인 경로 |
| 검색 결과는 왜 달라졌나 | internal/application/service/chat_pipeline/ | 질의 이해 → 검색 → 재정렬 → 병합 → 제한 |
| Agent가 멈추지 않는다면 | internal/agent/engine.go | 반복 예산·취소·정체 감지·문맥 압축 |
| 답변이 한꺼번에 표시된다면 | frontend/src/api/chat/streame.ts | Nginx 버퍼링 → 서버 이벤트 → 렌더링 버퍼 |
| 공간 간 데이터가 섞일까 | internal/application/access/ | 서비스 접근 검사 → 저장소의 공간 조건 → 검색 엔진 소유권 |
mcp-server/만 보고 현재 MCP 서버의 전부라고 판단하지 마세요.
README의 v0.8.2 설명은 Python 서버를 이전 구현으로 분류하고,
현재는 internal/router/router.go에서 RegisterMCPServerRoutes로
공간별 /mcp/:endpoint_id 경로를 연결합니다.
비슷한 이름의 오래된 코드와 현재 진입점을 구분하는 것도 소스 탐색의 일부입니다.
| 확장자 | 파일 수 | 물리적 줄 수 |
|---|---|---|
Go .go | 3,370 | 862,852 |
Vue .vue | 257 | 173,212 |
TypeScript .ts | 576 | 131,518 |
Python .py | 125 | 26,225 |
JavaScript .js | 14 | 1,837 |
Rust .rs | 4 | 1,127 |
집계 대상은 커밋 314a6fa의 Git 추적 파일 5,631개 중 위 확장자입니다.
빈 줄·주석·테스트·생성 코드·제3자 코드가 포함된 물리적 줄 수이며, 직접 작성한 운영 코드의 양과 같지 않습니다.
예를 들어 docs/의 Go 파일만 43,544줄이고 Swagger 생성 코드가 포함됩니다.
Go의 _test.go는 1,772개·360,231줄입니다.
나머지 Go 502,621줄도 생성 코드 등을 포함합니다.
internal/의 Go는 760,205줄, frontend/의 Vue는 172,792줄입니다.
큰 저장소를 공부하는 가장 현실적인 방법은 전체 정독이 아니라, 한 기능의 경로와 그 경로의 테스트를 함께 읽는 것입니다.
internal/application/service/chat_pipeline/search.go는 sync.WaitGroup으로 검색 작업을 기다리고,
공유 결과 목록을 sync.Mutex로 보호합니다.
internal/application/service/chat_pipeline/search_parallel.go는 더 바깥 단계에서 채팅 상태를 복제해 두 검색이 같은 슬라이스를 동시에 바꾸지 않게 합니다.
“goroutine을 썼다”보다 누가 어떤 데이터를 수정하는지가 먼저입니다.
실습 아이디어는 공유 상태 표를 만드는 것입니다.
검색어·청크 결과·엔티티 결과·오류를 각각 읽기 전용, 작업별 소유, 병합 단계 소유로 분류해 보세요.
같은 주소를 가리키는 슬라이스를 단순 복사하면 충분한지 ChatManage.Clone의 구현과 대조할 수 있습니다.
cmd/server/main.go의 종료 경로도 좋은 예제입니다.
첫 종료 신호에서 HTTP 요청을 정리하고, 별도의 시간 예산을 가진 문맥으로 자원을 정리합니다.
이미 만료된 요청 정리 문맥을 그대로 자원 정리에 재사용하지 않는 이유를 설명해 보세요.
internal/application/service/retriever/keywords_vector_hybrid_indexer.go의
KeywordsVectorHybridRetrieveEngineService.BatchIndex를 읽어 보세요.
벡터 색인 경로에는 batchSize = 40, maxConcurrency = 5,
windowSize = batchSize * maxConcurrency가 있습니다.
최대 200개 단위 창을 처리·저장한 뒤 다음 창으로 넘어가 모든 임베딩을 한꺼번에 붙잡지 않게 합니다.
같은 파일의 batchEmbedWithBackoff는 최초 호출을 포함해 최대 5회 시도하며, 재시도 대기의 시작값은 200ms입니다.
이를 업로드 작업의 MaxRetry(3)과 구분하세요.
앞은 처리 중 모델 호출의 재시도이고, 뒤는 큐 작업의 재시도입니다.
여러 단계의 재시도가 겹치면 요청 수와 대기 시간이 어떻게 늘 수 있을지 표로 계산해 볼 수 있습니다.
internal/infrastructure/chunker/splitter.go의 Chunk는
Start와 End를 rune 위치로 기록합니다.
Content의 rune 수는 End - Start와 같아야 합니다.
앞에 붙일 제목 경로인 ContextHeader는 별도 필드에 둡니다.
// Go의 문자열 길이 차이를 보여 주는 학습 예시
text := "한글"
fmt.Println(len(text)) // UTF-8 바이트: 6
fmt.Println(utf8.RuneCountInString(text)) // rune: 2
// internal/infrastructure/chunker/splitter.go의 핵심 조건
End - Start == utf8.RuneCountInString(Content)
rune은 유니코드 코드 포인트 단위이므로 모든 이모지나 결합 문자의 “눈에 보이는 한 글자”와 완전히 같지는 않습니다. 그래도 UTF-8 바이트 위치와 혼동하는 실수를 피하는 첫걸음입니다. 원문 강조 표시와 인용 위치를 정확히 연결하려면 위치 단위가 처음부터 끝까지 일관되어야 합니다.
config/config.yaml은 chunk_size: 512, chunk_overlap: 50입니다.
반면 Go 분할기의 상수는 DefaultChunkSize = 512, DefaultChunkOverlap = 80입니다.
internal/infrastructure/chunker/strategy.go의 NormalizeSplitterConfig는 크기·겹침이 0 이하일 때 상수로 보정합니다.
따라서 이 정규화 경로에서 0을 “겹침 없음”으로 간주하면 안 됩니다.
실제 문서에는 지식베이스 설정과 처리 옵션이 반영되므로 YAML 하나만 읽고 모든 청크의 겹침이 50이라고 단정할 수도 없습니다.
검색 품질을 바꾸는 작은 설정도 원문 위치·저장량·모델 입력 길이까지 연결해 읽어야 실제 의미가 드러납니다.
실습으로 제목 경로가 붙은 임베딩 입력과 저장된 Content를 나란히 그려 보세요.
왜 두 문자열의 길이가 달라도 원문 위치는 그대로여야 하는지 이해할 수 있습니다.
docreader/proto/docreader.proto의 Read는 한 응답을,
ReadStream은 메타데이터 후 이미지별 메시지를 반환합니다.
많은 페이지 이미지를 하나의 큰 응답에 담지 않도록 나눈 프로토콜입니다.
스트리밍 응답이어도 업로드된 입력 파일 자체가 자동으로 스트리밍 입력이 되는 것은 아닙니다.
실습 아이디어는 텍스트 1개와 이미지 300개가 있는 문서를 가정해 두 응답 형식을 그리는 것입니다. 한 메시지의 크기 제한과 전체 문서 처리 중 사용하는 메모리 제한이 왜 다른 문제인지 적어 보세요. 파싱 결과·이미지 저장·청크 분할의 담당 언어를 색깔 대신 세 열의 표로 나눠도 좋습니다.
useStream은 답변 문자열 외에도 로딩·진행·오류·중단 상태를 관리합니다.
오래된 스트림이 새 질문의 화면을 덮지 않도록 세대 번호를 확인하고,
재연결이 가능한 상태에서 30초 동안 이벤트가 없는 연결을 감지하는 타이머도 둡니다.
실습은 “질문 A 전송 → 질문 B 시작 → A의 늦은 이벤트 도착” 타임라인을 그리는 것입니다. 어느 조건이 A의 이벤트를 버리는지 찾고, 서버 작업 취소와 화면의 연결 중단이 같은지 따로 생각해 보세요. 화면 상태와 서버 실행 상태는 네트워크가 끊겼을 때 특히 어긋나기 쉽습니다.
internal/application/service/knowledge_create_test.go에는 저장 실패·레코드 생성 실패와 관련된 테스트가 있고,
internal/application/service/retriever/composite_partial_test.go는 부분 결과의 계약을 다룹니다.
internal/infrastructure/chunker/parent_child_offsets_test.go와 internal/agent/const_test.go처럼
함수 주변의 테스트를 읽으며 입력 → 예상 결과 → 지키려는 조건을 한 문장으로 요약해 보세요.
공식 설치 문서 website-docs/01-getting-started/02-installation.md는
표준 Docker 배포의 출발점으로 CPU 4코어·RAM 8GB를 권장합니다.
이는 보장된 최소 사양이나 부하 시험 결과가 아니며, 로컬 모델 가중치 메모리는 포함하지 않는다고 명시합니다.
| 사용 형태 | 확인 가능한 기준 | 추가로 계산할 것 |
|---|---|---|
| 소스 읽기 | 편집기와 검색 도구만 있으면 됨 | 모델·Docker·GPU 없이도 이 문서의 읽기 실습 가능 |
| 표준 서버 | 공식 문서 권장 4코어 / 8GB, Docker 20.10+와 Compose v2 | 문서 파싱 동시성, DB 색인, 원문 저장량 |
| Lite | SQLite + 메모리 실행, 별도 Redis 불필요 | 공식 문서에 고정 최소 RAM 수치 없음; 선택한 파서·모델 비용은 별도 |
| 외부 모델 API | 플랫폼 서버의 로컬 추론 GPU는 필수 아님 | 네트워크 지연, 호출 요금, 공급자 제한 |
| 로컬 모델 | Ollama 등 별도 모델 서비스 | 모델 크기·양자화·문맥 길이·동시 요청에 따른 RAM / VRAM |
| 선택 서비스 추가 | Neo4j·Milvus·Langfuse 등 profile | 각 서비스의 상주 메모리·볼륨·운영 비용 |
| 소스 빌드 | Go 1.26, CGO 도구, Node 24 계열, 파서는 Python 3.10.18 계열 | anydoc 빌드에는 Rust도 필요; UI 빌드의 Node 힙 기본값은 4096MB |
공식 설치 문서는 기본 ParadeDB 이미지의 x86 기준을 x86-64-v2로 설명합니다.
실제 호스트와 선택한 이미지의 지원 아키텍처를 확인해야 하며,
“모든 ARM·모든 오래된 CPU에서 동일하게 동작한다”는 뜻은 아닙니다.
Kubernetes 요구 버전은 helm/Chart.yaml에서도 확인할 수 있습니다.
docker-compose.yml의 docreader 기본값은
DOCREADER_GRPC_MAX_WORKERS=4, DOCREADER_GRPC_MAX_FILE_SIZE_MB=50입니다.
docreader/config.py는 파일 크기 설정을 바이트로 환산합니다.
50MiB급 입력 네 개의 데이터량만 약 200MiB이고, 여기에 압축 해제·PDF 렌더링·이미지·라이브러리 버퍼가 더해질 수 있습니다.
# Compose에 선언된 일부 기본값 — 실행 명령이 아닌 설정 예시
DOCREADER_GRPC_MAX_WORKERS=4
DOCREADER_GRPC_MAX_FILE_SIZE_MB=50
DOCREADER_PREPROCESS_MAX_WORKERS=1
DOCREADER_PREPROCESS_MEMORY_MB=2048
DOCREADER_PDF_RENDER_MAX_WORKERS=1
DOCREADER_PREPROCESS_MEMORY_MB는 이미지 전처리 설정입니다.
이를 docreader 프로세스 전체나 컨테이너 전체의 메모리 제한으로 읽으면 안 됩니다.
큰 파일이 많을 때는 병렬 개수를 늘리는 것이 처리량보다 메모리 압박을 먼저 키울 수 있습니다.
커밋 314a6fa의 제목은 큰 읽기 요청 후 메모리 회수에 관한 수정입니다.
docreader/utils/request_memory.py는 16MiB 이상 입력에 대해 스레드별 회수 표시를 남기고,
RequestThreadPoolExecutor._run의 finally에서 작업 반환 후
gc.collect()와 사용 가능한 Linux malloc_trim(0)을 호출합니다.
처리 함수 안에서는 gRPC가 요청 인자를 아직 붙잡을 수 있다는 점을 고려한 위치 선택입니다. 모든 요청에 수집을 강제하는 방식도 아니며, Linux가 아니거나 해당 함수가 없으면 같은 경로가 모두 실행되지는 않습니다. 이 코드만으로 메모리가 일정량까지 내려간다거나 성능이 몇 배 좋아진다고 결론낼 수는 없습니다.
작업대에서 종이를 치우는 것과, 비워진 작업대를 창고에 돌려주는 것은 다릅니다. 객체 참조가 사라지는 시점, Python이 객체를 회수하는 시점, 할당기가 운영체제에 메모리를 돌려주는 시점도 구분해야 합니다.
단순 계산으로 float32 1,024차원 벡터 10만 개는
100,000 × 1,024 × 4 = 409,600,000바이트, 약 391MiB입니다.
이는 예시일 뿐 WeKnora의 모든 모델이 1,024차원을 쓴다는 뜻이 아닙니다.
실제 저장 공간에는 원문·이미지·청크 본문·검색 색인·DB 메타데이터·백업이 더해집니다.
원문을 내 서버에 저장해도 외부 임베딩·채팅·OCR·파싱 API를 선택하면 관련 데이터가 외부로 전송될 수 있습니다. Lite 문서의 로컬 처리 설명도 실제 선택한 모델과 파서 설정을 함께 확인해야 합니다.
현재 설치 문서는 데스크톱 앱의 공식 설치 프로그램이 아직 배포되지 않았다고 설명합니다. 편의 기능을 소개한 화면과 지금 이용 가능한 배포 산출물을 구분해 보세요.
다음 과제는 코드를 실행하거나 저장소 파일을 바꾸지 않고 할 수 있습니다. 읽은 내용을 호출 지도·표·의사코드로 다시 표현하는 연습입니다. 예상 시간을 채우기보다 완료 기준을 자신의 말로 설명할 수 있는지 확인하세요.
목표: 업로드와 검색 준비가 왜 별도 상태인지 설명합니다.
읽을 곳: internal/application/service/knowledge_create.go의 CreateKnowledgeFromFile,
internal/application/service/knowledge_process.go의 ProcessDocument와 processChunks.
할 일: 파일 검사·파일 저장·DB 등록·큐 등록·파싱·색인의 여섯 단계를 화살표로 연결하세요. 각 화살표 옆에 문서 ID, 공간 ID, 파일 경로 중 무엇이 필요한지 적습니다.
산출물: 호출 지도 한 장과 “파일은 있는데 색인은 없다”가 가능한 이유 세 문장.
완료 기준: DB 등록 실패와 큐 등록 실패에서 반환값·파일 정리가 어떻게 다른지 설명할 수 있습니다.
# 저장소 루트에서 사용하는 읽기 전용 검색 예시
rg -n 'func .*CreateKnowledgeFromFile' internal/application/service/knowledge_create.go
rg -n 'func .*ProcessDocument|func .*processChunks' internal/application/service/knowledge_process.go
rg -n 'ParseStatus|EnableStatus|MaxRetry|Enqueue' internal/application/service/knowledge_create.go
목표: 원문 위치와 모델 입력이 달라질 수 있음을 이해합니다.
읽을 곳: internal/infrastructure/chunker/splitter.go의 Chunk와
EmbeddingContent, internal/infrastructure/chunker/strategy.go의 NormalizeSplitterConfig.
할 일: 짧은 한국어 매뉴얼 두 문단을 종이에 쓰고, 직접 정한 경계로 세 조각을 만드세요. 각 조각의 rune 시작·끝, 본문, 별도 제목 경로를 표에 적습니다. 단순화한 수작업 분할이며 실제 적응형 알고리즘의 출력을 재현했다고 주장하지 않습니다.
산출물: Content와 ContextHeader가 나뉜 장부.
완료 기준: 제목 경로를 모델 입력 앞에 붙여도 원문 Start가 바뀌면 안 되는 이유와,
겹침 0을 넣었을 때 정규화 함수가 선택하는 값을 설명할 수 있습니다.
목표: 빈 결과, 전체 실패, 부분 실패, 재정렬 실패를 분리합니다.
읽을 곳: internal/application/service/chat_pipeline/search_parallel.go, internal/application/service/chat_pipeline/rerank.go,
internal/application/service/retriever/composite.go와 internal/application/service/retriever/composite_partial_test.go.
할 일: ① 모든 검색 결과 없음 ② 청크 검색 성공·엔티티 검색 실패 ③ 검색 엔진 자체 오류 ④ 검색 성공·재정렬 API 실패를 가정합니다. 각 경우에 남는 결과, 반환 오류, 다음 단계 진행 여부를 표로 만듭니다.
산출물: 네 행의 실패 계약 표와 근거가 되는 함수 위치.
완료 기준: “오류가 있으면 무조건 결과도 없다”와 “결과가 있으면 모두 완전하다”가 왜 둘 다 잘못된 가정인지 코드로 설명할 수 있습니다.
목표: Agent의 능력보다 종료·재개 조건을 이해합니다.
읽을 곳: internal/agent/engine.go의 withinIterationBudget와
runReActIteration, internal/agent/act.go의 executeToolCalls.
할 일: 첫 응답은 검색 도구 호출, 두 번째는 문서 읽기, 세 번째는 최종 답변이라고 가정합니다. 여기에 “두 번째 응답이 출력 길이 제한으로 잘림”과 “사용자가 실행을 취소함” 분기를 추가합니다.
산출물: 턴·라운드·도구·종료 이유를 구분한 상태 전이표.
완료 기준: 불완전한 인자로 도구를 실행하지 않는 분기를 찾고, 대화 기록 복원과 한 턴의 실행 상태가 다른 수명이라는 점을 설명할 수 있습니다.
목표: 인터페이스와 권한 검사를 유지하며 구현을 교체하는 방법을 설계합니다.
읽을 곳: internal/types/interfaces/retriever.go,
internal/application/service/retriever/factory.go, internal/container/container.go.
할 일: 새 검색 저장소를 추가한다고 가정하고, 지원 검색 방식·색인·검색·삭제·오류를 의사코드로 적습니다. 존재하지 않는 저장소, 다른 공간의 저장소, 잠시 연결이 안 되는 저장소를 서로 다른 분기로 둡니다.
산출물: 구현 책임 표, 가짜 구현의 반환 예시, 등록 위치를 표시한 설계 메모.
완료 기준: 저장소 ID로 구현체를 얻는 것과 접근 권한을 확인하는 것이 왜 별도 절차인지 설명합니다.
DB마다 지원 기능이 다를 때 Support()가 필요한 이유도 적습니다.
“검색한다”보다 “지원 검색 방식에 맞는 구현체에 위임한다”, “에러 처리”보다 “일부 결과는 남기되 누락을 오류로 알린다”처럼 적으면 좋습니다. 코드를 다시 보지 않고 그 보장을 설명할 수 있을 때 다음 단계로 넘어가세요.
HTTP 요청·JSON·함수·배열의 기본 개념을 알고 있다는 출발점입니다. Go나 Vue가 처음이라면 첫 주를 더 길게 잡아도 됩니다. 시간표는 학습 제안이며, 한 주의 완료 조건을 설명할 수 있으면 다음 단계로 이동합니다.
| 주차 | 배울 주제 | WeKnora에서 읽을 곳 | 남길 결과물 |
|---|---|---|---|
| 1주 | Go 구조체·인터페이스·오류·문맥, HTTP 계층 | cmd/server/main.go, internal/types/interfaces/retriever.go |
Handler·Service·Repository의 역할을 구분한 지도 |
| 2주 | 문서 상태·중복 검사·비동기 작업·재시도 | internal/application/service/knowledge_create.go, internal/application/service/knowledge_process.go |
성공·실패·재처리의 상태표와 과제 1 |
| 3주 | 청크·임베딩·키워드 / 벡터 검색·재정렬 | internal/infrastructure/chunker/, internal/application/service/retriever/, internal/application/service/chat_pipeline/ |
문서 세 개·질문 열 개로 구성한 평가 자료 초안 |
| 4주 | Go 동시성·오류 계약·테스트 대역 | internal/application/service/chat_pipeline/search_parallel.go, internal/application/service/retriever/composite_partial_test.go |
공유 상태 소유권 표와 과제 3 |
| 5주 | ReAct·도구 호출·문맥 예산·사용자 개입 | internal/agent/engine.go, internal/agent/act.go, internal/agent/approval/ |
정상 종료·취소·보류를 나눈 상태 전이표 |
| 6주 | Vue 스트림 상태·권한·배포·관측 | frontend/src/api/chat/streame.ts, internal/application/service/retriever/factory.go, 빌드·CI 설정 |
브라우저부터 모델까지의 지연·오류·데이터 이동 지도 |
3주 차 평가 자료에는 정확한 제품 코드 질문, 표현을 바꾼 질문, 두 문서를 비교하는 질문, 자료에 답이 없는 질문을 섞어 보세요. 각 질문에 “정답 문서”와 “근거 문단”을 직접 지정합니다. 당장 실행하지 않아도 어떤 결과를 성공으로 볼지 정할 수 있습니다.
나중에 실험할 때는 청크 크기·검색 후보 수·재정렬을 한 번에 모두 바꾸지 말고, 하나씩 바꾸며 근거가 검색되었는지와 답변이 근거를 따르는지를 따로 기록하세요. 좋은 RAG 학습은 그럴듯한 답변을 모으는 일보다, 어떤 근거를 찾았고 어디서 실패했는지 설명하는 일에서 시작합니다.
인터페이스, 큐, 파서, 도구, 스트림 모두 이 네 질문으로 읽을 수 있습니다. 모델 이름이 바뀌어도 적용되는 시스템 설계 습관입니다.
| 용어 | 쉬운 설명 |
|---|---|
| 지식베이스 | 문서와 검색 설정을 묶는 단위입니다. 폴더 하나와 완전히 같은 개념은 아닙니다. |
| RAG | 자료를 먼저 검색하고 그 자료를 참고해 답변을 만드는 방식입니다. |
| 파싱 | PDF·문서 파일에서 텍스트·표·이미지 등 사용할 수 있는 내용을 추출하는 과정입니다. |
| OCR | 스캔이나 사진 속 글자를 텍스트로 읽는 기술입니다. 원래 텍스트가 있는 PDF 추출과는 다릅니다. |
| 청크 | 검색과 모델 입력을 위해 나눈 문서 조각입니다. 문맥과 원문 위치를 함께 관리해야 합니다. |
| 겹침 | 인접 청크가 일부 내용을 함께 가지게 하는 방식입니다. 경계의 정보 손실과 중복 저장 사이의 선택입니다. |
| 부모·자식 청크 | 작은 조각으로 검색하고 더 넓은 문맥을 연결하기 위해 크기가 다른 조각을 관계로 묶는 방식입니다. |
| 임베딩 | 텍스트나 이미지를 검색에 쓸 숫자 벡터로 바꾸는 작업입니다. |
| 벡터 차원 | 벡터 한 개를 구성하는 숫자의 개수입니다. 모델·저장 공간·색인 설정과 연결됩니다. |
| BM25 | 질문 단어가 문서에서 얼마나 중요한지 등을 점수로 만드는 키워드 검색 방법입니다. |
| 하이브리드 검색 | 키워드와 벡터 등 서로 다른 검색 단서를 함께 활용하는 방식입니다. |
| 재정렬 | 먼저 찾은 후보를 질문과 다시 비교해 더 관련 있는 순서로 평가하는 단계입니다. |
| Top-K | 상위 K개 결과를 사용한다는 뜻입니다. 후보 수집 단계와 최종 문맥 단계의 K는 다를 수 있습니다. |
| ReAct | 모델 판단, 도구 실행, 결과 관찰을 반복해 다음 행동을 정하는 Agent 실행 방식입니다. |
| 도구 호출 | 모델이 함수 이름과 인자를 요청하고 앱이 실제 함수를 실행하는 구조입니다. 모델의 텍스트만으로 실행되지는 않습니다. |
| MCP | AI 앱과 외부 도구·자료를 연결하는 공통 프로토콜입니다. 연결 자체가 무제한 권한을 의미하지 않습니다. |
| 문맥 예산 | 모델에 보낼 대화·자료·도구 정의와 출력에 사용할 수 있는 토큰의 한도입니다. |
| 문맥 압축 | 오래된 대화를 요약하거나 줄여 제한 안에 다음 요청을 넣는 처리입니다. 원문 전체 보존과는 다른 목적입니다. |
| 작업 큐 | 오래 걸리는 일을 요청 처리와 분리해 실행하도록 맡기는 구조입니다. 내구성과 재시도는 구현에 따라 달라집니다. |
| 멱등성 | 같은 작업이 반복되어도 의도하지 않은 중복 효과가 생기지 않도록 만드는 성질입니다. |
| 지수 백오프 | 재시도 사이의 대기 시간을 점차 늘려, 실패 중인 서비스에 요청이 몰리지 않게 하는 방법입니다. |
| 공간 / 테넌트 | 문서·사용자·설정을 구분하는 서비스의 논리적 경계입니다. DB 조건과 접근 검사가 함께 필요합니다. |
| RBAC | 사용자의 역할에 따라 할 수 있는 일을 정하는 권한 관리 방식입니다. |
| Principal | 현재 행동의 주체입니다. 웹 사용자 외에 임베드 방문자나 MCP 호출 주체도 구분할 수 있습니다. |
| SSE | 열린 HTTP 연결을 통해 서버가 이벤트를 계속 보내는 방식입니다. 답변·도구 진행·완료를 전달할 수 있습니다. |
| gRPC / Protobuf | 서비스 간 호출 방식과 메시지 형식을 정하는 도구입니다. 언어가 달라도 같은 계약을 사용할 수 있습니다. |
| rune | Go에서 유니코드 코드 포인트를 나타내는 타입입니다. UTF-8 바이트 수와 구분해야 합니다. |
| 어댑터 | 서로 다른 외부 서비스 사용법을 내부 공통 인터페이스에 맞추는 연결 구현입니다. |
| 관측 가능성 | 로그·실행 구간·토큰 사용량 등으로 시스템 안에서 무슨 일이 일어났는지 파악할 수 있는 성질입니다. |
| RSS 메모리 | 프로세스가 실제 물리 메모리에 올려 두고 있는 양을 나타내는 지표입니다. 살아 있는 객체 크기만의 합은 아닙니다. |
용어를 외웠는지보다 소스의 어느 경계에서 쓰이는지 설명해 보세요. 예를 들어 “큐”는 업로드와 처리 사이, “SSE”는 서버 실행과 화면 사이, “어댑터”는 공통 서비스와 외부 구현 사이에 놓입니다.
| 항목 | 확인 값 / 읽는 방법 |
|---|---|
| 선정 근거 | GitHub Trending Go Monthly #8 · 2026-10-23 분석 |
| 스타 / 포크 | 35,175 / 4,724 · 2026-10-23 GitHub 기준 |
| 주 언어 / 생성일 | Go / 2025-07-22 |
| 최근 push | 2026-10-23 · 저장소 활동 시점이며 릴리스 날짜와 다름 |
| 최신 릴리스 | v0.8.3 · 2026-10-14 |
| 분석 HEAD | 314a6fafd718724b5e6a11b42650b51db6b588d8 · 2026-10-23 |
| 최근 커밋 내용 | 큰 docreader 읽기 요청 후 메모리 회수 수정 · #4628 |
| 코드 규모 | Git 추적 파일 5,631개; Go 862,852줄, Vue 173,212줄, TypeScript 131,518줄, Python 26,225줄. JavaScript·Rust를 포함한 여섯 확장자 합계 1,196,771줄. 빈 줄·테스트·생성 코드 등 포함. |
| 테스트 규모 | Go의 _test.go 1,772개·360,231줄. 테스트 통과율이나 커버리지 수치가 아님. |
| 라이선스 표시 | GitHub 메타는 NOASSERTION.
README는 MIT로 소개하고, LICENSE 본문도 제3자 구성요소를 제외한 WeKnora에 MIT를 명시. |
| 배포 형태 | 표준 Compose·Helm, Lite, 소스 기반 데스크톱 앱. 형태별 기능·의존성이 다름. |
이 글의 소스 설명은 커밋 314a6fa 기준입니다.
최신 릴리스 v0.8.3과 같은 스냅샷이라고 가정하면 안 됩니다.
기능 소개 중 모델 호환성·대규모 처리 성능은 README 또는 공식 문서의 설명이며,
실제 모델·문서·부하에 대한 재현 시험 결과를 뜻하지 않습니다.
CI에 검사가 정의되어 있어도 해당 커밋에서 모두 통과했다는 보장은 없습니다. 처리량·메모리·정확도의 실행 실측값은 제시하지 않습니다. 줄 수에는 테스트와 생성 코드가 포함되며, 전체 누적 커밋 수나 기여자 비율은 이 자료의 검증 범위가 아닙니다.
MIT 소개와 GitHub의 NOASSERTION 표시는 구분해야 합니다.
재배포에 앞서 LICENSE 원문과 제3자 구성요소 고지를 함께 확인하세요.
검색 결과와 위키 페이지도 자동 생성된 지식이므로 원문 근거와 최신성을 검토할 필요가 있습니다.