camel-agent is a blueprint-driven Apache Camel component for agent orchestration.
camel-agent-core:agent:Camel component, kernel, blueprint parser, tool registry, schema checks.camel-agent-persistence-dscope: persistence adapter usingdscope-camel-persistence(redis,jdbc,redis_jdbc).camel-agent-spring-ai: Spring AI multi-provider gateway (openai,anthropic,vertex gemini).camel-agent-starter: Spring Boot auto-configuration.samples/agent-support-service: runnable Camel Main support sample.
- Java:
21 - Camel:
4.15.0 - Jackson:
2.20.0 - JUnit:
5.10.2 - DScope persistence:
1.1.0 - DScope AGUI:
1.1.0
- Changelog:
docs/CHANGELOG.md - Product guide:
docs/PRODUCT_GUIDE.md
mvn -q test
mvn clean installInstall local AGUI and persistence artifacts, then run this project with local profile:
./scripts/bootstrap-local-dscope-deps.shThis activates -Pdscope-local for local DScope dependency alignment used by runtime modules and samples.
agent:agentId?blueprint=classpath:agents/support/agent.md&persistenceMode=redis_jdbc&strictSchema=true&timeoutMs=30000&streaming=true
Runtime can resolve agents from a catalog instead of a single blueprint:
agent:
agents-config: classpath:agents/agents.yaml
blueprint: classpath:agents/support/agent.md # optional legacy fallbackCatalog behavior:
- multiple named plans
- multiple versions per plan
- one default plan
- one default version per plan
- sticky conversation selection persisted as
conversation.plan.selected
Request entrypoints can pass planName and planVersion. When omitted, runtime uses sticky selection for the conversation, then catalog defaults.
Camel Agent now integrates camel-a2a-component as a first-class protocol bridge.
Runtime config:
agent:
runtime:
a2a:
enabled: true
public-base-url: http://localhost:8080
exposed-agents-config: classpath:agents/a2a-exposed-agents.yamlExposed-agent config is separate from agents.yaml. It maps public A2A identities to local plans:
agents:
- agentId: support-ticket-service
name: Support Ticket Service
defaultAgent: true
planName: ticketing
planVersion: v1Inbound endpoints:
POST /a2a/rpcGET /a2a/sse/{taskId}GET /.well-known/agent-card.json
Outbound behavior:
- blueprint tools can target
a2a:endpoints - runtime persists remote task/conversation correlation
- audit trail records outbound/inbound A2A transitions
Shared infrastructure behavior:
- agent-side A2A classes stay in
camel-agent-core - generic task/session/event persistence comes from
camel-a2a-component - if
a2aTaskService,a2aTaskEventService, anda2aPushConfigServiceare already bound, Camel Agent reuses them instead of creating a private task space - this allows multiple agent flows and non-agent routes to share the same A2A session/task runtime
Default mode is redis_jdbc (Redis fast path + JDBC source-of-truth behavior inherited from camel-persistence).
Implemented support:
DscopeChatMemoryRepositorypersists Spring AI chat messages todscope-camel-persistence.SpringAiMessageSerdehandles serialization/deserialization forUSER,SYSTEM,ASSISTANT(including tool calls), andTOOLmessages.- Starter auto-config creates:
ChatMemoryRepositorybacked by DScope persistenceMessageWindowChatMemorywith configurable window
Starter properties:
agent.chat-memory-enabled(default:true)agent.chat-memory-window(default:100)
Set agent.audit-granularity (default: info) to control persistence verbosity:
none: persist no audit events.info: persist process steps only.error: persist process steps and include error payload data for error events.debug: persist process steps with full payloads and metadata.
By default, audit trail uses the same persistence store as context/state.
To store audit trail in a separate JDBC backend, configure either:
agent:
audit:
backend: jdbc
jdbc:
url: jdbc:postgresql://localhost:5432/agent_audit
username: agent_audit_user
password: ${AGENT_AUDIT_DB_PASSWORD}
driver-class-name: org.postgresql.DriverOr namespaced equivalent:
agent:
audit:
persistence:
backend: jdbc
jdbc:
url: jdbc:postgresql://localhost:5432/agent_auditDistributed task ownership is implemented via persistence-backed lease locks (flowType=agent.task.lock):
tryClaimTask(taskId, ownerId, leaseSeconds)uses optimistic append semantics.resumeTask(taskId)claims lease before processing and releases after completion.- Conflicting resume attempts return a lock message instead of double-processing.
Starter properties:
agent.task-claim-owner-id(optional; defaults to generated node id)agent.task-claim-lease-seconds(default:120)
mvn -f samples/agent-support-service/pom.xml -DskipTests compile exec:javaOr run with local hidden secrets file (samples/agent-support-service/.agent-secrets.properties):
samples/agent-support-service/run-sample.shSee sample-specific usage and test guidance in:
samples/agent-support-service/README.md
For local no-key A2A demo runs, the sample also includes:
io.dscope.camel.agent.samples.DemoA2ATicketGateway
Use it to simulate support-agent -> A2A ticket-service -> local ticket route behavior without a live model backend:
./mvnw -q -f samples/agent-support-service/pom.xml \
-Dagent.runtime.spring-ai.gateway-class=io.dscope.camel.agent.samples.DemoA2ATicketGateway \
-Dagent.runtime.routes-include-pattern=classpath:routes/kb-search.camel.yaml,classpath:routes/kb-search-json.camel.xml,classpath:routes/ticket-service.camel.yaml,classpath:routes/ag-ui-platform.camel.yaml,classpath:routes/admin-platform.camel.yaml \
exec:javasamples/agent-support-service/src/main/resources/application.yaml configures runtime provider routing:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com
agent:
runtime:
ai:
mode: spring-ai
spring-ai:
provider: openai # openai | gemini | claude
api-mode: ${AGENT_OPENAI_API_MODE:chat}
model: gpt-5.4
temperature: 0.2
max-tokens: 800
openai:
# Supported values:
# - chat (default): Spring AI OpenAI Chat Completions
# - responses-http: reserved (not yet implemented)
# - responses-ws: delegated to pluggable OpenAiResponsesGateway implementation
responses-ws:
endpoint-uri: wss://api.openai.com/v1/responses
model: gpt-5.4Notes:
- OpenAI in this gateway uses Spring AI OpenAI Chat client (
chatmode). responses-httpis a planned mode and currently returns a terminal guidance message.responses-wsis routed through a pluggableOpenAiResponsesGateway; if no plugin is wired, the gateway returns a terminal guidance message.- The sample runtime also enables A2A by default through
agent.runtime.a2a.enabled=trueand exposes the ticket service throughsupport.ticket.manage. - Gemini uses Spring AI Vertex Gemini client and requires:
agent.runtime.spring-ai.gemini.vertex.project-idagent.runtime.spring-ai.gemini.vertex.location
- Claude uses Spring AI Anthropic client (
/v1/messages).
Before running the sample with external providers, verify network and credentials:
nc -vz api.openai.com 443
curl -I https://api.openai.com/v1/models
curl https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"401from OpenAI endpoints usually means missing/invalid API key in the current shell.421with the OpenAI welcome body indicates endpoint/path mismatch (request hitting API root instead of chat endpoint).- If runtime behavior looks stale after code changes, rebuild/install local snapshots from the repo root.
- If tests are unexpectedly skipped, verify
maven.test.skipis not enabled in environment/system properties.
For detailed sample troubleshooting and commands, see:
samples/agent-support-service/README.md
Spring Framework dependency remediation was applied for known spring-core vulnerabilities (including annotation detection issues reported against older releases).
What was changed:
- centralized Spring Framework version management via
spring.framework.version - pinned
org.springframework:spring-corein rootdependencyManagement - updated
camel-agent-spring-aito consume managed Spring version
Current resolved baseline:
org.springframework:spring-core:6.1.21
Verification commands:
mvn -U -f camel-agent-spring-ai/pom.xml -DskipTests dependency:tree -Dincludes=org.springframework:spring-core
mvn -U -f camel-agent-starter/pom.xml -DskipTests dependency:tree -Dincludes=org.springframework:spring-core
mvn -U -f pom.xml verifySample integration tests verify route selection and context carry-over behavior:
- first prompt asks for knowledge base help, route/tool selected:
kb.search - second prompt asks to file a ticket, route/tool selected:
support.ticket.manage - second-turn LLM evaluation context includes first-turn KB result
- negative case: direct ticket prompt without prior KB turn does not inject KB context
Run:
mvn -f samples/agent-support-service/pom.xml -Dtest=SpringAiAuditTrailIntegrationTest testagent.md now supports a jsonRouteTemplates section for safe dynamic route generation.
Runtime behavior:
- templates are parsed from blueprint YAML blocks
- each template is exposed as a callable tool (via
toolName) - LLM returns only template parameters
- runtime expands placeholders into Camel JSON DSL, validates it, dynamically loads route, and executes it
- dynamic route metadata is persisted through existing
DynamicRouteStatepersistence
See sample blueprint template in:
samples/agent-support-service/src/main/resources/agents/support/agent.md
Blueprint tools entries with endpointUri starting with mcp: are treated as MCP service definitions.
Runtime behavior:
- on agent startup, runtime calls MCP
tools/listfor each configuredmcp:endpoint - discovered MCP tools are merged into the runtime tool registry used for LLM evaluation
- MCP tool execution uses MCP
tools/callwith{ name, arguments } - MCP discovery payload is written to audit as
mcp.tools.discovered(full payload available indebuggranularity)
Example seed tool in agent.md:
tools:
- name: support.mcp
description: MCP support backend
endpointUri: mcp:http://localhost:3001/mcpsamples/agent-support-service uses AGUI component runtime routes and a built-in UI page:
- open
http://localhost:8080/agui/ui - frontend supports AGUI text transport switch:
POST /agui/agent(POST+SSE)WS /agui/rpc(AGUI over WebSocket)
- backend responds with AGUI events for the selected transport; frontend renders assistant output from AGUI message content events
/agui/stream/{runId}is available for split-transport clients
For run commands and endpoint examples, see samples/agent-support-service/README.md.
Realtime note:
POST /realtime/session/{conversationId}/eventsupports route-driven session-context updates aftertranscript.finalrouting.POST /realtime/session/{conversationId}/initseeds pre-conversation agent context from blueprint metadata before the first user turn.- Agent/tool routes can return a patch via exchange header/property (
realtimeSessionUpdate, aliases:realtime.session.update,sessionUpdate) or assistant JSON body (realtimeSessionUpdate,realtimeSession,sessionUpdate). - Patch is deep-merged into browser session context for the same
conversationId; next/realtime/session/{conversationId}/tokenuses merged context.
Seeded pre-conversation context fields (sample runtime):
session.metadata.camelAgent.agentProfile.namesession.metadata.camelAgent.agentProfile.versionsession.metadata.camelAgent.agentProfile.purposesession.metadata.camelAgent.agentProfile.tools[]session.metadata.camelAgent.context.agentPurposesession.metadata.camelAgent.context.agentFocusHint
Purpose length configuration:
agent.runtime.realtime.agent-profile-purpose-max-chars(aliases:agent.runtime.realtime.agentProfilePurposeMaxChars,agent.realtime.agent-profile-purpose-max-chars,agent.realtime.agentProfilePurposeMaxChars)- default:
240 - set
0(or negative) to disable truncation and keep full seeded purpose text.
Voice UX and transcript updates (sample frontend):
- Single dynamic voice toggle button (
Start Voice/Stop Voice) with idle/live/busy states. - Runtime-selectable pause profiles for VAD (
Fast=800ms,Normal=1200ms,Patient=1800ms) applied to relay and WebRTC session config. - Pause milliseconds shown in both listening status text and the pause label (
Pause (<ms>ms)). - Mobile behavior uses icon-only voice button while preserving dynamic
titleandaria-labeltext. - WebRTC transcript log panel includes input/output transcript lines and clear-log action.
- Voice output transcript de-duplication ensures one final assistant transcript entry per completed output (with spacing preserved).
- Collapsible
Instruction seed (debug)panel shows the current pre-conversation instruction context; it auto-opens when transport is switched to WebRTC (and on initial load when already in WebRTC mode).
The kernel supports control messages for orchestration testing:
task.async <checkpoint>: creates a persisted task inWAITINGstate.route.instantiate <templateId>: creates a dynamic route lifecycle record (CREATED -> STARTED).AgentKernel.resumeTask(taskId): resumes and completes waiting tasks (RESUMED -> FINISHED).
