포스트

RAG 연습 (9) — 키워드 매칭을 버리고 multi-tier (계정코드 + 거래처 마스터 +a) 를 채택하였음

RAG 연습 (9) — 키워드 매칭을 버리고 multi-tier (계정코드 + 거래처 마스터 +a) 를 채택하였음

RAG 연습 (8) 끝에서 “세무 검토자 노드가 키워드 매칭 수준이라 더 나가야 한다”고 적었습니다. 이번에 그걸 손봤습니다. 결론부터 말하면 거래 설명 텍스트를 보지 않고, 계정코드·금액·날짜·거래처 마스터를 같이 봅니다.

무엇이 문제였나

이전 세무 노드는 거래 설명(description)에 들어 있는 키워드로만 룰을 발화시켰습니다.

1
2
3
4
5
_TAX_RULES = [
    ("WH-001", ["컨설팅", "서비스", "라이선스"], "원천징수 검토"),
    ("VAT-001", ["광고", "마케팅"],              "매입세액공제 확인"),
    ("CUT-001", ["", "분기말", "12월"],        "컷오프 리스크"),
]

이전 방식의 문제는 두 가지였습니다.

자유 텍스트에 의존했습니다. “IT 컨설팅”이라고 쓰면 WH-001이 잡히지만, 같은 거래를 누가 “외부 인력 투입”·”프로젝트 지원”이라고 쓰면 안 잡혔습니다. ERP 거래 설명은 담당자마다 입력 방식이 달라서 동일한 거래가 다른 키워드로 들어옵니다. 동일한 거래가 경우에 따라 다르게 검토된다는 문제입니다.

거래당 룰 하나만 발화했습니다(break). “해외 클라우드 비용”은 부가세 역발행 대상(WH-002)이면서 동시에 비거주자 원천징수 대상(VEN-001)입니다. 두 리스크는 성격이 달라 둘 다 검토해야 하는데 한쪽만 나오고 끝났습니다. 이 경우 코드를 수정해야 하는데 다음에서 소개하는 tier별 플래그 룰로 바꾸면 함께 해결됩니다.

3-tier 플래그 룰로 바꿨습니다

세 가지 기준을 순서대로 붙였습니다.

1
2
3
Tier 1 — 계정코드 + 금액
Tier 2 — 거래 날짜
Tier 3 — 거래처 마스터

각 tier는 독립적으로 발화합니다. 한 거래가 세 tier 모두에 걸리면 플래그도 셋이 붙습니다.

Tier 1: 계정코드 prefix + 금액 임계값

1
2
3
4
5
6
7
8
_ACCOUNT_RULES = [
    ("WH-001",  {"521","522","523"}, Decimal("330000"),
     "외부 용역비 52x 계정 33만 원 이상 — 원천징수 대상 확인"),
    ("VAT-001", {"531","532"},       Decimal("0"),
     "광고·마케팅비 53x — 매입세액공제 요건 확인"),
    ("WH-002",  {"561","562"},       Decimal("0"),
     "클라우드·SaaS 56x — 해외 공급자 부가세 역발행 여부 확인"),
]

account_code.startswith(prefix)로 매칭합니다. 계정코드 5210이면 description에 “컨설팅”이 한 글자도 없어도 521 접두로 WH-001이 발화합니다.

원천징수에는 금액 조건이 같이 붙습니다. 소득세법 기준 33만 원이 3.3% 적용 최저선이라 그걸 임계값으로 잡았습니다.

Tier 2: 날짜 기반 컷오프

1
2
3
if day >= 25 and any(acct.startswith(p) for p in _CUTOFF_ACCOUNTS):
    quarter_end = month in (3, 6, 9, 12)
    detail = f"월말{'(분기말)' if quarter_end else ''} 거래 — 기간귀속 컷오프 검토"

description에 “분기말”이라는 단어가 없어도 거래 날짜가 25일 이후면 컷오프 리스크를 붙입니다. 분기말(3·6·9·12월)이면 별도 표시까지.

Tier 3: 거래처 마스터

1
2
3
4
5
6
7
8
9
VENDOR_MASTER = {
    "V-AmazonKR": {"type": "overseas",     "country": "US"},
    "V-Unknown":  {"type": "unregistered"},
}

_VENDOR_RULES = [
    ("VEN-001", "overseas",     "비거주자 원천징수 22% + 조세조약 확인"),
    ("VEN-002", "unregistered", "적격증빙 미수취 — 매입세액공제 불인정 + 가산세"),
]

거래처 코드를 마스터 테이블에 조회해 속성(해외·미등록 등)을 가져옵니다. 지금은 하드코딩된 dict인데, 실제 ERP에선 거래처 테이블 조회로 바꾸면 됩니다.

처음 보는 용어 짧게 짚기

  • 계정코드: 회계에서 거래를 분류하는 번호 체계. 외부 용역비는 521·광고비는 531·클라우드는 561 같은 식으로 계정 그룹별로 prefix가 정해져 있습니다.
  • 역발행 부가세: 해외 공급자(예: AWS)에서 서비스를 받을 때 국내 매입자가 본인 명의로 세금계산서를 만들어 부가세를 내는 제도. 빠뜨리면 가산세.
  • 매입세액공제: 매입할 때 낸 부가세를 환급·공제받는 절차. 적격증빙(세금계산서·신용카드매출전표 등)이 있어야 가능.
  • 비거주자 원천징수: 해외 법인에 돈을 보낼 때 국내에서 세금을 떼고 송금. 조세조약이 적용되면 세율이 낮아집니다.
  • 컷오프: 거래를 어느 회계 기간에 잡을지. 분기말 직전 거래가 다음 분기로 밀리면 분식의 신호라 따로 봅니다.

break를 없앤 이유

이전 코드는 한 거래가 첫 룰에 걸리면 break로 끝냈습니다. 이번에 제거했습니다.

T009 거래(거래처 V-AmazonKR, 계정 5610, 클라우드 사용료)를 예로 들면:

  • Tier 1에서 WH-002 (56x 계정) 발화
  • Tier 3에서 VEN-001 (해외 법인) 발화

WH-002는 부가세 역발행 여부, VEN-001은 원천징수 세율 조약 적용 여부라 검토 포인트가 다릅니다. 하나만 보고 나머지를 놓치면 실무에서 놓칩니다.

결과 비교

구분이전이번
트리거 기준거래 설명 키워드계정코드 + 금액 + 날짜 + 거래처
플래그 종류WH-001, VAT-001, CUT-001+ WH-002, VEN-001, VEN-002
거래당 플래그 수1개 (break)복수 가능
컷오프 발화 조건“말”·”분기말” 키워드day ≥ 25
해외 법인 탐지없음VEN-001
미등록 거래처 탐지없음VEN-002
18건 픽스처 기준 세무 플래그8건10건

플래그 분포는 이렇게 잡혔습니다.

1
2
3
4
5
6
WH-001  : T003, T004       — 외부용역비 52x 계정
VAT-001 : T005, T016       — 광고비 53x 계정
WH-002  : T009             — 클라우드 56x 계정
CUT-001 : T016, T017, T018 — 4월 25일 이후 거래
VEN-001 : T009             — V-AmazonKR (해외)
VEN-002 : T010             — V-Unknown (미등록)

T009와 T016이 각각 두 플래그를 받는 게 보입니다. break를 없애지 않았으면 T009의 VEN-001은 영영 안 나왔습니다.

새 플래그도 RAG 쿼리로 자동 변환

(8)편에서 만든 “플래그 → 검색 쿼리” 매핑에 새 항목을 추가했습니다.

1
2
3
"WH-002": "클라우드 SaaS 해외 공급자 부가세 역발행",
"VEN-001": "해외 법인 비거주자 원천징수 세율 기준",
"VEN-002": "적격증빙 미수취 가산세 법인세법",

VEN-001이 잡힌 거래에는 “해외 법인 비거주자 원천징수” 쿼리로 검색된 법령 청크가 자동으로 붙습니다.

다음 단계

거래처 마스터는 아직 하드코딩된 dict입니다. 실제 환경으로 옮기려면 세 가지가 필요합니다.

  • 거래처 테이블 조인 (asyncpg 쿼리로 교체)
  • 계정코드 계층을 설정 파라미터로 분리 (ERP마다 코드 체계가 다름)
  • 금액 임계값을 법인 규모별로 분리 (중소·대기업 따라 원천징수 기준이 다름)

DB 연동과 함께 다음 글에서 다루겠습니다.

(클로드 코드의 도움을 받았습니다.)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.