Online Search
AI4J's current online search is not a general-purpose retrieval framework, nor does it automatically feed web crawl results into the Retriever system.
Seen from the source, it is actually something more specific:
A prompt augmentation layer that wraps IChatService.
If this is not spelled out clearly, it is most easily conflated with offline RAG, MCP tools, and function calling.
1. Where the source entry point is
The current core entry points are quite explicit:
websearch/ChatWithWebSearchEnhance.javawebsearch/searxng/SearXNGConfig.javawebsearch/searxng/SearXNGRequest.javawebsearch/searxng/SearXNGResponse.java
The main class definition is:
public class ChatWithWebSearchEnhance implements IChatService
This signature already states its architectural positioning:
- It is not a
Retriever - It is not a
RagService - It is not a tool executor
- It is a wrapper around
IChatService
2. How it actually works
The current implementation is very direct.
Whichever path you take:
chatCompletion(...)chatCompletionStream(...)
it first calls:
addWebSearchResults(chatCompletion)
and then hands the modified ChatCompletion to the underlying chatService.
The logic of addWebSearchResults(...) is:
- Take the text content of the last message
- Use this text as the query to perform an online search
- Serialize the search results into JSON text
- Rewrite the content of the last user message
- Inject into the new prompt:
- A fixed Chinese instruction
- A "web references" block
- A "user question" block
So the essence of Online Search in current AI4J is not a three-stage "query + retrieval + grounding" pipeline, but rather:
Search first, then splice the search results directly into the last user prompt.
3. Why it is not traditional RAG
It differs significantly from offline RAG.
Offline RAG typically goes through:
- ingest
- chunk
- embed
- retrieve
- rerank
- assemble context
Whereas ChatWithWebSearchEnhance has none of these layers:
- No
RagQuery - No
RagHit - No
Retriever - No
Reranker - No
RagTrace
It only does two things:
- Call an external search API
- Rewrite the last text message in the chat request
So the more accurate positioning of this layer is:
Freshness augmentation, not knowledge-base RAG.
4. What role SearXNG plays in this chain
The current online search directly depends on SearXNG.
performWebSearch(query) will:
- Read
SearXNGConfigfromConfiguration - Validate that
searXNGConfig.getUrl()is non-null - Send a GET request with
OkHttpClient - Parse the JSON into
SearXNGResponse - Truncate the number of results per
searXNGConfig.getNums() - Then serialize the results as a whole via
JSON.toJSONString(...)
There is an important engineering fact here:
In the current implementation, search results do not enter the model context in a structured way; instead, they are first serialized wholesale into a JSON string and then spliced into the prompt.
In other words, what the model ultimately sees is "a text prompt with JSON search results embedded", not a set of strongly-typed document objects.
5. Why it is designed as an IChatService wrapper layer
Seen from the code, the core advantage of this design is low intrusiveness:
- No need to modify the provider SDK main flow
- No need to introduce a new tool protocol
- Both synchronous and streaming chat can reuse it
- To upstream callers, it is still an
IChatService
This makes it well suited as an augmentation layer for "quickly wiring online search into a conversation".
But this design also brings natural boundaries:
- It can only augment the chat path
- It does not naturally port to
Responses-style event streams - It cannot retain an independent retrieval-hit structure like RAG
- It does not produce intermediate state like
RagTrace
6. Three default behaviors most worth noting in the current implementation
6.1 It directly rewrites the original ChatCompletion
addWebSearchResults(...) does not copy the request; it directly modifies the content of the last message.
This means:
- If the caller later reuses the same
ChatCompletionobject - What it sees is already the "augmented prompt"
This is a very typical wrapper side effect, and must be spelled out clearly when documenting.
6.2 It only looks at the last message
The code directly takes:
chatCompletion.getMessages().get(chatLen - 1)
So the current semantics are:
- Only the last message text is used as the search query
- It does not synthesize the whole conversation to do search query rewrite
If your multi-turn conversation needs to "search with context", this default layer does not do that for you.
6.3 By default it injects a fixed Chinese instruction
This instruction explicitly requires the model to:
- Answer based on the web references and the user question
- Use Markdown
- List references at the end of the answer
- When references are insufficient, supplement with its own knowledge or state uncertainty
This shows that online search is currently not only data augmentation, but also prompt policy injection.
7. What the current failure path looks like
Failures in performWebSearch(...) are distinguished by type:
- If no
SearXNG urlis configured, it throwsCommonExceptiondirectly - If the upstream returns non-2xx, it throws the typed exception decoded by
HttpErrorDecoder(AiAuthException/AiRateLimitException/AiServerErrorException/AiClientException), carrying the original error message returned by the upstream, with the status code readable viagetStatusCode() - On parsing or network exceptions, it throws
CommonException, with the message including the underlying exception'sgetMessage()
Therefore, when troubleshooting, you can distinguish directly:
try {
String results = enhance.performWebSearch(query);
} catch (AiRateLimitException e) {
// Upstream rate-limited; can back off and retry
} catch (AiAuthException e) {
// Credential issue
} catch (AiHttpException e) {
// Other upstream HTTP errors; e.getStatusCode() + e.getMessage() already contains the upstream original
} catch (CommonException e) {
// Missing config, or network/parsing failure
}
In v2.4.2 and earlier, all of the above cases are flattened into the same CommonException("SearXNG request failed") and cannot be distinguished. See issue #228.
8. The most real safety and quality boundaries of this layer
Because it is essentially injecting web search results into the prompt as-is, you must be very clear about the differences between it and an offline knowledge base:
- Results are fresh, but stability is weak
- Data is open, but noise and prompt injection risk are higher
- No chunk-level structure control
- No independent rerank/trace mechanism
In other words, Online Search is better suited for:
- "What is today's news"
- "What changed in the latest version of some library"
- "Supplement with the latest public online material"
It is not suited to directly replace:
- Enterprise internal knowledge bases
- Strictly auditable citation systems
- Production RAG that needs stable replay
9. What is the most stable way to collaborate with offline RAG
If this layer is placed into a more complete system, the most stable division of roles is usually:
- Offline RAG handles stable, structured, traceable internal knowledge
- Online Search handles time-sensitive open-web supplementation
Do not invert this and let online search serve as the primary knowledge source with the offline library as a garnish. Because, seen from the current implementation, the structured governance capability of online search is clearly weaker than that of offline RAG.
10. Five easiest pitfalls
10.1 Describing it as "AI4J's general-purpose search subsystem"
The current implementation is only an IChatService wrapper, not a unified search framework.
10.2 Assuming it automatically enters the Retriever/Reranker chain
It does not. It is not wired into the RagService main line.
10.3 Ignoring that the request is rewritten in place
If the same ChatCompletion object is reused, this side effect propagates directly.
10.4 Treating the last message as the full conversation intent
The current search query comes only from the last message text; multi-turn semantics may be lost.
10.5 Assuming "listing references" equals a hard citation constraint
Currently this is only a prompt-level requirement, not a hard-constraint citation system.
11. The conclusion most worth remembering from this page
AI4J's current Online Search is essentially an IChatService-level online search augmentation wrapper:
- It uses the last message as the query
- Calls SearXNG to pull public web results
- Splices the result JSON directly into the user prompt
- Then hands it to the underlying chat service
What it solves is "giving a conversation online-supplementation capability", not "replacing the structured retrieval chain of offline RAG".