포스트

RAG 연습 (8) — 질문 대신 ERP 거래 배치를 던지면 RAG가 알아서 법령을 찾는다

RAG 연습 (8) — 질문 대신 ERP 거래 배치를 던지면 RAG가 알아서 법령을 찾는다

지금까지 (1)~(7)편의 RAG는 다 같은 모양이었습니다. 사람이 질문을 적으면 시스템이 법령을 검색해 답하는 방식. “본인/대리인 판단 기준은?” 같은 질문을 던지는 거죠. 그래서 평가용 골든셋도 사람이 30개 만들어 놓아야 했습니다.

이번에 바꾼 건 입력입니다. 질문 대신 ERP 거래 묶음을 던집니다. 거래 데이터를 보고 시스템이 알아서 “이 거래엔 어떤 법령이 걸리지?”를 판단합니다. 사람이 질문을 만들 필요가 없어집니다.

1
2
입력: ERP 거래 18건
출력: 통합 리스크 리포트 + 상위 경보 + 관련 법령

왜 이게 의미가 있나

기존 RAG는 사람이 질문을 정확히 알아야 작동합니다. “이 거래에 뭐가 걸리는지” 자체를 모르는 상황이면 질문도 못 만듭니다. 회계 감사 현장이 딱 그렇습니다. 거래는 수천 건씩 들어오는데 어떤 거래의 어디가 문제인지 사람이 일일이 보고 질문을 만드는 건 비현실적입니다.

그래서 흐름을 뒤집었습니다. 거래가 트리거입니다. 의심스러운 패턴을 룰이 먼저 잡고, 잡힌 패턴이 RAG에 던질 질문을 알아서 만듭니다.

1
2
이전:  사람 → "중복 거래 탐지 기준은?" → RAG → 답
이번:  거래 묶음 → 룰이 중복 거래 자동 탐지 → "중복 거래 탐지 기준" 자동 쿼리 → RAG → 리포트

어떻게 동작하나 — 네 사람이 차례로 거래를 본다고 생각하면

LangGraph로 작은 워크플로우를 만들고 노드 4개를 연결했습니다. 각 노드를 한 사람의 검수자라고 생각하면 편합니다.

거래 묶음이 들어오면 두 사람이 동시에 봅니다. 부정 탐지자는 다섯 가지 룰로 의심 거래를 즉시 잡습니다(벤포드·중복·라운드 넘버·한도 직하·속도 이상). 세무 검토자는 거래 설명에서 “IT 컨설팅”·”광고비”·”분기말” 같은 키워드를 보고 세무 플래그를 답니다(원천징수·VAT·기간 귀속).

두 사람이 잡은 상위 경보를 법령 사서가 받습니다. “라운드 넘버 편향 발생” 같은 탐지 결과를 그대로 “라운드 넘버 편향 회계 조작 탐지” 같은 검색 쿼리로 변환해 벡터 DB에서 법령 청크를 가져옵니다. 마지막으로 리포트 작성자가 세 사람의 결과(부정·세무·법령)를 한 번에 모아 리스크 등급을 매기고 본문을 씁니다.

(상단의 도식이 이 흐름을 한 컷에 보여줍니다.)

처음 들으면 낯선 용어 몇 개를 짧게 짚어둡니다.

  • 벤포드 법칙: 자연스러운 거래 금액들의 첫 자리 숫자 분포는 1이 30%, 2가 17%… 식으로 한쪽에 쏠려 있습니다. 분포가 평평하면 사람이 손으로 만든 숫자라는 신호.
  • 라운드 넘버: 100만 원·500만 원처럼 끝자리가 0인 금액. 진짜 거래는 47,300원·182,500원 같은 임의 숫자가 정상이고, 라운드 넘버가 몰려 있으면 임의로 적어 넣은 의심.
  • 한도 직하: 결재 한도(예: 100만 원) 바로 아래에 980,000·960,000원처럼 거래가 군집. 한도 위로 올라가면 결재가 필요하니 일부러 한도 밑으로 쪼개는 패턴.
  • 속도 이상: 같은 거래처에 평소보다 짧은 시간에 거래가 몰리는 현상.
  • 원천징수: 외부 용역비 같은 지급 시 세금을 미리 떼고 주는 제도. 빠뜨리면 가산세 부과 대상.
  • VAT·기간 귀속: 부가가치세, 그리고 거래를 어느 회계기간에 잡을지의 문제(분기말 마감 직전 거래가 다음 분기로 밀리면 분식의 신호).
  • 벡터 DB: 문장을 숫자 묶음으로 바꿔 저장한 데이터베이스. 의미가 비슷한 문서를 빠르게 찾기 위한 구조. 시리즈 (2)편에서 다뤘습니다.

핵심은 세 번째 노드입니다. 이전 RAG에서는 사람이 했던 “쿼리를 만드는 일”을 룰이 대신합니다. “라운드 넘버 비율이 83.3%로 임계 초과”라는 탐지 결과가 나오면 그걸 그대로 “라운드 넘버 편향 회계 조작 탐지”라는 검색 쿼리로 변환합니다.

결과 1 — 룰만 켰을 때

18건짜리 픽스처 거래(테스트용으로 의도적으로 패턴을 심어둔 데이터)를 넣고 RAG는 끄고 돌렸습니다.

룰 기반 탐지만 켰을 때의 리스크 리포트 — 통합 리스크 CRITICAL, 경보 4건 상위 경보 표: round_number_bias 0.90 / duplicate_txn 0.60 / just_below_threshold 0.53 / WH-001 원천징수 0.50 두 건. 우측 하단 “검색된 법령 컨텍스트”는 RAG 비활성 상태라 비어 있다.

순위위험도플래그점수무슨 거래에서
1HIGHround_number_bias0.901·2·3·5·10백만 원처럼 끝자리가 0인 거래가 몰림
2HIGHduplicate_txn0.60같은 거래처에 같은 금액이 3일 안에 두 번
3HIGHjust_below_threshold0.53결재 한도(100만 원) 바로 아래에 거래 두 건 군집
4·5MEDIUMWH-001 (원천징수)0.50외부 IT 컨설팅 비용 두 건에 원천징수 검토 플래그

미리 심어둔 패턴들이 다 잡혔습니다. 이 단계까지는 룰 기반 부정탐지 엔진이라 RAG가 없어도 됩니다.

결과 2 — RAG를 켜면

같은 거래 18건에서 “RAG 법령 검색 포함”만 체크하고 다시 돌렸습니다.

RAG 켰을 때의 리스크 리포트 — 우측 하단 검색된 법령 컨텍스트가 채워짐 같은 거래 18건에 RAG 옵션을 켜면, 위 경보 네 개에 대해 자동 생성된 쿼리로 검색된 법령 청크가 우측 하단에 함께 표시된다.

상위 경보 네 개에서 시스템이 알아서 만든 쿼리:

  • 라운드 넘버 편향 회계 조작 탐지
  • 중복 거래 탐지 내부통제 기준
  • 결재 한도 직하 분할 결제 부정 탐지
  • 외부 용역비 원천징수 세율 기준

각 쿼리로 pgvector에서 법령 청크를 가져옵니다. 화면에 같이 붙는 컨텍스트 예시:

1
2
3
4
[내부감사 부정탐지 — 라운드 넘버 이상]
실제 거래는 임의 금액(예: 47,300원, 182,500원)이 정상.
부정·오류 거래는 1,000원·10,000원 단위의 라운드 넘버로
나타나는 경향. 이론적 기댓값: 끝자리 0 비율 약 10~11% ...

경보가 자기 근거를 스스로 찾아오는 구조입니다. 감사인은 경보만 보고 “이게 왜 의심스러운지”를 따로 검색하지 않아도 됩니다.

스크린샷 좌측 패널의 “Phase 6A”는 내부 단계 번호라 무시해도 됩니다.

구현하다 한 번 막힌 곳

법령 사서 노드가 비동기 함수입니다(retriever가 비동기라). LangGraph는 비동기 노드가 섞이면 그래프 전체를 비동기 진입점(ainvoke)으로 실행해야 하는데 처음엔 동기 진입점에서 노드 안쪽에 asyncio.run을 끼워 넣었다가 이벤트 루프 충돌로 에러가 났습니다. 진입점 한 곳에서만 비동기 처리를 하고 노드는 그대로 두는 식으로 정리했습니다.

다음 단계

지금 세무 검토자 노드는 키워드 매칭 수준입니다. 실제 ERP에서 의미 있는 세무 리스크를 잡으려면 더 나가야 합니다. 계정 코드와 금액을 같이 보는 룰(예: “계정 5210에 100만 원 이상이면 원천징수 체크”), 거래처 마스터에서 해외 법인 여부 조인, 정기 감사 일정 연동 같은 것들. 다음 글에서 다루겠습니다.

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

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