[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7367":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":23,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":42,"readmeContent":43,"aiSummary":44,"trendingCount":16,"starSnapshotCount":16,"syncStatus":45,"lastSyncTime":46,"discoverSource":47},7367,"embabel-agent","embabel\u002Fembabel-agent","embabel","Agent framework for the JVM. Pronounced Em-BAY-bel \u002Fɛmˈbeɪbəl\u002F","https:\u002F\u002Fdocs.embabel.com\u002Fembabel-agent\u002Fguide\u002F0.4.0-SNAPSHOT",null,"Kotlin",3657,358,48,45,0,16,98,255,90,29.67,"Apache License 2.0",false,"main",true,[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"agent","agentic-ai","agents","ai","ai-agents","aiagentframework","genai","generative-ai","java","kotlin","llms","multi-agents","multi-agents-orchestration","multi-agents-system","spring","2026-06-12 02:01:38","# Embabel Agent Framework\n\n\u003Cimg align=\"left\" src=\"https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent\u002Fblob\u002Fmain\u002Fembabel-agent-api\u002Fimages\u002F315px-Meister_der_Weltenchronik_001.jpg?raw=true\" width=\"180\">\n\n[![Docs](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdocs-live-brightgreen)](https:\u002F\u002Fdocs.embabel.com\u002Fembabel-agent\u002Fguide\u002F0.1.2-SNAPSHOT\u002F)\n[![MvnRepository](https:\u002F\u002Fbadges.mvnrepository.com\u002Fbadge\u002Fcom.embabel.agent\u002Fembabel-agent-api\u002Fbadge.svg?label=MvnRepository)](https:\u002F\u002Fmvnrepository.com\u002Fartifact\u002Fcom.embabel.agent\u002Fembabel-agent-api)\n![Build](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent\u002Factions\u002Fworkflows\u002Fmaven.yml\u002Fbadge.svg)\n[![YourKit](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FProfiling-YourKit-blue)](https:\u002F\u002Fwww.yourkit.com\u002F)\n[![JProfiler](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FProfiled%20with-JProfiler-blue)](https:\u002F\u002Fwww.ej-technologies.com\u002Fproducts\u002Fjprofiler\u002Foverview.html)\n[![Quality Gate Status](https:\u002F\u002Fsonarcloud.io\u002Fapi\u002Fproject_badges\u002Fmeasure?project=embabel_embabel-agent&metric=alert_status)](https:\u002F\u002Fsonarcloud.io\u002Fsummary\u002Fnew_code?id=embabel_embabel-agent)\n[![Discord](https:\u002F\u002Fimg.shields.io\u002Fdiscord\u002F1277751399261798401?logo=discord)](https:\u002F\u002Fdiscord.gg\u002Ft6bjkyj93q)\n\n[\u002F\u002F]: # ([![Quality Gate Status]&#40;https:\u002F\u002Fsonarcloud.io\u002Fapi\u002Fproject_badges\u002Fmeasure?project=embabel_embabel-agent&metric=alert_status&token=d275d89d09961c114b8317a4796f84faf509691c&#41;]&#40;https:\u002F\u002Fsonarcloud.io\u002Fsummary\u002Fnew_code?id=embabel_embabel-agent&#41;)\n\n[\u002F\u002F]: # ([![Bugs]&#40;https:\u002F\u002Fsonarcloud.io\u002Fapi\u002Fproject_badges\u002Fmeasure?project=embabel_embabel-agent&metric=bugs&#41;]&#40;https:\u002F\u002Fsonarcloud.io\u002Fsummary\u002Fnew_code?id=embabel_embabel-agent&#41;)\n![Kotlin](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fkotlin-%237F52FF.svg?style=for-the-badge&logo=kotlin&logoColor=white)\n![Java](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fjava-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white)\n![Spring](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fspring-%236DB33F.svg?style=for-the-badge&logo=spring&logoColor=white)\n![Spring Boot](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSpring%20Boot-6DB33F.svg?style=for-the-badge&logo=Spring-Boot&logoColor=white)\n![Apache Tomcat](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fapache%20tomcat-%23F8DC75.svg?style=for-the-badge&logo=apache-tomcat&logoColor=black)\n![Apache Maven](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FApache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white)\n![JUnit](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FJUnit5-25A162.svg?style=for-the-badge&logo=JUnit5&logoColor=white)\n![ChatGPT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FchatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)\n![Jinja](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fjinja-white.svg?style=for-the-badge&logo=jinja&logoColor=black)\n![JSON](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FJSON-000?logo=json&logoColor=fff)\n![GitHub Actions](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fgithub%20actions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white)\n![SonarQube](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSonarQube-black?style=for-the-badge&logo=sonarqube&logoColor=4E9BCD)\n![Docker](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdocker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)\n![IntelliJ IDEA](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FIntelliJIDEA-000000.svg?style=for-the-badge&logo=intellij-idea&logoColor=white)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fembabel\u002Fembabel-agent?style=for-the-badge&logo=apache&color=brightgreen)](https:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0)\n[![Commits](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fcommit-activity\u002Fm\u002Fembabel\u002Fembabel-agent.svg?label=commits&style=for-the-badge&logo=git&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent\u002Fpulse)\n\n&nbsp;&nbsp;&nbsp;&nbsp;\n\nEmbabel (Em-BAY-bel) is a framework for authoring agentic flows on the JVM that seamlessly mix LLM-prompted interactions\nwith code and domain models. Supports\nintelligent path finding towards goals. Written in Kotlin\nbut offers a natural usage\nmodel from Java.\nFrom the creator of Spring.\n\n&nbsp;\n\n## Key Concepts\n\nModels agentic flows in terms of:\n\n- **Actions**: Steps an agent takes\n- **Goals**: What an agent is trying to achieve\n- **Conditions**: Conditions to assess before executing an action or determining that a goal has been achieved.\n  Conditions are reassessed after each action is executed.\n- **Domain model**: Objects underpinning the flow and informing Actions, Goals and Conditions.\n- **Plan**: A sequence of actions to achieve a goal. Plans are dynamically formulated by the system, not the programmer.\n  The\n  system replans after the completion of each action, allowing it to adapt to new information as well as observe the\n  effects of the previous action.\n  This is effectively an [OODA loop](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOODA_loop).\n\n> Application developers don't usually have to deal with these concepts directly,\n> as most conditions result from data flow defined in code, allowing the system to infer\n> pre and post conditions.\n\nThese concepts underpin these differentiators versus other agent frameworks:\n\n- **Sophisticated planning.** Goes beyond a finite state machine or sequential execution\n  with nesting by introducing a true planning step, using a\n  non-LLM AI algorithm. This enables the system to perform tasks it wasn’t programmed to do by combining known\n  steps in\n  a novel order, as well as make decisions about parallelization and other runtime behavior.\n- **Superior extensibility and reuse**: Because of dynamic planning, adding more domain objects, actions, goals and\n  conditions\n  can extend the capability of the system, _without editing FSM definitions_ or existing code.\n- **Strong typing and the benefits of object orientation**: Actions, goals and conditions are informed by a domain\n  model, which can\n  include behavior. Everything is strongly typed and prompts and\n  manually authored code interact cleanly. No more magic maps. Enjoy full refactoring support.\n\nOther benefits:\n\n- **Platform abstraction**: Clean separation between programming model and platform internals allows running locally\n  while\n  potentially offering higher QoS in production without changing application code.\n- **Designed for LLM mixing**: It is easy to build applications that mix LLMs, ensuring the most cost-effective yet\n  capable solution.\n  This enables the system to leverage the strengths of different models for different tasks. In particular, it\n  facilitates\n  the use of local models for point tasks. This can be important for cost and privacy.\n- **Built on Spring and the JVM,** making it easy to access existing enterprise functionality and capabilities.\n  For example:\n    - Spring can inject and manage agents, including using Spring AOP to decorate functions.\n    - Robust persistence and transaction management solutions are available.\n- **Designed for testability** from the ground up. Both unit testing and agent end to end testing are easy.\n\nFlows can be authored in one of two ways:\n\n- An annotation-based model similar to Spring MVC, with types annotated with the Spring stereotype `@Agent`, using\n  `@Goal`, `@Condition` and\n  `@Action` methods.\n- Idiomatic Kotlin DSL with `agent {` and `action {` blocks.\n\nEither way, flows are backed by a domain model of objects that can have rich behavior.\n\n> We are working toward allowing natural language actions and goals to be deployed.\n\nThe planning step is pluggable.\n\nThe default planning approach is\n[Goal Oriented Action Planning](https:\u002F\u002Fmedium.com\u002F@vedantchaudhari\u002Fgoal-oriented-action-planning-34035ed40d0b).\nGOAP is a popular AI planning algorithm used in gaming. It allows for dynamic decision-making and action selection based\non the current state of the world and the goals of the agent.\n\nGoals, actions and plans are independent of GOAP. Embabel also\nsupports [Utility AI](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FUtility_system) out of the box, which can run the same actions but\nchooses actions\nbased on (potentially dynamic) utility scores rather than strict preconditions and postconditions. This is valuable for\nexploration and\nopen-ended tasks, when we do not need to achieve a specific goal but want to maximize overall utility.\n\nThe framework executes via an `AgentPlatform` implementation.\n\nAn agent platform supports the following modes of execution:\n\n- **Focused**, where user code requests particular functionality: User code calls a method to run a particular agent,\n  passing in input. This is ideal for code-driven flows such as a flow invoked in response to an incoming event.\n- **Closed**, where user intent (or another incoming event) is classified to choose an agent. The platform tries to\n  find a\n  suitable agent among all the agents it knows about.\n  Agent choice is dynamic, but only actions defined within the particular agent\n  will run.\n- **Open**, where the user's intent is assessed and the platform uses _all_ its resources to try to achieve it. The\n  platform tries to find a\n  suitable goal among all the goals it knows about and builds a custom agent to achieve it from the start state,\n  including relevant actions and conditions. The platform will not proceed if it is unconvinced as to the applicability\n  of any goal. The `GoalChoiceApprover` interface provides developers a way to limit goal choice further.\n\nOpen mode is the most powerful, but least deterministic.\n> In open mode, the platform is capable of finding novel paths that were not envisioned by developers, and even\n> combining functionality from multiple providers.\n\nEven in open mode, the platform will only perform individual steps\nthat have been specified. (Of course, steps may themselves be LLM\ntransforms, in which case the prompts are controlled by user code but the\nresults are still non-deterministic.)\n\nPossible future modes:\n\n- **Evolving** mode: Where the platform can work with multiple goals in the same process and modify a running process to\n  add further goals and agents.\n  For example, an action can realize that it has become important to achieve additional goals.\n\nEmbabel agent systems will also support federation, both with other Embabel systems (allowing planning to incorporate\nremote actions and goals) and third party agent frameworks.\n\n## Quick Start\n\nGet an agent running in under 5 minutes.\n\nCreate your own agent repo from our [Java](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fjava-agent-template)\nor [Kotlin](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fkotlin-agent-template) GitHub template by clicking the \"Use this template\"\nbutton.\n\nYou'll have an agent running in under a minute\nif you already have an `OPENAI_API_KEY` and have Maven installed.\n\n**📚 For examples and tutorials**, see\nthe [Embabel Agent Examples Repository](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent-examples)\n\n**🚗 For a sophisticated, realistic example application**, see\nthe [Tripper travel planner agent](https:\u002F\u002Fgithub.com\u002Fembabel\u002Ftripper)\n\n\u003Cimg src=\"images\u002Ftripper_output1.jpg\" alt=\"Travel Planner Output\" width=\"600\"\u002F>\n\n*AI-generated travel itinerary with detailed recommendations*\n\n\u003Cimg src=\"images\u002Ftripper_map.jpg\" alt=\"Interactive map\" width=\"600\"\u002F>\n\n*Map link included in output*\n\n## Why Is Embabel Needed?\n\nTL;DR Because the evolution of agent frameworks is early and there's a lot of room for improvement; because an agent\nframework on the JVM will deliver great business value.\n\n- _Why do we need an agent framework at all_? We can write code without higher level abstractions, directly invoking\n  LLMs and controlling flow directly in code. However, a higher level agent framework offers compelling benefits. For\n  example:\n    - Breaking up LLM interactions, making them simpler and more focused. This maximizes reuse and minimizes cost and\n      errors. It often allows us to use cheaper models for point interactions.\n    - Facilitating both unit and integration testing, which remain as important with agentic systems as with any other\n      software systems.\n    - Increasing composability where subflows and individual actions can be reused\n    - Making applications more manageable and robust, enabling a workflow manager to control their execution and retry\n      operations while maintaining previous state\n    - Enhancing safety through the ability to apply guardrails in many places\n- _Why do we need an agent framework for the JVM when solutions exist in Python?_: While agent frameworks initially\n  appeared predominantly Python, it's early and there's plenty of room for novel and\n  superior\n  approaches. The key adjacency is not the LLM--which is a simple HTTP call away--but existing code and\n  infrastructure\n  assets that are more valuable on the JVM than in Python.\n- _Why not use just Spring AI?_ Spring AI is great. We build on it, and embrace the Spring component model. However, we\n  believe that most applications should work with higher\n  level APIs. An analogy: Spring AI exists at the level of the Servlet API, while Embabel is more like Spring MVC.\n  Complex requirements are much easier to express and test in Embabel than with direct use of Spring AI.\n- _Why not attempt to contribute this project to Spring?_ This project requires different governance\n  from Spring, where most projects exist in stable environments and dependability and stability outweighs rapid\n  innovation. Second, the\n  concepts are not JVM-specific. We hope that Embabel will become the leading agent framework across platforms. While\n  the Spring brand is valuable in Java, it is not in TypeScript or Python.\n\n## Show Me The Code\n\nIn Java or Kotlin, agent implementation code is intuitive and easy to test.\n\n\u003Cdetails open>\n\u003Csummary>Java\u003C\u002Fsummary>\n\n```java\n\n@Agent(description = \"Find news based on a person's star sign\")\npublic class StarNewsFinder {\n\n    private final HoroscopeService horoscopeService;\n    private final int storyCount;\n\n    \u002F\u002F Services are injected by Spring\n    public StarNewsFinder(\n            HoroscopeService horoscopeService,\n            @Value(\"${star-news-finder.story.count:5}\") int storyCount) {\n        this.horoscopeService = horoscopeService;\n        this.storyCount = storyCount;\n    }\n\n    @Action\n    public StarPerson extractStarPerson(UserInput userInput, Ai ai) {\n        return ai\n                .withLlm(OpenAiModels.GPT_41)\n                .createObjectIfPossible(\n                        \"\"\"\n                                Create a person from this user input, extracting their name and star sign:\n                                %s\"\"\".formatted(userInput.getContent()),\n                        StarPerson.class\n                );\n    }\n\n    @Action\n    public Horoscope retrieveHoroscope(StarPerson starPerson) {\n        return new Horoscope(horoscopeService.dailyHoroscope(starPerson.sign()));\n    }\n\n    \u002F\u002F toolGroups specifies tools that are required for this action to run\n    @Action(toolGroups = {CoreToolGroups.WEB})\n    public RelevantNewsStories findNewsStories(\n            StarPerson person,\n            Horoscope horoscope,\n            Ai ai) {\n        var prompt = \"\"\"\n                %s is an astrology believer with the sign %s.\n                Their horoscope for today is:\n                    \u003Choroscope>%s\u003C\u002Fhoroscope>\n                Given this, use web tools and generate search queries\n                to find %d relevant news stories summarize them in a few sentences.\n                Include the URL for each story.\n                Do not look for another horoscope reading or return results directly about astrology;\n                find stories relevant to the reading above.\n                \n                For example:\n                - If the horoscope says that they may\n                want to work on relationships, you could find news stories about\n                novel gifts\n                - If the horoscope says that they may want to work on their career,\n                find news stories about training courses.\"\"\".formatted(\n                person.name(), person.sign(), horoscope.summary(), storyCount);\n        return ai\n                .withDefaultLlm()\n                .createObject(prompt, RelevantNewsStories.class);\n    }\n\n    \u002F\u002F The @AchievesGoal annotation indicates that completing this action\n    \u002F\u002F achieves the given goal, so the agent can be complete\n    @AchievesGoal(\n            description = \"Write an amusing writeup for the target person based on their horoscope and current news stories\",\n            export = @Export(\n                    remote = true,\n                    name = \"starNewsWriteupJava\",\n                    startingInputTypes = {StarPerson.class, UserInput.class})\n    )\n    @Action\n    public Writeup writeup(\n            StarPerson person,\n            RelevantNewsStories relevantNewsStories,\n            Horoscope horoscope,\n            Ai ai) {\n        var llm = LlmOptions\n                .withModel(OpenAiModels.GPT_41_MINI)\n                \u002F\u002F High temperature for creativity\n                .withTemperature(0.9);\n\n        var newsItems = relevantNewsStories.getItems().stream()\n                .map(item -> \"- \" + item.getUrl() + \": \" + item.getSummary())\n                .collect(Collectors.joining(\"\\n\"));\n\n        var prompt = \"\"\"\n                Take the following news stories and write up something\n                amusing for the target person.\n                \n                Begin by summarizing their horoscope in a concise, amusing way, then\n                talk about the news. End with a surprising signoff.\n                \n                %s is an astrology believer with the sign %s.\n                Their horoscope for today is:\n                    \u003Choroscope>%s\u003C\u002Fhoroscope>\n                Relevant news stories are:\n                %s\n                \n                Format it as Markdown with links.\"\"\".formatted(\n                person.name(), person.sign(), horoscope.summary(), newsItems);\n        return ai\n                .withLlm(llm)\n                .createObject(prompt, Writeup.class);\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>Kotlin\u003C\u002Fsummary>\n\n```kotlin\n@Agent(description = \"Find news based on a person's star sign\")\nclass StarNewsFinder(\n    \u002F\u002F Services such as Horoscope are injected by Spring\n    private val horoscopeService: HoroscopeService,\n    \u002F\u002F Potentially externalized by Spring\n    @param:Value(\"\\${star-news-finder.story.count:5}\")\n    private val storyCount: Int = 5,\n) {\n\n    @Action\n    fun extractPerson(\n        userInput: UserInput,\n        ai: Ai\n    ): StarPerson =\n        \u002F\u002F All prompts are typesafe\n        ai.withDefaultLlm()\n            .createObject(\"Create a person from this user input, extracting their name and star sign: $userInput\")\n\n    \u002F\u002F This action doesn't use an LLM\n    \u002F\u002F Embabel makes it easy to mix LLM use with regular code\n    @Action\n    fun retrieveHoroscope(starPerson: StarPerson) =\n        Horoscope(horoscopeService.dailyHoroscope(starPerson.sign))\n\n    \u002F\u002F This action uses tools\n    \u002F\u002F \"toolGroups\" specifies tools that are required for this action to run\n    @Action(toolGroups = [ToolGroup.WEB])\n    fun findNewsStories(\n        person: StarPerson,\n        horoscope: Horoscope,\n        ai: Ai,\n    ): RelevantNewsStories =\n        ai.withDefaultLlm().createObject(\n            \"\"\"\n            ${person.name} is an astrology believer with the sign ${person.sign}.\n            Their horoscope for today is:\n                \u003Choroscope>${horoscope.summary}\u003C\u002Fhoroscope>\n            Given this, use web tools and generate search queries\n            to find $storyCount relevant news stories summarize them in a few sentences.\n            Include the URL for each story.\n            Do not look for another horoscope reading or return results directly about astrology;\n            find stories relevant to the reading above.\n\n            For example:\n            - If the horoscope says that they may\n            want to work on relationships, you could find news stories about\n            novel gifts\n            - If the horoscope says that they may want to work on their career,\n            find news stories about training courses.\n        \"\"\".trimIndent()\n        )\n\n    \u002F\u002F The @AchievesGoal annotation indicates that completing this action\n    \u002F\u002F achieves the given goal, so the agent run will be complete\n    @AchievesGoal(\n        description = \"Write an amusing writeup for the target person based on their horoscope and current news stories\",\n    )\n    @Action\n    fun writeup(\n        person: StarPerson,\n        relevantNewsStories: RelevantNewsStories,\n        horoscope: Horoscope,\n        ai: Ai,\n    ): Writeup =\n        ai\n            .withLlm(\n                LlmOptions\n                    .withModel(model)\n                    .withTemperature(0.9)\n            )\n            .createObject(\n                \"\"\"\n            Take the following news stories and write up something\n            amusing for the target person.\n\n            Begin by summarizing their horoscope in a concise, amusing way, then\n            talk about the news. End with a surprising signoff.\n\n            ${person.name} is an astrology believer with the sign ${person.sign}.\n            Their horoscope for today is:\n                \u003Choroscope>${horoscope.summary}\u003C\u002Fhoroscope>\n            Relevant news stories are:\n            ${relevantNewsStories.items.joinToString(\"\\n\") { \"- ${it.url}: ${it.summary}\" }}\n\n            Format it as Markdown with links.\n        \"\"\".trimIndent()\n            )\n\n}\n```\n\n\u003C\u002Fdetails>\n\n\nThe following domain classes ensure type safety:\n\n\u003Cdetails open>\n\u003Csummary>Java\u003C\u002Fsummary>\n\n```java\n\n@JsonClassDescription(\"Person with astrology details\")\n@JsonDeserialize(as = StarPerson.class)\npublic record StarPerson(\n        String name,\n        @JsonPropertyDescription(\"Star sign\") String sign\n) implements Person {\n\n    @JsonCreator\n    public StarPerson(\n            @JsonProperty(\"name\") String name,\n            @JsonProperty(\"sign\") String sign\n    ) {\n        this.name = name;\n        this.sign = sign;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n}\n\npublic record Horoscope(String summary) {\n}\n\n@JsonClassDescription(\"Writeup relating to a person's horoscope and relevant news\")\npublic record Writeup(String text) implements HasContent {\n\n    @JsonCreator\n    public Writeup(@JsonProperty(\"text\") String text) {\n        this.text = text;\n    }\n\n    @Override\n    public String getContent() {\n        return text;\n    }\n}\n\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>Kotlin\u003C\u002Fsummary>\n\n```kotlin\ndata class RelevantNewsStories(\n    val items: List\u003CNewsStory>\n)\n\ndata class NewsStory(\n    val url: String,\n\n    val summary: String,\n)\n\ndata class Subject(\n    val name: String,\n    val sign: String,\n)\n\ndata class Horoscope(\n    val summary: String,\n)\n\ndata class FunnyWriteup(\n    override val text: String,\n) : HasContent\n```\n\n\u003C\u002Fdetails>\n\nIt's easy to unit test your agents to ensure that they correctly execute logic\nand pass the correct prompts and hyperparameters to LLMs. For example:\n\n```java\npublic class StarNewsFinderTest {\n\n    @Test\n    void writeupPromptMustContainKeyData() {\n        HoroscopeService horoscopeService = mock(HoroscopeService.class);\n        StarNewsFinder starNewsFinder = new StarNewsFinder(horoscopeService, 5);\n        var context = new FakeOperationContext();\n        context.expectResponse(new com.embabel.example.horoscope.Writeup(\"Gonna be a good day\"));\n\n        NewsStory cockatoos = new NewsStory(\n                \"https:\u002F\u002Ffake.com.au\",\n                \"Cockatoo behavior\",\n                \"Cockatoos are eating cabbages\"\n        );\n\n        NewsStory emus = new NewsStory(\n                \"https:\u002F\u002Fmorefake.com.au\",\n                \"Emu movements\",\n                \"Emus are massing\"\n        );\n\n        StarPerson starPerson = new StarPerson(\"Lynda\", \"Scorpio\");\n        RelevantNewsStories relevantNewsStories = new RelevantNewsStories(Arrays.asList(cockatoos, emus));\n        Horoscope horoscope = new Horoscope(\"This is a good day for you\");\n\n        starNewsFinder.writeup(starPerson, relevantNewsStories, horoscope, context);\n\n        var prompt = context.getLlmInvocations().getFirst().getPrompt();\n        var toolGroups = context.getLlmInvocations().getFirst().getInteraction().getToolGroups();\n\n\n        assertTrue(prompt.contains(starPerson.getName()));\n        assertTrue(prompt.contains(starPerson.sign()));\n        assertTrue(prompt.contains(cockatoos.getSummary()));\n        assertTrue(prompt.contains(emus.getSummary()));\n\n        assertTrue(toolGroups.isEmpty(), \"The LLM should not have been given any tool groups\");\n    }\n}\n```\n\n## Dog Food Policy\n\nWe believe that all aspects of software development and business can and should\nbe greatly accelerated through the use of AI agents. The ultimate decision\nmakers remain human, but they can and should be greatly augmented.\n\n> This project practices extreme dogfooding.\n\n\u003C!-- TODO photo of Duke with kibble -->\n\nOur key principles:\n\n1. **We will use AI agents to help every aspect of the project:** coding, documentation, community management, producing\n   marketing copy etc.\n   Any\n   human performing a task should ask why it cannot be automated, and strive toward maximum automation.\n2. **Developers retain ultimate control.** Developers are responsible for guiding agents toward the solution and\n   iterating\n   as necessary. A developer who commits or merges an agent contribution\n   is responsible for ensuring that it meets the project coding standards, which are\n   independent of the use of agents. For example, code must be human-readable.\n3. **We will favour open source agents built on the Embabel platform,** and contribute improvements. While\n   commercial agents\n   may be more advanced in some areas, we believe that our\n   platform is the best general solution for automation and by dogfooding we will improve it fastest.\n   By open sourcing agents used on our open source projects, we will maximize benefit to the community.\n4. **We will prioritize agents that help accelerate our progress.** Per the flight safety advice to fit your own mask\n   before helping others, we will prioritize\n   agents that help us accelerate our own progress. This will not only produce useful examples, but increase overall\n   project velocity.\n\nDevelopers must carefully read all code they commit and improve generated code if possible.\n\n> Coding agents are a special case. While the `embabel-agent-code` submodule offers support for project modification\n> that is useful for project bootstrapping, coding agents are the most mature of commercial agents, and their vendors\n> are\n> heavily subsidising their users, making it economically irrational to insist on our own platform.\n\n## Getting Started\n\n- Get the bits\n- Set up your environment\n- Run the application\n\n### Getting the bits\n\nChoose one of the following:\n\n- Clone the repository via `git clone https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent`\n- Create a new Spring Boot project and add the necessary dependencies (see \"Using Embabel Agent Framework in Your\n  Project\" below)\n\n### Environment variables\n\n> Environment variables are consistent with common usage, rather than Spring AI.\n> For example, we prefer `OPENAI_API_KEY` to `SPRING_AI_OPENAI_API_KEY`.\n\nRequired:\n\n- `OPENAI_API_KEY`: For the OpenAI API\n\nOptional:\n\n- `ANTHROPIC_API_KEY`: For the Anthropic API. Necessary for the coding agent.\n- `MINIMAX_API_KEY`: For the [MiniMax](https:\u002F\u002Fwww.minimax.io) API. Supports MiniMax-M2.7 and MiniMax-M2.7-highspeed models.\n\n> We strongly recommend providing both an OpenAI and Anthropic key, as some examples require both. And it's important to\n> try to find the best LLM for a given task, rather than automatically choose a familiar provider.\n\n### Services\n\nYou will need a Docker Desktop version [`>4.43.2`](https:\u002F\u002Fdocs.docker.com\u002Fdesktop\u002Frelease-notes\u002F).\nBe sure to activate the following MCP tools from the catalog:\n\n- Brave Search\n- Fetch\n- Puppeteer\n- Wikipedia\n\n> You can also set up your own MCP tools using Spring AI conventions. See the `application-docker-desktop.yml` file for\n> an example.\n\nIf you're running Ollama locally, include the `embabel ollama starter` and Embabel will automatically connect to your\nOllama\nendpoint and make all models available.\n\n```xml\n\n\u003Cdependency>\n    \u003CgroupId>com.embabel.agent\u003C\u002FgroupId>\n    \u003CartifactId>embabel-agent-starter-ollama\u003C\u002FartifactId>\n\u003C\u002Fdependency>\n```\n\n### Running\n\nCreate your own agent project with\n\n```\nuvx --from git+https:\u002F\u002Fgithub.com\u002Fembabel\u002Fproject-creator.git project-creator\n```\n\n### Example Agents\n\n> **📚 For examples and tutorials**, see\n> the [Embabel Agent Examples Repository](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent-examples)\n\n```bash\n# Clone and run examples\ngit clone https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent-examples\ncd embabel-agent-examples\u002Fscripts\u002Fkotlin\n.\u002Fshell.sh\n```\n\n#### Shell Commands\n\nSpring Shell is an easy way to interact with the Embabel agent framework, especially during development.\n\nType `help` to see available commands. Use `execute` or `x` to run an agent:\n\n```\nexecute \"Lynda is a Scorpio, find news for her\" -p -r\n```\n\nThis will look for an agent, choose the star finder agent and\nrun the flow. `-p` will log prompts `-r` will log LLM responses.\nOmit these for less verbose logging.\n\nOptions:\n\n- `-p` logs prompts\n- `-r` logs LLM responses\n\nUse the `chat` command to enter an interactive chat with the agent.\nIt will attempt to run the most appropriate\nagent for each command.\n\n> Spring Shell supports history. Type `!!` to repeat the last command.\n> This will survive restarts, so is handy when iterating on an agent.\n\n#### Further examples\n\nExample commands within the shell:\n\n```\n# Perplexity style deep research\n# Requires both OpenAI and Anthropic keys and Docker Desktop with the MCP extension (or your own web tools)\nexecute \"research the recent australian federal election. what is the position of the greens party?\"\n\n# x is a shortcut for execute\nx \"fact check the following: holden cars are still made in australia; the koel is a bird native only to australia; fidel castro is justin trudeau's father\"\n\n```\n\n### Bringing in additional LLMs\n\n#### Local models with well-known providers\n\nThe Embabel Agent Framework supports local models from:\n\n- Ollama: Simply add `embabel-agent-starter-ollama` starter to your pom.xml and your local Ollama endpoint will be\n  queries. All local models will be\n  available.\n- Docker: Add the `embabel-agent-starter-dockermodels` starter to your pom.xml and your local Docker endpoint will be\n  queried. All local models will be available.\n- LMStudio: This uses the openAI compatible client. Just include LMStudio as a dependency and make sure your LMStudio\n  server is running.\n\n#### Custom LLMs\n\nYou can define an LLM for any provider for which a Spring AI `ChatModel` is available.\n\nSimply define Spring beans of type `Llm`.\nSee the `OpenAiConfiguration` class as an example.\n\nRemember:\n\n- Provide the knowledge cutoff date if you know it\n- Make the configuration class conditional on any required API key.\n\n## Roadmap\n\nThis project is in its early stages, but we have big plans.\nThe milestones and issues in this repository are a good reference.\nOur key goals:\n\n- **Become the natural way to Gen AI-enable Java applications**, and especially those built on Spring.\n- **Prove the power of the approach**. Demonstrate that this approach is the best way to build safe, dependable, Gen AI\n  applications.\n  In particular:\n    - Demonstrate the power of extensibility without modification, by adding goals and actions\n    - Demonstrate the potential to become the PaaS for natural language\n    - Demonstrate the potential of agent federation within the GOAP model\n    - Demonstrate budget-aware agents, such as \"Research the following topic, spending up to 20c if you are still\n      learning\"\n    - Integrate with data stores and demonstrate the power of surfacing existing functionality inside an organization\n- **Take the model to other platforms**: The conceptual framework is not JVM specific. Once established, we intend to\n  create TypeScript\n  and Python projects.\n\nThere is a lot to do, and you are awesome. We look forward to your contribution!\n\n## Application Design\n\n### Domain objects\n\nApplications center around domain objects. These can be instantiated by LLMs or user\ncode, and manipulated by user code.\n\nUse Jackson annotations to help LLMs with descriptions as well as mark fields to ignore.\nFor example:\n\n```kotlin\n@JsonClassDescription(\"Person with astrology details\")\ndata class StarPerson(\n    override val name: String,\n    @get:JsonPropertyDescription(\"Star sign\")\n    val sign: String,\n) : Person\n```\n\nSee [Java Json Schema Generation - Module Jackson](https:\u002F\u002Fgithub.com\u002Fvictools\u002Fjsonschema-generator\u002Ftree\u002Fmain\u002Fjsonschema-module-jackson)\nfor documentation of the library used.\n\nDomain objects can have behaviors that are automatically exposed to LLMs when they are in scope. Simply annotate methods\nwith the Spring AI `@Tool` annotation.\n\n> When exposing `@Tool` methods on domain objects, be sure that the tool is safe to invoke. Even the best LLMs can get\n> trigger-happy. For example, be careful about methods that can mutate or delete data. This is likely better modeled via\n> an explicit call to a non-tool method on the same domain class, in a code action.\n\n## Using Embabel as an MCP server\n\nYou can use the Embabel agent platform as an MCP server from a\nUI like Claude Desktop. The Embabel MCP server is available over SSE.\n\nConfigure Claude Desktop as follows in your `claude_desktop_config.yml`:\n\n```json\n{\n  \"mcpServers\": {\n    \"embabel\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote\",\n        \"http:\u002F\u002Flocalhost:8080\u002Fsse\"\n      ]\n    }\n  }\n}\n\n```\n\nSee [MCP Quickstart for Claude Desktop Users](https:\u002F\u002Fmodelcontextprotocol.io\u002Fquickstart\u002Fuser) for how to configure\nClaude Desktop.\n\nThe [MCP Inspector](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Finspector) is a helpful tool for interacting with your\nEmbabel\nSSE server, manually invoking tools and checking the exposed prompts and resources.\n\nStart the MCP Inspector with:\n\n```bash\nnpx @modelcontextprotocol\u002Finspector\n```\n\n## Consuming MCP Servers\n\nThe Embabel Agent Framework provides built-in support for consuming Model Context Protocol (MCP) servers, allowing you\nto extend your applications with powerful AI capabilities through standardized interfaces.\n\n### What is MCP?\n\nModel Context Protocol (MCP) is an open protocol that standardizes how applications provide context and extra\nfunctionality to large language models. Introduced by Anthropic, MCP has emerged as the de facto standard for connecting\nAI agents to tools, functioning as a client-server protocol where:\n\n- **Clients** (like Embabel Agent) send requests to servers\n- **Servers** process those requests to deliver necessary context to the AI model\n\nMCP simplifies integration between AI applications and external tools, transforming an \"M×N problem\" into an \"M+N\nproblem\" through standardization - similar to what USB did for hardware peripherals.\n\n### Configuring MCP in Embabel Agent\n\nTo configure MCP servers in your Embabel Agent application, add the following to your `application.yml`:\n\n```yaml\nspring:\n  ai:\n    mcp:\n      client:\n        enabled: true\n        name: embabel\n        version: 1.0.0\n        request-timeout: 30s\n        type: SYNC\n        stdio:\n          connections:\n            docker-mcp:\n              command: docker\n              args:\n                - run\n                - -i\n                - --rm\n                - alpine\u002Fsocat\n                - STDIO\n                - TCP:host.docker.internal:8811\n```\n\nThis configuration sets up an MCP client that connects to a Docker-based MCP server. The connection uses STDIO transport\nthrough Docker's socat utility to connect to a TCP endpoint.\n\n### Docker Desktop MCP Integration\n\nDocker has embraced MCP with their Docker MCP Catalog and Toolkit, which provides:\n\n1. **Centralized Discovery** - A trusted hub for discovering MCP tools integrated into Docker Hub\n2. **Containerized Deployment** - Run MCP servers as containers without complex setup\n3. **Secure Credential Management** - Centralized, encrypted credential handling\n4. **Built-in Security** - Sandbox isolation and permissions management\n\nThe Docker MCP ecosystem includes over 100 verified tools from partners like Stripe, Elastic, Neo4j, and more, all\naccessible through Docker's infrastructure.\n\n### Learn More\n\n- [Docker MCP Documentation](https:\u002F\u002Fdocs.docker.com\u002Fdesktop\u002Ffeatures\u002Fgordon\u002Fmcp\u002F)\n- [Docker MCP Servers Repository](https:\u002F\u002Fgithub.com\u002Fdocker\u002Fmcp-servers)\n- [Introducing Docker MCP Catalog and Toolkit](https:\u002F\u002Fwww.docker.com\u002Fblog\u002Fintroducing-docker-mcp-catalog-and-toolkit\u002F)\n- [MCP Introduction and Overview](https:\u002F\u002Fwww.philschmid.de\u002Fmcp-introduction)\n\n## A2A\n\nEmbabel integrates with the [A2A](https:\u002F\u002Fgithub.com\u002Fgoogle-a2a\u002FA2A) protocol, allowing you to connect to other\nA2A-enabled agents and\nservices.\n\nEnable the `a2a` Spring profile to start the A2A server.\n\nYou'll need the following environment variable:\n\n- `GOOGLE_STUDIO_API_KEY`: Your Google Studio API key, which is used for Gemini.\n\nStart the Google A2A web interface using the `a2a` Docker profile:\n\n```bash\ndocker compose --profile a2a up\n```\n\nGo to the web interface running within the container at `http:\u002F\u002Flocalhost:12000\u002F`.\n\nConnect to your agent at `host.docker.internal:8080\u002Fa2a`. Note that `localhost:8080\u002Fa2a` won't work as the server\ncannot access it when running in a Docker container.\n\n## Running Tests\n\n### Unit tests\n\nRun the unit tests via Maven. This will not require an internet connection or any external services.\n\n```bash\nmvn test\n```\n\n### Integration tests\n\nIntegration tests (`*IT`) hit real provider APIs and are excluded from the default `mvn test` run.\nTo run them, ensure the following environment variables are set:\n\n- `OPENAI_API_KEY`\n- `ANTHROPIC_API_KEY`\n- `DEEPSEEK_API_KEY`\n- `MISTRAL_API_KEY`\n\nThen run:\n\n```bash\nmvn -Dtest='*IT,!LLMOllama*IT' -Dsurefire.failIfNoSpecifiedTests=false test\n```\n\nThis runs all integration tests except Ollama (which requires a local Ollama server).\nTo run a specific module's integration tests, add `-pl`:\n\n```bash\nmvn -Dtest='*IT,!LLMOllama*IT' -Dsurefire.failIfNoSpecifiedTests=false test -pl embabel-agent-openai\n```\n\n## Spring profiles\n\nSpring profiles are used to configure the application for different environments and behaviors.\n\nModel profiles:\n\n- `docker-desktop`: Talking to Docker Desktop with the MCP extension. **This is recommended for the best experience,\n  with Docker-provided web tools.**\n\nLogging profiles:\n\n- `severance`: [Severance](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=xEQP4VVuyrY&ab_channel=AppleTV) specific logging. Praise\n  Kier!\n- `starwars`: Star Wars specific logging. Feel the force\n- `colossus`: Colossus specific logging. The Forbin Project.\n\n## Testing\n\nA key goal of this framework is ease of testing.\nJust as Spring eased testing of early enterprise Java applications,\nthis framework facilitates testing of AI applications.\n\nTypes of testing:\n\n- Unit tests: All agents are unit testable, like any Spring-managed beans. Construct them with mock objects; call\n  individual action methods. The testing library facilitates testing prompts.\n- Integration tests: tbd\n\n## Logging\n\nAll logging in this project is either debug logging in the relevant\nclass itself, or results from the stream of events of type `AgentEvent`.\n\nEdit `application.yml` if you want to see debug logging from the relevant classes and packages.\n\nAvailable logging experiences:\n\n- `severance`: Severance logging. Praise Kier\n- `starwars`: Star Wars logging. Feel the force. The default as it's understood throughout the galaxy.\n- `colossus`: Colossus logging. The Forbin Project.\n- `montypython`: Monty Python logging. No one expects it.\n- `hh`: Hitchhiker's Guide to the Galaxy logging. The answer is 42.\n\nIf none of these profiles is chosen, Embabel will use vanilla logging.\nThis makes me sad.\n\n## Adding Embabel Agent Framework to Your Project\n\n### Maven Central Availability\n\n**Since version 0.2.0**, Embabel Agent Framework is available directly on Maven Central, simplifying dependency\nmanagement. You no longer need to configure custom repositories for stable releases.\n\n---\n\n### Maven\n\n#### For version 0.2.0 and above (Recommended)\n\nSimply add the Embabel Spring Boot starter dependency to your `pom.xml`:\n\n```xml\n\n\u003Cdependency>\n    \u003CgroupId>com.embabel.agent\u003C\u002FgroupId>\n    \u003CartifactId>embabel-agent-starter\u003C\u002FartifactId>\n    \u003Cversion>${embabel-agent.version}\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\nNo additional repository configuration is needed! Maven Central is configured by default.\n\n#### For versions prior to 0.2.0 or for SNAPSHOT versions\n\nYou need to add the Embabel repositories to your `pom.xml`:\n\n```xml\n\n\u003Crepositories>\n    \u003Crepository>\n        \u003Cid>embabel-releases\u003C\u002Fid>\n        \u003Curl>https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-release\u003C\u002Furl>\n        \u003Creleases>\n            \u003Cenabled>true\u003C\u002Fenabled>\n        \u003C\u002Freleases>\n        \u003Csnapshots>\n            \u003Cenabled>false\u003C\u002Fenabled>\n        \u003C\u002Fsnapshots>\n    \u003C\u002Frepository>\n    \u003Crepository>\n        \u003Cid>embabel-snapshots\u003C\u002Fid>\n        \u003Curl>https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-snapshot\u003C\u002Furl>\n        \u003Creleases>\n            \u003Cenabled>false\u003C\u002Fenabled>\n        \u003C\u002Freleases>\n        \u003Csnapshots>\n            \u003Cenabled>true\u003C\u002Fenabled>\n        \u003C\u002Fsnapshots>\n    \u003C\u002Frepository>\n\u003C\u002Frepositories>\n```\n\nThen add the dependency:\n\n```xml\n\n\u003Cdependency>\n    \u003CgroupId>com.embabel.agent\u003C\u002FgroupId>\n    \u003CartifactId>embabel-agent-starter\u003C\u002FartifactId>\n    \u003Cversion>${embabel-agent.version}\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n---\n\n### Gradle (Kotlin DSL)\n\n#### For version 0.2.0 and above (Recommended)\n\nAdd the required repositories to your `build.gradle.kts`:\n\n```kotlin\nrepositories {\n    mavenCentral()\n    maven {\n        name = \"Spring Milestones\"\n        url = uri(\"https:\u002F\u002Frepo.spring.io\u002Fmilestone\")\n    }\n}\n```\n\nAdd the Embabel Agent starter:\n\n```kotlin\ndependencies {\n    implementation(\"com.embabel.agent:embabel-agent-starter:${embabelAgentVersion}\")\n}\n```\n\n#### For versions prior to 0.2.0 or for SNAPSHOT versions\n\nAdd all required repositories to your `build.gradle.kts`:\n\n```kotlin\nrepositories {\n    mavenCentral()\n    maven {\n        name = \"embabel-releases\"\n        url = uri(\"https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-release\")\n        mavenContent {\n            releasesOnly()\n        }\n    }\n    maven {\n        name = \"embabel-snapshots\"\n        url = uri(\"https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-snapshot\")\n        mavenContent {\n            snapshotsOnly()\n        }\n    }\n    maven {\n        name = \"Spring Milestones\"\n        url = uri(\"https:\u002F\u002Frepo.spring.io\u002Fmilestone\")\n    }\n}\n```\n\nAdd the Embabel Agent starter:\n\n```kotlin\ndependencies {\n    implementation(\"com.embabel.agent:embabel-agent-starter:${embabelAgentVersion}\")\n}\n```\n\n---\n\n### Gradle (Groovy DSL)\n\n#### For version 0.2.0 and above (Recommended)\n\nAdd the required repositories to your `build.gradle`:\n\n```groovy\nrepositories {\n    mavenCentral()\n    maven {\n        name = 'Spring Milestones'\n        url = 'https:\u002F\u002Frepo.spring.io\u002Fmilestone'\n    }\n}\n```\n\nAdd the Embabel Agent starter:\n\n```groovy\ndependencies {\n    implementation \"com.embabel.agent:embabel-agent-starter:${embabelAgentVersion}\"\n}\n```\n\n#### For versions prior to 0.2.0 or for SNAPSHOT versions\n\nAdd all required repositories to your `build.gradle`:\n\n```groovy\nrepositories {\n    mavenCentral()\n    maven {\n        name = 'embabel-releases'\n        url = 'https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-release'\n        mavenContent {\n            releasesOnly()\n        }\n    }\n    maven {\n        name = 'embabel-snapshots'\n        url = 'https:\u002F\u002Frepo.embabel.com\u002Fartifactory\u002Flibs-snapshot'\n        mavenContent {\n            snapshotsOnly()\n        }\n    }\n    maven {\n        name = 'Spring Milestones'\n        url = 'https:\u002F\u002Frepo.spring.io\u002Fmilestone'\n    }\n}\n```\n\nAdd the Embabel Agent starter:\n\n```groovy\ndependencies {\n    implementation 'com.embabel.agent:embabel-agent-starter:${embabelAgentVersion}'\n}\n```\n\n---\n\n### Important Notes\n\n#### Spring Milestones Repository\n\nThe Spring Milestones repository is required because the Embabel BOM (`embabel-agent-dependencies`) has transitive\ndependencies on experimental Spring components, specifically the `mcp-bom`. This BOM is not available on Maven Central\nand is only published to the Spring milestone repository.\n\n**Note for Gradle users:** Unlike Maven, Gradle does not inherit repository configurations declared in parent POMs or\nBOMs. Therefore, it is necessary to explicitly declare the Spring milestone repository in your repositories block to\nensure proper resolution of all transitive dependencies.\n\n#### Repository Types\n\n- **Maven Central** (since v0.2.0): For stable releases 0.2.0 and above\n- **Embabel Releases Repository**: For stable releases prior to 0.2.0\n- **Embabel Snapshots Repository**: For development\u002Fsnapshot versions (e.g., `0.3.0-SNAPSHOT`)\n\n---\n\n### Quick Start Examples\n\n#### Maven with latest stable version\n\n```xml\n\n\u003Cdependency>\n    \u003CgroupId>com.embabel.agent\u003C\u002FgroupId>\n    \u003CartifactId>embabel-agent-starter\u003C\u002FartifactId>\n    \u003Cversion>0.3.0\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n#### Gradle Kotlin DSL with latest stable version\n\n```kotlin\nimplementation(\"com.embabel.agent:embabel-agent-starter:0.3.0\")\n```\n\n#### Gradle Groovy DSL with latest stable version\n\n```groovy\nimplementation 'com.embabel.agent:embabel-agent-starter:0.3.0'\n```\n\n## Getting Started with Observability\n\nAdd full tracing and metrics to your Embabel agents with zero code changes.\n\n### 1. Add the dependency\n\n```xml\n\u003Cdependency>\n    \u003CgroupId>com.embabel.agent\u003C\u002FgroupId>\n    \u003CartifactId>embabel-agent-starter-observability\u003C\u002FartifactId>\n    \u003Cversion>${embabel-agent.version}\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n### 2. Add an exporter\n\nPick **one** (or combine multiple):\n\n**Zipkin** (simplest — no account needed):\n```xml\n\u003Cdependency>\n    \u003CgroupId>io.opentelemetry\u003C\u002FgroupId>\n    \u003CartifactId>opentelemetry-exporter-zipkin\u003C\u002FartifactId>\n\u003C\u002Fdependency>\n```\n\n**Langfuse** (LLM-focused observability):\n```xml\n\u003Cdependency>\n    \u003CgroupId>com.quantpulsar\u003C\u002FgroupId>\n    \u003CartifactId>opentelemetry-exporter-langfuse\u003C\u002FartifactId>\n    \u003Cversion>0.4.0\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n### 3. Configure\n\n```yaml\n# Enable observability\nembabel:\n  observability:\n    enabled: true\n    service-name: my-agent-app\n\n# Enable Spring Boot tracing\nmanagement:\n  tracing:\n    enabled: true\n    sampling:\n      probability: 1.0\n\n  # Zipkin exporter\n  zipkin:\n    tracing:\n      endpoint: http:\u002F\u002Flocalhost:9411\u002Fapi\u002Fv2\u002Fspans\n```\n\nTo use Langfuse instead of (or alongside) Zipkin:\n```yaml\nmanagement:\n  langfuse:\n    enabled: true\n    endpoint: https:\u002F\u002Fcloud.langfuse.com\u002Fapi\u002Fpublic\u002Fotel  # or your self-hosted URL\n    public-key: pk-lf-...\n    secret-key: sk-lf-...\n```\n\n### 4. Start Zipkin and run\n\n```bash\ndocker run -d -p 9411:9411 openzipkin\u002Fzipkin\n.\u002Fmvnw spring-boot:run\n```\n\nOpen [http:\u002F\u002Flocalhost:9411](http:\u002F\u002Flocalhost:9411) — run an agent and you will see traces like:\n\n```\nAgent: CustomerServiceAgent\n├── Action: AnalyzeRequest\n│   └── ChatModel: gpt-4 (Spring AI)\n│       └── tool:searchKnowledgeBase\n├── Action: GenerateResponse\n│   └── ChatModel: gpt-4 (Spring AI)\n└── status: completed [duration=2340ms]\n```\n\nWith Langfuse, you get a rich LLM-focused view of your agent traces:\n\n\u003Cimg src=\"embabel-agent-observability\u002Fdocs\u002Flangfuse.png\" alt=\"Langfuse Tracing\" width=\"800\"\u002F>\n\n### What gets traced automatically\n\n- Agent lifecycle (creation, execution, completion, failures)\n- Every action as a child span\n- LLM calls with token usage (via Spring AI)\n- Tool invocations with input\u002Foutput\n- Planning and replanning iterations\n- State transitions and lifecycle states\n\n### Track custom operations with `@Tracked`\n\nUse the `@Tracked` annotation to add observability spans to your own methods — inputs, outputs, duration, and errors are captured automatically:\n\n```java\n@Tracked(\"enrichCustomer\")\npublic Customer enrich(Customer input) {\n    \u002F\u002F Your logic here\n}\n```\n\nYou can specify a type and description for richer traces:\n\n```java\n@Tracked(value = \"callPaymentApi\", type = TrackType.EXTERNAL_CALL, description = \"Payment gateway call\")\npublic PaymentResult processPayment(Order order) {\n    \u002F\u002F ...\n}\n```\n\nWhen called within an agent execution, `@Tracked` spans are automatically nested under the current action:\n\n```\nAgent: CustomerServiceAgent\n├── Action: ProcessOrder\n│   ├── @Tracked: enrichCustomer (PROCESSING)\n│   ├── ChatModel: gpt-4\n│   └── @Tracked: callPaymentApi (EXTERNAL_CALL)\n└── status: completed\n```\n\nFor the full configuration reference, MDC log correlation, and advanced options, see the [Observability Module Documentation](embabel-agent-observability\u002FREADME.md).\n\n---\n\n## Contributing\n\nWe welcome contributions to the Embabel Agent Framework.\n\nLook at the [coding style guide](embabel-agent-api\u002F.embabel\u002Fcoding-style.md) for style guidelines.\nThis file also informs coding agent behavior.\n\n## Miscellaneous\n\n- _Why the name Embabel?_\n  The \"babel\" part is ultimately inspired by the story of the Tower of Babel, perhaps via Douglas\n  Adams' [babelfish](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=iuumnjJWFO4&ab_channel=BBCStudios).\n  Per @lasuac:\n  _While Adams' fish in the ear enabled universal translation between species, Embabel aims at translating human intent\n  to JVM code, AI models, and enterprise systems._\n  \"embabel\" also sounds like \"enable.\"\n- Milestone names are Australian animals. Mythical animals such as \"bunyip\" and \"yowie\" are used for futures that may or\n  not be implemented.\n- README badges come from [here](https:\u002F\u002Fgithub.com\u002FIleriayo\u002Fmarkdown-badges)\n  and [here](https:\u002F\u002Fhome.aveek.io\u002FGitHub-Profile-Badges\u002F).\n- Don't forget to join [Discord](https:\u002F\u002Fdiscord.gg\u002Ft6bjkyj93q) to collaborate with the Embabel community. It is a good\n  place to receive support, showcase your work, discuss ideas and connect with like-minded people.\n\n## Star history\n\n[![Star History Chart](https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=embabel\u002Fembabel-agent&type=Date)](https:\u002F\u002Fstar-history.com\u002F#embabel\u002Fembabel-agent&Date)\n\n## Contributors\n\n[![Embabel contributors](https:\u002F\u002Fcontrib.rocks\u002Fimage?repo=embabel\u002Fembabel-agent)](https:\u002F\u002Fgithub.com\u002Fembabel\u002Fembabel-agent\u002Fgraphs\u002Fcontributors)\n\n\n\n--------------------\n(c) Embabel Software Inc 2024-2025.\n","Embabel Agent Framework 是一个为 JVM 设计的代理框架，旨在促进多代理系统和生成式 AI 的开发。它使用 Kotlin 语言编写，支持包括 Spring Boot 在内的多种 Java 生态技术栈，能够与 Apache Tomcat 和 Maven 等工具无缝集成。其核心功能包括但不限于多代理协调、基于 LLMs 的智能代理创建以及通过 ChatGPT 接口实现自然语言处理能力。此外，该项目还提供了详尽的文档和支持社区，便于开发者快速上手。适合需要构建复杂交互逻辑或多代理协作场景的应用程序，如聊天机器人、虚拟助手等。",2,"2026-06-11 03:11:57","top_language"]