Skip to content

Add Embabel AgentPlatform integration plan for agentic aggregates#301

Merged
jwijgerd merged 8 commits into
mainfrom
copilot/create-agentic-aggregate-structure
Apr 8, 2026
Merged

Add Embabel AgentPlatform integration plan for agentic aggregates#301
jwijgerd merged 8 commits into
mainfrom
copilot/create-agentic-aggregate-structure

Conversation

Copilot AI commented Apr 6, 2026

Copy link
Copy Markdown
Contributor
  • Consolidated plan files into plans/embabel-integration-plan.md
  • Incorporated all architect feedback from first review
  • Added event application mechanism (section 4.3)
  • Added test agent implementation (section 5.2)
  • Added aggregate discovery via DiscoverAggregateServicesAction (section 3.6)
  • Reworked to agentic handler adapter pattern (AgenticCommandHandlerFunctionAdapter / AgenticEventHandlerFunctionAdapter)
  • Added AgenticProcessHandler interface implemented on both agentic handler adapters
  • Added AgentTask record with String id field, tracked in state's activeAgentTasks list
  • Updated proceed(state)proceed(AgentTask, state) in AgenticProcessHandler and both adapter implementations
  • Added proceedAgentTasks() method on KafkaAggregateRuntime, called from AgenticAggregatePartition when no Kafka records are returned
  • Added section 4.3.1 documenting the proceedAgentTasks() flow for active task advancement

…: agentHandledCommands, agentHandledEvents, agentProducedErrors

Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/4c3ee463-b499-461a-bf05-e5c611164cf5

Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
@jwijgerd
jwijgerd marked this pull request as ready for review April 8, 2026 08:24
Copilot AI review requested due to automatic review settings April 8, 2026 08:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a consolidated Embabel AgentPlatform integration plan for the agentic aggregates module, and removes the prior plan location.

Changes:

  • Introduces a new integration plan document covering AgentPlatform wiring, agent-handled commands/events, and agent-produced error events.
  • Documents phased implementation steps (runtime injection, partition integration, Embabel components, and testing).
  • Removes the legacy embabel-integration/ plan directory (verified absent in the repo).

Comment thread plans/embabel-integration-plan.md Outdated
**Changes**:
- In `AgenticAggregateRuntimeFactory.getObject()`, look up the `AgentPlatform` bean from `applicationContext`
- Pass it as a constructor argument to `new KafkaAgenticAggregateRuntime(kafkaRuntime, objectMapper, stateClass, agentPlatform)`
- Handle the case where `AgentPlatform` is not available (e.g., during unit tests without Embabel auto-configuration): fall back to a null reference with a warning log, making agent-based command processing a no-op

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AgentPlatform not being available should be a fatal error, the application shold not start

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4AgentPlatform unavailability is now a fatal error. The plan specifies: "fail fast with a fatal error — the application must not start without a configured AgentPlatform."

Comment thread plans/embabel-integration-plan.md Outdated

### 2.2 Memory Management Action: `StoreMemoryAction`

**What**: An `@Action`-annotated method that creates a `StoreMemoryCommand` from the current `AgentProcess` context and sends it to the aggregate's command bus.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to refactor out the use of internal commands for memory management. The Embabel actions should produce events that are applied to the state (using the built-in EventSourcingHandlers in this case). The commands will be removed in a later stage

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4StoreMemoryAction now produces MemoryStoredEvent directly (applied via the built-in @EventSourcingHandler which calls MemoryAwareState.withMemory()). Added a note that StoreMemoryCommand will be removed in a later stage.

Comment thread plans/embabel-integration-plan.md Outdated

### 2.3 Memory Management Action: `ForgetMemoryAction`

**What**: An `@Action`-annotated method that creates a `ForgetMemoryCommand` to remove a specific memory by ID or by criteria.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here, return the event not the command

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4ForgetMemoryAction now produces MemoryRevokedEvent directly (applied via the built-in @EventSourcingHandler which calls MemoryAwareState.withoutMemory()). ForgetMemoryCommand noted for later removal.

Comment thread plans/embabel-integration-plan.md Outdated

**Why**: Some actions only make sense when the agent has prior context. For example, "recall memories" is only meaningful when memories exist.

### 2.6 Condition: `HasAggregateStateCondition`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In another feature we will implement a mechanism where the framework will ensusre that for an AgenticAggregate the State is always created. This makes sense because it is a singleton. So this condition can be removed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4HasAggregateStateCondition (section 2.6) is removed with a note that the framework ensures singleton state is auto-created. Also removed hasAggregateState from the blackboard conditions in section 4.3.

Comment thread plans/embabel-integration-plan.md
Comment thread plans/embabel-integration-plan.md Outdated

## Open Questions for Architect

1. **LLM Model Configuration**: Should the `@AgenticAggregateInfo` annotation include LLM model preferences (model name, temperature), or should this be purely configuration-based (application.yaml)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LLM models should be configured at the Embabel level, so not in the AgenticAggregateInfo. So indeed configuration based or in the Emabel code / annotations

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4 — Resolved as: LLM models are configured at the Embabel level (Embabel code/annotations or application configuration), not in @AgenticAggregateInfo.

Comment thread plans/embabel-integration-plan.md Outdated
## Open Questions for Architect

1. **LLM Model Configuration**: Should the `@AgenticAggregateInfo` annotation include LLM model preferences (model name, temperature), or should this be purely configuration-based (application.yaml)?
2. **Agent Scope**: Should each agentic aggregate get its own `Agent` instance (deployed to the `AgentPlatform`), or should the `AkcesAgentComponent`'s actions be shared across all agentic aggregates?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each AgenticAggregate gets one Agent instance, so when multiple AgenticAggregates are deployed in a single VM instance (sharing the AgentPlatform) the all use their own Agent instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4 — Resolved as: Each AgenticAggregate gets its own Agent instance. Multiple aggregates in a single VM share the AgentPlatform but each uses its own Agent.

Comment thread plans/embabel-integration-plan.md Outdated

1. **LLM Model Configuration**: Should the `@AgenticAggregateInfo` annotation include LLM model preferences (model name, temperature), or should this be purely configuration-based (application.yaml)?
2. **Agent Scope**: Should each agentic aggregate get its own `Agent` instance (deployed to the `AgentPlatform`), or should the `AkcesAgentComponent`'s actions be shared across all agentic aggregates?
3. **Synchronous vs. Async Agent Execution**: Should `agentProcess.run()` be called synchronously within the Kafka poll loop, or should we use `agentProcess.tick()` for incremental execution with periodic Kafka heartbeats?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible for an AgenticAgent to have multiple running processes in the future. Currently we don't have support for this (we would need to add more state etc). For now use AgentProcess.tick() but if there is an active AgentProcess we must call tick() even if there are no records coming back from the kafka poll

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4 — Resolved as: Use AgentProcess.tick() for incremental execution. tick() must be called on every poll loop iteration if there is an active process, even without Kafka records. Multiple processes per agent noted as future scope. Updated the result translation section (4.2) accordingly.

Comment thread plans/embabel-integration-plan.md Outdated
1. **LLM Model Configuration**: Should the `@AgenticAggregateInfo` annotation include LLM model preferences (model name, temperature), or should this be purely configuration-based (application.yaml)?
2. **Agent Scope**: Should each agentic aggregate get its own `Agent` instance (deployed to the `AgentPlatform`), or should the `AkcesAgentComponent`'s actions be shared across all agentic aggregates?
3. **Synchronous vs. Async Agent Execution**: Should `agentProcess.run()` be called synchronously within the Kafka poll loop, or should we use `agentProcess.tick()` for incremental execution with periodic Kafka heartbeats?
4. **MCP Tool Scoping**: Should MCP tools (from sidecar servers) be automatically added to the agent's tool groups, or should they be explicitly configured per agentic aggregate?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP tools added in the core AgenticAggregate should be available to all Agents

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4 — Resolved as: MCP tools from the core AgenticAggregate are available to all Agents. Shared tools are the default, no per-aggregate scoping needed.

Comment thread plans/embabel-integration-plan.md Outdated
2. **Agent Scope**: Should each agentic aggregate get its own `Agent` instance (deployed to the `AgentPlatform`), or should the `AkcesAgentComponent`'s actions be shared across all agentic aggregates?
3. **Synchronous vs. Async Agent Execution**: Should `agentProcess.run()` be called synchronously within the Kafka poll loop, or should we use `agentProcess.tick()` for incremental execution with periodic Kafka heartbeats?
4. **MCP Tool Scoping**: Should MCP tools (from sidecar servers) be automatically added to the agent's tool groups, or should they be explicitly configured per agentic aggregate?
5. **Error Event Validation at Runtime**: Should the `AgentProcessResultTranslator` strictly validate that emitted error events are in the `agentProducedErrors` set, or should it also accept system error events without explicit declaration?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should accept all ErrorEvents, even if they are not declared (log a warning if this happens)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a1d02e4 — Resolved as: Accept all ErrorEvents at runtime, even undeclared ones. Log a warning for undeclared error events but don't reject. Updated the result translation section (4.2) and integration test descriptions accordingly.

…mands for memory, remove HasAggregateStateCondition, add memory limits, resolve design decisions

Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/e39a68f0-0493-4be8-99ab-d03126aa1a11

Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
Copilot AI requested a review from jwijgerd April 8, 2026 08:57

@jwijgerd jwijgerd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I am missing a mechanism on applying DomainEvent instances created from the Embabel runtime. Find a solution where after each process step (i.e. each call of AgentProcess.tick() any events that are generated are collected an applied. This should be done in the Akces code. It looks like we need to expose the (now private) processEvent methods in the KafkaAggregateRuntime. Maybe it is a better idea to extend KafkaAggregateRuntime (and make the methods protected) instead of using it as a delegate? Consider this solution, but only if it makes sense

Comment thread plans/embabel-integration-plan.md Outdated
| `AgentHandledCommandsRegistrationTest` | Verifies `agentHandledCommands` create `CommandType` entries without handler adapters |
| `AgentHandledEventsRegistrationTest` | Verifies `agentHandledEvents` create external `DomainEventType` entries |

### 5.2 Integration Tests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create a test agent implementation, base it on the example you give in the beginning of the plan

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added section 5.2 "Test Agent Implementation: TestTradingAdvisorAgent" in 7de97ce. Includes a full TestTradingAdvisorAggregate (with deterministic @CommandHandler for creation + agent-handled TestAnalyzeMarketCommand), TestTradingAdvisorState implementing MemoryAwareState, all command/event/error event classes, and a TestTradingAdvisorAgentComponent @EmbabelComponent with test actions. Also added integration test cases for the full agent loop, external events, and the event application mechanism.

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

In general I am missing a mechanism on applying DomainEvent instances created from the Embabel runtime. Find a solution where after each process step (i.e. each call of AgentProcess.tick() any event...

Added section 4.3 "Event Application Mechanism" in 7de97ce. The plan proposes refactoring KafkaAgenticAggregateRuntime to extend KafkaAggregateRuntime instead of delegating, making processDomainEvent(), materialize(AggregateStateRecord), and getDomainEventType(Class<?>) protected. A new applyAgentEvents() method chains processDomainEvent() calls for events collected from the blackboard after each tick(). Includes a fallback note if inheritance doesn't fit structurally.

Copilot AI requested a review from jwijgerd April 8, 2026 09:37

---

## Phase 3: Akces Platform Development Goals, Actions, and Conditions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a way to discover other aggregates in the system (by exposing the AggregateServiceRecords) this includes other AgenticAggregates. We need this to be able to determine what commands we can send to achieve a Goal

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added DiscoverAggregateServicesAction (section 3.6) in 85ec129. The AggregateServiceRecords from the Akces-Control topic are placed on the blackboard by the agentic handler adapters before agent invocation. The action exposes aggregate name, command topic, supported commands, produced events, consumed events, and type (STANDARD/AGENTIC) — enabling the agent to discover what commands can be sent to other aggregates to achieve a Goal.

Comment thread plans/embabel-integration-plan.md Outdated

### Registration Flow for Agent Properties

In `AgenticAggregateBeanFactoryPostProcessor`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not be possible to create new implementations of the CommandHandlerFunctionAdapter and the EventHandlerFunctionAdapter for the Agentic handlers? We can then already inject the AgentPlatform (and other resources needed) and already know we are handling a Command or a DomainEvent in the Embabel agent. We could the also setup the Blackboard etc in this implementation. Does this make sense?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked the plan in 85ec129 to use AgenticCommandHandlerFunctionAdapter and AgenticEventHandlerFunctionAdapter. The adapters implement CommandHandlerFunction / EventHandlerFunction, so they plug directly into the existing KafkaAggregateRuntime.handleCommand() flow. Inside apply(), the adapter sets up the Blackboard (command/event, state, memories, AggregateServiceRecords), creates the AgentProcess, calls tick(), and returns the resulting events as Stream<DomainEvent>. This eliminates the need to make processDomainEvent protected or refactor to inheritance — the delegation pattern stays as-is.

…pattern

- Add DiscoverAggregateServicesAction (section 3.6) exposing AggregateServiceRecords
  to enable agents to discover other aggregates and their supported commands
- Rework registration flow to use AgenticCommandHandlerFunctionAdapter and
  AgenticEventHandlerFunctionAdapter instead of bypassing handlers
- Adapters plug into existing CommandHandlerFunction/EventHandlerFunction contracts,
  routing through the Embabel agent inside apply()
- Simplify event application mechanism (section 4.3): no need to make
  processDomainEvent protected or refactor to inheritance
- Update blackboard content (section 4.4) to include aggregate service records
- Update file inventory and test descriptions

Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/f31ba8d2-cb16-4310-95ea-e7af6584122a

Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
Copilot AI requested a review from jwijgerd April 8, 2026 10:00
Comment thread plans/embabel-integration-plan.md Outdated
- Create a `AgentProcessResultTranslator` utility class to handle this translation
- Accept all `ErrorEvent` instances at runtime, even if not declared in `agentProducedErrors`. Log a warning for undeclared error events but do not reject them.

**Note on `tick()` execution**: Use `AgentProcess.tick()` for incremental execution. If there is an active `AgentProcess`, `tick()` must be called on every poll loop iteration, even if no Kafka records are returned.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can only call tick() once in the handler and then we need to return control to the Akces runtime. This exposes a problem where we need to know if there are running AgentProcess instances that need another tick. Idea: we extend the handles with a new interface: AgenticProcessHandler. This interface also has a proceed(AgenticState) method that also returns DomainEvents and does a similar loop as the apply method. Currently we have no notion in the state of running Agent Tasks so for now implement it by calling the AgentProcess.tick() method until the end state. We will change this later when we add the AgentTask notion to the state (done in a separate feature)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 8eac209 — Added AgenticProcessHandler interface with proceed(S state) returning Stream<E>, implemented on both AgenticCommandHandlerFunctionAdapter and AgenticEventHandlerFunctionAdapter. Current implementation calls tick() in a loop until end state inside apply(). The proceed() method is stubbed for future use when AgentTask support is added to the state, at which point apply() will call tick() only once and proceed() will handle subsequent ticks on active tasks.

…ntic handler adapters

- Both AgenticCommandHandlerFunctionAdapter and AgenticEventHandlerFunctionAdapter
  now implement AgenticProcessHandler<S, E> interface
- Interface defines proceed(state) method for future incremental tick support
- Current implementation: apply() calls tick() in loop until end state (temporary)
- Future: single tick per invocation with proceed() for active AgentTask advancement
- Added AgenticProcessHandler.java to file inventory
- Updated sections 4.1, 4.2, 4.3 for consistency with tick-to-completion approach

Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/eac8c191-eeee-47ad-b736-2fd859322b62

Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
…dAgentTasks() to plan

- Added AgentTask record with String id field to the Agentic Handler Adapters section
- Described activeAgentTasks property on agentic aggregate state
- Updated AgenticProcessHandler.proceed() signature from proceed(state) to proceed(AgentTask, state)
- Updated both adapter implementations (command and event) with new proceed(AgentTask, state) signature
- Added section 4.3.1 proceedAgentTasks() on KafkaAggregateRuntime
- Added AgenticAggregatePartition flow showing proceedAgentTasks() called when no Kafka records
- Updated all forward references (sections 4.1, 4.2, 4.3) to use new signatures
- Added AgentTask.java to file inventory

Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/3cc284de-0f6f-4e06-85c3-c30a423df340

Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
@jwijgerd
jwijgerd merged commit 4d9f9ab into main Apr 8, 2026
3 checks passed
@jwijgerd
jwijgerd deleted the copilot/create-agentic-aggregate-structure branch April 8, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants