Skip to content

About

A production-oriented learning project for building a multi-user REST agent with Spring Boot, Spring AI, Anthropic Claude

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Springboot AI Agent

A production-oriented learning project for building a multi-user REST agent with Spring Boot, Spring AI, Anthropic Claude, bounded conversation memory, and Java tool calling. It intentionally keeps the controller thin, makes ownership explicit, and isolates provider details in one service.

1. Architecture

graph TD
  Client --> Controller
  Controller --> AgentService
  AgentService --> ConversationService
  AgentService --> ChatClient
  ChatClient --> MemoryAdvisor
  MemoryAdvisor --> ChatMemory
  ChatClient --> Claude[Anthropic Claude]
  Claude -->|tool call when useful| JavaTools
  JavaTools --> Claude
  Claude --> Controller
Loading

ChatModel is Spring AI's provider-neutral model abstraction. The Anthropic starter creates its Claude implementation from spring.ai.anthropic.* properties. ChatClient is a fluent facade over that model: the injected builder follows the builder pattern, allowing shared system instructions and advisors to be configured once. A prompt combines system and user messages; call().content() returns the final text.

The model is selected by spring.ai.anthropic.chat.options.model. temperature controls randomness (0.2 favors consistent answers/tool decisions) and max-tokens limits generated output, latency, and cost. Set ANTHROPIC_API_KEY in the environment—never commit it.

2. Package layout

com.rajeshkawali.aiagent
├── config          # Spring bean wiring
├── controller      # HTTP adapters only
├── dto             # immutable API contracts
├── exception       # safe error translation
├── memory          # Spring AI memory adapter
├── model           # domain metadata
├── prompts         # reusable system instructions
├── service         # orchestration and ownership rules
└── tools           # deterministic capabilities available to Claude

3. Memory and multi-user isolation

sequenceDiagram
  participant U as User
  participant C as Controller
  participant S as AgentService
  participant M as ChatMemory
  participant A as Claude
  U->>C: POST /chat (userId, conversationId, message)
  C->>S: validated ChatRequest
  S->>S: verify conversation ownership
  S->>M: load bounded history by conversationId
  S->>A: system prompt + history + message
  A-->>S: final response
  S->>M: advisor stores completed turn
  S-->>U: ChatResponse
Loading

MessageWindowChatMemory stores framework message objects keyed by conversation ID. It fetches a sliding window before each model call and updates it after the call, preserving context while bounding token growth. The supplied InMemoryChatMemoryRepository loses data on restart and is local to one process. For real deployments, retain ConversationMemoryService and replace the repository with a supported JDBC, Redis, MongoDB, Cassandra, or Neo4j implementation. The separate ConversationService records who owns each ID; every read, delete, and chat verifies ownership.

Spring AI requires an explicit ChatMemory.CONVERSATION_ID for memory advisors. This project passes it on every chat call—there is no accidental shared default conversation.

4. Tool calling

flowchart LR
  Q[User request] --> L[Claude sees tool schemas]
  L --> D{Tool useful?}
  D -- Yes --> J[Spring AI invokes @Tool Java method]
  J --> R[Tool result]
  R --> L
  D -- No --> F[Final response]
  L --> F
Loading

Each public capability has @Tool(description = ...). When AgentService supplies tool objects through .tools(...), Spring AI derives schemas from those methods and sends them to Claude. Claude chooses whether to issue a tool call; Spring AI executes the matching Java method, returns the result to Claude, and Claude writes the user-facing response. This is why calculator and text answers are exact rather than model guesses.

Available tools:

  • CalculatorTool: add, subtract, multiply, divide (zero is rejected).
  • DateTimeTool: UTC date, time, and date-time.
  • WeatherTool: clearly labelled mock weather.
  • CurrencyTool: clearly labelled fixed-rate mock conversion, never financial advice.
  • TextTool: word/character count and uppercase/lowercase transforms.

Production tools should validate inputs, have timeouts/retries around external calls, log only safe metadata, and return errors suitable for the model to explain. Tool objects are explicitly registered per request rather than globally exposing arbitrary beans.

5. System prompt and guardrails

AgentSystemPrompt is a component so the stable system instruction is central, reviewable, and reusable. It tells the agent to use supplied tools for deterministic facts, never invent results, and label mock outputs. Keep system prompts short, specific, and tested; do not place secrets or untrusted user input in them. Application-level authorization and validation remain the real guardrails—the prompt is not a security boundary.

6. Run it

Requirements: Java 21 and Maven 3.9+.

set ANTHROPIC_API_KEY=your_key_here
mvn spring-boot:run

On PowerShell:

$env:ANTHROPIC_API_KEY = 'your_key_here'
mvn spring-boot:run

application.yml documents every non-default property inline. Override the model, token limit, or memory window with your normal Spring profile/environment configuration.

7. REST API

Create a conversation first:

curl -X POST http://localhost:8080/conversation/new -H "Content-Type: application/json" -d '{"userId":"alice"}'

Response:

{"id":"e8...","userId":"alice","createdAt":"2026-08-03T10:00:00Z"}

Send a chat turn (Claude can decide to use a tool):

curl -X POST http://localhost:8080/chat -H "Content-Type: application/json" -d '{"userId":"alice","conversationId":"e8...","message":"What is 19.5 multiplied by 4?"}'

Read or delete history; userId is deliberately required for ownership validation:

curl "http://localhost:8080/conversation/e8...?userId=alice"
curl -X DELETE "http://localhost:8080/conversation/e8...?userId=alice"
curl http://localhost:8080/health

Endpoints: POST /conversation/new allocates an owned ID; POST /chat executes an agent turn; GET /conversation/{id} returns bounded history; DELETE /conversation/{id} deletes metadata and memory; GET /health is a dependency-free liveness check.

8. Errors, logging, and tests

GlobalExceptionHandler provides consistent JSON errors for validation (400), absent conversations (404), cross-user access (403), and unexpected failures (500). The application logs request IDs/lengths and response lengths rather than message contents, which avoids casually recording potentially sensitive prompts. Add correlation IDs and an audit policy in production.

Tool unit tests verify deterministic behavior. HealthControllerTest is a MockMvc web slice test: it validates the HTTP contract without requiring an Anthropic API key or live model. Service-level testing should mock ChatClient or run against a provider test double; real-provider integration tests should be separately profiled and disabled by default to protect credentials and spend.

Run all tests:

mvn test

9. Troubleshooting and next steps

  • Startup cannot authenticate: ensure ANTHROPIC_API_KEY exists in the process environment and restart the app.
  • 404 on chat: create the conversation first and use the exact returned ID.
  • 403: the userId does not own the conversation.
  • Memory disappears: expected with the in-memory learning implementation; use a persistent repository.
  • No tool invocation: tool use is model-directed. Phrase tasks that need exact computation/current data and retain the tool descriptions.

Recommended evolutions: Spring Security/OAuth identity instead of request userId; a persistent conversation repository and Spring AI memory repository; actuator/metrics/tracing; rate limiting; streaming endpoint; idempotency keys; provider fallback; semantic long-term memory; integration tests in a protected CI profile.

About

A production-oriented learning project for building a multi-user REST agent with Spring Boot, Spring AI, Anthropic Claude

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages