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-twilio: Twilio-facing telephony adapter built on provider-neutral SIP/outbound-call contracts from core.camel-agent-starter: Spring Boot auto-configuration.samples/agent-support-service: runnable Camel Main support sample.
Core platform capabilities:
- Blueprint-driven agents defined in Markdown with tool schemas, realtime metadata, AGUI pre-run configuration, and JSON route templates.
- Camel-native tool execution through local routes, Kamelets, MCP-discovered tools, and A2A peer agents.
- Multi-plan catalog routing with versioned blueprints, sticky conversation plan selection, and legacy single-blueprint fallback.
- Persistence-backed conversations, task state, audit trail projection, and optional Spring AI chat-memory integration.
- Browser AGUI and realtime voice support with session init, transcript event handling, relay integration, and runtime refresh hooks.
Recent additions:
- Blueprint static resources can load Markdown, PDF, local file, classpath, and HTTP(S) content into chat and realtime instruction context.
- Route-driven agent session invocation now has a structured contract in core via
AgentSessionServiceandAgentSessionInvokeProcessor. - Realtime voice runtime now also supports SIP-style ingress routes, browser session seeding, and route-driven session context patches.
- Outbound support calling uses provider-neutral telephony contracts in core with a Twilio adapter module and sample support-call flow.
- 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 - Architecture:
docs/architecture.md - Development guide:
docs/DEVELOPMENT_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
- Blueprint system instructions plus fenced YAML sections for tools, realtime settings, AGUI pre-run behavior, and resource declarations.
- Tool backends spanning direct Camel routes, Kamelets, MCP tool discovery, A2A remote agents, and generated JSON route templates.
- Multi-plan routing with
agent.agents-config, per-requestplanNameandplanVersion, and persisted conversation plan selection. - Stateful runtime behavior covering conversation history, task state, audit events, archive streams, and optional Spring AI chat memory.
- UI and integration surfaces including AGUI, browser realtime, HTTP/SSE A2A endpoints, admin/runtime refresh endpoints, and SIP-style voice entrypoints.
Any Camel route can invoke the runtime through either the raw agent: contract or the structured session façade.
Raw endpoint contract:
agent.conversationIdis the durable backend session key.- omit
agent.conversationIdto create a new conversation. - reuse the same
agent.conversationIdto continue the conversation.
Minimal route form:
from("direct:agent-call")
.setBody(simple("${body[prompt]}"))
.setHeader("agent.conversationId", simple("${body[conversationId]}"))
.setHeader("agent.planName", simple("${body[planName]}"))
.setHeader("agent.planVersion", simple("${body[planVersion]}"))
.to("agent:support?plansConfig={{agent.agents-config}}&blueprint={{agent.blueprint}}");Structured session contract:
camel-agent-corenow providesAgentSessionRequest,AgentSessionResponse,AgentSessionService, andAgentSessionInvokeProcessor.conversationId,sessionId, andthreadIdare normalized so ordinary Camel routes can use either UI-style or backend-style identifiers.- arbitrary
paramsare preserved as exchange properties underagent.session.paramsand returned in the structured response.
For the canonical request/response payloads, see docs/PRODUCT_GUIDE.md and docs/DEVELOPMENT_GUIDE.md.
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
For shared-service behavior and task/session model details, see docs/architecture.md and docs/DEVELOPMENT_GUIDE.md.
Default persistence mode is redis_jdbc (Redis fast path + JDBC source-of-truth behavior inherited from camel-persistence).
Core persistence responsibilities include:
- conversation event history
- task snapshots
- dynamic route metadata
- persisted plan-selection state
- audit projection data
- optional conversation archive streams
Audit trail control:
- set
agent.audit-granularity(default:info) none: persist no audit eventsinfo: persist process steps onlyerror: persist process steps and include error payload data for error eventsdebug: 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_auditConversation archive note:
- archive persistence is transcript-focused and separate from the main audit stream
- use it for replay and transcript UX scenarios, not as the primary source of task or plan-selection state
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)
Distributed 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 docs/PRODUCT_GUIDE.md for field-level template structure and the sample blueprint for a concrete example.
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)
See docs/PRODUCT_GUIDE.md for the seed-tool shape and sample blueprint examples.
Execution-facing fields in agent.md can be tokenized and resolved at runtime.
Supported token forms:
{{key}}{{key:defaultValue}}${NAME}${NAME:defaultValue}
Typical use cases:
- environment-specific endpoint hosts
- secret tokens passed in endpoint query strings or headers-derived URIs
- runtime-selected
agent:endpoint targets for AGUI or route/session entrypoints
Example:
tools:
- name: support.mcp
endpointUri: mcp:https://{{agent.crm.host}}/mcp?token=${CRM_TOKEN}Critical execution-target fields now fail fast if a placeholder remains unresolved at runtime. See docs/PRODUCT_GUIDE.md for the exact field list and behavior.
The runtime already normalizes a small set of agent-scoped identifiers onto Camel headers and exchange properties. Use these values when you want agent.md or a route template to react to the current request instead of static configuration.
Common values:
agent.conversationId- canonical conversation key for the active agent runagent.agui.sessionId- UI/session correlation keyagent.agui.threadId- optional thread correlation keyagent.session.params- request params map passed toAgentSessionServiceagent.session.params.<key>- flattened request param, for exampleagent.session.params.customerIdcallerId/fromNumber- adapter-supplied telephony identity when a SIP or Twilio bridge populates it
For realtime SIP and Twilio handoffs, the runtime also copies telephony identity into agent.session.params.callerId and agent.session.params.fromNumber, and persists the same values into realtime audit events when they are present in session metadata.
Generic pattern:
- Put the value on the Camel exchange as a header or property in the route or processor that enters the agent.
- Read it in Camel DSL with
${header.someName}or${exchangeProperty.someName}. For the standard runtime values, use the exact header names such as${header.agent.conversationId}and${header.agent.agui.sessionId}. - If the value should survive the agent call, copy it into one of the standard headers above or into
agent.session.params.<key>.
Example:
steps:
- setHeader:
name: agent.conversationId
simple: ${header.conversationId}
- setProperty:
name: agent.session.params.callerId
simple: ${header.callerId}Blueprints can declare a resources section to stage static reference material into agent context.
Supported resource locations:
classpath:file:- plain file path
http:https:
Supported content handling:
- Markdown and plain text are injected as text.
- PDF documents are resolved through Camel PDF extraction.
- resources can target
chat,realtime, or both viaincludeIn.
Runtime behavior:
- resource text is appended to chat instructions during kernel construction.
- realtime session init seeds resource-backed instruction context for browser and SIP voice sessions.
- runtime refresh re-resolves blueprint resources and can push updated context to active conversations.
See docs/PRODUCT_GUIDE.md for the full resources[] schema and docs/DEVELOPMENT_GUIDE.md for implementation details.
samples/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.
For seeded session fields, realtime configuration properties, and sample endpoint details, see samples/agent-support-service/README.md and docs/PRODUCT_GUIDE.md.
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).
SIP and telephony note:
POST /sip/adapter/v1/session/{conversationId}/start,/turn, and/endreuse the same realtime processors and route agent flow.- outbound support calling uses provider-neutral
OutboundSipCallRequest/OutboundSipCallResult/SipProviderClientcontracts in core. - the sample uses
support.call.outboundplusSupportOutboundCallProcessorto place a provider call and return correlation data immediately. POST /telephony/onboarding/openai-twilioandGET /telephony/onboarding/openai-twiliogenerate and reload reusable onboarding plans backed by deterministic conversation ids of the formtelephony:onboarding:{tenantId}:{agentId}.GET /audit/conversation/sipreturns SIP-specific lifecycle and correlation data for both onboarding records and live call conversations.- recommended OpenAI voice topology is
Twilio Elastic SIP Trunk -> OpenAI SIP URI -> /openai/realtime/sip/webhook; the Java sample remains the webhook/orchestration runtime, not a SIP endpoint.
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).
