Skip to content

Commit cb7f08c

Browse files
markpollacksolnone
authored andcommitted
add effective agents section (spring-projects#3247)
Signed-off-by: Solomon Hsu <[email protected]>
1 parent 1cf542a commit cb7f08c

File tree

2 files changed

+257
-0
lines changed

2 files changed

+257
-0
lines changed

spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
* Guides
118118
** https://github.com/spring-ai-community/awesome-spring-ai[Awesome Spring AI]
119119
** xref:api/chat/prompt-engineering-patterns.adoc[]
120+
** xref:api/effective-agents.adoc[Building Effective Agents]
120121
** xref:api/cloud-bindings.adoc[Deploying to the Cloud]
121122
122123
// * xref:contribution-guidelines.adoc[Contribution Guidelines]
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
In a recent research publication, https://www.anthropic.com/research/building-effective-agents[Building Effective Agents], Anthropic shared valuable insights about building effective Large Language Model (LLM) agents. What makes this research particularly interesting is its emphasis on simplicity and composability over complex frameworks. Let's explore how these principles translate into practical implementations using https://docs.spring.io/spring-ai/reference/index.html[Spring AI].
2+
3+
image::https://raw.githubusercontent.com/spring-io/spring-io-static/refs/heads/main/blog/tzolov/spring-ai-agentic-systems.jpg[Agent Systems, width=350]
4+
5+
While the pattern descriptions and diagrams are sourced from Anthropic's original publication, we'll focus on how to implement these patterns using Spring AI's features for model portability and structured output. We recommend reading the original paper first.
6+
7+
The https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns[agentic-patterns] directory in the spring-ai-examples repository contains all the code for the examples that follow.
8+
9+
== Agentic Systems
10+
11+
The research publication makes an important architectural distinction between two types of agentic systems:
12+
13+
. *Workflows*: Systems where LLMs and tools are orchestrated through predefined code paths (e.g., prescriptive systems)
14+
. *Agents*: Systems where LLMs dynamically direct their own processes and tool usage
15+
16+
The key insight is that while fully autonomous agents might seem appealing, workflows often provide better predictability and consistency for well-defined tasks. This aligns perfectly with enterprise requirements where reliability and maintainability are crucial.
17+
18+
Let's examine how Spring AI implements these concepts through five fundamental patterns, each serving specific use cases:
19+
20+
=== 1. https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns/chain-workflow[Chain Workflow]
21+
22+
The Chain Workflow pattern exemplifies the principle of breaking down complex tasks into simpler, more manageable steps.
23+
24+
image::https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F7418719e3dab222dccb379b8879e1dc08ad34c78-2401x1000.png&w=3840&q=75[Prompt Chaining Workflow]
25+
26+
*When to Use:*
27+
- Tasks with clear sequential steps
28+
- When you want to trade latency for higher accuracy
29+
- When each step builds on the previous step's output
30+
31+
Here's a practical example from Spring AI's implementation:
32+
33+
[source,java]
34+
----
35+
public class ChainWorkflow {
36+
private final ChatClient chatClient;
37+
private final String[] systemPrompts;
38+
39+
public String chain(String userInput) {
40+
String response = userInput;
41+
for (String prompt : systemPrompts) {
42+
String input = String.format("{%s}\n {%s}", prompt, response);
43+
response = chatClient.prompt(input).call().content();
44+
}
45+
return response;
46+
}
47+
}
48+
----
49+
50+
This implementation demonstrates several key principles:
51+
52+
- Each step has a focused responsibility
53+
- Output from one step becomes input for the next
54+
- The chain is easily extensible and maintainable
55+
56+
=== 2. https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns/parallelization-workflow[Parallelization Workflow]
57+
58+
LLMs can work simultaneously on tasks and have their outputs aggregated programmatically.
59+
60+
image::https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F406bb032ca007fd1624f261af717d70e6ca86286-2401x1000.png&w=3840&q=75[Parallelization Workflow]
61+
62+
*When to Use:*
63+
- Processing large volumes of similar but independent items
64+
- Tasks requiring multiple independent perspectives
65+
- When processing time is critical and tasks are parallelizable
66+
67+
[source,java]
68+
----
69+
List<String> parallelResponse = new ParallelizationWorkflow(chatClient)
70+
.parallel(
71+
"Analyze how market changes will impact this stakeholder group.",
72+
List.of(
73+
"Customers: ...",
74+
"Employees: ...",
75+
"Investors: ...",
76+
"Suppliers: ..."
77+
),
78+
4
79+
);
80+
----
81+
82+
=== 3. https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns/routing-workflow[Routing Workflow]
83+
84+
The Routing pattern implements intelligent task distribution, enabling specialized handling for different types of input.
85+
86+
image::https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F5c0c0e9fe4def0b584c04d37849941da55e5e71c-2401x1000.png&w=3840&q=75[Routing Workflow]
87+
88+
*When to Use:*
89+
- Complex tasks with distinct categories of input
90+
- When different inputs require specialized processing
91+
- When classification can be handled accurately
92+
93+
[source,java]
94+
----
95+
@Autowired
96+
private ChatClient chatClient;
97+
98+
RoutingWorkflow workflow = new RoutingWorkflow(chatClient);
99+
100+
Map<String, String> routes = Map.of(
101+
"billing", "You are a billing specialist. Help resolve billing issues...",
102+
"technical", "You are a technical support engineer. Help solve technical problems...",
103+
"general", "You are a customer service representative. Help with general inquiries..."
104+
);
105+
106+
String input = "My account was charged twice last week";
107+
String response = workflow.route(input, routes);
108+
----
109+
110+
=== 4. https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns/orchestrator-workers-workflow[Orchestrator-Workers]
111+
112+
image::https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F8985fc683fae4780fb34eab1365ab78c7e51bc8e-2401x1000.png&w=3840&q=75[Orchestration Workflow]
113+
114+
*When to Use:*
115+
- Complex tasks where subtasks can't be predicted upfront
116+
- Tasks requiring different approaches or perspectives
117+
- Situations needing adaptive problem-solving
118+
119+
[source,java]
120+
----
121+
public class OrchestratorWorkersWorkflow {
122+
public WorkerResponse process(String taskDescription) {
123+
// 1. Orchestrator analyzes task and determines subtasks
124+
OrchestratorResponse orchestratorResponse = // ...
125+
126+
// 2. Workers process subtasks in parallel
127+
List<String> workerResponses = // ...
128+
129+
// 3. Results are combined into final response
130+
return new WorkerResponse(/*...*/);
131+
}
132+
}
133+
----
134+
135+
Usage Example:
136+
137+
[source,java]
138+
----
139+
ChatClient chatClient = // ... initialize chat client
140+
OrchestratorWorkersWorkflow workflow = new OrchestratorWorkersWorkflow(chatClient);
141+
142+
WorkerResponse response = workflow.process(
143+
"Generate both technical and user-friendly documentation for a REST API endpoint"
144+
);
145+
146+
System.out.println("Analysis: " + response.analysis());
147+
System.out.println("Worker Outputs: " + response.workerResponses());
148+
----
149+
150+
=== 5. https://github.com/spring-projects/spring-ai-examples/tree/main/agentic-patterns/evaluator-optimizer-workflow[Evaluator-Optimizer]
151+
152+
image::https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F14f51e6406ccb29e695da48b17017e899a6119c7-2401x1000.png&w=3840&q=75[Evaluator-Optimizer Workflow]
153+
154+
*When to Use:*
155+
- Clear evaluation criteria exist
156+
- Iterative refinement provides measurable value
157+
- Tasks benefit from multiple rounds of critique
158+
159+
[source,java]
160+
----
161+
public class EvaluatorOptimizerWorkflow {
162+
public RefinedResponse loop(String task) {
163+
Generation generation = generate(task, context);
164+
EvaluationResponse evaluation = evaluate(generation.response(), task);
165+
return new RefinedResponse(finalSolution, chainOfThought);
166+
}
167+
}
168+
----
169+
170+
Usage Example:
171+
172+
[source,java]
173+
----
174+
ChatClient chatClient = // ... initialize chat client
175+
EvaluatorOptimizerWorkflow workflow = new EvaluatorOptimizerWorkflow(chatClient);
176+
177+
RefinedResponse response = workflow.loop(
178+
"Create a Java class implementing a thread-safe counter"
179+
);
180+
181+
System.out.println("Final Solution: " + response.solution());
182+
System.out.println("Evolution: " + response.chainOfThought());
183+
----
184+
185+
== Spring AI's Implementation Advantages
186+
187+
Spring AI's implementation of these patterns offers several benefits that align with Anthropic's recommendations:
188+
189+
=== https://docs.spring.io/spring-ai/reference/api/chat/comparison.html[Model Portability]
190+
191+
[source,xml]
192+
----
193+
<dependency>
194+
<groupId>org.springframework.ai</groupId>
195+
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
196+
</dependency>
197+
----
198+
199+
=== https://docs.spring.io/spring-ai/reference/api/structured-output-converter.html[Structured Output]
200+
201+
[source,java]
202+
----
203+
EvaluationResponse response = chatClient.prompt(prompt)
204+
.call()
205+
.entity(EvaluationResponse.class);
206+
----
207+
208+
=== https://docs.spring.io/spring-ai/reference/api/chatclient.html[Consistent API]
209+
210+
- Uniform interface across different LLM providers
211+
- Built-in error handling and retries
212+
- Flexible prompt management
213+
214+
== Best Practices and Recommendations
215+
216+
- *Start Simple*
217+
- Begin with basic workflows before adding complexity
218+
- Use the simplest pattern that meets your requirements
219+
- Add sophistication only when needed
220+
221+
- *Design for Reliability*
222+
- Implement clear error handling
223+
- Use type-safe responses where possible
224+
- Build in validation at each step
225+
226+
- *Consider Trade-offs*
227+
- Balance latency vs. accuracy
228+
- Evaluate when to use parallel processing
229+
- Choose between fixed workflows and dynamic agents
230+
231+
== Future Work
232+
233+
These guides will be updated to explore how to build more advanced Agents that combine these foundational patterns with sophisticated features:
234+
235+
*Pattern Composition*
236+
- Combining multiple patterns to create more powerful workflows
237+
- Building hybrid systems that leverage the strengths of each pattern
238+
- Creating flexible architectures that can adapt to changing requirements
239+
240+
*Advanced Agent Memory Management*
241+
- Implementing persistent memory across conversations
242+
- Managing context windows efficiently
243+
- Developing strategies for long-term knowledge retention
244+
245+
*Tools and Model-Context Protocol (MCP) Integration*
246+
- Leveraging external tools through standardized interfaces
247+
- Implementing MCP for enhanced model interactions
248+
- Building extensible agent architectures
249+
250+
== Conclusion
251+
252+
The combination of Anthropic's research insights and Spring AI's practical implementations provides a powerful framework for building effective LLM-based systems.
253+
254+
By following these patterns and principles, developers can create robust, maintainable, and effective AI applications that deliver real value while avoiding unnecessary complexity.
255+
256+
The key is to remember that sometimes the simplest solution is the most effective. Start with basic patterns, understand your use case thoroughly, and only add complexity when it demonstrably improves your system's performance or capabilities.

0 commit comments

Comments
 (0)