🔧 Tool‑Calling 파이프라인
ChatClient + ToolCallbacks로 내부 도메인 기능을 화이트리스트 실행.
- fetch/validate/update 도구 스코프 최소화
- 내부 실행 허용 플래그로 오남용 방지
민생지원금 신청/검증을 위한 AI 보조 백엔드. Spring Boot 3.5 + Spring AI로 Tool‑Calling 파이프라인을 구성하고, Qdrant 기반 RAG로 규정 버전별 검증을 일관 적용합니다.
ChatClient + ToolCallbacks로 내부 도메인 기능을 화이트리스트 실행.
프롬프트/규정 문서를 벡터화 저장, 질문 시 컨텍스트 주입.
문자열 조립 없이 JPA Specification으로 where/order/limit 처리.
규정/버전별 검증 로직 상이, 운영 쿼리 스크립트 의존, 사람 오류.
RAG로 규정 문맥 주입 + Tool‑Calling으로 안전한 내부 함수 실행 + JPA Spec.
검증 일관성 향상·온보딩 단축(정량 수치로 교체 권장: 예) 수작업 검토 30%↓).
Client (HTTP) │ ▼ Spring Boot API (3.5.4, Java 17) ├─ Controller: /api/supportFund/* ├─ Services: Tool‑Calling, RAG, Versioning └─ Repos: JPA (MySQL) │ ├─ MySQL 8.x (JPA/Hibernate) ├─ Qdrant (Vector DB) ← EmbeddingModel └─ OpenAI Chat (gpt‑4o‑mini) Infra: Gradle 8.14.3, Docker Compose(Qdrant)
주요 버전: Spring AI 1.0.1(BOM), spring‑ai‑starter‑model‑openai, spring‑ai‑starter‑vector‑store‑qdrant, mysql‑connector‑j 8.3.0
관심사 분리
jdbc:mysql://<host>:3306/aikit
src/main/java/com/foongdoll/aikit/comm/ai/service/QdrantService.java:133
SearchPoints req = SearchPoints.newBuilder() .setCollectionName(COLLECTION) .addAllVector(qVec) .setFilter(Filter.newBuilder().addMust(matchKeyword("promptType", promptType)).build()) .setWithPayload(enable(true)) // payload 반환 .setLimit(k) .build(); List<ScoredPoint> hits = qdrant.searchAsync(req).get(); return hits.stream() .map(sp -> sp.getPayload().getOrDefault("text", JsonWithInt.Value.newBuilder().setStringValue("").build()).getStringValue()) .filter(s -> !s.isBlank()) .toList();
payload 선별 추출로 LLM 입력 토큰 절약.
comm/ai/toolcalling/FunctionUtils.java:83
switch (op) { case "eq" -> preds.add(cb.equal(path, castValue(value, type))); case "in" -> { /* ... */ } case "like" -> preds.add(cb.like(cb.lower(path.as(String.class)), "%"+s.toLowerCase()+"%")); case "gte" -> preds.add(gePredicate(cb, path, castValue(value, type))); case "lte" -> preds.add(lePredicate(cb, path, castValue(value, type))); case "between" -> { /* ... */ } default -> { /* skip */ } }
문자열 JPQL 없이 안전한 where/between/like.
comm/ai/utils/AiUtils.java:110
var opts = ToolCallingChatOptions.builder() .toolCallbacks(callbacks) .internalToolExecutionEnabled(true) .toolNames("FETCH_VERSION","FETCH_APPLICATION","VALIDATE_APPLICATION", "BUILD_REPORT","UPDATE_STATUS","UPDATE_RECORD") .build(); var resp = chatClient.prompt().system(system).user(userContent).options(opts).call();
화이트리스트로 도구 범위 최소화, 내부 실행 허용 제어.
comm/ai/toolcalling/Functions.java:164
Specification<ExampleEntity> spec = buildSpec(args.getWhere()); List<ExampleEntity> targets = repo.findAll(spec); if (targets.isEmpty()) return "조건에 맞는 항목이 없습니다."; for (ExampleEntity e : targets) applySet(e, args.getSet()); repo.saveAll(targets); return String.format("총 %d건 업데이트 완료", targets.size());
example/service/impl/ExampleServiceImpl.java:54
switch (response.getVersion().replace(".", "").toLowerCase()){ case "v001": repo.save(V001.builder().name(dto.getName()).build()); break; case "v002": v002Repo.save(V002.builder().name(dto.getName()).hasCar(v002.getHasCar()).build()); break; }
메서드 | 경로 | 설명 |
---|---|---|
POST | /api/supportFund/request | 민생지원금 신청(LLM 검증 후 저장) |
POST | /api/supportFund/handle | 의도별 조회/검증/리포트/업데이트 |
POST | /api/supportFund/version/select | 버전 목록 조회 |
POST | /api/supportFund/version/update | 버전 사용/등록 업데이트 |
POST | /api/supportFund/vector/init | Qdrant 컬렉션 초기화/재색인 |
Socket: N/A (게이트웨이 없음)
협업과 코드 리뷰를 좋아합니다. 필요 시 실사용 데모·코드 워크스루 제공 가능합니다.