From 83a01c42e55b272f96ecc484458cb6f75efbcfa8 Mon Sep 17 00:00:00 2001 From: Jinsoo Shin <59598545+jsshin2022@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:30:09 +0900 Subject: [PATCH] feat(site): topic-first flow and public dashboard on GitHub Pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the starter kit (05 "주제 고르는 법", option B): a social signal only chooses the topic; the analysis covers every policy in that topic. - catalog/topics.yaml: 6 topics with events, collection method, and the three gates (when / who / what) - core/discovery: match social text to a topic first, then list its policies - core/adapters/law.py + scripts/refresh_law_snapshots.py: 법제처 ordinance search to build region x date treatment tables (needs LAW_OC) - site/build.py: static dashboard (topic cards, in-browser topic matcher, timeline, gates, datasets, analysis results) — no server - .github/workflows/pages.yml: build and deploy to GitHub Pages on main; refreshes ordinance snapshots when the LAW_OC secret exists Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EMnmZPBD5PQJW1UQAARN7q --- .github/workflows/pages.yml | 50 +++ .gitignore | 1 + README.md | 9 +- .../figures/event_study.png | Bin 45732 -> 44661 bytes cases/t3-land-permit-2025/flow_log.json | 34 +- cases/t3-land-permit-2025/run_manifest.json | 6 +- catalog/topics.yaml | 98 +++++ core/adapters/law.py | 79 ++++ core/agent/nodes.py | 2 +- core/discovery/__init__.py | 13 +- core/discovery/catalog.py | 43 +++ core/discovery/match.py | 24 +- core/discovery/pipeline.py | 14 +- scripts/refresh_law_snapshots.py | 36 ++ site/build.py | 340 ++++++++++++++++++ tests/core/test_law_adapter.py | 17 + tests/test_site_build.py | 25 ++ 17 files changed, 762 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 catalog/topics.yaml create mode 100644 core/adapters/law.py create mode 100644 scripts/refresh_law_snapshots.py create mode 100644 site/build.py create mode 100644 tests/core/test_law_adapter.py create mode 100644 tests/test_site_build.py diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..cdcfaff --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,50 @@ +name: pages + +# main 에 반영되면 정적 대시보드(_site/)를 만들어 GitHub Pages 로 배포한다. +# 최초 1회: Settings → Pages → Build and deployment → Source: "GitHub Actions" +# 선택: Settings → Secrets → Actions 에 LAW_OC(법제처 OC) 를 넣으면 조례 목록을 자동 수집한다. +on: + push: + branches: [main] + workflow_dispatch: + schedule: + - cron: "0 18 * * 0" # 매주 월요일 03:00 KST 데이터 갱신 + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + env: + LAW_OC: ${{ secrets.LAW_OC }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + - run: pip install -e . + - name: Refresh ordinance snapshots (법제처, optional) + if: ${{ env.LAW_OC != '' }} + run: python scripts/refresh_law_snapshots.py + - run: python site/build.py + - uses: actions/upload-pages-artifact@v3 + with: + path: _site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index aba5276..f0f97ec 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ Thumbs.db .idea/ .vscode/ *.swp +_site/ diff --git a/README.md b/README.md index e94d302..751e166 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ --- +**대시보드**: https://causalinferencelab.github.io/policy-effect-analytics-agent/ (GitHub Pages, `main` 반영 시 자동 배포) + ## 왜 하나요? 1. 공공·사회 문제를 **데이터로 정의**하고, 그 해법(정책)이 **실제로 어떤 효과를 냈는지 추정**합니다. @@ -75,9 +77,12 @@ cp -r cases/_template cases/group3-youth-rent # 폴더명: <조>-<주제>, 소 | 6 | 리포트·플랫폼 | `report.md`, Streamlit 반영 | | 7 | 발표·회고·공개 정리 | 최종 PR 머지, 릴리스 태그 | -## 이슈 → 데이터 → 효과 +## 이슈 → 주제 → 정책 전체 → 효과 -소셜 반응(뉴스 제목·SNS 글) 한 건에서 출발해 정책을 찾고, 공공데이터를 추천하고, 효과 분석까지 잇습니다. +소셜 반응(뉴스 제목·SNS 글)은 **어느 주제를 볼지까지만** 정합니다. 화제가 된 정책 하나만 골라 분석하면 +결과를 보고 사례를 고르는 셈이 되기 때문입니다(출발 키트 05). 주제가 정해지면 그 주제의 정책을 +전부 모으고(법제처 조례·고시), 세 관문(언제·누가·무엇을)을 통과한 것만 분석합니다. +주제 목록: `catalog/topics.yaml` · 정적 대시보드: `python site/build.py` → `_site/` 1. **정책 식별**: `catalog/policies.yaml`(정책 × 데이터셋 카탈로그)에서 키워드로 찾습니다. LLM은 후보 중에서 고르는 보조 역할만 합니다. 2. **데이터셋 추천**: 카탈로그에 검증해 둔 데이터셋과 공공데이터포털 실시간 검색 결과를 보여줍니다. diff --git a/cases/t3-land-permit-2025/figures/event_study.png b/cases/t3-land-permit-2025/figures/event_study.png index dcefe06b6cf798425afe3961ce4ac37497213985..eb9dff985cf86330a0089fa81ba009c81fbc650d 100644 GIT binary patch literal 44661 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 diff --git a/cases/t3-land-permit-2025/flow_log.json b/cases/t3-land-permit-2025/flow_log.json index 86b00b2..960e731 100644 --- a/cases/t3-land-permit-2025/flow_log.json +++ b/cases/t3-land-permit-2025/flow_log.json @@ -4,17 +4,17 @@ "question": "2025년 3월 24일 강남·서초·송파·용산구 전체 아파트를 토지거래허가구역으로 지정한 뒤, 서울의 나머지 21개 자치구와 비교해 이 4개 구의 아파트 매매 거래건수와 ㎡당 가격이 달라졌는가?\n", "status": "warn", "verdict": "conditional", - "started_at": "2026-09-26T07:07:15+00:00", - "ended_at": "2026-09-26T07:07:21+00:00", + "started_at": "2026-09-26T07:28:46+00:00", + "ended_at": "2026-09-26T07:28:52+00:00", "steps": [ { "index": 1, "step": "define_problem", "title": "① 문제 정의", "status": "ok", - "started_at": "2026-09-26T07:07:15+00:00", - "ended_at": "2026-09-26T07:07:15+00:00", - "duration_s": 0.011, + "started_at": "2026-09-26T07:28:46+00:00", + "ended_at": "2026-09-26T07:28:46+00:00", + "duration_s": 0.012, "message": "분석계획 검증 완료, 사전 등록(git 커밋) 확인: t3-land-permit-2025", "artifacts": { "plan": "plan.yaml", @@ -27,8 +27,8 @@ "step": "collect", "title": "② 데이터 수집", "status": "warn", - "started_at": "2026-09-26T07:07:15+00:00", - "ended_at": "2026-09-26T07:07:15+00:00", + "started_at": "2026-09-26T07:28:46+00:00", + "ended_at": "2026-09-26T07:28:46+00:00", "duration_s": 0.0, "message": "기존 스냅샷 재사용 (새로 받으려면 파일 삭제 후 재실행) / ⚠️ 합성 데이터 — 결과는 실제 정책 효과가 아님", "artifacts": { @@ -47,8 +47,8 @@ "step": "structure_metrics", "title": "③ 지표 구조화·품질 점검", "status": "ok", - "started_at": "2026-09-26T07:07:15+00:00", - "ended_at": "2026-09-26T07:07:15+00:00", + "started_at": "2026-09-26T07:28:46+00:00", + "ended_at": "2026-09-26T07:28:46+00:00", "duration_s": 0.004, "message": "12개 점검 모두 통과", "artifacts": { @@ -61,9 +61,9 @@ "step": "estimate", "title": "④ 효과 추정", "status": "warn", - "started_at": "2026-09-26T07:07:15+00:00", - "ended_at": "2026-09-26T07:07:20+00:00", - "duration_s": 4.896, + "started_at": "2026-09-26T07:28:46+00:00", + "ended_at": "2026-09-26T07:28:51+00:00", + "duration_s": 5.026, "message": "event_study: -0.403 [-0.494, -0.323] → identified", "artifacts": { "log_trades": { @@ -89,8 +89,8 @@ "step": "guard", "title": "⑤ 과잉해석 가드", "status": "ok", - "started_at": "2026-09-26T07:07:20+00:00", - "ended_at": "2026-09-26T07:07:20+00:00", + "started_at": "2026-09-26T07:28:51+00:00", + "ended_at": "2026-09-26T07:28:51+00:00", "duration_s": 0.0, "message": "판정 identified, 서술=template", "artifacts": { @@ -104,9 +104,9 @@ "step": "report", "title": "⑥ 리포트·재현 기록", "status": "ok", - "started_at": "2026-09-26T07:07:20+00:00", - "ended_at": "2026-09-26T07:07:21+00:00", - "duration_s": 0.31, + "started_at": "2026-09-26T07:28:51+00:00", + "ended_at": "2026-09-26T07:28:52+00:00", + "duration_s": 0.334, "message": "리포트 2개 그림 포함 작성", "artifacts": { "report": "report.md", diff --git a/cases/t3-land-permit-2025/run_manifest.json b/cases/t3-land-permit-2025/run_manifest.json index e44f554..1657c3e 100644 --- a/cases/t3-land-permit-2025/run_manifest.json +++ b/cases/t3-land-permit-2025/run_manifest.json @@ -1,8 +1,8 @@ { "case_id": "t3-land-permit-2025", - "started_at": "2026-09-26T07:07:15+00:00", - "finished_at": "2026-09-26T07:07:20+00:00", - "git_sha": "0e9570f1f745fa79b42366e581270e928bb1a41d", + "started_at": "2026-09-26T07:28:46+00:00", + "finished_at": "2026-09-26T07:28:52+00:00", + "git_sha": "3ccf0b76a74f39efc81fdcd9537e96c99efece83", "git_dirty": true, "plan_sha256": "8f7e7b2d06338470404f27bdcac6a6acf9784785e5851a6729ca6673b27a1ff2", "data_sha256": "c35f969dc154aa5732b6e05759185eb3cf246f9a3f9641c5182ab89214a43aa6", diff --git a/catalog/topics.yaml b/catalog/topics.yaml new file mode 100644 index 0000000..24ef0b3 --- /dev/null +++ b/catalog/topics.yaml @@ -0,0 +1,98 @@ +# 주제(topic) 카탈로그 — 소셜 신호는 "어느 주제를 볼지"까지만 정합니다. +# +# 출발 키트 05 "주제 고르는 법"의 B 방식: +# 소셜 신호 → 주제 지정 → 그 주제의 정책을 전부 모으기(법령·조례·고시) → 3개 관문 → 계획 먼저 → 효과 추정 +# 소셜 신호는 분석 대상을 고르는 데 쓰지 않고, ① 주제 좁히기 ② 선반영(미리 반응) 점검 ③ 관심도 자체를 결과로 — 에만 씁니다. +# +# collect: 정책 목록을 모으는 방법 +# law_ordinance : 법제처 자치법규 API로 조례 제정일·지자체를 자동 수집 (query 사용) +# notice : 부처·지자체 고시문에서 지정·해제 이력을 수집 (에이전트 과제) +# curated : 카탈로그에 적은 정책만 (전국 단위 등) + +version: 1 +topics: + - id: housing-regulation + name: 부동산 거래 규제 + question: 토지거래허가구역·규제지역 지정과 해제는 거래와 가격을 바꿨나? + keywords: [부동산, 집값, 아파트, 토지거래허가, 토허제, 토허구역, 토허, 규제지역, 투기과열, 조정대상, 강남3구, 용산, 잠삼대청, 풍선효과, 실거래가, 거래량] + policies: [T3] + collect: + method: notice + note: "국토부·서울시 고시에서 지정·해제 이력 전체(2017~)를 수집 → 지역×시점 처치표. 정리된 표가 없어 에이전트에게 맡기기 좋은 일" + events: + - {date: 2020-06-23, what: "잠실·삼성·대치·청담 토지거래허가구역 지정"} + - {date: 2025-02-12, what: "잠실·삼성·대치·청담 지정 해제 (발표·시행 월 기준)"} + - {date: 2025-03-24, what: "강남·서초·송파·용산 전역 지정", source: "https://www.ajunews.com/view/20250324064239711"} + - {date: 2025-10-20, what: "서울 전역·경기 12곳 지정 (10·15 대책)", source: "https://www.korea.kr/news/policyNewsView.do?newsId=148950973"} + gates: + when: {status: pass, note: "고시·보도자료로 날짜 확인"} + who: {status: pass, note: "지정 구 vs 미지정 구"} + what: {status: key, note: "국토부 실거래가 API — 키 발급 전이라 지금은 시뮬레이션"} + + - id: local-currency + name: 지역화폐 (지역사랑상품권) + question: 지역화폐를 일찍 도입한 지자체의 지역 소비·소상공인 매출이 달라졌나? + keywords: [지역화폐, 지역사랑상품권, 지역상품권, 상품권, 지역경제, 골목상권, 소상공인] + policies: [] + collect: + method: law_ordinance + query: [지역사랑상품권, 지역화폐] + note: "법제처 자치법규 API로 지자체별 조례·시행일자 자동 수집 (무료, 신청 즉시 승인). 발행 지자체가 2018년 66곳 → 2019년 172곳으로 늘어 시차 도입 비교가 가능. 검색 결과는 최신 개정 기준이라 '제정일'은 연혁으로 확인" + gates: + when: {status: pass, note: "조례 제정일을 법제처 API로 자동 수집"} + who: {status: pass, note: "일찍 도입 vs 늦게 도입 지자체 (시차 도입)"} + what: {status: key, note: "조폐공사 결제정보 API·상권 매출 — 키 필요"} + pitfalls: ["옆 지자체 소비를 끌어온다는 지적 → 인접 지자체 대조군 오염", "조례 제정일과 실제 발행일이 다름", "이름이 같아도 할인율·규모가 다름"] + + - id: traffic-safety + name: 교통안전 + question: 속도 하향·처벌 강화 같은 교통안전 정책은 사고를 줄였나? + keywords: [교통사고, 교통안전, 안전속도, "5030", 제한속도, 민식이법, 스쿨존, 어린이보호구역, 윤창호법, 음주운전, 보행자] + policies: [T4, T5, T6] + collect: + method: curated + note: "지자체별 조기 시행일(5030)은 보도자료·고시로 추가 수집" + gates: + when: {status: pass, note: "법 시행일 확정"} + who: {status: check, note: "5030은 조기 도입 지자체로 구분 가능, 민식이법·윤창호법은 전국 동시(관문 2 약함)"} + what: {status: key, note: "TAAS·도로교통공단 API — 키 필요"} + + - id: air-quality + name: 대기질 + question: 운행제한·계절관리제는 미세먼지와 이산화질소를 줄였나? + keywords: [미세먼지, 초미세먼지, 대기질, 공기, 녹색교통, 5등급, 운행제한, 계절관리제, NO2, 이산화질소, PM2.5] + policies: [T1, T8] + collect: + method: curated + note: "시행일만 보고 시행(오염도와 무관) → 비교가 깔끔" + gates: + when: {status: pass, note: "2019.12 시작, 매년 12~3월"} + who: {status: check, note: "녹색교통: 사대문 안 측정소 소수(합성통제 필요). 계절관리제: 수도권 위주 → 비수도권 비교 가능한지 확인"} + what: {status: pass, note: "에어코리아 확정자료 파일 (API는 하루 500회 제한)"} + + - id: retail-hours + name: 대형마트 의무휴업 + question: 의무휴업일을 평일로 바꾼 자치구의 골목상권 매출이 달라졌나? + keywords: [대형마트, 의무휴업, 휴업일, 전통시장, 골목상권, 상권, 매출] + policies: [T2] + collect: + method: notice + note: "자치구별 전환 고시일 수집 (시차 도입)" + gates: + when: {status: check, note: "전환 자치구 목록·날짜를 고시로 구축해야 함"} + who: {status: pass, note: "전환 vs 미전환 자치구"} + what: {status: key, note: "서울 상권분석서비스 추정매출 — 키 필요"} + + - id: birth-marriage + name: 출산·혼인 지원 + question: 지자체 출산·혼인장려금은 출생과 혼인을 늘렸나? + keywords: [출산, 출생, 저출산, 출산장려금, 출산지원금, 혼인, 결혼장려금, 혼인장려금, 인구] + policies: [T7] + collect: + method: law_ordinance + query: [출산장려금, 혼인장려금] + note: "법제처 자치법규 API로 조례 제정일 수집 → 처치 데이터셋 구축이 과제의 절반" + gates: + when: {status: pass, note: "조례 제정일 자동 수집"} + who: {status: pass, note: "도입 vs 미도입 시군구"} + what: {status: key, note: "KOSIS 시군구 출생·혼인 — 키 필요"} diff --git a/core/adapters/law.py b/core/adapters/law.py new file mode 100644 index 0000000..b35effd --- /dev/null +++ b/core/adapters/law.py @@ -0,0 +1,79 @@ +"""법제처 국가법령정보 공동활용 API — 자치법규(조례) 검색 어댑터. + +- 인증: open.law.go.kr 에서 신청(무료, 즉시 승인)한 OC 값을 LAW_OC 환경변수에 넣는다. +- 용도: 주제(예: 지역사랑상품권)의 조례를 지자체별로 모아 '어느 지역이 언제부터'를 자동으로 만든다 + (출발 키트 05 관문 1). +- 주의: 검색 결과의 날짜는 **현행(최신 개정) 기준**이다. 처음 제정된 날은 연혁 조회로 확인해야 한다. + 그래서 이 어댑터가 만든 표는 '후보 처치표'이고, 제정일 확정은 사람이 검증한다. +""" + +from __future__ import annotations + +import time +import xml.etree.ElementTree as ET + +import pandas as pd + +from .base import BaseAdapter, SourceMeta + +SEARCH_URL = "https://www.law.go.kr/DRF/lawSearch.do" +KEEP = [ + "자치법규ID", + "자치법규명", + "지자체기관명", + "제개정구분명", + "공포일자", + "시행일자", + "자치법규종류", +] + + +def parse_ordinance_xml(xml: bytes | str) -> tuple[list[dict], int]: + root = ET.fromstring(xml) + total = int(root.findtext("totalCnt") or 0) + rows = [{c.tag: (c.text or "").strip() for c in item} for item in root.iter("law")] + return rows, total + + +class LawOrdinanceAdapter(BaseAdapter): + meta = SourceMeta( + name="법제처 자치법규 검색", + provider="법제처 국가법령정보 공동활용", + license="other", # 법령·자치법규 본문은 저작권 보호 대상이 아님(저작권법 제7조). 메타데이터만 사용 + url="https://open.law.go.kr", + ) + api_key_env = "LAW_OC" + + def fetch(self, queries: list[str], per_page: int = 100, max_pages: int = 20) -> pd.DataFrame: + import requests + + rows: list[dict] = [] + for q in queries: + page, total = 1, None + while page <= max_pages and (total is None or (page - 1) * per_page < total): + r = requests.get( + SEARCH_URL, + params={ + "OC": self.api_key(), + "target": "ordin", + "type": "XML", + "query": q, + "display": per_page, + "page": page, + }, + timeout=30, + ) + r.raise_for_status() + got, total = parse_ordinance_xml(r.content) + rows += [{**g, "query": q} for g in got] + page += 1 + time.sleep(0.3) + return tidy(pd.DataFrame(rows)) + + +def tidy(df: pd.DataFrame) -> pd.DataFrame: + if df.empty: + return pd.DataFrame(columns=[*KEEP, "query", "year"]) + df = df[[c for c in [*KEEP, "query"] if c in df.columns]].drop_duplicates("자치법규ID") + date = pd.to_datetime(df["시행일자"], format="%Y%m%d", errors="coerce") + return df.assign(year=date.dt.year).sort_values("시행일자").reset_index(drop=True) diff --git a/core/agent/nodes.py b/core/agent/nodes.py index 36161c0..2040794 100644 --- a/core/agent/nodes.py +++ b/core/agent/nodes.py @@ -342,7 +342,7 @@ def report(s: FlowState): figs["Raw trends"] = "figures/raw_trends.png" if "coefs" in r.extra: note = ( - "per-period CIs cluster-robust, reference only: few treated units" + "per-period CIs: reference only (few treated units)" if "few_treated_clusters" in r.triggers else "cluster-robust" ) diff --git a/core/discovery/__init__.py b/core/discovery/__init__.py index 77c4ac6..58cac82 100644 --- a/core/discovery/__init__.py +++ b/core/discovery/__init__.py @@ -1,4 +1,7 @@ -"""소셜 반응 → 정책 식별 → 데이터셋 추천 → 분석계획 연결 (Flow ① 앞단). +"""소셜 반응 → 주제 → 주제 안의 정책 전체 → 데이터셋 → 분석계획 (Flow ① 앞단). + +원칙(출발 키트 B): 소셜 신호는 '어느 주제를 볼지'까지만 정한다. 화제가 된 정책 하나만 +골라 분석하면 결과를 보고 사례를 고르는 셈이 되어 효과가 과대 추정된다. from core.discovery import discover r = discover("토허제 확대하고 강남 집값 잡혔나요?") @@ -11,8 +14,8 @@ - 분석 방법은 LLM이 아니라 카탈로그의 design(규칙 기반 판별 결과)으로 정한다. """ -from .catalog import Dataset, Policy, load_catalog -from .match import Match, match_issue +from .catalog import Dataset, Policy, Topic, load_catalog, load_topics +from .match import Match, TopicMatch, match_issue, match_topic from .pipeline import DiscoveryResult, discover from .search import SearchHit, search_datago @@ -20,6 +23,10 @@ "Dataset", "Policy", "load_catalog", + "Topic", + "load_topics", + "TopicMatch", + "match_topic", "Match", "match_issue", "SearchHit", diff --git a/core/discovery/catalog.py b/core/discovery/catalog.py index 8ff3b88..50832a2 100644 --- a/core/discovery/catalog.py +++ b/core/discovery/catalog.py @@ -70,3 +70,46 @@ def design_ko(self) -> str: def load_catalog(path: str | Path = DEFAULT_CATALOG) -> list[Policy]: raw = yaml.safe_load(Path(path).read_text(encoding="utf-8")) return [Policy.model_validate(p) for p in raw["policies"]] + + +DEFAULT_TOPICS = ROOT / "catalog" / "topics.yaml" + +GateStatus = Literal["pass", "check", "key", "fail"] +GATE_KO = {"when": "언제", "who": "누가", "what": "무엇을"} +GATE_STATUS_KO = {"pass": "통과", "check": "확인 필요", "key": "API 키 필요", "fail": "탈락"} + + +class Gate(BaseModel): + status: GateStatus + note: str = "" + + +class Collect(BaseModel): + method: Literal["law_ordinance", "notice", "curated"] + query: list[str] = [] + note: str = "" + + +class Event(BaseModel): + date: date + what: str + source: str | None = None + + +class Topic(BaseModel): + """소셜 신호가 가리키는 '주제'. 분석 대상은 주제 안의 정책 전체다(출발 키트 B 방식).""" + + id: str + name: str + question: str + keywords: list[str] + policies: list[str] = [] + collect: Collect + events: list[Event] = [] + gates: dict[Literal["when", "who", "what"], Gate] + pitfalls: list[str] = [] + + +def load_topics(path: str | Path = DEFAULT_TOPICS) -> list[Topic]: + raw = yaml.safe_load(Path(path).read_text(encoding="utf-8")) + return [Topic.model_validate(t) for t in raw["topics"]] diff --git a/core/discovery/match.py b/core/discovery/match.py index d976c76..e627a0d 100644 --- a/core/discovery/match.py +++ b/core/discovery/match.py @@ -10,7 +10,7 @@ import re from dataclasses import dataclass, field -from .catalog import Policy +from .catalog import Policy, Topic @dataclass @@ -68,3 +68,25 @@ def _llm_pick(text: str, cands: list[Match]) -> None: m.picked_by, m.reason = "llm", f"LLM: {ans.get('reason', '')} ({m.reason})" cands.insert(0, cands.pop(i)) return + + +@dataclass +class TopicMatch: + topic: Topic + score: float + hits: list[str] + + +def match_topic( + text: str, topics: list[Topic], policies: list[Policy], top_k: int = 3 +) -> list[TopicMatch]: + """주제 키워드 + 주제에 속한 정책 키워드로 점수. 소셜 신호는 주제까지만 정한다.""" + by_id = {p.id: p for p in policies} + t = _norm(text) + out = [] + for tp in topics: + kws = dict.fromkeys(tp.keywords + [k for pid in tp.policies for k in by_id[pid].keywords]) + hits = [k for k in kws if _norm(k) in t] + if hits: + out.append(TopicMatch(tp, float(sum(min(len(_norm(k)), 6) for k in hits)), hits)) + return sorted(out, key=lambda m: m.score, reverse=True)[:top_k] diff --git a/core/discovery/pipeline.py b/core/discovery/pipeline.py index 548cfb6..f84c215 100644 --- a/core/discovery/pipeline.py +++ b/core/discovery/pipeline.py @@ -5,8 +5,8 @@ from dataclasses import dataclass, field from pathlib import Path -from .catalog import ROOT, Policy, load_catalog -from .match import Match, match_issue +from .catalog import ROOT, Policy, Topic, load_catalog, load_topics +from .match import Match, TopicMatch, match_issue, match_topic from .search import SearchHit, search_datago @@ -16,6 +16,12 @@ class DiscoveryResult: matches: list[Match] live_hits: list[SearchHit] = field(default_factory=list) live_status: str = "실시간 검색 안 함" + topics: list[TopicMatch] = field(default_factory=list) + policies_in_topic: list[Policy] = field(default_factory=list) + + @property + def topic(self) -> Topic | None: + return self.topics[0].topic if self.topics else None @property def top(self) -> Policy | None: @@ -46,6 +52,10 @@ def discover( ) -> DiscoveryResult: cat = catalog if catalog is not None else load_catalog() res = DiscoveryResult(text=text, matches=match_issue(text, cat, use_llm=use_llm)) + res.topics = match_topic(text, load_topics(), cat) + if res.topic: + by_id = {p.id: p for p in cat} + res.policies_in_topic = [by_id[i] for i in res.topic.policies] if live_search and res.top and res.top.search_terms: res.live_hits, res.live_status = search_datago(res.top.search_terms) return res diff --git a/scripts/refresh_law_snapshots.py b/scripts/refresh_law_snapshots.py new file mode 100644 index 0000000..1d45777 --- /dev/null +++ b/scripts/refresh_law_snapshots.py @@ -0,0 +1,36 @@ +"""법제처 API로 주제별 조례 스냅샷을 갱신한다 → catalog/snapshots/law_.csv + +GitHub Actions(pages.yml)에서 LAW_OC 시크릿이 있을 때만 실행된다. 로컬: LAW_OC=... python scripts/refresh_law_snapshots.py +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from core.adapters.base import MissingAPIKey # noqa: E402 +from core.adapters.law import LawOrdinanceAdapter # noqa: E402 +from core.discovery import load_topics # noqa: E402 + + +def main() -> int: + out_dir = ROOT / "catalog" / "snapshots" + ad = LawOrdinanceAdapter() + for t in load_topics(): + if t.collect.method != "law_ordinance": + continue + try: + df = ad.fetch(t.collect.query) + except MissingAPIKey as e: + print(f"건너뜀: {e}") + return 0 + path = ad.save(df, out_dir / f"law_{t.id}.csv", {"queries": t.collect.query}) + print(f"{t.id}: 조례 {len(df)}건 → {path.relative_to(ROOT)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/site/build.py b/site/build.py new file mode 100644 index 0000000..3939c1f --- /dev/null +++ b/site/build.py @@ -0,0 +1,340 @@ +"""정적 대시보드 빌더 → _site/ (GitHub Pages, 서버 없음) + + python site/build.py # _site/index.html, _site/topics/.html + python -m http.server -d _site # 로컬 미리보기 + +입력(모두 레포에 커밋된 파일): catalog/topics.yaml, catalog/policies.yaml, +catalog/snapshots/law_.csv(있으면), cases//flow_log.json·figures/*.png +""" + +from __future__ import annotations + +import html +import json +import shutil +import sys +from pathlib import Path + +import pandas as pd + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from core.agent.guard import TRIGGER_KO # noqa: E402 +from core.discovery.catalog import ( # noqa: E402 + GATE_KO, + GATE_STATUS_KO, + load_catalog, + load_topics, +) + +OUT = ROOT / "_site" +REPO = "https://github.com/CausalInferenceLab/policy-effect-analytics-agent" +VERDICT = { + "identified": ("식별됨", "go"), + "conditional": ("조건부", "warn"), + "not_identified": ("식별 불가", "stop"), +} +GATE_CLASS = {"pass": "go", "check": "warn", "key": "warn", "fail": "stop"} +ACCESS_KO = {"api_key": "API 키(자동승인)", "file": "파일", "manual": "수동 수집"} +COLLECT_KO = { + "law_ordinance": "법제처 조례 자동 수집", + "notice": "고시문에서 이력 수집", + "curated": "카탈로그 목록", +} + +e = html.escape + +CSS = """ +:root{--paper:#f4f5f7;--surface:#fff;--ink:#16181d;--ink2:#454b57;--muted:#6f7683;--hair:#d9dce2; +--accent:#1d5b8f;--go:#1f6b4f;--go-bg:#e2f1ea;--warn:#8a5a00;--warn-bg:#fbefd7;--stop:#9b2c33;--stop-bg:#f8e3e4; +--sans:"Pretendard","Apple SD Gothic Neo","Malgun Gothic",system-ui,sans-serif} +@media (prefers-color-scheme:dark){:root{--paper:#121418;--surface:#1b1e24;--ink:#eceef2;--ink2:#c3c8d1;--muted:#8d94a1; +--hair:#2e333c;--accent:#7cb4e6;--go:#79d1a8;--go-bg:#16302a;--warn:#e7b35a;--warn-bg:#33291a;--stop:#ec8e95;--stop-bg:#361e21}} +*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:16px/1.7 var(--sans);word-break:keep-all} +a{color:var(--accent)}.wrap{max-width:1040px;margin:0 auto;padding:40px 16px 80px} +header.top{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;border-bottom:2px solid var(--ink);padding-bottom:14px} +header.top a{font-size:14px}h1{font-size:clamp(28px,4.4vw,40px);line-height:1.2;margin:22px 0 8px} +h2{font-size:21px;margin:44px 0 12px}h3{font-size:17px;margin:0 0 6px}.lede{color:var(--ink2);max-width:66ch;margin:0} +.muted{color:var(--muted);font-size:14px}.card{background:var(--surface);border:1px solid var(--hair);border-radius:10px;padding:18px} +.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(290px,1fr))} +.steps{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin-top:16px} +.step{background:var(--surface);border:1px solid var(--hair);border-radius:10px;padding:12px 14px;font-size:14px} +.step b{display:block;font-size:15px}.step .n{color:var(--accent);font-weight:700;font-size:13px} +.badge{display:inline-block;font-size:12.5px;font-weight:600;padding:2px 9px;border-radius:999px;white-space:nowrap} +.go{color:var(--go);background:var(--go-bg)}.warn{color:var(--warn);background:var(--warn-bg)}.stop{color:var(--stop);background:var(--stop-bg)} +.gates{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0 4px} +textarea{width:100%;min-height:74px;font:inherit;padding:12px;border-radius:10px;border:1px solid var(--hair);background:var(--surface);color:var(--ink)} +.hit{outline:2px solid var(--accent)}table{width:100%;border-collapse:collapse;font-size:14px} +th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--hair);vertical-align:top}th{color:var(--muted);font-weight:600} +.tablewrap{overflow-x:auto;background:var(--surface);border:1px solid var(--hair);border-radius:10px} +.banner{border-radius:10px;padding:12px 16px;margin:14px 0;font-weight:600} +.kpi{display:flex;gap:28px;flex-wrap:wrap;margin:8px 0}.kpi div{min-width:120px}.kpi .v{font-size:26px;font-weight:700} +.figs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}.figs img{width:100%;background:#fff;border-radius:8px;border:1px solid var(--hair)} +svg text{fill:var(--ink2);font:12px var(--sans)}ul{padding-left:20px}footer{margin-top:60px;color:var(--muted);font-size:13px;border-top:1px solid var(--hair);padding-top:14px} +""" + + +def page(title: str, body: str, depth: int = 0) -> str: + up = "../" * depth + return f""" +{e(title)} +

+
정책 효과 분석 플랫폼 +GitHub · 가짜연구소 인과추론팀 × OpenUp
+{body} +
모든 수치와 판정은 레포의 catalog/·cases/에서 GitHub Actions가 자동 생성합니다. +시뮬레이션 데이터로 만든 결과에는 별도 표시가 붙습니다. · {REPO}
+
""" + + +def gate_badges(t) -> str: + return "".join( + f'' + f"{GATE_KO[k]} · {GATE_STATUS_KO[g.status]}" + for k, g in t.gates.items() + ) + + +def timeline_svg(events) -> str: + """축 위에 점, 아래에 연도 눈금. 설명은 HTML 목록으로 (긴 한국어 라벨이 겹치지 않게).""" + if not events: + return "" + d = pd.to_datetime([ev.date for ev in events]) + lo = pd.Timestamp(year=d.min().year, month=1, day=1) + hi = pd.Timestamp(year=d.max().year + 1, month=1, day=1) + w, x0, x1 = 900, 20, 880 + + def x(t): + return x0 + (x1 - x0) * ((t - lo) / (hi - lo)) + + ticks = "".join( + f'' + f'{y}' + for y in range(lo.year, hi.year + 1) + ) + dots = "".join( + f'' + f'{i + 1}' + for i, t in enumerate(d) + ) + items = "".join( + f"
  • {ev.date:%Y.%m.%d} {e(ev.what)}" + + (f' 출처' if ev.source else "") + + "
  • " + for ev in events + ) + return ( + f'' + f'{ticks}{dots}' + f"
      {items}
    " + ) + + +def bars_svg(counts: pd.Series, label: str) -> str: + if counts.empty: + return "" + w, h, pad = 900, 220, 30 + bw = (w - 2 * pad) / len(counts) + mx = counts.max() + bars = [] + for i, (k, v) in enumerate(counts.items()): + bh = (h - 60) * v / mx + xx = pad + i * bw + bars.append( + f'' + f'{k}' + f'{v}' + ) + return f'{"".join(bars)}' + + +def case_block(case_rel: str, depth: int) -> str: + case = ROOT / case_rel + log_p = case / "flow_log.json" + if not log_p.exists(): + return '

    아직 실행 결과가 없습니다.

    ' + log = json.loads(log_p.read_text(encoding="utf-8")) + import yaml + + plan = yaml.safe_load((case / "plan.yaml").read_text(encoding="utf-8")) + dest = OUT / "cases" / case.name + dest.mkdir(parents=True, exist_ok=True) + figs = [] + for f in sorted((case / "figures").glob("*.png")): + shutil.copy(f, dest / f.name) + figs.append(f'{e(f.stem)}') + label, cls = VERDICT.get(log.get("verdict"), ("-", "warn")) + out = [] + if plan.get("synthetic_data"): + out.append( + '" + ) + out.append(f"

    질문 {e(plan['question'].strip())}

    ") + out.append(f'

    종합 판정 {label}

    ') + names = {o["col"]: o["name"] for o in plan["outcomes"]} + rows = [] + for r in log["results"]: + lab, c = VERDICT[r["verdict"]] + why = ", ".join(TRIGGER_KO.get(x, x) for x in r.get("triggers") or []) or "-" + rows.append( + f"{e(names.get(r['outcome'], r['outcome']))}{r['estimate']:+.3f}" + f"[{r['ci_low']:.3f}, {r['ci_high']:.3f}]{r['p_value']:.3f}" + f'{lab}{e(why)}' + ) + out.append( + '
    ' + f"{''.join(rows)}
    결과 지표추정치95% 신뢰구간p판정경고
    " + ) + narr = (log.get("guard") or {}).get("narrative") + if narr: + out.append(f'

    {e(narr).replace("**", "")}

    ') + if figs: + out.append(f'
    {"".join(figs)}
    ') + out.append( + f'

    분석계획·코드·리포트: {case_rel}

    ' + ) + return "\n".join(out) + + +def topic_page(t, policies) -> str: + by_id = {p.id: p for p in policies} + ps = [by_id[i] for i in t.policies] + body = [ + f"

    {e(t.name)}

    {e(t.question)}

    ", + f'
    {gate_badges(t)}
    ', + "

    1. 이 주제의 정책 전체

    ", + f"

    수집 방법: {COLLECT_KO[t.collect.method]} — {e(t.collect.note)}

    ", + timeline_svg(t.events), + ] + snap = ROOT / "catalog" / "snapshots" / f"law_{t.id}.csv" + if t.collect.method == "law_ordinance": + if snap.exists(): + df = pd.read_csv(snap) + cnt = df.dropna(subset=["year"]).astype({"year": int}).groupby("year").size() + body += [ + f"

    조례 시행연도 분포 (법제처, {len(df)}건)

    ", + bars_svg(cnt, "조례 시행연도"), + "

    현행 조례의 시행일자 기준입니다. 처음 제정된 날은 연혁 조회로 확인합니다.

    ", + ] + else: + body.append( + "

    법제처 API로 조례를 자동 수집할 주제입니다. 레포 시크릿 LAW_OC를 " + "등록하면 GitHub Actions가 다음 배포 때 지자체별 조례 목록과 시행일을 채웁니다.

    " + ) + body.append("

    2. 세 가지 관문

    ") + for k, g in t.gates.items(): + body.append( + f'

    {GATE_KO[k]}

    ' + f"{GATE_STATUS_KO[g.status]}

    {e(g.note)}

    " + ) + body.append("
    ") + if ps: + body.append("

    3. 분석 가능한 정책과 추천 데이터

    ") + for p in ps: + ds = "".join( + f"{e(d.name)}{e(d.provider)}" + f"{e(d.granularity)}{ACCESS_KO[d.access]}{e(d.license)}" + for d in p.datasets + ) + sup = "go" if p.support == "지원" else "warn" + body.append( + f'

    {e(p.name)}

    ' + f"

    {e(p.summary)}

    " + f'

    설계 {p.design_ko} {e(p.support)}

    ' + f"
    " + f"{ds}
    데이터셋제공단위접근라이선스
    " + + ( + "
    식별상 함정
      " + + "".join(f"
    • {e(x)}
    • " for x in p.pitfalls) + + "
    " + if p.pitfalls + else "" + ) + + "
    " + ) + cases = [p.sample_case for p in ps if p.sample_case] + if cases: + body.append("

    4. 효과 분석 결과

    ") + body += [case_block(c, depth=1) for c in cases] + if t.pitfalls: + body.append( + "

    주의할 점

      " + "".join(f"
    • {e(x)}
    • " for x in t.pitfalls) + "
    " + ) + body.append( + "

    소셜 신호는 여기에만 씁니다

    • 이 주제를 고르는 데
    • " + "
    • 시행 전 1년 검색량·기사 수로 미리 반응했는지 점검하는 데
    • " + "
    • '정책이 알려지긴 했나'를 묻는다면 관심도 자체를 결과 지표로
    " + ) + return page(t.name, "\n".join(body), depth=1) + + +def index_page(topics, policies) -> str: + cards = [] + for t in topics: + cards.append( + f'' + f"

    {e(t.name)}

    {e(t.question)}

    " + f'
    {gate_badges(t)}
    ' + ) + kw = { + t.id: list( + dict.fromkeys( + t.keywords + + [k for pid in t.policies for p in policies if p.id == pid for k in p.keywords] + ) + ) + for t in topics + } + steps = [ + ("소셜 신호", "뉴스 제목·SNS 글"), + ("주제", "신호는 주제까지만 정함"), + ("정책 전체 수집", "법제처 조례·고시로 지역×시점"), + ("세 관문", "언제 · 누가 · 무엇을"), + ("사전 등록", "계획을 먼저 커밋"), + ("효과 추정", "판정: 식별됨·조건부·식별 불가"), + ] + body = f""" +

    소셜 반응에서 정책 효과까지

    +

    사람들이 이야기하는 정책이 실제로 효과가 있었는지, 공공데이터로 확인합니다. +화제가 된 정책 하나만 골라 분석하지 않고, 그 주제의 정책을 전부 모아 비교합니다. +화제성으로 사례를 고르면 결과를 보고 사례를 고르는 셈이 되기 때문입니다.

    +
    {"".join(f'
    {i + 1}{a}{b}
    ' for i, (a, b) in enumerate(steps))}
    +

    지금 어떤 이야기가 궁금하세요?

    + +

    글을 입력하면 해당하는 주제를 찾아 표시합니다. (브라우저 안에서만 동작, 서버로 보내지 않음)

    +

    주제

    {"".join(cards)}
    +""" + return page("정책 효과 분석 플랫폼", body) + + +def main() -> None: + topics, policies = load_topics(), load_catalog() + if OUT.exists(): + shutil.rmtree(OUT) + (OUT / "topics").mkdir(parents=True) + (OUT / "index.html").write_text(index_page(topics, policies), encoding="utf-8") + for t in topics: + (OUT / "topics" / f"{t.id}.html").write_text(topic_page(t, policies), encoding="utf-8") + (OUT / ".nojekyll").write_text("") + print(f"_site/ 생성: 주제 {len(topics)}개") + + +if __name__ == "__main__": + main() diff --git a/tests/core/test_law_adapter.py b/tests/core/test_law_adapter.py new file mode 100644 index 0000000..a3a6f56 --- /dev/null +++ b/tests/core/test_law_adapter.py @@ -0,0 +1,17 @@ +import pandas as pd + +from core.adapters.law import parse_ordinance_xml, tidy + +# 법제처 자치법규 검색 응답 형식 (필드 구조만 재현한 최소 예시) +XML = """ordin2 +<자치법규ID>1<자치법규명>가 지역화폐 조례<지자체기관명>경기도 가군 +<제개정구분명>제정<공포일자>20190101<시행일자>20190101 +<자치법규ID>2<자치법규명>나 지역사랑상품권 조례<지자체기관명>강원도 나군 +<제개정구분명>일부개정<공포일자>20210301<시행일자>20210301""" + + +def test_parse_and_tidy(): + rows, total = parse_ordinance_xml(XML) + assert total == 2 and rows[0]["지자체기관명"] == "경기도 가군" + df = tidy(pd.DataFrame(rows + rows)) # 중복 제거 + assert len(df) == 2 and df["year"].tolist() == [2019, 2021] diff --git a/tests/test_site_build.py b/tests/test_site_build.py new file mode 100644 index 0000000..2c23aa2 --- /dev/null +++ b/tests/test_site_build.py @@ -0,0 +1,25 @@ +"""정적 대시보드가 모든 주제 페이지를 만들고, 로컬 경로·개인정보를 담지 않는지.""" + +import importlib.util +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def test_site_builds(tmp_path, monkeypatch): + spec = importlib.util.spec_from_file_location("site_build", ROOT / "site" / "build.py") + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + monkeypatch.setattr(mod, "OUT", tmp_path / "_site") + mod.main() + out = tmp_path / "_site" + topics = mod.load_topics() + assert (out / "index.html").exists() + for t in topics: + page = (out / "topics" / f"{t.id}.html").read_text(encoding="utf-8") + assert t.name in page + housing = (out / "topics" / "housing-regulation.html").read_text(encoding="utf-8") + assert "시뮬레이션 데이터 결과" in housing # 합성 결과 표시 + for f in out.rglob("*.html"): + text = f.read_text(encoding="utf-8") + assert "/home/" not in text and "/tmp/" not in text