https://docs.embabel.com/embabel-agent/guide/0.1.3/ 공식 문서를 바탕으로 작성된 글입니다.
@Action 메서드를 통해 특정 목표를 달성하는 자체 포함 컴포넌트앞선 글을 참고하자.
Finite State Machine을 넘어 비LLM AI 알고리즘을 사용한 계획 수립으로 새로운 단계 조합을 가능하게 한다.
동적 계획 수립으로 기존 코드를 수정하지 않고 기능을 확장할 수 있습니다. 즉, 도메인 객체와 액션을 쉽게 추가할 수 있다.
액션, 목표, 조건이 모두 정적으로 타입이 정의되어 있어 리팩토링 시 IDE가 전체 흐름을 정확히 추적해준다.
문자열 기반의 “마법 같은 맵”을 제거하고(문자열 기반 매핑 제거), 안정적인 구조를 유지할 수 있다.
actions = {
"search_product": search_product_action,
"apply_discount": apply_discount_action,
"checkout": checkout_action,
}
대신
@Action
fun findProduct(productId: ProductId): Product { ... }
프로그래밍 모델과 실제 실행 플랫폼을 깔끔하게 분리해 어떤 환경에서도 유연하게 에이전트를 배포할 수 있다.
여러 종류의 LLM을 상황에 맞게 조합해 사용할 수 있으며, 비용 효율성과 개인정보 보호 측면에서 더 나은 구성을 만들 수 있다.
Spring 기반으로 동작하기 때문에 엔터프라이즈 애플리케이션에서 사용하는 다양한 기능과 견고한 데이터 영속성 계층을 자연스럽게 활용할 수 있다.
단위 테스트부터 엔드-투-엔드 에이전트 테스트까지 처음부터 고려된 구조로 설계되어 있어, 안정적인 테스트 환경을 쉽게 구축할 수 있다.
에이전트 프레임워크는 하나의 큰 작업을 여러 개의 작은 상호작용 단위로 나누어 다루며, 이를 다음과 같은 요소로 모델링한다
@Agent(description = "Find news based on a person's star sign") // 1
public class StarNewsFinder {
private final HoroscopeService horoscopeService; // 2
private final int storyCount;
public StarNewsFinder(
HoroscopeService horoscopeService, // 3
@Value("${star-news-finder.story.count:5}") int storyCount) {
this.horoscopeService = horoscopeService;
this.storyCount = storyCount;
}
@Action // 4
public StarPerson extractStarPerson(UserInput userInput, OperationContext context) { // 5
return context.ai()
.withLlm(OpenAiModels.GPT_41)
.createObject("""
Create a person from this user input, extracting their name and star sign:
%s""".formatted(userInput.getContent()), StarPerson.class); // 6
}
@Action // 7
public Horoscope retrieveHoroscope(StarPerson starPerson) { // 8
// Uses regular injected Spring service - not LLM
return new Horoscope(horoscopeService.dailyHoroscope(starPerson.sign())); // 9
}
@Action(toolGroups = {CoreToolGroups.WEB}) // 10
public RelevantNewsStories findNewsStories(
StarPerson person, Horoscope horoscope, OperationContext context) { // 11
var prompt = """
%s is an astrology believer with the sign %s.
Their horoscope for today is: %s
Given this, use web tools to find %d relevant news stories.
""".formatted(person.name(), person.sign(), horoscope.summary(), storyCount);
return context.ai().withDefaultLlm().createObject(prompt, RelevantNewsStories.class); // 12
}
@AchievesGoal(description = "Write an amusing writeup based on horoscope and news") // 13
@Action
public Writeup writeup(
StarPerson person, RelevantNewsStories stories, Horoscope horoscope,
OperationContext context) { // 14
var llm = LlmOptions.fromCriteria(ModelSelectionCriteria.getAuto())
.withTemperature(0.9); // 15
var prompt = """
Write something amusing for %s based on their horoscope and these news stories.
Format as Markdown with links.
""".formatted(person.name());
return context.ai().withLlm(llm).createObject(prompt, Writeup.class); // 16
}
}
이 조그만한 예시에서 우리는 JVM 기반의 Embabel 에이전트 프레임워크의 특징을 볼 수 있다.
private final HoroscopeService horoscopeService;@Action, @AchievesGoal 같은 어노테이션만 붙이면 도구 등록, 액션 연결, 목표 달성 흐름이 자동으로 구성된다.context.ai().withLlm(OpenAiModels.GPT_41)context.ai().withDefaultLlm()아래는 embabel 공식 문서에서 설명하고 있는 프레임워크 핵심 기능들이다.
1. Agent 선언
@Agent 애노테이션은 이 클래스가 여러 단계를 거치는 에이전트임을 정의한다.
Spring 통합
일반적인 Spring DI가 그대로 적용되며, LLM 기반 작업과 기존 비즈니스 로직을 자연스럽게 함께 사용할 수 있다.
서비스 주입
HoroscopeService는 일반 Spring Bean처럼 주입되며, 에이전트는 AI 기능과 기존 서비스 호출을 자유롭게 조합할 수 있다.
액션 정의
@Action이 붙은 메서드는 에이전트가 수행할 수 있는 하나의 단계로, 각 메서드는 에이전트가 가진 능력(capability)을 나타낸다.
입력 조건 추론
extractStarPerson(UserInput userInput, …) 시그니처는 다음을 의미한다:
UserInput 객체가 있어야 함 출력 조건 생성
이 액션이 StarPerson을 반환하면 Embabel은 다음을 이해한다.
StarPerson이 에이전트 상태에 추가됨 비 LLM 액션
모든 액션이 LLM을 사용하는 것은 아니다. 이 예시는 전통적인 서비스 로직과 AI 로직을 함께 사용할 수 있음을 보여준다.
데이터 흐름 연결
retrieveHoroscope(StarPerson starPerson)는 다음을 의미한다:
StarPerson이 먼저 존재해야 함 서비스 통합:
도구 사용 요구사항
toolGroups = {CoreToolGroups.WEB}은 이 액션이 웹 검색 도구에 접근해야 함을 나타낸다.
다중 입력 의존성
findNewsStories()는 StarPerson과 Horoscope 두 데이터를 모두 필요로 하며, 복잡한 데이터 흐름을 자동으로 조합할 수 있음을 보여준다.
툴 지원 LLM 호출
이 단계에서 LLM은 웹 도구를 활용하여 운세 관련 최신 뉴스를 검색할 수 있다.
목표 달성 표시
@AchievesGoal은 이 메서드가 에이전트의 최종 목표를 이루는 종결 단계임을 나타낸다.
복합 데이터 요구사항
마지막 액션은 StarPerson, RelevantNewsStories, Horoscope 세 데이터를 모두 입력으로 사용하며, 에이전트의 자동 오케스트레이션 능력을 보여준다.
창의적 출력 설정
temperature가 0.9로 설정된 LLM은 더욱 창의적이고 재미있는 글을 생성하도록 최적화되어 있다. (0.9가 창의적이다? 라고 볼 수 있는지에 대해서는 잘 모르겠다 ㅎㅎ)
최종 출력
이 액션은 Writeup을 반환하며, 에이전트의 전체 목표가 성공적으로 완료되었음을 의미한다.
위 예시의 모든 @Action 메서드에서 다음과 같은 부분이 반복됨을 알 수 있다.
Embabel은 각 액션의 타입 시그니처(입력·출력 타입) 를 분석하여, 어떤 순서로 액션을 실행해야 목표를 달성할 수 있는지를 자동으로 계산한다.
예를 들어 Writeup을 만들기 위해 필요한 흐름을 Embabel은 다음과 같이 스스로 도출한다:
1. 최종 결과인 Writeup을 생성하려면 writeup() 액션이 필요하다.
2. writeup()은 StarPerson, RelevantNewsStories, Horoscope을 입력으로 받는다.
3. StarPerson을 얻기 위해 → extractStarPerson() 실행
4. Horoscope을 얻기 위해 → retrieveHoroscope() 실행 (입력: StarPerson)
5. RelevantNewsStories를 얻기 위해 → findNewsStories() 실행
결과적으로 자동 생성된 실행 순서:
UserInput
→ extractStarPerson()
→ StarPerson
→ retrieveHoroscope()
→ Horoscope
→ findNewsStories()
→ RelevantNewsStories
→ writeup()
→ Writeup