PlanChooser + Plan A~D 운영 규칙

JERRY·2025년 12월 15일

Project

목록 보기
11/14

수정 설계 플랜 완성본: PlanChooser + Plan A~D 운영 규칙

본 문서는 기존 “수정 설계 플랜(Plan A~D)” 초안을 실제 운영 가능한 형태로 보완한 최종 계획표입니다.
핵심은 (1) 질문을 구조화(JSON) → (2) Plan을 선택 → (3) 실행 중 결과에 따라 자동 완화/전환(fallback) → (4) 케이스(case=sub_code) 단위로 근거팩을 구성하는 것입니다.


0. 목표와 범위

목표

  • 사용자의 자연어 질문을 목적(goal) / 제약(constraints) / 핵심 유사축(axes)로 분해하고,
  • 상황에 맞는 검색 계획(Plan A~D)을 선택하여,
  • 빈 결과(0 hit), 편향(한 보고서/한 케이스 쏠림), 라우팅 오작동을 최소화하면서,
  • 실무형 “유사사례(case) 중심” 결과를 안정적으로 제공한다.

전제

  • 검색/집계는 기본적으로 case 단위(sub_code)로 수행한다.
    • chunk는 근거 발췌(evidence) 용도로만 사용한다.
  • 메타데이터 커버리지가 불완전할 수 있으므로(예: action_type/problem/criteria 결측),
    • 하드 필터(AND 강제)와 소프트 스코어링(가점)을 구분하고,
    • Plan 실행 중 자동 전환(fallback)을 반드시 둔다.

1. 공통 0단계: Slot/Intent 추출 스키마(고정)

PlanChooser의 입력은 항상 “정규화된 router_json”이어야 합니다. (스키마 고정)

1.1 router_json 예시(권장 스키마)

{
  "mode": "search | report",
  "goal": "similar_case | disposition | stats | report_draft | policy_check",
  "constraints": {
    "org": ["환경부", "보건복지부"],
    "audit_field": ["환경", "보건/복지"],
    "date_range": {"start": "2024-01-01", "end": "2024-12-31"},
    "year_ref": ["올해", "작년", "최근3년"],
    "count": 5,
    "amount": null
  },
  "axes": {
    "issue": true,
    "action": false,
    "procedure": false,
    "result": false
  },
  "keywords": ["병가", "해외여행", "복무"],
  "missing_slots": ["org", "date_range"],
  "confidence": {
    "goal": 0.78,
    "constraints": 0.64,
    "axes": 0.55
  }
}

1.2 DateNormalizer(규칙 기반 보정)

LLM만으로 “올해/작년/최근”을 처리하면 흔들릴 수 있으므로, 규칙 기반 보정 단계를 둡니다.

  • 올해 = 현재연도(YYYY-01-01 ~ YYYY-12-31)
  • 작년 = 현재연도-1
  • 최근 N년 = [현재-N+1, 현재] 범위
  • 상/하반기/분기 표현을 기간으로 변환

DateNormalizer는 router_json의 date_range를 최종 확정값으로 덮어쓰고, 변환 로그를 남깁니다.


2. PlanChooser: 운영 가능한 선택 규칙(최종)

2.1 기본 우선순위(규칙 기반, deterministic)

PlanChooser는 “우선순위 + 점수(score) + 결과 기반 fallback” 3단으로 운영합니다.

(A) 우선순위 규칙(가장 안정적인 운영용)

  1. goal == stats 또는 질문에 집계 의도(“몇 건/추이/증가/비중/기관별/연도별”)가 강함 → Plan D
  2. goal == disposition 또는 처분/수위(“징계/주의/통보/시정/적정 처분/수위/기준”)가 핵심 → Plan C
  3. constraints(기관/기간/분야/대상/금액 등) 명확한 제약 2개 이상Plan A
  4. 그 외(모호/정보 부족/제약 불명확) → Plan B

위 규칙으로 1차 선택을 하되, 아래 2.2의 “점수 + fallback”이 실제 운영 안정성을 결정합니다.


2.2 점수 기반 보정(선택 안정화)

우선순위만으로 흔들리는 경우를 줄이기 위해 Plan별 점수를 계산합니다.

(B) 핵심 지표

  • constraints_count: 확정 제약 슬롯 개수
    • 예: org(기관) + date_range(기간) + audit_field(분야) = 3
  • constraints_conf: constraints.confidence
  • goal_conf: goal.confidence
  • aggregation_signal: 집계형 키워드/표현 감지(“몇 건”, “추이”, “증가”, “비율” 등)
  • disposition_signal: 처분/수위 키워드 감지(“징계”, “주의”, “통보”, “수위”, “처분” 등)

(C) 점수 예시(권장)

  • Score_D = 2.0aggregation_signal + 0.5goal_conf
  • Score_C = 2.0disposition_signal + 0.5goal_conf
  • Score_A = 1.2constraints_count + 1.0constraints_conf
  • Score_B = 0.5 (default baseline)

(D) 최종 선택

  • 우선순위 규칙으로 후보 Plan을 정하되,
  • Score_C/Score_D가 명확히 높으면 C/D로,
  • 그렇지 않으면 A/B로 보정.

실제 구현에서는 “우선순위(룰) → 점수로 tie-break” 구조가 가장 운영하기 쉽습니다.


3. Plan 실행 공통 규칙(필수 가드레일)

3.1 Case 단위(=sub_code) 후보군을 먼저 만든다

  • 1차 후보는 반드시 sub_code 기준으로 dedupe/집계한다.
  • chunk는 근거 발췌를 위해 “케이스별 상위 N개”만 유지한다.

권장:

  • max_chunks_per_case = 3
  • max_cases_return = 20 (검색 응답)
  • min_cases_for_report = 3 (보고서/처분추천 근거팩 최소)

3.2 결과 기반 fallback(자동 전환) — 운영 안정성의 핵심

Plan 실행은 항상 “후보 케이스 수”를 기준으로 자동 전환한다.

  • MIN_CASES = 20 (너무 적으면 제약 완화 또는 Plan B로)
  • MAX_CASES = 2000 (너무 많으면 필터 강화 또는 샘플링/Plan D 일부 차용)

fallback ladder (권장)

  • Plan A → (0 hit or < MIN_CASES) → 제약 완화(기간/기관/처분 순) → 그래도 부족하면 Plan B
  • Plan B → (너무 광범위 > MAX_CASES) → audit_field/기간 보조 필터 추가 → 필요 시 Plan A로 전환
  • Plan C → (처분 후보가 너무 적음) → 처분을 하드 필터가 아니라 “가점”으로 완화 → 그래도 부족하면 Plan B
  • Plan D → (기간/축이 없어서 집계 불가능) → date_range 자동 보정(최근 1~3년) → 그래도 불가하면 Plan B로 질의 정밀화 유도

4. Plan별 실행 설계(Plan A~D)

아래는 “무엇을 하드로 걸고(필터), 무엇을 소프트로 쓰는지(가점/재정렬)”가 핵심입니다.

Plan A) 제약이 강한 케이스: “필터 먼저, 유사도는 나중”

트리거

  • constraints_count ≥ 2 AND 제약이 명확

전략
1) 하드 필터로 후보 케이스 축소

  • audit_field(분야), category/org(기관), date_range(기간)
    2) 후보군 내부에서 issue/action 유사도(벡터/하이브리드) + rerank
    3) 케이스 단위로 dedupe 후 상위 케이스 반환

하드 필터(권장)

  • audit_field
  • date_range(있으면)
  • org/category(있으면)

소프트(가점/재정렬)

  • action_type (있으면 가점, 결측 많으면 하드 금지)
  • problem/keyword_list (있으면 가점)

출력 포맷

  • “조건을 만족한 유사사례 TOP N”
  • 케이스별 대표 근거 1~3개 청크

주요 위험

  • AND 필터 과도 → 0 hit
    → 기간/기관/처분 순으로 자동 완화

Plan B) 정보 부족 케이스: “유사도 먼저, 필터는 보조”

트리거

  • 모호/정보 부족/제약 불명확, 또는 Plan A/C/D 실패 fallback

전략
1) issue 중심 dense/hybrid 검색으로 넓게 후보 확보(리콜 우선)
2) 후보에서 메타(기관/기간/처분)를 추출해 그룹핑(케이스 단위)
3) “주요 패턴 3가지” 요약 + 추가 슬롯(missing_slots) 질문 유도(선택)

하드 필터(최소화)

  • audit_field가 명확히 있으면만 적용(없으면 미적용)
  • 기간/기관은 기본적으로 보조 필터(soft)

소프트(가점/재정렬)

  • constraints 일치 가점(기관/기간/처분)
  • rerank(상위 후보에만)

출력 포맷

  • “유사사례 패턴 요약(3개) + 대표 사례 샘플(각 2~3건)”
  • missing_slots가 있으면 “추가 질문 1~2개”를 함께 제시

주요 위험

  • 동일 케이스 청크 쏠림
    → 케이스 단위 dedupe + case별 chunk 제한 필수

Plan C) “처분/수위”가 핵심: “처분 축을 우선”

트리거

  • goal == disposition 또는 처분/수위 키워드 강함

전략(운영형)
1) 처분(action_type) 신호로 후보군을 확보하되,

  • 하드 필터 강제 금지(결측이 많기 때문)
  • “포함(OR 성격)” 또는 “가점” 중심으로 운영
    2) 후보군 내부에서 issue 유사도 + rerank
    3) 출력은 “처분 그룹별 묶음 + 대표 근거” 형태

권장 구현(안전)

  • 1차 후보: Plan A/B 방식으로 넓게 확보(분야/기간이 있으면 적용)
  • 2차 재정렬:
    • action_type이 질의 처분과 겹치면 큰 가점
    • action(조치 텍스트)에 처분 키워드가 있으면 중간 가점
    • issue(problem) 유사도가 높으면 가점

출력 포맷

  • “처분 유형별로 유사사례 묶음(예: 징계/통보/주의…)”
  • 각 묶음 대표 사례 2~5건 + 근거 청크

주요 위험

  • action_type 결측으로 처분 기반 후보군이 급격히 줄어듦
    → action_type을 “필터”가 아니라 “정렬/가점”로, 부족하면 Plan B로 자동 전환

Plan D) “몇 건/추세”가 핵심: “집계 우선, 사례는 샘플링”

트리거

  • goal == stats 또는 “몇 건/추이/증가/비중/기관별” 등 집계 의도

전략
1) 제약(기간/기관/분야/주제)으로 case 단위 집계 수행

  • COUNT(DISTINCT sub_code) 기준
    2) 표/요약 생성(가능하면 그래프)
    3) 각 그룹에서 대표 사례 3~5건 샘플링(근거 제공)

집계 축(권장)

  • 시간: audit_end_date(우선), 없으면 audit_start_date 또는 doc_upload_date로 보정
  • 그룹: audit_field, category(org), action_type(단, 결측 편향 안내), 키워드 주제

출력 포맷

  • “집계 테이블(또는 요약) + 대표 사례 샘플”
  • 통계형 질문에는 TopK 나열 대신 집계 결과가 메인

주요 위험

  • chunk 기준 집계(절대 금지) → 결과 왜곡
  • action_type 기반 집계는 결측 편향 큼 → “기재된 케이스만 집계” 주석 필요

5. 최종 계획표(요약 매트릭스)

Plan트리거(PlanChooser)1차 후보 생성필터(하드)정렬(소프트)산출물실패 시 전환
A제약 ≥2, 명확필터로 케이스 축소 후 유사도audit_field/org/date_rangeissue/action 유사도 + rerank + action_type 가점조건 만족 유사사례 TOP N제약 완화 → B
B모호/정보부족유사도(리콜)로 케이스 확보최소(있으면만)제약 일치 가점 + rerank(상위만)패턴 요약 + 샘플너무 많으면 A 또는 D 일부
C처분/수위 중심넓게 확보 후 처분 중심 재정렬처분 하드 금지(원칙)action_type/action 가점 + issue 유사도처분 그룹 + 대표 근거부족하면 B
D건수/추세 중심집계 우선(case)기간/분야/기관필요 시 샘플 rerank집계표 + 사례샘플집계 불가면 B

6. 운영 체크리스트(리스크 구간과 대응)

6.1 위험 구간 1: 0 hit / 과도한 필터

  • Plan A/C에서 자주 발생
  • 대응: 기간 → 기관 → 처분 순으로 완화, 그래도 부족하면 Plan B

6.2 위험 구간 2: “처분 축 우선(Plan C)”의 데이터 결측

  • action_type 결측이 많을 수 있음
  • 대응: 처분을 “필터”가 아니라 “정렬/가점”으로만 사용 + action 텍스트 보조

6.3 위험 구간 3: “집계(Plan D)”의 단위 오류

  • 반드시 case 단위( DISTINCT sub_code )로 집계
  • chunk 기준 집계는 왜곡을 유발

6.4 위험 구간 4: 라우팅(search vs report) 오분기

  • 안정화 권장:
    • 1) 기본은 search로 근거팩 확보
    • 2) 근거팩이 충분할 때(report 조건 충족)만 report 생성
    • 3) 부족하면 “추가 조건 요청” 또는 Plan B 패턴 요약으로 전환

7. Langflow 구현 가이드(컴포넌트 관점)

공통

1) Slot/Intent Extractor (+ DateNormalizer)
2) PlanChooser (본 문서 규칙을 구현한 라우터)
3) Plan별 Branch(A/B/C/D) 서브플로우
4) Evidence Pack Builder (case 단위 근거팩)
5) Answer/Report Generator

PlanChooser 출력(권장)

  • selected_plan: "A"|"B"|"C"|"D"
  • normalized_router_json: 보정된 date_range 포함
  • execution_params: top_k, min_cases, max_cases, relax_steps 등

실행 로그(필수)

  • selected_plan, constraints_count, applied_filters, result_cases, fallback_steps
  • 운영 중 “왜 이 Plan이 선택/전환됐는지”를 재현 가능해야 함

8. 다음 단계(실행 우선순위)

1) Slot/Intent Extractor 스키마 고정 + DateNormalizer 룰 확정
2) PlanChooser 컴포넌트 구현(룰+점수+fallback)
3) Case 단위 후보군/근거팩 표준화(sub_code 중심)
4) Plan D 집계 쿼리 템플릿 확정(시간축 정의 포함)
5) 모니터링/리그레션 테스트(질문 50~100개)로 라우팅 안정화


부록 A. PlanChooser 의사코드(운영형)

input: router_json
router_json = DateNormalizer(router_json)

signals:
  agg = detect_aggregation(router_json, user_text)
  disp = detect_disposition(router_json, user_text)
  c_cnt = count_confirmed_constraints(router_json.constraints)
  c_conf = router_json.confidence.constraints
  g_conf = router_json.confidence.goal

priority_choice:
  if agg: plan = D
  else if disp: plan = C
  else if c_cnt >= 2 and c_conf >= 0.6: plan = A
  else: plan = B

score_choice (tie-break / override):
  scoreD = 2*agg + 0.5*g_conf
  scoreC = 2*disp + 0.5*g_conf
  scoreA = 1.2*c_cnt + 1.0*c_conf
  scoreB = 0.5
  plan = argmax_with_priority(plan, score*)

execute(plan) -> result_cases

fallback:
  if result_cases == 0 or result_cases < MIN_CASES:
     relax_constraints()
     rerun
     if still low: plan = B; rerun
  if result_cases > MAX_CASES:
     add_light_filters() or switch_to_D_sampling()

0개의 댓글