- 게이트웨이/모델이 system 역할을 무시해 평문으로 답하는 경우(팀원 VM /api/ito + 339 실측) LLM_MERGE_SYSTEM=1 로 system 을 user 앞에 합쳐 보낸다 - summarize.ping 이 (그대로 → merge → merge+json_mode off) 순으로 시도해 되는 .env 값을 알려준다. --real 은 실제 조각 추출 프롬프트로 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
abap-indexing
ABAP 소스코드 인덱싱 서버. 약 1만 본의 ABAP 프로그램 소스와 프로그램 메타정보를
정규화 → 구조 파싱 → (LLM 로직 조각 추출) → 인덱싱하고, 질의 API 를 제공한다.
계획서: ABAP_INDEXING_PLAN.md (Downloads), 진행 가정: ASSUMPTIONS.md.
조각의 코드 원문은 로직만이고, 붙여넣는 데 필요한 선언(정의부)은 따로 수집한다 — ABAP 은
내부테이블·스트럭처를 TOP 에 선언하므로 로직만 복사하면 컴파일되지 않는다.
쓸지 말지는 붙여넣는 쪽 LLM 이 정한다. → docs/definition-block-design.md
인덱스의 1차 단위는 로직 조각(logic chunk) 이다. 파서가 자른 FORM/METHOD 는 LLM 에게 코드를
나눠 보여주는 창이고, LLM 이 그 안에서 업무적으로 의미 있는 조각(특정 SQL, DB 갱신, BAPI 호출,
집계 LOOP …)을 골라내 설명을 붙인다. 의미 없는 코드는 인덱스에서 버린다. → docs/logic-chunk-design.md
소비자: opencode-be 레포의 OpenCode 커스텀 툴(.opencode/tool/_index_client.ts)이
이 서버의 HTTP API 를 호출한다 (기본 http://127.0.0.1:8100).
파이프라인
python -m ingest.from_sap ZFI01 # Stage 0: SAP(ZAA_ICF)에서 패키지의 프로그램 소스 → data/raw (sap/ = abap-mcp 복사본)
data/raw/*.txt (수집 JSON)
→ python -m ingest.normalize # Stage 1: 줄바꿈 아티팩트 제거, 프로그램/인클루드 파일화
→ python -m parser.run # Stage 2: unit·참조·데이터플로우·호출그래프·정의부 (LLM 없이)
→ python -m index.loader # Stage 4: SQLite(FTS5) 적재
→ python -m summarize.runner # Stage 3: 프로그램 요약 → unit 별 로직 조각 추출
→ python -m wiki_out.run --all # OKF v0.2 위키 (프로그램 문서 + tables/functions 엔티티)
→ python -m query.api # Stage 5: 질의 API :8100
→ python -m eval.run_eval # Stage 6: recall / 조각 적중 / 추적 평가
설치 / 실행
python -m venv .venv && .venv/Scripts/python -m pip install -e ".[dev]" # Linux/mac: .venv/bin/python
cp .env.example .env
# data/raw/ 에 수집 파일(ZFI01.txt, ZFIR10070.txt 형식) 배치
python -m ingest.normalize && python -m parser.run && python -m index.loader
python -m query.api # 127.0.0.1:8100
테스트: python -m pytest (샘플 데이터가 있으면 §4.6 실측 검증 포함)
파서나 색인 로직을 바꿨으면 소스가 그대로라도 재적재가 필요하다:
python -m index.loader --force
(정의부를 처음 붙이는 기존 DB 도 python -m parser.run && python -m index.loader --force 한 번이 필요하다 —
declaration 테이블은 빈 채로 만들어지고 재적재 때 채워진다.)
서버가 코드 변경을 반영하지 않을 때
/dashboard 와 /wiki-viewer 는 템플릿(html)을 요청마다 다시 읽지만, 파이썬 모듈은
프로세스에 캐시된다(sys.modules). 그래서 CSS·JS 를 고치면 새로고침만으로 보이지만
wiki_out/viewer.py 같은 파이썬 코드를 고치면 서버를 다시 띄워야 한다.
(실제로 조각 내비게이션을 추가했는데 사이드바가 계속 0 으로 보인 적이 있다.)
- 개발 중에는 자동 재시작:
python -m query.api --reload - 낡았는지 확인:
GET /health의started_at이 코드 수정 시각보다 이전인지 본다
Stage 3 을 LLM 키 없이 돌리기
--llm file 은 프롬프트를 data/llm_jobs/ 에 파일로 내놓고 응답 파일을 기다린다.
사람이 채워도 되고, 코딩 에이전트가 프롬프트를 읽고 채워도 된다.
python -m summarize.runner --llm file --program ZFIR10070 --limit 3 # 1) 프롬프트 내놓기
python -m summarize.jobs list --pending # 2) 대기 목록
python -m summarize.jobs show <job_id> # 프롬프트 읽기
python -m summarize.jobs answer <job_id> --file answer.json # 3) 응답 저장(스키마 검증)
python -m summarize.runner --llm file --program ZFIR10070 --limit 3 # 4) 같은 명령 → 적재
프로그램 요약은 unit 프롬프트의 문맥이므로 먼저 답해야 한다 — 요약이 대기 중이면 그 프로그램의 unit 프롬프트는 나오지 않는다(요약이 채워진 뒤 프롬프트 내용이 달라져 두 번 답하게 되는 것을 막는다).
백엔드 3종: --llm api(환경변수 키) / --llm file(키 불필요) / --llm fake(배선 검증용 더미).
/ingest 가 뒤이어 돌리는 요약의 백엔드는 SUMMARIZE_BACKEND=api|file|off 로 고른다.
큐 현황은 GET /summaries/jobs 또는 python -m summarize.jobs stats.
결과를 다른 PC 로 가져가기
방법 A — 단일 HTML 스냅샷 (파이썬·서버 불필요)
python -m wiki_out.viewer # → data/wiki-viewer.html (약 1.2 MB)
python -m query.dashboard # → data/dashboard.html (약 1.4 MB)
데이터가 파일 안에 박히므로 그 파일 하나만 복사해 브라우저로 열면 된다. 로직 조각의 소스 원문과 자연어 설명, 테이블·펑션 문서, 관측소 수치가 모두 들어 있다. 외부 스크립트·이미지를 받지 않으며, 웹폰트는 못 받아도 시스템 한글 글꼴로 폴백한다(폐쇄망 가능).
안 되는 것: 조각 검색(/search/logic)과 trace_variable·get_call_graph·get_table_usage
같은 구조 도구. 이건 DB 질의라 서버가 필요하다 (뷰어 사이드바의 문서 검색은 동작).
방법 B — 인덱스째로 옮기기 (전체 기능)
가져갈 것은 소스 + data/index.db + wiki/ 뿐이다 (합쳐 약 14 MB).
config/ ingest/ parser/ index/ summarize/ query/ wiki_out/ eval/ docs/
pyproject.toml
data/index.db ← 조각·요약·심볼·호출그래프 + 인클루드 소스 원문까지 전부 들어있다
wiki/ ← 사람 교정본이 있으면 함께 (없으면 재생성 가능)
data/normalized/·data/parsed/·data/llm_jobs/는 가져갈 필요 없다 — 재생성 가능하고,
조각의 소스 원문은 include.code 로 DB 안에 있다. 새 PC 에서:
python -m venv .venv && .venv/Scripts/python -m pip install -e ".[dev]"
python -m query.api # 관측소·뷰어·검색 API 전부 동작
python -m wiki_out.run --all # (선택) 위키 재생성 — DB 만으로 된다
실측 검증: 위 최소 세트만 복사한 디렉토리에서 위키 재생성(283건 검증 통과)·스냅샷 생성·
search_logic·get_table_usage·trace_variable·서버 기동이 모두 정상 동작했다.
API
| Method | Path | 설명 |
|---|---|---|
| GET | /health |
상태 + 적재 통계 |
| GET | /search/logic?q=&top_k=&program=&kind= |
로직 조각 검색 — 자연어 로직 질의의 1차 진입점. 프로그램 단위로 묶어 반환 |
| GET | /summaries/jobs |
LLM 키 없이 돌릴 때의 프롬프트 대기 큐 |
| GET | /chunks/{chunk_id} |
조각 메타 + 코드 원문 + 정의부(붙여넣기용 선언, ?decls=false 로 끔). chunk_id 의 # 은 %23 으로 인코딩 |
| GET | /programs/{name}/declarations?scope= |
프로그램의 선언 카탈로그 (정의부) |
| GET | /programs/{name}/chunks?unit_id= |
프로그램(또는 unit)의 조각 목록 |
| GET | /search/programs?q=&top_k= |
프로그램 검색 — 이름·타이틀 기반 얇은 색인 (FTS + 한글 2-gram) |
| GET | /search/units?q=&program= |
unit 검색 — 이름·주석·한 줄 요약 기반 얇은 색인 |
| GET | /programs/{name}/summary |
프로그램 요약(LLM) + 구조 요약(파서, 조각 목록 포함) |
| GET | /programs/{name}/source |
정규화된 전체 소스 (opencode-be 의 fetch_abap 용) |
| GET | /programs/{name}/units/{unit}/code |
unit 코드 원문(400줄 상한) + 요약 + 정의부 |
| GET | /programs/{name}/trace/{symbol} |
변수 선언 + 쓰기 지점 (via_perform 재귀 전개, 깊이 5) |
| GET | /programs/{name}/call-graph?unit= |
호출 그래프 |
| GET | /programs/{name}/who-calls/{unit} |
역참조 |
| GET | /tables/{name}/usage |
테이블 읽기/쓰기 프로그램·unit |
오류: 404(존재하지 않는 프로그램/심볼 — 유사 후보를 detail 에 포함), 503(DB 오류).
원칙 (계획서)
- 사실은 파서가, 해석은 LLM이 — tables/calls/writes 는 파서 결과가 항상 우선. 조각의 줄 번호도 first_line 앵커로 검증하고, 조각의 테이블·호출은 파서로 다시 뽑는다.
- 코드 원문은 로직, 선언은 정의부로 분리. 조각에는 로직만 담아 검색 품질을 지키고, 붙여넣을 때
필요한 선언은
declaration테이블에서 의존까지 묶어 따로 조립한다 (index/decls.py). 인덱스는 재료와 분류만 넘기고, 쓸지 말지는 붙여넣는 쪽이 정한다. - 인덱스 단위는 LLM 이 골라낸 로직 조각. 파서의 문법 단위(FORM/METHOD/FUNCTION/MODULE/이벤트 블록)는 LLM 에게 보여주는 창이자 조각의 컨테이너다. 프로그램 요약을 먼저 만들어 문맥으로 붙인다.
- 모든 산출물은 코드 해시에 묶여 증분 처리 (unit code_hash 가 같으면 조각 보존).
- 요약·태그·키워드는 한국어 우선 + SAP 영어 용어·객체명 병기.
현재 상태 / 남은 일
- Stage 1·2·4·5·6 동작. 샘플 14본(문장 24,578) 실측: 미인식 문장 30건(0.12%), 프로그램별 최대 0.46%
(프로그램 하나만 보면 안 된다 —
tests/test_parser_fieldsymbol.py가 전체를 잰다) - Stage 3(로직 조각 추출): 구조 완료. LLM 키 없이
--llm file로 실행 가능 (위 참고) - 임베딩/벡터 검색: 모델 미정으로 보류 — FTS(+2-gram, 동의어 확장)만 사용 중
- DB: SQLite 기본. PostgreSQL 전환 스키마는
index/schema_postgres.sql - 보강 과제 10건 중 9건 반영 완료 — 판정·적용 내역은
docs/수정사항-적용.md(1 색인 반영 / 3 텍스트 심볼 FTS / 4 용어 사전 질의 확장 / 6 중복 조각 복제 / 8 엔티티 위키·계층 index / 10 병렬 실행 + 429 백오프. 2·5·9 는 이미 완료·무효) - 남은 일: 7번 정답셋 확대. 개발자 질의 50~100개를 받아 recall@10/MRR 을 재고 베이스라인과
비교해야 한다. 이게 없으면 위 검색 변경들의 정밀도 효과를 검증할 수 없다
(튜닝 대상 값:
query/expand.py의EXPANDED_WEIGHT,MAX_SYNONYMS, 2단 검색 보충 조건) - 프로세스 위키 페이지("입고 처리는 세 경로")는 reduce 단계가 필요해 조각 데이터가 쌓인 뒤로 보류