현재 프로젝트는 FTA·보타이 시뮬레이터와 링크테이블(Trigger 156 · BOM 291 · Driver 87 · 승인매핑 1,902건)로
위험사건의 구조를 코드화하는 데 성공했습니다. 그러나 이 구조지식은 "무엇이 무엇과 연결되는가"는 알려주지만,
"그 사건이 어떤 내규·지침·법령의 어느 조항에 걸리는가", "실제 상황에서 어떤 시나리오로 전개되는가", "그 결과를 어떤 보고서로 정리해야 하는가"는 담지 못합니다.
이 간극을 메우는 것이 언어모델 + 벡터DB(RAG) 계층입니다.
지금까지 (정적 구조지식)
· 링크테이블·마스터코드로 FTA/보타이 자동 생성
· 위험도 산정 = 결정적 집계 연산
· 사람이 규정을 찾아보고, 시나리오를 쓰고, 보고서를 손으로 작성
· 규정 개정 시 반영 지연, 담당자별 품질 편차
제안 솔루션 (검색증강 생성지식)
· 내규·지침·법령을 벡터DB에 임베딩해 의미검색
· 위험사건 코드 → 관련 규범 조항 자동 연결
· 상황 입력 → 시나리오·평가·보고서 초안 자동 생성
· 모든 문장에 출처 조·항 인용 → 검토자 신뢰 확보
설계 관점 한 줄: 링크테이블이 "뼈대(구조)"라면, RAG 지식베이스는 "살(규범·맥락)"입니다.
두 계층을 결합하면 코드(TRG/BOM/DRV)로 검색범위를 좁히고, 규범 텍스트로 근거를 채우는 하이브리드 지식엔진이 됩니다.
2. 지식베이스 설계 — 3계층 지식을 하나의 검색공간으로
RAG의 품질은 "무엇을 넣느냐"가 8할입니다. 본 솔루션은 성격이 다른 3계층을 각기 다른 방식으로 색인해 하나의 검색 오케스트레이션으로 묶습니다.
📜
계층 A — 규범지식 (내규·지침·법령)철도안전법·시행령·시행규칙, 국토부 고시·지침, 사내 안전관리규정·작업내규·매뉴얼. 비정형 장문 → 조/항/호 단위 구조인식 청킹 후 임베딩
Vector (pgvector)
🧩
계층 B — 구조지식 (FTA·보타이 링크테이블)Trigger·BOM·Driver 마스터 + 1,902 승인매핑 + 보타이 예방/완화 배리어. 정형 코드 → RDB 그대로 + 각 노드 설명문을 임베딩하여 코드↔의미 양방향 검색
RDB + Vector
📈
계층 C — 수치지식 (고장이력·RAMS)고장이력 4-테이블, 운용이력, λ·MTBF·가용도 산정값. 정형 수치 → RDB 집계/함수 호출(Tool)로 실시간 조회. 생성 시점에 최신 통계 주입
RDB Tool
핵심: 규범(A)은 의미검색, 구조(B)는 코드조인 + 의미검색, 수치(C)는 함수호출로 가져옵니다.
세 경로를 한 프롬프트에 합치는 것이 이 솔루션의 엔지니어링 난이도이자 차별점입니다.
2-1. 규범 문서의 "구조인식 청킹" — 왜 단순 분할로는 안 되는가
법령·규정은 조(條)–항(項)–호(號) 위계와 상호 참조("제12조제3항에 따라…")가 핵심입니다.
고정 길이(예: 500자)로 자르면 조항 경계가 깨지고 인용 정확도가 무너집니다. 조항 경계를 인식해 자르고, 각 청크에 메타데이터(법령명·조·항·시행일·개정이력)를 부착합니다.
ingest/chunk_regulation.py — 조·항 단위 구조인식 청킹 + 메타데이터 부착import re
from dataclasses import dataclass
# 제N조(제목) ... 형태의 조 경계와 ①②③ / 1.2.3. 항·호를 인식
ARTICLE = re.compile(r"제(\d+)조(?:의(\d+))?\s*\(([^)]*)\)")
CLAUSE = re.compile(r"(?:^|\s)([①-⑮]|\d+\.)\s")
@dataclassclassRegChunk:
law_name: str; effective_date: str # 법령명·시행일(개정 추적)
article: str; clause: str; text: str
ref_codes: list # 연계된 TRG/BOM/DRV 코드defchunk_regulation(raw: str, law_name: str, effective_date: str):
chunks, cur = [], Nonefor line in raw.splitlines():
m = ARTICLE.search(line)
if m: # 새 조(條) 시작 → 청크 확정if cur: chunks.append(cur)
art = f"제{m.group(1)}조" + (f"의{m.group(2)}"if m.group(2) else"")
cur = RegChunk(law_name, effective_date, art, m.group(3), line, [])
elif cur:
cur.text += "\n" + line
if cur: chunks.append(cur)
return [c for c in chunks iflen(c.text) > 20] # 너무 짧은 조 제거
효과: 청크 = 하나의 "조" 단위이므로, 검색 결과가 곧 인용 가능한 최소 단위가 됩니다.
생성 답변에 「철도안전법 제39조의3 제2항(시행 2026.x.x)」처럼 정확한 근거를 붙일 수 있습니다.
3. 아키텍처 — 온프레미스 RAG 파이프라인
망분리·데이터 반출 금지 환경을 전제로, 전 구성요소를 사내에서 구동합니다. 임베딩·생성 모두 로컬 모델을 사용해 외부 API 호출 0건을 보장합니다.
1수집·전처리PDF/HWP/DOCX 규정 → 텍스트화 → 조·항 구조인식 청킹 ETL
2임베딩bge-m3(다국어·한국어 강함) 또는 KoSimCSE로 벡터화 Ollama
3색인pgvector에 벡터+메타데이터 적재, HNSW 인덱스 pgvector
4검색코드필터 + 벡터유사도 + 키워드(BM25) 하이브리드 Retrieve
5생성Gemma3가 근거청크 기반 시나리오/보고서 작성 Gemma3
6검증·인용근거 인용 부착 + RAGAS 자동평가 + 검토승인 Guard
3-1. 스택 선택 근거 (온프레미스 전제)
구성요소
채택
선택 근거
LLM(생성)
Gemma3 (Ollama 서빙)
사내 GPU에서 구동, 한국어 성능·라이선스 양호. 무거운 보고서 생성은 대형 파라미터, 실시간 태깅은 경량 모델로 모델 라우팅
임베딩
bge-m3 / KoSimCSE
한국어·법령체 문어에 강함, 1024차원. Ollama로 동일 인프라에서 서빙해 운영 단순화
벡터스토어
PostgreSQL + pgvector
기존 RDB(고장이력·RAMS)와 동일 DB 안에 벡터 저장 → 코드조인·수치조회·의미검색을 한 트랜잭션에서. 별도 벡터DB 운영부담 제거
faithfulness(근거충실도)·answer relevancy·context precision을 정량화 → 규정 오인용 여부를 자동 감지
왜 pgvector인가: 이 솔루션의 본질은 "코드(구조) + 벡터(규범) + 수치(통계)"의 결합입니다.
세 가지를 서로 다른 저장소에 두면 조인이 애플리케이션 레벨로 새어나가 복잡해집니다. 한 PostgreSQL 안에 두면 WHERE trg_code = '...' AND embedding <=> query < θ 한 쿼리로 끝납니다.
3-2. 벡터 스키마 & 적재 (pgvector)
db/schema.sql — 규범 청크 벡터 테이블 (기존 RDB와 동일 DB)CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE reg_chunk (
id bigserial PRIMARY KEY,
law_name text, -- 철도안전법 / 안전관리규정 …
article text, -- 제39조의3
clause text, -- 제2항
effective_date date, -- 시행일(개정 추적)
doc_type text, -- law | notice | internal
ref_codes text[], -- {TRG-0001, BOM-...} 연계코드
content text,
embedding vector(1024) -- bge-m3 차원
);
-- 의미검색용 HNSW + 코드/유형 필터용 B-tree·GIN 병행CREATE INDEX ON reg_chunk USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON reg_chunk USING gin (ref_codes);
CREATE INDEX ON reg_chunk (doc_type, effective_date);
ingest/embed_load.py — Ollama 임베딩 → pgvector 적재import ollama, psycopg
from pgvector.psycopg import register_vector
defembed(text: str) -> list:
# 온프레미스 Ollama 임베딩 (외부 호출 없음)
r = ollama.embed(model="bge-m3", input=text)
return r["embeddings"][0]
defload_chunks(chunks, dsn: str):
with psycopg.connect(dsn) as conn:
register_vector(conn)
for c in chunks:
conn.execute(
"""INSERT INTO reg_chunk
(law_name, article, clause, effective_date, doc_type,
ref_codes, content, embedding)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s)""",
(c.law_name, c.article, c.clause, c.effective_date,
c.doc_type, c.ref_codes, c.text, embed(c.text)))
conn.commit()
3-3. 하이브리드 검색 — 코드로 좁히고, 의미로 찾고, 키워드로 보정
위험사건 코드가 주어지면 ①연계코드 필터로 후보를 좁힌 뒤 ②벡터 유사도로 정렬하고,
법조문 특유의 정확 용어(예: "정거장", "선로전환기")는 ③키워드 매칭으로 보정합니다. 개정 추적을 위해 시행일 최신본만 필터링합니다.
retrieve/hybrid_search.py — 코드필터 + 벡터 + 키워드 하이브리드defretrieve(conn, query: str, ref_codes: list = None, k: int = 6):
qvec = embed(query)
sql = """
SELECT law_name, article, clause, content,
1 - (embedding <=> %(qv)s) AS sim, -- 코사인 유사도
ts_rank(to_tsvector('simple', content),
plainto_tsquery('simple', %(q)s)) AS kw -- 키워드 점수
FROM reg_chunk
WHERE effective_date = (SELECT max(effective_date) -- 최신 시행본
FROM reg_chunk r2 WHERE r2.article = reg_chunk.article)
{code_filter}
ORDER BY (0.75 * (1 - (embedding <=> %(qv)s)) + 0.25 * ...) DESC -- 가중 결합
LIMIT %(k)s"""
code_filter = "AND ref_codes && %(codes)s"if ref_codes else""return conn.execute(sql.format(code_filter=code_filter),
{"qv": qvec, "q": query,
"codes": ref_codes, "k": k}).fetchall()
주의 — 규범 도메인의 함정: 일반 RAG와 달리 법령은 "틀린 조항 인용"이 곧 규정 위반 리스크입니다.
① 최신 시행본만 검색(개정 반영), ② 유사도 임계값 미달 시 "관련 규정 없음"을 명시(억지 인용 금지), ③ 생성 후 인용된 조항이 실제 검색결과에 있었는지 역검증 — 3중 안전장치가 필수입니다.
4. 두 가지 핵심 생성 기능
4-1. 위험 시나리오 생성 Scenario
위험사건 코드(TRG) 또는 상황 설명을 입력하면, FTA 원인경로(구조지식) + 관련 규범(규범지식) + 고장통계(수치지식)를 결합해
"어떻게 전개되고, 어디서 막을 수 있으며, 어떤 규정 위반이 되는가"를 서사형 시나리오로 생성합니다.
입력
TRG-0001(제동장치 고장) + 차종/노선 선택
결합
FTA 6대 경로 · 보타이 배리어 · 관련 조항 · λ/MTBF
출력
사건전개 서사 · 악화/완화 요인 · 위반 우려 조항 · 예방 배리어 점검항목
활용: 안전교육 사례, 위험성평가 워크숍 시드, 비상대응 시나리오 초안, 신규자 훈련 자료.
4-2. 평가·보고서 생성 Report
고장/장애 데이터 또는 사건 개요를 입력하면 위험도평가서·사고조사 보고서·안전점검 결과보고서 등 규정 양식에 맞춰 초안을 생성합니다.
수치는 RDB에서 실시간 조회(할루시네이션 차단), 서술·판단 근거는 규범 청크에서 인용합니다.
입력
사건 개요 / 고장이력 레코드 / 기간·설비 범위
주입
λ·MTBF·가용도(Tool 실측) · 5×5 위험도 · 근거조항
출력
규정 양식 보고서 초안(.docx) · 각 판단에 출처 인용 · 검토체크리스트
활용: 위험도평가서, RAMS 결과보고, 안전관리 정기보고, 개선요구·시정조치 문서.
4-3. LangGraph 오케스트레이션 — 검색→생성→자기검증 순환
보고서 생성은 단발 프롬프트가 아니라 계획→검색→작성→인용검증→(부족 시)재검색의 상태그래프로 구현합니다.
근거가 부족하면 스스로 재검색하고, 인용이 실제 검색결과와 불일치하면 반려하는 자기검증 루프가 신뢰성의 핵심입니다.
graph/report_agent.py — LangGraph 상태그래프 (검색·생성·검증 루프)from langgraph.graph import StateGraph, END
from typing import TypedDict
classS(TypedDict):
query: str; codes: list; ctx: list # 검색된 근거청크
stats: dict; draft: str; verified: bool; retries: int
defplan(s): # 어떤 규범·통계가 필요한지 계획
s["codes"] = link_table_lookup(s["query"]); return s
defsearch(s): # 하이브리드 검색 + RDB 통계 Tool 호출
s["ctx"] = retrieve(conn, s["query"], s["codes"])
s["stats"] = rams_stats(s["codes"]); return s
defwrite(s): # Gemma3: 근거청크·통계로만 작성, 문장마다 [출처] 태그
s["draft"] = gemma_generate(REPORT_TMPL, s["ctx"], s["stats"]); return s
defverify(s): # 인용 조항이 실제 ctx에 존재? faithfulness 통과?
s["verified"] = check_citations(s["draft"], s["ctx"]); return s
defroute(s): # 검증 실패 & 재시도 여력 → 재검색, 아니면 종료return"search"if (not s["verified"] and s["retries"] < 2) else END
g = StateGraph(S)
for n, fn in [("plan",plan),("search",search),("write",write),("verify",verify)]:
g.add_node(n, fn)
g.set_entry_point("plan")
g.add_edge("plan","search"); g.add_edge("search","write"); g.add_edge("write","verify")
g.add_conditional_edges("verify", route) # 자기검증 순환
report_agent = g.compile()
이 구조의 가치: 생성물이 근거를 스스로 검증하고 부족하면 스스로 재검색합니다.
규범 도메인에서 "그럴듯하지만 틀린 조항"을 최종 산출물에서 걸러내는 것이 사람 검토자의 부담을 실질적으로 줄이는 지점입니다.
5. 신뢰성·거버넌스 — 규범 AI가 반드시 갖춰야 할 안전장치
① 근거 강제(Grounding)
모든 판단 문장에 「법령명 제○조제○항」 인용을 강제. 근거 없는 문장은 생성 규칙에서 차단. 검색결과에 없는 조항 인용 시 자동 반려(역검증).
② 개정 추적
시행일 메타데이터로 최신본만 검색. 규정 개정 시 재임베딩 파이프라인이 구본을 만료 처리 → "지난 규정으로 판단" 리스크 제거.
③ 정량 자동평가
RAGAS로 faithfulness·context precision 상시 측정. 임계 미달 답변은 검토큐로 라우팅. 회귀 테스트셋으로 모델 교체 영향 검증.
④ 망분리·데이터 주권
임베딩·생성 전부 온프레미스(Ollama). 규정·고장데이터 외부 반출 0건. 공공기관 보안·데이터 거버넌스 요건 충족.
⑤ 사람-검토 워크플로
생성물은 "초안"으로만 취급, 승인 전 배포 불가. FTA/보타이 승인체계(APPROVED)와 동일한 거버넌스 계승.
⑥ 모름의 명시
근거 부족 시 억지 생성 대신 "관련 규정 미확인" 표기. FTA의 UNKNOWN(D9xxx) 철학을 생성 계층에도 일관 적용.
가장 중요한 설계 원칙: 이 솔루션은 판단을 대체하지 않고 근거를 조립합니다.
법령 해석·최종 판단의 책임은 사람에게 있으며, AI는 "관련 조항을 빠짐없이·정확히·출처와 함께" 모아주는 역할에 한정하는 것이 규제·감사 대응 측면에서 안전합니다.
6. 착수 로드맵 — 우선순위 순
#
단계
내용
산출물
1
규범 코퍼스 구축
철도안전법 3단(법·령·규칙) + 핵심 사내 규정 5~10종을 텍스트화·구조인식 청킹. 파일럿 범위는 제동·신호 등 1~2개 설비군으로 한정해 품질 검증
청킹 코퍼스 · 메타데이터 스키마
2
pgvector 색인 & 검색 PoC
기존 RDB에 pgvector 확장, bge-m3 임베딩 적재. 코드필터+벡터 하이브리드 검색의 Top-k 정확도(정답 조항 포함율) 측정
검색 모듈 · 정확도 리포트
3
링크테이블 ↔ 규범 연계 매핑
TRG/BOM/Driver 코드에 관련 조항을 ref_codes로 연결. 최다빈도 Driver(D601·D602·D9002 등)부터 착수해 커버리지 조기 확보
코드-규범 매핑테이블
4
시나리오 생성 프로토타입
TRG 입력 → FTA경로+규범+통계 결합 시나리오 생성. 안전교육/워크숍 1건에 실제 투입해 현업 피드백 수집
시나리오 생성기 · 사용성 평가
5
보고서 생성 + LangGraph 검증루프
위험도평가서 1종을 대상으로 자기검증 그래프 구현. RAGAS 기준선 설정, 근거 역검증 통과율을 배포 게이트로 지정
보고서 에이전트 · 평가 대시보드
6
거버넌스·운영 정착
규정 개정 시 재임베딩 자동화, 검토승인 워크플로, 접근권한·감사로그. FTA 승인체계와 통합 운영
운영 SOP · 재색인 파이프라인
Quick Win: 이미 보유한 온프레미스 RAG 자산(Ollama·Gemma3·pgvector 경험)과 FTA/보타이 구조지식(1,902 매핑)이 있으므로,
1~2단계는 신규 구축이 아니라 기존 자산의 결합입니다. 좁은 설비군 1개로 시나리오 생성 데모를 먼저 성공시켜 조직 내 설득력을 확보하는 전략을 권장합니다.