StructVerify
문서가 규칙을 지켰는지, 수치가 사실인지 코드 한 줄로 검사하는 파이썬 라이브러리입니다.
내 규정집(PDF)이나 내 데이터 소스(CSV · DB · 문서 · 공공통계)를 정답으로 삼아 문서를 대조하고,
결과에서 평범한 True / False를 읽습니다.
import structverify as sv
rules = sv.Ruleset.from_file("safety_standard.pdf", provider="upstage")
v = rules.check("총납 함량은 120mg/kg으로 측정되었다")
v.compliant # False <- 평범한 True/False
v.article # "제3조 (총납) ... 100mg/kg 이하"
v.rule_value, v.claim_value, v.unit # (100.0, 120.0, 'mg/kg')
공식 문서: 사용법 · 레퍼런스 · 학습 가이드 · 3분 데모 영상
목차
- 무엇을 하는 라이브러리인가
- 설치
- 빠른 시작
- 데이터 소스: 무엇이든 정답으로
- 동작 구조
- 감독 학습 루프: 쓸수록 정확해지는 검증
- 결과 객체
- 설정 · 로깅 · 진행 대시보드
- 검증 실적
- 예제
- 프로젝트 구조
- 테스트
- 알려진 한계
- 라이선스
무엇을 하는 라이브러리인가
기업과 기관에서는 시험성적서, 경영 보고서, 공시 자료 같은 문서가 정해진 기준과 실제 데이터에 부합하는지 사람이 규정집과 원본 데이터를 대조하며 수작업으로 확인합니다. StructVerify는 이 대조 작업을 두 갈래의 API로 자동화합니다.
| 검증 종류 | 진입점 | 정답 기준 | 판정 |
|---|---|---|---|
| 규정 준수 검사 | Ruleset |
내 규정집 (PDF/텍스트) | compliant / violation / unverifiable |
| 사실 검증 | sv.verify · Verifier |
내 데이터 소스 (CSV · DB · 문서 · KOSIS 통계) | match / mismatch / unverifiable |
핵심 설계 원칙은 다음과 같습니다.
- 내 데이터를 정답으로. 공공 데이터에 고정된 기존 사실검증 도구와 달리, 사용자의 규정 PDF · 참조 CSV · 사내 DB를 그대로 정답 기준으로 삼습니다. 도메인에 독립적입니다.
- 판정은 코드가, 결정론적으로. LLM은 기준값과 측정값을 추출하고 근거를 수집하는 역할만 합니다. 준수/위반, 일치/불일치 판정은 허용오차와 한도 규칙에 따라 코드가 결정론적으로 계산하므로 같은 입력에는 같은 판정이 나옵니다.
- 결과가 곧 불리언.
.ok·.compliant·.violated,bool(result), 순회(iterable),.to_dict(). 함수에 점을 찍어 부르면 원하는 값이 바로 나옵니다. - 원시 테이블만 있어도 검증. 지표 정의가 없는 트랜잭션 테이블이라도 에이전트가 스키마를 조사해 읽기 전용 SQL을 스스로 작성하고, 집계·비율 주장까지 검증합니다.
- provider에 얽매이지 않음.
upstage·openai·gemini·hcx중 무엇이든 설정 한 줄로 전환하고, 직접 학습한 모델도 주입할 수 있습니다. - 감독 학습 루프 내장. 검증에서 축적된 확정 정답으로 자체 모델을 LoRA 파인튜닝하고, 데이터 품질 검사 · 실시간 학습 감독 · 자가평가 후 채택까지 라이브러리가 관리합니다.
설치
pip install structverify # 코어: 규정 준수 + CSV/문서 사실검증 (의존성 7개)
pip install "structverify[db]" # + 회사 DB 검증 (SQLAlchemy: sqlite/postgres/Snowflake 등)
pip install "structverify[kosis]" # + 공공통계(KOSIS) 사실검증
pip install "structverify[training]" # + 감독 학습 루프 GPU 레시피 (NVIDIA, unsloth QLoRA)
pip install "structverify[training-mac]" # + Apple Silicon 학습 (MLX)
pip install "structverify[all]" # 전부
코어는 pydantic · httpx · pyyaml · openai · pdfplumber · python-dotenv · json5만 받습니다.
무거운 의존성은 전부 필요할 때만 extra로 설치되며, import도 지연 로딩이라 가볍습니다.
Python 3.10 이상을 지원합니다.
API 키는 인자로 직접 넘기거나(api_key="up_..."), 환경변수 또는 작업 디렉토리의
.env 파일에 두면 자동으로 읽습니다 (UPSTAGE_API_KEY · OPENAI_API_KEY ·
GEMINI_API_KEY · NCP_API_KEY).
빠른 시작
규정 준수 검사: 내 규정집으로
PDF/텍스트 규정집(안전기준 · 사내정책 · 표시기준 등)을 조항 단위로 색인하고, 문장이 그 규정을 지켰는지 판정합니다. 규정집만 있으면 외부 데이터 없이 바로 동작합니다.
import structverify as sv
rules = sv.Ruleset.from_file("safety_standard.pdf", provider="upstage")
len(rules) # 색인된 조항 수
v = rules.check("총납 함량은 120mg/kg으로 측정되었다")
v.compliant # False
v.violated # True
v.article # "제3조 (총납) ... 100mg/kg 이하"
v.rule_value, v.claim_value, v.unit # (100.0, 120.0, 'mg/kg')
v.reason # 판정 근거 (자연어)
# 문서 전체: 측정값이 있는 줄마다 자동 판정
for v in rules.check_file("시험성적서.pdf"):
print("준수" if v.compliant else "위반", v.article, v.rule_value, v.claim_value)
조항이 많은 큰 규정집은 agent=True로 ReAct 에이전트 루프를 켭니다. 한 번의 검색으로
적용 조항을 놓칠 수 있을 때, 검색 - 판정 - 쿼리 재구성 - 재검색을 판정이 확정될 때까지
스스로 반복합니다.
rules = sv.Ruleset.from_file("big_rulebook.pdf", provider="upstage", agent=True)
v = rules.check("화장품 납 함량은 30㎍/g으로 측정되었다")
v.violated # True
v.iterations # 몇 바퀴 만에 확정했는지 (1이면 한 번에, 2 이상이면 재검색함)
사실 검증: 데이터 소스와 대조
문서의 수치 주장을 설정된 데이터 소스와 대조합니다.
import structverify as sv
# 한 줄 검증
report = sv.verify("과수농가 65세 이상 비율은 64.2%다", provider="upstage")
report.ok # 거짓 주장이 없으면 True
if report: # Report 자체가 True/False로 동작
print("거짓 주장 없음")
# 엔진 재사용 (여러 문서를 검증할 때)
engine = sv.Verifier(provider="upstage", data=sv.DataSource.csv("reference.csv"))
report = engine.verify(long_text)
report.mismatches # 반박된 주장만
for r in report: # 순회 가능, len(report) == 주장 수
print(r.verdict, r.confidence, r.value, r.reason)
PDF 파일도 그대로 넣을 수 있습니다: engine.verify_document("경영실적보고서.pdf").
모든 동기 메서드는 a 접두사의 비동기 쌍을 가집니다 (check/acheck, verify/averify).
데이터 소스: 무엇이든 정답으로
DataSource 팩토리로 정답 기준을 연결합니다. 경로 문자열만 넘겨도 확장자로 추론합니다.
sv.DataSource.csv("부채비율.csv") # 참조 통계 CSV
sv.DataSource.csv("t.csv", columns={"value": "amount"}) # 컬럼명 매핑
sv.DataSource.docs("policies/") # 회사 문서 폴더 (의미검색)
sv.DataSource.db("postgresql://...", table="kpi") # 정돈된 지표 표
sv.DataSource.kosis() # 내장 KOSIS 공공통계
에이전틱 DB 검증: 원시 테이블 그대로
지표 정의가 없는 원시 트랜잭션 테이블은 agentic=True 하나로 검증합니다. 에이전트가
스키마와 표본을 조사해 주장마다 읽기 전용 집계 SELECT를 자동 생성하며, 합계 · 평균 ·
비중 같은 집계/비율 주장까지 대조합니다. SQLAlchemy DSN이면 어떤 DB든 연결됩니다
(sqlite · PostgreSQL · MySQL · Snowflake 등).
# 원시 주문 테이블에서 직접 검증
raw = sv.DataSource.db("snowflake://user:pw@account/DB/SCHEMA?warehouse=WH",
agentic=True, tables=["ORDERS", "LINEITEM"])
report = sv.verify("1998년 연간 순매출은 3.2조 달러를 기록했다", provider="upstage", data=raw)
report[0].verdict # 'mismatch' (실제 집계값과 대조한 결과)
# 지표가 수백 개인 대형 표는 임베딩 의미검색으로 자동 전환
sv.DataSource.db(dsn, table="BIG_KPI", use_embedding="auto", embed_threshold=200)
모든 SQL은 읽기 전용으로만 실행되며, 쓰기 구문은 실행 전에 차단됩니다.
동작 구조
- 문서에서 수치 주장을 탐지하면, 소스 프로파일러가 기준 자료를 먼저 조사해 검색 전략을 수립하고, 주장 하나마다 RuntimeAgent(ReAct 루프)가 독립 실행됩니다.
- 에이전트는 Planner - 도구 실행(의미 검색 · 에이전틱 SQL · 검색어 재구성 · 표 샘플 조사) - Reflect를 반복하며 근거를 확보합니다. 근거가 부족하면 재계획하고, 최대 10회 반복하며, 신뢰도 0.9 이상이면 조기 종료합니다.
- 시도 이력은 워크스페이스(
./agent_workspace)의 작업 메모리에 기록되어 중복 검색을 방지하고, 확정된 값은verified_facts캐시로 다음 주장에서 재사용됩니다. - 준수/사실 판정은 LLM이 아닌 코드가 허용오차 · 한도 규칙으로 결정론적으로 계산합니다.
- 검증에서 축적된 확정 정답은 감독 학습 루프를 거쳐 개선된 모델로 파이프라인에 다시 주입됩니다.
파이프라인 내부 구현(에이전트 루프 · 도구 11종 · 워크스페이스 캐시 · 설정 스키마)은 docs/architecture-internals.md에 정리되어 있습니다.
감독 학습 루프: 쓸수록 정확해지는 검증
검증에서 나온 확정 정답으로 자체 7B 모델을 LoRA 파인튜닝합니다. 단순한 학습 스크립트가 아니라 감독 3종이 학습의 앞 · 중간 · 뒤를 지킵니다.
| 감독 | 시점 | 역할 |
|---|---|---|
| DataCurator | 학습 전 | 중복 · 라벨 오류 · 태스크 편중을 격리하고 사유를 기록 |
| TrainDoctor | 학습 중 · 후 | 실시간 손실 감독(터미널 한 줄 UI). 수렴 정체나 발산이면 스스로 조기 종료하고, 학습 후 수렴 스텝 진단과 처방 제공 |
| EvalGate | 학습 후 | 학습 전/후 엔진을 같은 평가셋으로 채점해 개선된 경우에만 채택 (회귀 방지) |
from structverify.training import LearningLoop
loop = LearningLoop(engine, base_model="unsloth/Qwen2.5-7B-Instruct")
loop.add_seed().add_reports(reports).add_jsonl("corrections.jsonl") # 데이터 수집 (체이닝)
ds, curation = loop.prepare("train.jsonl") # DataCurator: 품질검사 후 clean 데이터셋
loop.train(ds, "./adapter", backend="auto", # NVIDIA는 QLoRA, Apple Silicon은 MLX
run=True, early_stop=True, patience=200) # 수렴하면 스스로 멈춤
loop.diagnose("./adapter/trainer_state.json") # 사후 진단과 처방
gate = loop.evaluate(eval_set, tuned_engine=tuned) # 개선 확인 후에만 채택
학습 중 터미널에는 감독 상태 한 줄만 갱신됩니다 (색상은 tty 자동 감지,
NO_COLOR / SV_COLOR 지원):
🧠 336/2496 ▓░░░░░░░ 13% · loss 0.108 ▆█▄▄▁▁ · lr 1.5e-04 · ETA 2:29 · ⚡1 · 🩺 수렴 유지
✂ step 336: 200스텝 동안 이동평균 개선 없음. 더 학습해도 이득이 없어 조기 종료합니다.
GPU 레시피는 extra로 제공됩니다. [training]은 NVIDIA(RTX 3060 · Colab T4/L4, unsloth
QLoRA 4bit), [training-mac]은 Apple Silicon(MLX)용입니다. 수집 · 품질검사 · 진단 · 평가
등 코어 기능은 GPU 없이 동작합니다. 학습 결과는
python -m structverify.training.recipe.sample --adapter ./adapter로 바로 확인합니다.
학습된 모델 주입
어댑터를 Ollama나 vLLM으로 서빙하고, base_url과 티어별 모델 오버라이드로 파이프라인에
주입합니다. 전체 교체가 아니라 학습된 자리만 점진적으로 전환할 수 있습니다.
cfg = sv.build_config(provider="upstage", api_key="none", data=data)
cfg["llm"]["base_url"] = "http://localhost:11434/v1" # 자체 서빙 주소
cfg["llm"]["models"] = {"structured": "sv-tuned"} # 판정·추출 자리만 교체
tuned = sv.Verifier(config=cfg)
gate = EvalGate(base).evaluate(eval_set, tuned_engine=tuned)
if gate.accepted:
engine = tuned # 나빠졌으면 자동 거부
자세한 절차는 학습 가이드를 참조하세요.
결과 객체
| 객체 | 핵심 불리언 | 주요 필드 |
|---|---|---|
Verdict (규정 준수) |
.compliant · .violated · bool(v) |
.article .rule_value .claim_value .unit .reason .iterations |
Report (문서 단위) |
.ok · bool(report) |
.results .matches .mismatches .unverifiable (순회 가능, len()) |
Result (주장 단위) |
.ok · .is_match · bool(r) |
.verdict .claim .reason .confidence .value .source |
모든 결과 객체는 .to_dict()(JSON 직렬화)와 사람이 읽기 좋은 repr을 지원합니다.
확인이 불가능한 주장은 억지로 판정하지 않고 unverifiable(보류)로 남깁니다. 근거 없이
일치/불일치를 보고하는 경우는 코드 단계에서 보류로 강등됩니다.
설정 · 로깅 · 진행 대시보드
import structverify as sv
# 로깅: 콘솔 + 파일, verbose로 상세도 제어
sv.configure_logging("verification.log", level="INFO", verbose=False)
# config를 미리 만들어 재사용. 세부 키는 dict로 직접 조정
cfg = sv.build_config(
provider="upstage", # upstage | openai | gemini | hcx
tolerance=2.0, # 수치 허용오차(%)
data="reference.csv",
)
cfg["llm"]["max_concurrency"] = 2 # 429(rate limit)가 나면 낮추기
cfg["agent"]["loop"]["max_iterations"] = 6 # 주장당 에이전트 반복 상한
engine = sv.Verifier(config=cfg)
# 진행상황: 로컬 웹 대시보드 + 터미널 진행바
# 환경변수로도 제어 가능: SV_PROGRESS=off|terminal|web
with sv.progress_dashboard(): # 웹 대시보드가 브라우저로 열림
report = engine.verify(text)
검증 실적
완성본 기준으로 실제 데이터에서 end-to-end 검증을 마쳤습니다.
에이전틱 DB 검증 (Snowflake TPCH_SF10, 약 6천만 행 원장)
원시 테이블(고객 150만 · 주문 1,500만 · 라인아이템 5,998만 행)을 정답으로, 오류 5건을 심어 둔 공식 경영실적 보고서 PDF의 수치 주장 20건을 검증했습니다.
| 판정 | 건수 | 내용 |
|---|---|---|
| 일치 (match) | 9 | 총매출 · 지역별 매출 · 고객 수 등 집계 주장 |
| 불일치 (mismatch) | 3 | 심어 둔 오류를 실제 집계값과 함께 적발 |
| 보류 (unverifiable) | 8 | 원장에서 확인 불가능한 주장을 무리하게 판정하지 않고 보류 |
| 오탐 (거짓 경보) | 0 | 맞는 수치를 틀렸다고 판정한 사례 없음 |
감독 학습 루프 (Google Colab L4 GPU)
학습 데이터 1만 건으로 전체 루프(수집 - DataCurator - QLoRA 학습 - TrainDoctor - 어댑터 검증)를 실행했습니다. 실시간 감독의 조기 종료로 2,496스텝 예정 학습이 516스텝에 완료되었고, 산출 어댑터의 품질이 동일함을 확인했습니다.
이 외에 어린이제품 안전기준 · 화장품 안전기준 · 식품 영양표시 기준 등 실제 규정집으로 규정 준수 검사를 검증했습니다. pytest 테스트 226개가 통과합니다.
예제
시연 데이터가 동봉되어 있어 바로 실행됩니다. 각 파일 상단에 필요한 extra와 실행법이 적혀 있습니다.
| 예제 | 내용 |
|---|---|
| 01_quickstart_conformance.py | 규정 준수 검사 빠른 시작 (Ruleset, 에이전트 모드) |
| 02_factcheck_csv.py | CSV를 정답으로 사실 검증 |
| 03_database_agentic.py | 회사 DB 검증: 정돈된 표와 에이전틱 모드 |
| 04_config_logging_progress.py | 설정 · 로깅 · 진행 대시보드 |
| 05_training_loop.py | 감독 학습 루프 전체 흐름 |
| 06_custom_model_injection.py | 학습된 모델 주입과 EvalGate 채택 |
python examples/01_quickstart_conformance.py
프로젝트 구조
| 디렉토리 | 역할 |
|---|---|
structverify/core |
파이프라인 · 데이터 모델 · 설정 로더 |
structverify/preprocessing |
PDF/DOCX/URL/텍스트 추출, 문장 분리 |
structverify/detection |
수치 주장 탐지, 스키마 추출 |
structverify/agent |
RuntimeAgent ReAct 루프, 도구, 워크스페이스 |
structverify/retrieval |
데이터 소스(CSV · DB · 문서 · KOSIS), 카탈로그 의미검색 |
structverify/verification |
결정론 판정 (허용오차 · 한도 규칙 · 단위 계열 가드) |
structverify/training |
감독 학습 루프 (DataCurator · TrainDoctor · EvalGate · GPU 레시피) |
structverify/library |
고수준 API (Ruleset · Verifier · DataSource · build_config) |
sv_platform/ |
FastAPI 플랫폼 (REST API · 인증 · Job). structverify를 감싸는 선택 구성요소 |
내부 구현 상세는 docs/architecture-internals.md, 플랫폼은 sv_platform/README.md를 참조하세요.
테스트
pip install -e ".[dev]"
pytest # 전체 (226개 통과)
pytest structverify/agent/ # 모듈 단위
pytest -k "schema_inductor" # 키워드 필터
알려진 한계
- 확인할 수 없는 주장은 보류(
unverifiable)로 남기는 안전 우선 설계라, 근거가 부족한 환경에서는 보류 비율이 높아질 수 있습니다. - KOSIS 공공통계는 응답이 1~2년 지연되는 경우가 있어 최신 시점 주장은 보류될 수 있습니다.
- 행정구역 개명("강원도"와 "강원특별자치도" 등)이나 아주 오래된 통계 시리즈는 카탈로그 매칭에 실패할 수 있습니다.
- 순위 · 예측 · 주관적 표현은 검증 가능한 수치가 없어 의도적으로 탐지 대상에서 제외합니다.
라이선스
StructVerify는 MIT 라이선스입니다. 자유롭게 사용 · 수정 · 배포할 수 있습니다.
의존성도 모두 permissive 라이선스만 사용합니다.
| 범위 | 패키지 | 라이선스 |
|---|---|---|
| 코어 | pydantic, pyyaml · httpx, python-dotenv, uvicorn · openai, json5, asyncpg, trafilatura, neo4j, mlflow, boto3, snowflake · pdfplumber, kss, redis, fastapi, sqlalchemy, langfuse, python-docx | MIT · BSD-3 · Apache-2.0 |
| 격리(opt-in) | PyMuPDF ([pdf-ocr] extra) |
AGPL-3.0 |
유일한 copyleft인 PyMuPDF(AGPL)는 고급 OCR PDF 파이프라인 전용으로 [pdf-ocr] extra에
격리했습니다. 기본 PDF 처리는 pdfplumber(MIT)를 사용하므로 pip install structverify와
[all] 어떤 경로로도 AGPL이 설치되지 않습니다.
Release files for structverify 0.3.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| structverify-0.3.6.tar.gz | 487.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| structverify-0.3.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / structverify-0.3.6.tar.gz
| Download URL | structverify-0.3.6.tar.gz |
|---|---|
| Size | 487.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2142a4e04e9afc644016d2d027ae94ecb200293696fbbf07fae8781c778b2c0
|
|
BLAKE2b-256 checksum How to use checksums |
4dc8c510ee00df535f1f61f2162960f3e143ec0dc938cf292d36dd892546cab0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / structverify-0.3.6-py3-none-any.whl
| Download URL | structverify-0.3.6-py3-none-any.whl |
|---|---|
| Size | 544.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
21f8486d696a56e0f8735f19e8fd6e979c8babc05920c2c171e44594273ad9d1
|
|
BLAKE2b-256 checksum How to use checksums |
c321fc7e54001e466aa229148b7bb754e3441af2fb6f02b80373afe1fbe8fd33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|