structverify 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- structverify/__init__.py +83 -0
- structverify/adaptation/__init__.py +0 -0
- structverify/adaptation/adapter_trainer.py +341 -0
- structverify/adaptation/feedback_store.py +31 -0
- structverify/adaptation/kosis_crawler.py +317 -0
- structverify/adaptation/sample_builder.py +149 -0
- structverify/adaptation/synthetic_generator.py +320 -0
- structverify/adaptation/update_embeddings.py +178 -0
- structverify/agent/__init__.py +21 -0
- structverify/agent/builder_agent.py +226 -0
- structverify/agent/conformance_agent.py +171 -0
- structverify/agent/dependency_planner.py +151 -0
- structverify/agent/indexing_agent.py +153 -0
- structverify/agent/indexing_planner.py +169 -0
- structverify/agent/integration_example.py +182 -0
- structverify/agent/loop.py +1165 -0
- structverify/agent/memory.py +207 -0
- structverify/agent/planner.py +817 -0
- structverify/agent/prompts/__init__.py +15 -0
- structverify/agent/prompts/planner_prompts.py +219 -0
- structverify/agent/prompts/reflect_prompts.py +387 -0
- structverify/agent/reflect.py +227 -0
- structverify/agent/runtime_agent.py +1272 -0
- structverify/agent/schemas.py +262 -0
- structverify/agent/source_profiler.py +229 -0
- structverify/agent/tools/__init__.py +64 -0
- structverify/agent/tools/base.py +222 -0
- structverify/agent/tools/calculate.py +244 -0
- structverify/agent/tools/catalog_search.py +859 -0
- structverify/agent/tools/deep_explore.py +293 -0
- structverify/agent/tools/explore_catalog.py +423 -0
- structverify/agent/tools/fetch_evidence.py +922 -0
- structverify/agent/tools/finish.py +423 -0
- structverify/agent/tools/meta_explore.py +267 -0
- structverify/agent/tools/query_rewriter.py +134 -0
- structverify/agent/tools/read_original.py +144 -0
- structverify/agent/tools/replan.py +365 -0
- structverify/agent/workspace.py +958 -0
- structverify/api.py +804 -0
- structverify/config/default.yaml +350 -0
- structverify/core/__init__.py +0 -0
- structverify/core/config_loader.py +30 -0
- structverify/core/pipeline.py +280 -0
- structverify/core/schemas.py +362 -0
- structverify/detection/__init__.py +26 -0
- structverify/detection/_config.py +163 -0
- structverify/detection/_llm.py +24 -0
- structverify/detection/candidate/__init__.py +1 -0
- structverify/detection/candidate/heuristic.py +60 -0
- structverify/detection/candidate/llm.py +51 -0
- structverify/detection/candidate_scorer.py +81 -0
- structverify/detection/claim_detector.py +164 -0
- structverify/detection/claims/__init__.py +1 -0
- structverify/detection/claims/worthiness.py +142 -0
- structverify/detection/domain/__init__.py +1 -0
- structverify/detection/domain/classify.py +84 -0
- structverify/detection/domain/preview.py +36 -0
- structverify/detection/domain/registry.py +99 -0
- structverify/detection/domain_classifier.py +75 -0
- structverify/detection/prompts/__init__.py +1 -0
- structverify/detection/prompts/candidate.py +38 -0
- structverify/detection/prompts/claim_worthiness.py +48 -0
- structverify/detection/prompts/domain.py +41 -0
- structverify/detection/prompts/schema.py +508 -0
- structverify/detection/prompts_loader.py +167 -0
- structverify/detection/schema/__init__.py +1 -0
- structverify/detection/schema/expand.py +83 -0
- structverify/detection/schema/induce.py +441 -0
- structverify/detection/schema/regenerate.py +162 -0
- structverify/detection/schema/temporal_hints.py +130 -0
- structverify/detection/schema/validate.py +193 -0
- structverify/detection/schema_inductor.py +112 -0
- structverify/detection/synthetic_generator.py +270 -0
- structverify/explanation/__init__.py +0 -0
- structverify/explanation/_config.py +18 -0
- structverify/explanation/_llm.py +25 -0
- structverify/explanation/explainer.py +183 -0
- structverify/explanation/fallback.py +29 -0
- structverify/explanation/formatters.py +75 -0
- structverify/explanation/prompts/__init__.py +1 -0
- structverify/explanation/prompts/match.py +27 -0
- structverify/explanation/prompts/mismatch.py +20 -0
- structverify/explanation/prompts/multihop.py +16 -0
- structverify/explanation/prompts/unverifiable.py +17 -0
- structverify/graph/__init__.py +0 -0
- structverify/graph/claim_graph.py +226 -0
- structverify/graph/document_graph.py +487 -0
- structverify/graph/graph_builder.py +238 -0
- structverify/graph/graph_multihop.py +335 -0
- structverify/graph/graph_store.py +281 -0
- structverify/graph/provenance.py +52 -0
- structverify/memory/__init__.py +44 -0
- structverify/memory/agent_memory.py +142 -0
- structverify/memory/embedder.py +69 -0
- structverify/memory/exemplar_store.py +241 -0
- structverify/memory/normalizer.py +91 -0
- structverify/memory/schema.py +119 -0
- structverify/memory/storage/__init__.py +29 -0
- structverify/memory/storage/jsonl_store.py +117 -0
- structverify/memory/working_memory.py +370 -0
- structverify/preprocessing/Dockerfile.scraper +27 -0
- structverify/preprocessing/__init__.py +0 -0
- structverify/preprocessing/extractor.py +574 -0
- structverify/preprocessing/pdf/__init__.py +16 -0
- structverify/preprocessing/pdf/fields.py +95 -0
- structverify/preprocessing/pdf/markdown.py +107 -0
- structverify/preprocessing/pdf/models.py +34 -0
- structverify/preprocessing/pdf/ocr.py +172 -0
- structverify/preprocessing/pdf/pipeline.py +74 -0
- structverify/preprocessing/pdf/reader.py +119 -0
- structverify/preprocessing/pdf/scoring.py +61 -0
- structverify/preprocessing/scraper_sandbox.py +561 -0
- structverify/preprocessing/segmenter.py +48 -0
- structverify/preprocessing/sir_builder.py +240 -0
- structverify/progress.py +591 -0
- structverify/retrieval/__init__.py +0 -0
- structverify/retrieval/base.py +208 -0
- structverify/retrieval/base_connector.py +85 -0
- structverify/retrieval/catalog_ranker.py +300 -0
- structverify/retrieval/catalog_search.py +583 -0
- structverify/retrieval/chunking.py +92 -0
- structverify/retrieval/custom_csv_source.py +386 -0
- structverify/retrieval/custom_db_source.py +396 -0
- structverify/retrieval/custom_docs_source.py +152 -0
- structverify/retrieval/dimension_resolver.py +281 -0
- structverify/retrieval/evidence_subgraph.py +63 -0
- structverify/retrieval/kosis_connector.py +1192 -0
- structverify/retrieval/kosis_relevance.py +142 -0
- structverify/retrieval/kosis_source.py +1541 -0
- structverify/retrieval/query_builder.py +72 -0
- structverify/retrieval/registry.py +133 -0
- structverify/retrieval/relevance_judge.py +141 -0
- structverify/retrieval/row_matcher.py +267 -0
- structverify/storage/__init__.py +0 -0
- structverify/storage/db_manager.py +157 -0
- structverify/storage/dwh_manager.py +92 -0
- structverify/storage/init_db.py +99 -0
- structverify/storage/raw_storage.py +29 -0
- structverify/training/__init__.py +26 -0
- structverify/training/curator.py +124 -0
- structverify/training/dataset.py +134 -0
- structverify/training/doctor.py +99 -0
- structverify/training/evalgate.py +96 -0
- structverify/training/generate.py +101 -0
- structverify/training/loop.py +116 -0
- structverify/training/recipe/train_mlx.py +99 -0
- structverify/training/recipe/train_qlora.py +104 -0
- structverify/training/tasks.py +79 -0
- structverify/utils/__init__.py +0 -0
- structverify/utils/embedding_client.py +248 -0
- structverify/utils/llm_client.py +809 -0
- structverify/utils/logger.py +81 -0
- structverify/verification/__init__.py +0 -0
- structverify/verification/_config.py +45 -0
- structverify/verification/adapters.py +405 -0
- structverify/verification/conformance.py +117 -0
- structverify/verification/decide_verdict.py +216 -0
- structverify/verification/decide_verdict_agent.py +454 -0
- structverify/verification/growth_diff.py +267 -0
- structverify/verification/row_match.py +345 -0
- structverify/verification/units.py +64 -0
- structverify/verification/verdict_thresholds.py +232 -0
- structverify/verification/verifier.py +84 -0
- structverify-0.3.0.dist-info/METADATA +903 -0
- structverify-0.3.0.dist-info/RECORD +168 -0
- structverify-0.3.0.dist-info/WHEEL +5 -0
- structverify-0.3.0.dist-info/licenses/LICENSE +21 -0
- structverify-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""detection/prompts_loader.py — domain-pack YAML 로드·few-shot 헬퍼.
|
|
2
|
+
|
|
3
|
+
domain_classifier.py의 _load_domain_pack()에서 분리 (로직 move-only).
|
|
4
|
+
|
|
5
|
+
[김예슬 - 2026-04-22] domain-packs/{domain}/prompts.yaml
|
|
6
|
+
TODO [김예슬]: claim_detector/candidate_scorer few-shot 주입은 #13 이후 연결
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
15
|
+
from structverify.detection._config import domain_packs_dir as _domain_packs_dir
|
|
16
|
+
from structverify.utils.logger import get_logger
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def domain_packs_dir(config: dict | None) -> str:
|
|
22
|
+
"""config에서 domain-packs 루트 경로 (detection._config 위임)."""
|
|
23
|
+
return _domain_packs_dir(config)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def prompts_yaml_path(domain: str, config: dict | None) -> str:
|
|
27
|
+
"""domain-packs/{domain}/prompts.yaml 절대/상대 경로."""
|
|
28
|
+
return os.path.join(domain_packs_dir(config), domain, "prompts.yaml")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_domain_pack(domain: str, config: dict | None = None) -> dict[str, Any] | None:
|
|
32
|
+
"""
|
|
33
|
+
domain-packs/{domain}/prompts.yaml 로드.
|
|
34
|
+
없으면 None 반환 (에러 아님).
|
|
35
|
+
"""
|
|
36
|
+
yaml_path = prompts_yaml_path(domain, config)
|
|
37
|
+
|
|
38
|
+
if not os.path.exists(yaml_path):
|
|
39
|
+
logger.debug(f"Domain Pack 없음: {yaml_path}")
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
with open(yaml_path, encoding="utf-8") as f:
|
|
44
|
+
pack = yaml.safe_load(f)
|
|
45
|
+
logger.info(f"Domain Pack 로드: {yaml_path}")
|
|
46
|
+
if isinstance(pack, dict):
|
|
47
|
+
return pack
|
|
48
|
+
return None
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.warning(f"Domain Pack 로드 실패: {yaml_path} — {e}")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_domain_prompts(domain: str, config: dict | None = None) -> dict[str, Any] | None:
|
|
55
|
+
"""load_domain_pack 별칭 — claim_detector TODO 명칭과 동일."""
|
|
56
|
+
return load_domain_pack(domain, config)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def few_shot_examples_from_pack(
|
|
60
|
+
pack: dict[str, Any] | None,
|
|
61
|
+
*,
|
|
62
|
+
section: str = "few_shot_examples",
|
|
63
|
+
) -> list[Any]:
|
|
64
|
+
"""
|
|
65
|
+
pack dict에서 few-shot 예시 리스트 추출.
|
|
66
|
+
|
|
67
|
+
section 키가 없으면 빈 리스트 (동작 변경 없이 주입 전 단계용).
|
|
68
|
+
"""
|
|
69
|
+
if not pack:
|
|
70
|
+
return []
|
|
71
|
+
raw = pack.get(section)
|
|
72
|
+
if isinstance(raw, list):
|
|
73
|
+
return raw
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def format_few_shot_block(examples: list[Any]) -> str:
|
|
78
|
+
"""
|
|
79
|
+
few-shot 예시를 프롬프트에 붙일 블록 문자열로 변환.
|
|
80
|
+
|
|
81
|
+
examples 원소: str 또는 {"input": ..., "output": ...} dict.
|
|
82
|
+
"""
|
|
83
|
+
if not examples:
|
|
84
|
+
return ""
|
|
85
|
+
lines: list[str] = ["", "[도메인 few-shot 예시]"]
|
|
86
|
+
for i, ex in enumerate(examples, 1):
|
|
87
|
+
if isinstance(ex, str):
|
|
88
|
+
lines.append(f" {i}. {ex}")
|
|
89
|
+
elif isinstance(ex, dict):
|
|
90
|
+
inp = ex.get("input") or ex.get("sentence") or ""
|
|
91
|
+
out = ex.get("output") or ex.get("label") or ""
|
|
92
|
+
lines.append(f" {i}. input={inp!r} → {out!r}")
|
|
93
|
+
else:
|
|
94
|
+
lines.append(f" {i}. {ex!r}")
|
|
95
|
+
return "\n".join(lines)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def inject_few_shot(
|
|
99
|
+
base_prompt: str,
|
|
100
|
+
pack: dict[str, Any] | None,
|
|
101
|
+
*,
|
|
102
|
+
section: str = "few_shot_examples",
|
|
103
|
+
) -> str:
|
|
104
|
+
"""base_prompt 뒤에 pack의 few-shot 블록을 붙인다. pack 없으면 원문 그대로."""
|
|
105
|
+
block = format_few_shot_block(few_shot_examples_from_pack(pack, section=section))
|
|
106
|
+
if not block:
|
|
107
|
+
return base_prompt
|
|
108
|
+
return base_prompt.rstrip() + block + "\n"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def resolve_prompt_for_step(
|
|
112
|
+
base_prompt: str,
|
|
113
|
+
domain: str | None,
|
|
114
|
+
config: dict | None,
|
|
115
|
+
*,
|
|
116
|
+
step: str,
|
|
117
|
+
) -> str:
|
|
118
|
+
"""
|
|
119
|
+
domain-pack에서 step별 few-shot을 찾아 base_prompt에 붙인다.
|
|
120
|
+
|
|
121
|
+
키 우선순위: {step}_few_shot → {step}_examples → {step} → few_shot_examples
|
|
122
|
+
pack/예시 없으면 base_prompt 그대로 (기존 동작 유지).
|
|
123
|
+
"""
|
|
124
|
+
if not domain:
|
|
125
|
+
return base_prompt
|
|
126
|
+
pack = load_domain_pack(domain, config)
|
|
127
|
+
if not pack:
|
|
128
|
+
return base_prompt
|
|
129
|
+
for section in (f"{step}_few_shot", f"{step}_examples", step, "few_shot_examples"):
|
|
130
|
+
if few_shot_examples_from_pack(pack, section=section):
|
|
131
|
+
return inject_few_shot(base_prompt, pack, section=section)
|
|
132
|
+
return base_prompt
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def resolve_step_prompt(
|
|
136
|
+
default_template: str,
|
|
137
|
+
fmt_kwargs: dict[str, Any],
|
|
138
|
+
domain: str | None,
|
|
139
|
+
config: dict | None,
|
|
140
|
+
*,
|
|
141
|
+
step: str,
|
|
142
|
+
) -> str:
|
|
143
|
+
"""step별 프롬프트 해석 — 도메인팩이 `{step}_prompt`로 *기본 정의 자체를 교체* 가능.
|
|
144
|
+
|
|
145
|
+
few-shot append(resolve_prompt_for_step)로는 base 프롬프트의 강한 기준을 못 이기는
|
|
146
|
+
경우(예: 컴플라이언스 check-worthiness)를 위해, 도메인팩이 base 프롬프트를 통째로
|
|
147
|
+
바꿀 수 있게 한다. 교체 없으면 default_template. 이후 few-shot 예시를 append.
|
|
148
|
+
|
|
149
|
+
default_template / 교체 template 은 fmt_kwargs로 .format() 된다 (예: {sentence}).
|
|
150
|
+
"""
|
|
151
|
+
tmpl = default_template
|
|
152
|
+
# 1) config.advanced.prompt_overrides.{step} — 배포 전역(도메인 무관). 컴플라이언스 배포용.
|
|
153
|
+
_ov = ((config or {}).get("advanced", {}) or {}).get("prompt_overrides", {}) or {}
|
|
154
|
+
if _ov.get("enabled") and isinstance(_ov.get(step), str) and _ov[step].strip():
|
|
155
|
+
tmpl = _ov[step]
|
|
156
|
+
logger.info(f"[prompts] step={step} config override 프롬프트 적용")
|
|
157
|
+
# 2) 도메인팩 {step}_prompt — 도메인별. config override 없을 때만.
|
|
158
|
+
pack = load_domain_pack(domain, config) if domain else None
|
|
159
|
+
if tmpl is default_template and pack and isinstance(pack.get(f"{step}_prompt"), str):
|
|
160
|
+
tmpl = pack[f"{step}_prompt"]
|
|
161
|
+
logger.info(f"[prompts] step={step} 도메인({domain}) 프롬프트 교체 적용")
|
|
162
|
+
base = tmpl.format(**fmt_kwargs)
|
|
163
|
+
if pack:
|
|
164
|
+
for section in (f"{step}_few_shot", f"{step}_examples", step, "few_shot_examples"):
|
|
165
|
+
if few_shot_examples_from_pack(pack, section=section):
|
|
166
|
+
return inject_few_shot(base, pack, section=section)
|
|
167
|
+
return base
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""detection/schema — Step 5 schema induction 로직 (LLM·검증·후처리)."""
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""detection/schema/expand.py — induce_schemas 후처리 (dedup + claim 복제).
|
|
2
|
+
|
|
3
|
+
schema_inductor.py에서 분리 (로직 move-only, 동작 변경 없음).
|
|
4
|
+
|
|
5
|
+
[v6.13] 한 claim → 여러 ClaimSchema 시 claim 복제
|
|
6
|
+
[v6.17] value=null 중복 schema 제거
|
|
7
|
+
[2026-05-21] seen_keys dedup — agent loop 회귀 차단
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from uuid import uuid4
|
|
12
|
+
|
|
13
|
+
from structverify.core.schemas import Claim, ClaimSchema
|
|
14
|
+
from structverify.utils.logger import get_logger
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _dedup_null_schemas(schemas: list[ClaimSchema]) -> list[ClaimSchema]:
|
|
20
|
+
# [v6.17] value=null 중복 schema 제거
|
|
21
|
+
# LLM이 value를 못 채우고 indicator만 같은 빈 schema를 N개 만드는
|
|
22
|
+
# 경우만 정리. 단, population까지 같아야 진짜 중복으로 간주.
|
|
23
|
+
# ★ "동작구 10.6%, 성동구 8.9%"처럼 지역만 다른 정상 다중 수치는
|
|
24
|
+
# population이 다르므로 합쳐지지 않음 (이전엔 다 뭉개지던 버그).
|
|
25
|
+
# [2026-05-21] seen_keys로 통합 — 같은 (indicator, time, population) 키에
|
|
26
|
+
# *value 있는* schema가 이미 존재하면 *value=null* 후속 schema는 폐기.
|
|
27
|
+
# 효과: LLM이 base claim에 "합계출산율 0.79명" 정상 schema +
|
|
28
|
+
# "합계출산율 null" 빈 schema를 *함께* 출력하던 케이스에서, 빈 schema가
|
|
29
|
+
# 별도 sub-claim으로 살아남아 agent loop이 4 iter 돌다가
|
|
30
|
+
# "주장값=None명 vs KOSIS 0.8명" 으로 끝나던 회귀를 차단.
|
|
31
|
+
deduped: list[ClaimSchema] = []
|
|
32
|
+
seen_keys: set[tuple] = set()
|
|
33
|
+
for sch in schemas:
|
|
34
|
+
key = (
|
|
35
|
+
sch.indicator or "",
|
|
36
|
+
sch.time_period or "",
|
|
37
|
+
sch.population or "", # ★ population 추가 — 지역별 구분
|
|
38
|
+
)
|
|
39
|
+
if sch.value is None and key in seen_keys:
|
|
40
|
+
logger.info(
|
|
41
|
+
f" [중복 제거] value=null schema 폐기 — 같은 키의 "
|
|
42
|
+
f"value 있는 schema가 이미 존재 "
|
|
43
|
+
f"(indicator={sch.indicator}, time={sch.time_period}, "
|
|
44
|
+
f"population={sch.population})"
|
|
45
|
+
)
|
|
46
|
+
continue
|
|
47
|
+
seen_keys.add(key)
|
|
48
|
+
deduped.append(sch)
|
|
49
|
+
return deduped
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _expand_claims_from_schemas(
|
|
53
|
+
claim: Claim,
|
|
54
|
+
schemas: list[ClaimSchema],
|
|
55
|
+
) -> list[Claim]:
|
|
56
|
+
"""첫 schema는 원래 claim에 부착, 나머지는 claim 복제 후 부착."""
|
|
57
|
+
if not schemas:
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
# 첫 schema는 원래 claim에 부착
|
|
61
|
+
claim.schema = schemas[0]
|
|
62
|
+
expanded: list[Claim] = [claim]
|
|
63
|
+
logger.info(
|
|
64
|
+
f"스키마 유도: {claim.sent_id} [1/{len(schemas)}] "
|
|
65
|
+
f"indicator={schemas[0].indicator}, value={schemas[0].value}, "
|
|
66
|
+
f"unit={schemas[0].unit}, time_period={schemas[0].time_period}, "
|
|
67
|
+
f"parent_path={schemas[0].parent_path}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 추가 schema들은 claim 복제 후 부착 (claim_id 새로 발급)
|
|
71
|
+
for i, sch in enumerate(schemas[1:], start=2):
|
|
72
|
+
cloned = claim.model_copy(update={
|
|
73
|
+
"claim_id": uuid4(),
|
|
74
|
+
"schema": sch,
|
|
75
|
+
})
|
|
76
|
+
expanded.append(cloned)
|
|
77
|
+
logger.info(
|
|
78
|
+
f"스키마 유도: {claim.sent_id} [{i}/{len(schemas)}] (복제) "
|
|
79
|
+
f"indicator={sch.indicator}, value={sch.value}, "
|
|
80
|
+
f"unit={sch.unit}, time_period={sch.time_period}, "
|
|
81
|
+
f"parent_path={sch.parent_path}"
|
|
82
|
+
)
|
|
83
|
+
return expanded
|