|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: 'Agentic AI with Quarkus - part 3' |
| 4 | +date: 2025-05-08T00:00:00Z |
| 5 | +tags: ai llm agents |
| 6 | +synopsis: 'Agentic AI with Quarkus - part 3' |
| 7 | +author: mariofusco |
| 8 | +--- |
| 9 | +:imagesdir: /assets/images/posts/agentic |
| 10 | + |
| 11 | +The https://quarkus.io/blog/agentic-ai-with-quarkus/[first part] of this blog post series briefly introduced agentic AI and discussed workflow patterns. Subsequently, the https://quarkus.io/blog/agentic-ai-with-quarkus-p2/[second installment] explored the proper agentic patterns showing how to implement them using Quarkus and its LangChain4j extension. |
| 12 | + |
| 13 | +The purpose of this third article is clarifying the differences between these two approaches, discussing their pros and cons and demonstrating with a practical example how to migrate an AI-infused service using a workflow pattern to a pure agentic implementation. |
| 14 | + |
| 15 | +In essence the most relevant difference between the two is that workflow patterns are programmatically orchestrated through predefined code paths, while agents autonomously decide their own processes and tool usage, maintaining control over how they execute tasks, making them more flexible and adaptable to various scenarios, but also in some cases less predictable and more prone to hallucinations. |
| 16 | + |
| 17 | +[.text-center] |
| 18 | +.AI Workflow Vs. Pure Agentic AI |
| 19 | +image::wrokflowVsAgents.png[align="center", alt="AI Workflow Vs. Pure Agentic AI"] |
| 20 | + |
| 21 | +== From Workflow to Agents |
| 22 | + |
| 23 | +https://quarkus.io/blog/agentic-ai-with-quarkus/#routing[Routing] is one of the workflow pattern presented in the first article of this series. There we used a first LLM service to categorize the user request and then used that category to programmatically reroute that request to one of three other LLMs acting as medical, legal or technical expert. |
| 24 | + |
| 25 | +[.text-center] |
| 26 | +.Routing pattern |
| 27 | +image::routing-pattern.png[width=70%, align="center", alt="Routing pattern"] |
| 28 | + |
| 29 | +In that example each expert was implemented as a separate and independent LLM service, and the routing to one of them was performed programmatically by the application code. Tracing the execution of a request like |
| 30 | + |
| 31 | +[source, shell] |
| 32 | +---- |
| 33 | +curl http://localhost:8080/expert/request/I%20broke%20my%20leg%20what%20should%20I%20do |
| 34 | +---- |
| 35 | + |
| 36 | +shows the sequence of steps performed to fulfill the user request: first the relatively fast, less than 2 seconds, classification phase performed by the Router Agent, then the more expensive invocation of the selected expert service, taking almost 25 seconds to generate its answer. |
| 37 | + |
| 38 | +[.text-center] |
| 39 | +.Tracing routing workflow pattern execution |
| 40 | +image::routing-workflow-trace.png[align=center, alt="Tracing routing workflow pattern execution"] |
| 41 | + |
| 42 | +However, Quarkus integration makes straightforward to also turn these LLM services into tools that can be invoked by other LLMs, simply annotating them with `@Tool` followed by a short description of the purpose of that tool, while preserving the possibility of also invoking the single expert directly as an independent LLM service. |
| 43 | + |
| 44 | +[source, java] |
| 45 | +---- |
| 46 | +public interface MedicalExpert { |
| 47 | +
|
| 48 | + @UserMessage(""" |
| 49 | + You are a medical expert. |
| 50 | + Analyze the following user request under a medical point of view and provide the best possible answer. |
| 51 | + The user request is {request}. |
| 52 | + """) |
| 53 | + @Tool("A medical expert") // <-- Allows to use this LLM also as a tool for other LLMs |
| 54 | + String medicalRequest(String request); |
| 55 | +} |
| 56 | +---- |
| 57 | + |
| 58 | +In this way it is possible to provide a second alternative implementation of the same expert interrogation service, this time using a pure agentic approach, where the Router Agent is replaced by a single LLM, having the three expert as tools, that can autonomously decide which expert to invoke based on the user request. |
| 59 | + |
| 60 | +[source, java] |
| 61 | +---- |
| 62 | +@RegisterAiService(modelName = "tool-use") |
| 63 | +@ApplicationScoped |
| 64 | +public interface ExpertsSelectorAgent { |
| 65 | +
|
| 66 | + @UserMessage(""" |
| 67 | + Analyze the following user request and categorize it as 'legal', 'medical' or 'technical', |
| 68 | + then forward the request as it is to the corresponding expert provided as a tool. |
| 69 | + Finally return the answer that you received from the expert without any modification. |
| 70 | +
|
| 71 | + The user request is: '{request}'. |
| 72 | + """) |
| 73 | + @ToolBox({MedicalExpert.class, LegalExpert.class, TechnicalExpert.class}) |
| 74 | + String askToExpert(String request); |
| 75 | +} |
| 76 | +---- |
| 77 | + |
| 78 | +The `@ToolBox` annotation is used to specify the list of tools that the agent can use, in this case the three experts. Note that, similarly to what it has been done for other agentic examples in the previous post of this series, this AI service has been configured to use a model that in the `application.properties` file has been named "tool-use" which is more capable of using tools in a proper way, in this case `qwen2.5` with 7 billions of parameters. |
| 79 | + |
| 80 | +[source,properties] |
| 81 | +---- |
| 82 | +quarkus.langchain4j.ollama.tool-use.chat-model.model-id=qwen2.5:7b |
| 83 | +quarkus.langchain4j.ollama.tool-use.chat-model.temperature=0 |
| 84 | +quarkus.langchain4j.ollama.tool-use.timeout=180s |
| 85 | +---- |
| 86 | + |
| 87 | +At this point also the agentic implementation of this expert interrogation service is ready and can be exposed with a different REST endpoint making it possible to use and compare these two alternative solutions. |
| 88 | + |
| 89 | +[source,java] |
| 90 | +---- |
| 91 | +@GET |
| 92 | +@Produces(MediaType.TEXT_PLAIN) |
| 93 | +@Path("request-agentic/{request}") |
| 94 | +public String requestAgentic(String request) { |
| 95 | + Log.infof("User request is: %s", request); |
| 96 | + return expertsSelectorAgent.askToExpert(request); |
| 97 | +} |
| 98 | +---- |
| 99 | + |
| 100 | +== Comparing the workflow and agentic approaches |
| 101 | + |
| 102 | +The two approaches are equivalent in terms of functionality, but they differ in the way they are implemented and the levels of control and flexibility that they offer. In particular the pure agentic solution is much simpler and more elegant, as it does not require any additional code to route the request to the right expert. The agent is able to do that by itself. It could also decide to use more than one expert if needed, something that would be impossible to do with the workflow approach, where the routing is hardcoded in the application code. |
| 103 | + |
| 104 | +On the other hand, the workflow approach is more predictable and easier to debug, as the routing logic is explicit and can be easily traced. For instance, the behavior of the Router Agent alone could be controlled and corrected with an output guardrail. Moreover, it also allows for more complex workflows, where the routing decision can depend on multiple factors and not just on the user request. |
| 105 | + |
| 106 | +Finally, as evidenced by tracing of the agentic execution, its current implementation has a major drawback in the fact that the overall time to fulfill the user request is significantly increased. |
| 107 | + |
| 108 | +[.text-center] |
| 109 | +.Tracing agentic routing execution |
| 110 | +image::routing-agentic-trace.png[align=center, alt="Tracing agentic routing execution"] |
| 111 | + |
| 112 | +This depends on how the agent is using the LLM expert as a tool: even though it has been explicitly required to forward the response of the expert as it is and without any modification, it seems to ignore this instruction and can't avoid to waste a relevant amount time reprocessing the expert's answer before returning it. In other words this is a side-effect of the fact that the agent is in complete control of the execution and there is no way to forward this control to a different LLM as it would be convenient in this case. |
0 commit comments