From 661f7a370ce5a126ef0110e57ef4c9dfd5e305d9 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Mon, 3 Aug 2026 17:06:08 +0200 Subject: [PATCH] CAMEL-24330: Improve AI component documentation based on user feedback Co-Authored-By: Claude Opus 4.6 Signed-off-by: Claus Ibsen --- .../main/docs/langchain4j-chat-component.adoc | 2 + .../src/main/docs/openai-component.adoc | 167 ++++++++++++++++++ .../camel-ai/src/main/docs/ai-summary.adoc | 47 +++++ 3 files changed, 216 insertions(+) diff --git a/components/camel-ai/camel-langchain4j-chat/src/main/docs/langchain4j-chat-component.adoc b/components/camel-ai/camel-langchain4j-chat/src/main/docs/langchain4j-chat-component.adoc index 50f9812ae7bb2..9ef1978ab8138 100644 --- a/components/camel-ai/camel-langchain4j-chat/src/main/docs/langchain4j-chat-component.adoc +++ b/components/camel-ai/camel-langchain4j-chat/src/main/docs/langchain4j-chat-component.adoc @@ -29,6 +29,8 @@ for this component: ---- +TIP: For a comparison of `camel-langchain4j-chat` vs `camel-openai` and guidance on which to choose, see xref:others:ai-summary.adoc[Choosing the Right AI Component]. If you need streaming responses, structured output (`outputClass` / `jsonSchema`), or MCP tool calling, see the xref:openai-component.adoc[OpenAI] component. + == URI format ---- diff --git a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc index d95cf54ce1392..928fbb335d930 100644 --- a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc +++ b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc @@ -1008,6 +1008,173 @@ String-valued fields are set directly. Non-string fields (numbers, booleans, obj NOTE: This maps fields from the response message's additional properties (fields not part of the standard schema). Standard response fields like `content`, `role`, and `tool_calls` are not accessible through this option. +== Tips for Production Use + +This section covers practical advice for using the OpenAI component in production routes, based on common patterns and pitfalls. + +=== Temperature and Model Parameters + +The `temperature` endpoint option controls how deterministic the model's output is. Lower values produce more consistent, predictable responses; higher values produce more varied, creative output. + +For structured extraction tasks (JSON parsing, data extraction, classification), use a low temperature such as `0.1` to reduce the chance of the model adding unexpected commentary or formatting around the structured output: + +[tabs] +==== +Java:: ++ +[source,java] +---- +from("direct:extract-skills") + .to("openai:chat-completion?temperature=0.1&outputClass=com.example.SkillList") + .log("Extracted: ${body}"); +---- + +YAML:: ++ +[source,yaml] +---- +- route: + from: + uri: direct:extract-skills + steps: + - to: + uri: openai:chat-completion + parameters: + temperature: 0.1 + outputClass: com.example.SkillList + - log: + message: "Extracted: ${body}" +---- +==== + +Temperature can also be set per-exchange via the `CamelOpenAITemperature` header. Other tuning options include `topP` (nucleus sampling) and `maxTokens` (response length limit). + +=== Dynamic Prompts + +The `CamelOpenAIUserMessage` header is evaluated per-exchange, so you can construct prompts dynamically using Camel's Simple language or any other expression: + +[tabs] +==== +Java:: ++ +[source,java] +---- +from("direct:summarize") + .setHeader("CamelOpenAIUserMessage", + simple("Summarize this ${header.documentType} in 3 bullet points: ${body}")) + .to("openai:chat-completion") + .log("Summary: ${body}"); +---- + +YAML:: ++ +[source,yaml] +---- +- route: + from: + uri: direct:summarize + steps: + - setHeader: + name: CamelOpenAIUserMessage + simple: "Summarize this ${header.documentType} in 3 bullet points: ${body}" + - to: + uri: openai:chat-completion + - log: + message: "Summary: ${body}" +---- +==== + +This is useful when chaining multiple enrichment steps where each step needs a different instruction based on the current exchange state. + +=== Use Structured Output for JSON Extraction + +When you need the model to return structured data (JSON objects, arrays, typed fields), prefer the built-in `outputClass` or `jsonSchema` options over manually parsing the model's text output. These options instruct the model to produce valid JSON matching your schema, which significantly reduces parsing failures: + +[source,java] +---- +// Recommended — structured output handles JSON formatting: +from("direct:extract") + .to("openai:chat-completion?outputClass=com.example.Skills") + .log("${body}"); + +// Avoid — manual JSON parsing is fragile: +from("direct:extract") + .setHeader("CamelOpenAIUserMessage", + constant("Extract skills as a JSON array. Respond with valid JSON only.")) + .to("openai:chat-completion") + .process(exchange -> { + // This breaks when the model adds commentary around the JSON + String json = exchange.getIn().getBody(String.class); + List skills = objectMapper.readValue(json, new TypeReference<>() {}); + exchange.getIn().setBody(skills); + }); +---- + +See the <> and <> sections above for full examples. For additional validation, pipe the response through the `camel-json-validator` component. + +=== Handling Model Output Errors + +A successful HTTP 200 response from the model does not guarantee the content is usable. The model may ignore formatting instructions, return truncated output, or produce content that does not match your expected shape. These are not Camel exceptions — they appear as downstream parsing failures in your own processors. + +For production routes, add a lightweight validation step after the model call: + +[source,java] +---- +from("direct:extract") + .to("openai:chat-completion?outputClass=com.example.Result") + .process(exchange -> { + String response = exchange.getIn().getBody(String.class); + if (response == null || response.isBlank()) { + throw new IllegalStateException("Empty model response"); + } + }) + .to("direct:downstream"); +---- + +Using `outputClass` or `jsonSchema` already reduces this risk substantially by constraining the model's output format at the API level. + +=== Prompt Management + +When your project grows beyond a few routes, keeping prompt strings inline in route definitions becomes hard to maintain. Consider these approaches: + +*Load prompts from resource files:* + +[source,java] +---- +from("direct:analyze") + .setHeader("CamelOpenAISystemMessage", + constant("resource:classpath:prompts/system-analyst.txt")) + .to("openai:chat-completion"); +---- + +*Use Camel property placeholders for reusable fragments:* + +[source,properties] +---- +# application.properties +prompt.system.analyst=You are a technical analyst. Be concise and factual. +prompt.output.json=Respond with valid JSON only, no commentary. +---- + +[source,java] +---- +from("direct:analyze") + .setHeader("CamelOpenAISystemMessage", constant("{{prompt.system.analyst}}")) + .setHeader("CamelOpenAIUserMessage", + simple("{{prompt.output.json}} Analyze: ${body}")) + .to("openai:chat-completion"); +---- + +TIP: For prompt templates with named variables (e.g., `{{dishType}}`), the xref:langchain4j-chat-component.adoc[LangChain4j Chat] component offers built-in template support via the `CHAT_SINGLE_MESSAGE_WITH_PROMPT` operation. + +=== Streaming Considerations + +The `streaming=true` option returns an `Iterator` that can be consumed with Camel's Split EIP (see the <> section above). This works well for pipeline-style processing where each chunk is handled as part of a longer integration flow. + +For user-facing scenarios that require Server-Sent Events (SSE) or WebSocket delivery to a browser, consider whether the Camel route is the right place to handle the streaming lifecycle. In some cases, a dedicated async handler with SSE transport provides tighter control over connection management and backpressure. Camel can still orchestrate the overall pipeline while delegating the streaming-to-client step to purpose-built infrastructure. + +NOTE: When MCP tools with `autoToolExecution` are active, streaming automatically falls back to non-streaming to allow the agentic tool-calling loop to function. See xref:others:openai-mcp.adoc[MCP Tool Calling] for details. + == Sub-Pages For more details on specific features, see: diff --git a/components/camel-ai/src/main/docs/ai-summary.adoc b/components/camel-ai/src/main/docs/ai-summary.adoc index 678447db0eba9..9ee37f057dcaf 100644 --- a/components/camel-ai/src/main/docs/ai-summary.adoc +++ b/components/camel-ai/src/main/docs/ai-summary.adoc @@ -4,6 +4,53 @@ The Camel AI components are a group of components for applying Apache Camel to various AI-related technologies. +== Choosing the Right AI Component + +Camel offers two main paths for integrating Large Language Models (LLMs) into routes: + +* **xref:openai-component.adoc[OpenAI]** — talks directly to OpenAI and any OpenAI-compatible API (OpenRouter, Ollama, vLLM, LM Studio). Native support for streaming, structured output (`outputClass` / `jsonSchema`), MCP tool calling, conversation memory, and the Responses API. Best when you are committed to the OpenAI ecosystem or using an OpenAI-compatible gateway. + +* **xref:langchain4j-chat-component.adoc[LangChain4j Chat]** — abstracts through https://github.com/langchain4j/langchain4j[LangChain4j] so you can switch LLM providers (OpenAI, Anthropic, Google Gemini, Mistral, Ollama, and others) by swapping a dependency. Also provides prompt templates with variables, RAG integration via the Content Enricher pattern, and multi-message conversation history. + +[cols="2,1,1"] +|=== +| Need | camel-openai | camel-langchain4j-chat + +| OpenAI or compatible API (OpenRouter, Ollama, vLLM) +| Yes +| Via LangChain4j provider + +| Switch providers without code changes +| No (OpenAI-compatible only) +| Yes + +| MCP tool calling / agentic loops +| Yes +| No (use xref:langchain4j-tools-component.adoc[langchain4j-tools] instead) + +| Streaming responses +| Yes +| Manual (via `StreamingChatLanguageModel`) + +| Structured output (JSON schema) +| Yes (`outputClass`, `jsonSchema`) +| No + +| Prompt templates with variables +| No (use Simple expressions) +| Yes (built-in `{{variable}}` syntax) + +| RAG pipelines +| Manual +| Yes (with `LangChain4jRagAggregatorStrategy`) + +| Embeddings +| Yes +| Via xref:langchain4j-embeddingstore-component.adoc[langchain4j-embeddingstore] +|=== + +TIP: If you already use an OpenAI-compatible API and want the richest feature set (streaming, MCP, structured output), start with `camel-openai`. If multi-provider flexibility is a hard requirement, use `camel-langchain4j-chat`. Both can coexist in the same project. + == {doctitle} components See the following for usage of each component: