Skip to content

Repository files navigation

IPX-AI

IPX Screenshot

변리사를 위한 특허 등록 플랫폼 IPX의 AI 서버


목차

  • [프로젝트 개요]
  • [시스템 아키텍처]
  • [기술 스택]
  • [AI 파이프라인]
    • [선행기술 탐색 파이프라인 (11단계)]
    • [선행기술 탐색 진행률 조회 파이프라인]
    • [특허 데이터 적재 파이프라인]
    • [신규성 분석 파이프라인]
    • [진보성 분석 파이프라인]
  • [트러블 슈팅]
  • [관련 레포지토리]

프로젝트 개요

IPX는 변리사가 출원하려는 발명의 선행기술 조사 및 특허 분석을 자동화하는 플랫폼입니다.
이 레포지토리는 AI 분석을 담당하는 Python FastAPI 서버입니다.

핵심 기능

기능 설명
선행기술 검색 OpenSearch, pgvector 기반 하이브리드 검색 (벡터 및 키워드 검색)
구성요소 추출 사용자 입력 발명 설명에서 청구항 구성요소 자동 추출
관련성 판단 Claude LLM으로 각 특허의 관련성 점수(0~100) 산출
신규성 분석 사용자 발명 구성요소와 선행기술 청구항 1:1 대비 (동일/유사/신규)
진보성 분석 주인용/부인용 발명(선행기술) 기반 4개 진보성 논리 자동 생성

비즈니스 맥락

  • 타겟 사용자: 특허 변리사, 특허법인
  • 기존 방식: 변리사가 직접 수십 건의 선행기술을 수작업으로 검토 → 수 시간 소요
  • IPX 방식: 자동 검색 + LLM 분석 → 핵심 선행기술 및 논리 자동 제공

시스템 아키텍처

  사용자(변리사)
      │
      │ HTTPS
      ▼
┌─────────────────────┐
│   Spring Boot 서버   │  ← 비즈니스 로직, 권한 검증, DB 관리
└─────────────────────┘
      │
      │ HTTP (내부 통신): 선행기술 탐색 요청, 구성요소 추출 요청, 신규성 및 진보성 분석 요청
      ▼
┌─────────────────────┐          ┌─────────────────────┐
│  Python FastAPI 서버│ ───────▶ │ Claude, Gemini API  │  LLM 분석
│    (해당 레포)       │          └─────────────────────┘
│                     │          ┌─────────────────┐
│                     │ ───────▶│   OpenSearch    │  BM25 키워드 검색
│                     │          └─────────────────┘
│                     │          ┌─────────────────┐
│                     │ ───────▶│ PostgreSQL      │  벡터 유사도 검색
└─────────────────────┘          │  (pgvector)     │
      │                          └─────────────────┘
      │ HTTP (분석 결과 반환)
      ▼
┌─────────────────────┐          ┌─────────────────┐
│   Spring Boot 서버   │ ───────▶│   PostgreSQL    │  결과 저장
└─────────────────────┘          │ (사용자/케이스)  │
      │                          └─────────────────┘
      │ HTTPS
      ▼
  사용자(변리사)

배포 환경

  • EC2: t3.large
  • Docker Compose: nginx, certbot, spring, python, postgres(pgvector), opensearch, redis 총 6개 컨테이너
  • 도메인: api.ipx-patent.com

데이터

  • 특허 DB: 약 15만 건을 OpenSearch와 pgvector 각각에 사전 적재
  • 주요 IPC 분포:
    • G06T 컴퓨터 그래픽스/영상처리: 67%
    • G06N 인공지능/머신러닝: 47%
    • G06Q 비즈니스 ICT: 23%
    • G06V 컴퓨터 비전: 12%
    • G16H / A61B 디지털 헬스케어: 13%

기술 스택

분류 기술
웹 프레임워크 FastAPI
LLM Claude Sonnet, Gemini
임베딩 모델 BGE-M3
벡터 검색 PostgreSQL + pgvector
텍스트 검색 OpenSearch

AI 파이프라인

선행기술 탐색 파이프라인 (11단계)

사용자의 발명 정보를 입력받아 관련 선행기술을 검색하고 관련성을 판단합니다.

Step 0: 사용자가 지정한 특허 확인 및 자동 적제
Step 1: 사용자 입력 자연어 의도 해석 — LLM으로 키워드 및 IPC 코드 추출 
Step 2: 동의어 확장 — 키워드 변형어 생성 
Step 3: IPC 코드 통합 — 사용자 입력 + LLM 추정 합집합 
Step 4: HyDE — 가상 특허 초록 생성 
Step 5: BGE-M3 임베딩 — 가상 초록 벡터화 
Step 6: 병렬 검색 — OpenSearch(BM25 키워드 검색) + pgvector(벡터 검색) 동시 실행 
Step 7: RRF 병합 — 두 검색 결과 RRF 알고리즘으로 통합 
Step 8: 정확한 개수 확보 — 중복 제거 후 목표 건수 필터링 
Step 9: mget 준비 — OpenSearch에서 필터링된 후보의 청구항/초록 병렬 조회 
Step 10: LLM 관련성 판단 — 각 특허에 대한 관련도 0~100 점수 산출 및 특허 특징, 요약 , 추천 이유 생성
Step 11: 최종 응답 조립 및 반환 (99%)

Step 0: required 특허 사전 확인 및 자동 적재

  • 사용자가 직접 지정한 출원번호에가 DB(OpenSearch)에 적재되어 있는지 확인
    • 없으면 KIPRIS API로 즉시 조회하여 DB에 자동 적재
  • 이 단계로 사용자가 반드시 포함하고 싶은 특허가 결과에 항상 포함됨을 보장

Step 1: LLM 의도 해석

  • 사용자 입력(발명 명칭, 상세, 기술 분야 등)을 Gemini에 전달
  • 검색 의도를 구조화된 JSON으로 추출: 유효한 입력 여부, 기술 키워드, 예상 IPC 코드 등
  • 의도가 부적절하면 여기서 종료 (invalid_input 상태)
    • 예: 광고성 문구, 기술적 실체가 없는 내용

Step 2: 동의어 확장

  • 의도 해석에서 추출한 키워드를 사전에 작성한 동의어 사전과 매칭
    • 예: 클러스터 → [클러스터링, 군집, 집단화] 확장
  • OpenSearch 동의어 사전 (synonyms_patent.txt) 활용

Step 3: IPC 통합

  • 아래 2가지를 통합하여 최종 IPC 필터 구성
    • 사용자가 입력한 IPC 코드 (userInputIpc)
    • LLM이 추정한 IPC 코드 (estimated_ipc)
  • 검색 정밀도 향상

Step 4: HyDE 가상 초록 생성

  • Claude에게 "이 발명 정보로 예상되는 선행 특허의 가상 초록"을 작성하게 함
    • 실제 특허와 유사한 서술 구조/전문 용어로 생성됨
  • 가상 초록을 임베딩하면 실제 특허의 초록과 벡터 공간에서 더 가까워짐
    • 검색 정확도 향상

Step 5: BGE-M3 임베딩

  • HyDE 가상 초록을 BGE-M3로 임베딩
  • 1024차원 정규화 벡터 생성
  • 코사인 유사도 계산을 위한 준비

Step 6: 병렬 검색

두 검색 방식을 동시에 실행:

OpenSearch 검색:

  • BM25 스코어링
  • 동의어 사전으로 확장된 키워드로 쿼리

pgvector 검색:

  • 코사인 유사도 검색
  • IPC 코드 필터링

Step 7: RRF 병합

  • 두 검색 결과의 순위를 RRF 공식으로 결합
    • 순위만 사용하므로 스코어 정규화 불필요
  • 병합된 순위로 재정렬

Step 7.5: 정확한 개수 확보

  • 사용자가 요청한 개수 (resultCount, 10~30건)만큼 상위에서 슬라이싱
  • required 특허가 요청 개수 안에 반드시 포함되도록 보장
  • 만약 병합 결과가 0건이면 그대로 종료

Step 8: 본문 데이터 준비

  • 상위 특허들의 상세 정보(청구항, 초록 등)를 OpenSearch mget으로 한 번에 조회
  • 다음 LLM 분석에 필요한 재료 준비 과정

Step 9: 병렬 LLM 추출

  • 상위 특허들에 대해 Claude API 병렬 호출
  • 각 특허마다 LLM이 산출:
    • reason (추천 이유)
    • summary (특허 요약)
    • purpose (기술 목적)
    • keywords (기술 키워드)
    • features (핵심 특징)

Step 10: 응답 조립

  • LLM 결과를 검색 결과와 매핑
  • Spring에 최종 응답 반환

기술적 의사결정

1. 하이브리드 검색

단일 검색 방식의 한계: 특허 검색은 키워드 매칭의미론적 유사성 모두 중

  • 키워드 매칭 필요: 전문 용어는 정확히 일치해야 함
  • 의미론적 유사성 필요: "차량 상태 진단 시스템"과 "자동차 이상 감지 장치"는 표현은 다르지만 같은 개념

옵션 A: 키워드 검색만 사용

  • 장점: 정확한 매칭, 빠른 응답
  • 단점: 표현이 다르면 유사 특허 놓침
    • "AI로 자동차 진단"과 "머신러닝 기반 차량 이상 탐지"가 다른 특허로 판정됨

옵션 B: 벡터 검색만 사용

  • 장점: 의미적 유사성 반영
  • 단점: 정확한 용어 매칭에 약함

옵션 C: Hybrid Search

OpenSearch (BM25 + 키워드) 담당:

  • 동의어 사전을 통한 키워드 확장
  • 특허 명칭, 초록, 청구항에서 키워드 매칭

pgvector 담당:

  • BGE-M3 모델로 생성한 1024차원 벡터의 코사인 유사도 검색
    • 텍스트 → 고차원 수치 배열 (1024차원)
    • 벡터 공간에서 코사인 거리 측정 (각도가 가까울 수록 유사)

결과: 두 검색 결과를 RRF로 순위 병합해 최종 결과 반환

2. HyDE (Hypothetical Document Embeddings)

LLM이 가상 답변 문서를 먼저 작성하게 한 뒤, 벡터로 바꿔 검색하는 기법

  1. 발명 설명 (자연어) → Cluade API를 통해 해당 발명 정보로 가상 초록 생성
  • 실제 특허 초록과 유사한 서술 구조/전문 용어로 생성됨
  1. 해당 가상 초록을 BGE-M3로 임베딩하여 벡터 생성
  2. 해당 벡터를 pgvector를 통해 벡터 공간에 저장된 실제 특허의 초록과 코사인 유사도 검색
  • 벡터 공간에서 더 가까워져 검색 정확도 향상

3. RRF (Reciprocal Rank Fusion)

병합의 필요성 OpenSearchpgvector가 각각 다른 기준으로 스코어링 후 결과 반환:

  • OpenSearch: BM25 스코어 (0~수십)
  • pgvector: 코사인 유사도 (0~1)
RRF_score = Σ 1 / (k + rank_i)   (k=60)
  • rank_i(d): 검색 방법 i에서 문서 d의 순위

  • OpenSearch (키워드 기반) + pgvector (의미 기반) 두 순위를 통합

  • 어느 한 방식에만 상위 랭크된 특허보다 두 방식 모두에서 높은 특허를 우선 반환

  1. 스코어 정규화 불필요
  • 순위만 사용 → 스코어 단위 무관
  • OpenSearch가 BM25 100점을 반환하든, pgvector가 0.8을 반환하든 순위로만 판단
  1. 구현 단순성
  • 몇 줄의 파이썬 코드로 구현
  • 튜닝 파라미터가 k 하나
    • k를 낮게 잡으면: 어느 한쪽 검색에서라도 최상위에 위치한 특허 우선시
    • k를 높게 잡으면: 키워드와 벡터 양쪽에서 고르게 5~10등 안에 든 무난한 특허 우선시

4. BGE-M3 임베딩 모델 선택 기준

1. 최대 8192 토큰 지원

  • 특허 청구항 + 초록 전체를 하나의 벡터로 임베딩 가능
  • 문서 분할/청킹 로직 불필요

2. 다국어 성능

  • 100+ 언어 지원, 한국어 포함 최상위 벤치마크

3. 오픈소스 (MIT 라이선스)

  • API 비용 없음
  • 로컬 GPU/CPU에서 실행 가능 (임베딩 모델 다운로드 받아 사용)
  • Sentence-Transformers 라이브러리 지원

4. 기타 모델들의 한계

  • API 비용 발생 (15만 건 임베딩해야함)
  • 외부 의존성 (장애 시 서비스 마비)
  • 한국어 성능 낮음 (영어 중심 학습)

선행기술 탐색 진행률 조회 파이프라인

문제: 비동기로 진행되는 탐색 API에서, 현재 탐색 진행 단계 및 진행률을 프론트가 실시간으로 조회해야함

해결: 프론트는 검색 실행 응답으로 받은 caseId로 Spring을 통해 Redis에 진행 상태를 주기적으로 조회하여 완료 여부 확인

요청 흐름

  • 프론트가 “탐색 실행 API” 응답으로 받은 caseId로 폴링
  • Spring이 해당 caseId를 키값으로 Redis에 직접 접근하여 상태 조회

상태 값 (status)

상태 의미 프론트 대응
in_progress 진행 중 계속 폴링
completed 정상 완료 (결과 있음) GET /api/cases/{caseId}/prior-arts로 결과 조회
no_results 정상 완료 (결과 0건) "선행기술을 찾지 못했습니다" 안내
invalid_input 사용자 입력 부적절 reasonInvalid 사유 표시 후 입력 화면으로
failed 시스템 오류 error 메시지 표시 후 재시도 유도
cancelled 사용자 취소 초기 화면으로 이동

Redis 저장 구조

Key: search:{caseId}

Hash Fields:

필드 설명
status in_progress / completed / no_results / invalid_input / failed / cancelled
step 현재 단계 설명 (예: "검색 의도 분석 중")
progress 진행률 (0~100)
started_at 검색 시작 시각
updated_at 마지막 갱신 시각
reason_invalid invalid_input 상태 시 사용자 입력 문제 사유
error failed 상태 시 시스템 오류 메시지

응답 예시

진행 중:

{
  "success": true,
  "data": {
    "caseId": 1,
    "status": "in_progress",
    "step": "검색 의도 분석 중",
    "progress": 45,
    "error": null,
    "reasonInvalid": null
  },
  "error": null
}

정상 완료:

{
  "success": true,
  "data": {
    "caseId": 1,
    "status": "completed",
    "step": "선행기술 탐색 완료",
    "progress": 100,
    "error": null,
    "reasonInvalid": null
  },
  "error": null
}

특허 데이터 적재

Google Colab에서 BGE-M3 임베딩 생성 후, EC2에 적재하는 방식을 사용합니다.

흐름

  1. Kipris Plus 사이트에서 특허 15만건 BULK 다운로드
  2. 로컬 특허 데이터(ZIP) → Google Drive 업로드
  3. Colab T4 GPU에서 BGE-M3 임베딩 생성 → checkpoint.json 저장
  4. AWS EC2로 전송 → 적재 로직 ingest_from_checkpoint.py 실행
  5. pgvector + OpenSearch 동시 적재

BULK 다운로드

  • KIPRIS API는 최대 월 1,000회 무료 사용 가능
  • IPX MVP 단계에서 적재할 특허는 15만 건
  • KIPRIS API로는 건수 부족 → KIPRIS Plus 홈페이지에서 IPC 코드별 수동 BULK 다운로드
  • 1회 다운로드당 최대 1만 건, 6개 파일(Bibliographic, Abstract, Claim, IPC, CPC, RelatedPerson) 제공
  • 15만 건 기준 15회 다운로드, 3개 배치(batch_1, batch_2, batch_3)로 Google Drive에 분할 저장

Colab 임베딩

  • 15만건의 특허에 대해 벡터 검색을 위한 임베딩 과정을 거치고 적재해야함
  • 로컬 혹은 EC2의 CPU 환경에서는 사실상 불가능
  • Colab GPU를 통한 임베딩 (도합 약 3시간 소요)
  1. 환경 설치 (sentence-transformers, pandas)
  2. Google Drive 마운트 + 배치 경로 설정
  3. ZIP 압축 해제
  4. BULK 파일 파싱 및 병합 (Bibliographic + Abstract + Claim + IPC + CPC)
  5. 텍스트 전처리 (HTML 태그 제거, 상투어 제거)
  6. BGE-M3 임베딩 생성 (T4 GPU, batch_size=32)
  7. checkpoint.json Google Drive 저장

신규성 분석 파이프라인

사용자가 등록하고자 하는 발명이 이미 존재하는 선행기술에는 없는 새로운 기능을 포함하고 있는 지의 여부 판별

Step 1: 상위 3건 선행기술 mget (OpenSearch에서 청구항/초록 조회)
Step 2: 3건에 대해 병렬 LLM 분석
        — 사용자 발명 구성요소(A, B, C, D) vs 선행기술 청구항 1:1 대비
        — 각 구성요소: 동일 / 유사 / 신규 판정
Step 3: 유사도 점수 계산 (동일=2, 유사=1, 신규=0) → 최고점 특허를 D1 선정
Step 4: DB 저장 (기존 분석 삭제 후 새로 저장, 단일 트랜잭션)

진보성 분석 파이프라인

사용자가 등록하고자 하는 발명이 이미 존재하는 선행기술 대비 얼마나 진보적(아래 4개의 카테고리가 기준)인지 판별

사용자가 선택한 주인용(D1)을 기준으로 진보성 분석을 자동 실행합니다.
Step 1: D1 확정 및 D2 후보 준비 (후보는 D1 제외 상위 N건)
Step 2: OpenSearch mget — D1과 후보들의 청구항/초록 조회
Step 3: LLM으로 D1과 더불어 진보성 분석하기 적합한 D2 선정 (없으면 null, D1 단독 분석)
Step 4: D1/D2 기반으로 심사에서 부정 당하기 쉬운 진보성 논리 카테고리 선정
Step 5: 선정된 카테고리별로 진보성 논리 자동 생성
Step 6: DB 저장

진보성 논리 4개 카테고리

카테고리 설명 D2 필요 여부
NUMERICAL_LIMIT 수치한정 — 본 발명이 정량적 성능 지표를 개시하고, D1 대비 개선률 명확한 경우 D1만 필요
COMBINATION_MOTIVATION 결합 동기 부재 (Teaching Away) — D1과 D2가 서로 다른 방향의 기술이거나 결합 동기가 없어 보이는 경우 D2 필수
COMMON_TECHNIQUE 주지관용기술 반박 — D1에 없는 구성요소 중 관용기술로 오해받을 만한 것이 있는 경우 D1만 필요
SIMPLE_DESIGN 단순설계변경 반박 — D1과 본 발명의 차이가 미묘하거나 파라미터 조정으로 보일 위험이 있는 경우 D1만 필요

트러블 슈팅

1. Self-invocation 문제

문제: @Async@Transactional을 같은 클래스에서 self-invocation하면 Spring AOP 프록시가 우회되어 두 어노테이션 모두 무효화됨

초기 코드

@Service
public class CaseSearchService {
		public SearchStartResponse startSearch() {
				asyncTask();
				transactional();
		}

    @Async  // self-invocation에서 무효
    public void asyncTask() { ... }

    @Transactional  // self-invocation에서 무효
    public void transactional() { ... }
}

해결: Service를 3계층으로 분리

CaseSearchService       → (트랜잭션 없음)
    
CaseSearchAsyncService  → @Async 스레드 (트랜잭션 없음)
    
CaseSearchTxService     → @Transactional 트랜잭션
  • 각 계층이 하나의 책임만 (SRP)
  • 프록시 우회 문제 해결
  • 진보성/신규성/검색 모두 동일 패턴 적용

현재 코드

@Service
public class CaseSearchService {
    /**
     * 선행기술 탐색
     */
    public SearchStartResponse startSearch(Long userId, SearchRequest request) {

        // 0. 재검색 시 진행 중 상태 확인
        if (request.caseId() != null) {
            checkNotInProgress(request.caseId());
        }

        // 1. Case, 구성요소 저장 (별도 Service의 트랜잭션 경유)
        Case caseEntity = txService.prepareSearch(userId, request);

        // 2. resultCount 기본값 처리
        int resultCount = request.resultCount() != null
                ? request.resultCount()
                : DEFAULT_RESULT_COUNT;

        // 3. Redis에 탐색 시작 상태 저장
        searchProgressService.markStarted(caseEntity.getId());

        // 4. 비동기 Python 호출
        asyncService.executeSearchAsync(caseEntity.getId(), request, resultCount);

        log.info("[Search] 검색 시작: caseId={}, userId={}",
                caseEntity.getId(), userId);

        return SearchStartResponse.of(caseEntity.getId());
    }
    
    ...
}

2. Race Condition 문제

문제: 프론트의 탐색 결과 조회Spring의 탐색 결과 DB 저장 시점 충돌 가능

초기 설계

Python에서 선행기술 탐색 완료Python이 Redis에 탐색 완료 상태 저장(status=completed) , Spring에 결과 반환
  ↓ (여기서 Spring은 DB에 결과 저장 )
  
프론트 폴링Redis에서 completed 상태 감지Spring에 결과 조회 API 호출DB에서 조회 : 아직  결과일 수도.. (Spring이 DB에 아직 저장  했다면)

Race Condition 해결책:

  1. Python에서 markCompleted 호출 제거
    • Python의 탐색 파이프라인 완료 ≠ 사용자가 결과 조회 가능한 상태
  2. Spring이 DB 저장 완료 후 Redis에 탐색 완료 상태 갱신 (markCompleted 호출)
Python에서 선행기술 탐색 완료Python이 Spring에 결과 반환Spring은 DB에 결과 저장 , Redis에 탐색 완료 상태 저장(status=completed)
  ↓  
  
프론트 폴링Redis에서 completed 상태 감지Spring에 결과 조회 API 호출DB에서 조회 : 탐색 결과 조회
@Transactional
public void saveSearchResults(Long caseId, ...) {
    // DB 저장 로직
		...
		
    // 트랜잭션 커밋 후, Redis에 완료 상태 업데이트
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                searchProgressService.markCompleted(caseId);
            }
        }
    );
}

결과: DB에 실제로 데이터가 커밋된 순간에만 프론트가 completed 상태 감지


3. Rate Limiting: LLM 무차별 호출 방지

해결: 사용자별 Rate Limit 어노테이션 도입 → 시간당 요청 수 제한

  • 인증된 사용자의 userId 기준으로 Redis에 호출 횟수 저장

Spring AOP 기반 구현:

@RestController
@RequestMapping("/api")
public class CaseSearchController {

		@RateLimit(apiName = "search")    // Rate Limit 어노테이션
		@PostMapping("/searches")    // 선행기술 탐색 API
		public ResponseEntity<ApiResponse<SearchStartResponse>> startSearch(
				...
		}

yml 설정:

ratelimit:
  limits:
    search: 20              # apiName: 시간당 호출 가능 횟수
    component-extract: 30
    manual-add: 30
    inventive-step: 10      
    novelty: 10            

Redis 키값:

Key: rate:{apiName}:{userId}:{hourBucket}    
Value: 해당 시간 동안의 요청 횟수 (카운트)
  • hourBucket: 매 시간 자정부터 경과 시간 (h 단위)

동작 흐름:

  • [카운트 ≤ limit] → 허용

    • 정상적으로 컨트롤러 로직을 실행하여 LLM 파이프라인을 호출
    • Redis에서 해당 키의 값을 1 증가
  • [카운트 > limit] → 차단

    컨트롤러 로직을 실행하지 않고, 즉시 429 Too Many Requests 예외를 발생시켜 요청 차단

--

관련 레포지토리

  • IPX-BE — Spring Boot 백엔드 (비즈니스 로직, DB 관리, API Gateway)

About

IPX AI(Python) server repo

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages