--- name: langchain4j-agent-design description: "Use when designing LangChain4j multi-agent orchestration architecture (pattern selection, agent boundaries, shared state/memory, guardrails, execution plan). Do not invoke for code generation (use langchain4j-scaffold), implementation review (use langchain4j-review), or OpenAI provider wiring (use langchain4j-openai-sdk)." allowed-tools: Read, Grep, Glob, Bash(git log *), Bash(find * -type d) --- # LangChain4j Multi-Agent System Design > **Version baseline**: LangChain4j 1.18.1 / 1.18.1-beta28, Spring Boot 3.5+ / 4.0.5+, Java 17+ Design the system described in `$ARGUMENTS` using LangChain4j's AgenticServices framework. First, read the codebase to understand the existing architecture: ``` !`find . \( -name "*.java" -o -name "*.kt" \) -path "*/agent*" -o -path "*/ai*" 2>/dev/null | head -20 || echo "no existing agent code"` ``` ## Deliverables ### 1. System Overview - Problem statement (2–3 sentences) - Key agents and their responsibilities - Chosen orchestration pattern and rationale - Technology stack (LangChain4j version, model provider, transport) ### 2. Orchestration Pattern Selection Choose the appropriate pattern: | Pattern | Use When | LangChain4j Builder | |---------|----------|---------------------| | **Sequential** | Linear pipeline, each step feeds the next | `sequenceBuilder()` | | **Parallel** | Independent agents run concurrently, results merged | `parallelBuilder()` / `parallelMapperBuilder()` | | **Loop** | Repeated refinement until quality threshold | `loopBuilder()` with `exitCondition` | | **Conditional** | Deterministic routing via predicates | `conditionalBuilder()` / `@ConditionalAgent` | | **Supervisor** | LLM decides which agents to invoke | `supervisorBuilder()` | | **Custom Planner** | Complex orchestration not fitting any built-in | `plannerBuilder()` with `Planner` impl | | **MCP Gateway** | Agents expose themselves as MCP tools | `McpToolProvider` + `AiServices` | | **Declarative** | Annotation-based, less code | `@SequentialAgent`, `@ParallelAgent`, `@ConditionalAgent` | | **A2A** | Agents in separate JVMs/containers need cross-process delegation | `langchain4j-agentic-a2a` + `@A2AClientAgent` | | **BDI** | Goals, beliefs, and plans must be explicit and revisable | `langchain4j-agentic` BDI pattern | ### A2A Pattern Detail (1.14.0) #### 9. Agent-to-Agent (A2A) Protocol **When to use:** Agents deployed as independent services (separate JVMs/containers) needing cross-process collaboration. One agent fully delegates a sub-task to a remote agent service. **Module:** `langchain4j-agentic-a2a` **Pattern:** ``` Orchestrator → @A2AClientAgent(url = "http://service") → Remote Agent Service ``` **Key considerations:** - Best for domain-bounded agents in microservices architectures - Network latency vs process isolation trade-off - 1.14.0 fix: typed interfaces correctly preserve actual argument types (use interfaces/records, not Map) ### 3. Agent Definitions For each agent, specify: - **Name** and **description** (what the LLM sees in the supervisor context) - **Type**: typed (`agentBuilder(Interface.class)`) or untyped (`agentBuilder()`) - **Input key(s)** and **output key** in `AgenticScope` - **Tools**: list of `@Tool` methods or `ToolProvider` implementations - **Chat model**: which model (e.g. gpt-4o, gpt-4o-mini for lighter agents) - **Memory**: stateful or stateless; memory window size - **Guardrails** (if applicable) ```java // Typed agent pattern @Agent(outputKey = "result") interface MyAgent { @SystemMessage("You are a specialist in ...") @UserMessage("{{request}}") String process(@V("request") String request); } // Build MyAgent agent = AgenticServices.agentBuilder(MyAgent.class) .chatModel(model) .tools(new MyTools()) .outputKey("result") .build(); ``` ### Optional Agents (1.13.0+) Declare agents as optional when their inputs may not always be available, preventing workflow failure on missing context. Use when: - An enrichment step (e.g., fetch customer history) may have no data for new users - A conditional branch may be skipped for certain workflow paths ### 4. Shared State (AgenticScope) Define all keys written/read in `AgenticScope`: ```java // Define TypedKeys for type safety — avoids string typos public static class UserRequest implements TypedKey {} public static class DomainCategory implements TypedKey { @Override public String defaultValue() { return "unknown"; } } public static class QualityScore implements TypedKey { @Override public Double defaultValue() { return 0.0; } } ``` State flow table: | Key | Type | Written by | Read by | Purpose | |-----|------|-----------|---------|---------| ### Persistent & Recoverable Execution State (1.13.0+) Execution state can be persisted to external storage (database, Redis) and recovered across process restarts. Design decisions: 1. Choose a `AgenticScopeStore` implementation (JPA, Redis, in-memory for dev) 2. Use unique workflow IDs for scope retrieval: `workflow.getAgenticScope("order-12345")` 3. Human-in-the-loop: pause workflow, write state `"approval": "PENDING"`, resume via external trigger that writes `"approval": "APPROVED"` and continues workflow When designing: identify which workflow steps require durability vs which can restart cleanly. ### Agentic Runtime Updates (1.14.0+) - Use `AgenticScope.executionContext` for per-run operational metadata that should not become domain state. - Subagents may be assembled as a `Collection`, useful for plugin-discovered or configuration-driven systems. - Agent invocation events fire for isolated agent calls as well as composed workflows. - Topology and execution HTML reports can be generated independently. ### 5. Memory Strategy For each agent decide: - **Stateless** (no memory) — for pure transformation agents - **`MessageWindowChatMemory`** — for prototypes; specify `maxMessages` - **`TokenWindowChatMemory`** — for production; specify `maxTokens` and estimator - **Persistent store** — implement `ChatMemoryStore` backed by PostgreSQL/Redis ### 6. Guardrails and Validation Define input and output guardrails: ```java // Input: reject harmful or off-topic requests @InputGuardrails(SafetyGuardrail.class) // Output: validate and auto-repair structured responses @OutputGuardrails(value = JsonGuardrail.class, maxRetries = 3) ``` Specify: - What each guardrail validates - Whether to use `fatal`, `failure`, `retry`, or `reprompt` - `maxRetries` for output guardrails ### 7. Observability Always include: - `AgentMonitor` attached to the top-level agent - `MicrometerMetricsChatModelListener` on all `ChatModel` instances - `AiServiceCompletedListener` / `AiServiceErrorListener` for service lifecycle - Log each agent's input/output via `AgentListener` ```java AgentMonitor monitor = new AgentMonitor(); topLevelAgent.listener(monitor); ChatModel model = AzureOpenAiChatModel.builder() .listeners(List.of(new MicrometerMetricsChatModelListener(meterRegistry))) .build(); ``` ### 8. Implementation Checklist Ordered tasks: 1. Define `TypedKey` classes for all `AgenticScope` keys 2. Implement `@Tool` classes for each agent's capabilities 3. Define typed agent interfaces with `@Agent`, `@SystemMessage`, `@UserMessage` 4. Build agents with `AgenticServices.agentBuilder()` 5. Implement input and output guardrails 6. Wire orchestration pattern (sequenceBuilder / parallelBuilder / etc.) 7. Add `AgentMonitor` and `MicrometerMetricsChatModelListener` 8. Implement `ChatMemoryStore` if persistence is needed 9. Add `AgentListener` for custom logging/tracing 10. Write unit tests for tools and guardrails 11. Write integration test for the full agent pipeline 12. Choose correct Spring Boot starter: `-spring-boot-starter` (Boot 3.x) or `-spring-boot4-starter` (Boot 4.0.5+) 13. For cross-service delegation: use A2A protocol (`langchain4j-agentic-a2a`) with typed interfaces 14. For workflows surviving restarts: design scope persistence strategy and unique workflow IDs 15. Declare agents as `optional` when their required inputs may legitimately be absent ### 9. Spring Boot Integration (if applicable) ```java // Auto-register as Spring bean @AiService interface MyOrchestrator { @SystemMessage("...") String process(String request); } // Tool beans — automatically discovered by @AiService @Component public class MyTools { @Tool("...") public String doSomething(String arg) { ... } } ``` Application properties: ```yaml langchain4j.azure-open-ai.chat-model: endpoint: ${AZURE_OPENAI_ENDPOINT} api-key: ${AZURE_OPENAI_KEY} deployment-name: gpt-4o temperature: 0.3 ``` ### 10. Considerations - **Model selection**: Use a capable model (gpt-4o) for planning/supervisor; lighter model (gpt-4o-mini) for leaf agents - **Token cost**: `SupervisorAgent` sends all sub-agent descriptions to the LLM per turn — keep descriptions concise - **Concurrency**: Parallel agents need thread-safe tool implementations; `AgenticScope` is not thread-safe per agent invocation - **Error handling**: Use `.errorHandler(...)` on each agent; `ErrorRecoveryResult.retry()` for transient errors - **MCP gateway**: If agents are microservices, expose each as an MCP server (`McpServer`) and use `McpToolProvider` in the orchestrator - **Skills vs tools/MCP**: Package reusable procedures as agent skills; expose live external systems, authenticated data, and shared tool catalogs through MCP/tools - **Native skills (experimental)**: Default to `Skills` Tool Mode; attach a filtered `McpToolProvider` to a skill when its live tools must appear only after activation. Do not use `ShellSkills` outside a trusted sandbox. - **Azure OpenAI**: Use `tokenCredential(new DefaultAzureCredentialBuilder().build())` instead of API keys in production For detailed code patterns and examples, see [reference.md](reference.md).