[ 
https://issues.apache.org/jira/browse/CAMEL-23945?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Federico Mariani updated CAMEL-23945:
-------------------------------------
    Attachment: LangChain4jAgentFactoryConcurrencyTest.java

> camel-langchain4j-agent: producer race on shared agent field in agentFactory 
> mode (fix needed on camel-4.18.x)
> --------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-23945
>                 URL: https://issues.apache.org/jira/browse/CAMEL-23945
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-langchain4j-agent
>    Affects Versions: 4.15.0, 4.18.0, 4.20.0
>            Reporter: Federico Mariani
>            Priority: Major
>         Attachments: LangChain4jAgentFactoryConcurrencyTest.java
>
>
> On 4.15.0 through 4.20.x (and the current {{camel-4.18.x}} branch), 
> {{LangChain4jAgentProducer.process()}} writes the agent resolved by the 
> {{AgentFactory}} to a *shared instance field* on every exchange and then 
> re-reads that field twice:
> {code:java}
> if (agentFactory != null) {
>     agent = agentFactory.createAgent(exchange);   // field write per exchange
> }
> AiAgentBody<?> aiAgentBody = agent.processBody(messagePayload, exchange);  // 
> field re-read
> ...
> String response = agent.chat(aiAgentBody, toolProvider);                  // 
> field re-read again
> {code}
> A producer is a singleton per endpoint and {{process()}} must be thread-safe. 
> With two concurrent exchanges selecting different agents (e.g. a multi-agent 
> {{AgentFactory}} dispatching by header), one thread's write can land between 
> another thread's write and reads: the message is processed by the *wrong 
> agent* (wrong model, system prompt, guardrails and chat memory — the 
> conversation turn is persisted into the wrong agent's memory store). Because 
> there are two independent field reads, a single exchange can even 
> {{processBody}} with one agent and {{chat}} with another. The window is tiny 
> so it does not reproduce in demos, but under sustained concurrent load it is 
> statistically inevitable.
> *Status on main*: fixed incidentally by CAMEL-23697 (commit {{03d8361bbb74}}, 
> 4.21.0), which replaced the pattern with a local read: {{Agent agent = 
> agentFactory != null ? agentFactory.createAgent(exchange) : this.agent;}} — 
> so 4.21.0+ is not affected.
> *Requested fix*: backport the local-variable pattern (one line) to the 
> {{camel-4.18.x}} LTS branch. Introduced by CAMEL-22397 (commit 
> {{91ad52d80fb0}}, first released in 4.15.0).
> Attached reproducer {{LangChain4jAgentFactoryConcurrencyTest}} compiles 
> against *camel-4.18.x* (not main, where the API changed with the fix): it 
> makes the race deterministic by blocking agent A inside {{processBody()}} — 
> between the producer's field write and its {{chat()}} field re-read — while a 
> second exchange selects agent B. The exchange that asked for agent A is 
> answered by agent B.
> _This issue was drafted by Claude Code on behalf of Federico Mariani._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to