diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-31 18:36:13 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-31 18:36:13 +0200 |
| commit | 283fc9ae0371f4d318a539f021a4b88e05fac190 (patch) | |
| tree | 0bf03685df84c275cb7ab3af3e20d94781a078cc /CHANGELOG.md | |
| parent | 8bda554af191c5749459cd258c793e4826c3f9c7 (diff) | |
| download | llamachat-283fc9ae0371f4d318a539f021a4b88e05fac190.tar.gz llamachat-283fc9ae0371f4d318a539f021a4b88e05fac190.zip | |
feat: web search via SearXNG
The model is offered a web_search tool and decides when a question needs
current information. llamachat runs the query against SearXNG's JSON API,
feeds the results back, and the model answers from them. Off unless both
search_enabled and search_url are set, so an upgrade never starts talking
to the network on its own.
The loop lives in backend.stream_chat: a round ending in tool_calls is
searched for, the result appended, and the request re-sent. After
max_searches rounds the tool is withdrawn, which forces an answer rather
than letting an uncertain model search forever.
Searches show as a collapsible block above the thinking block, listing
each query and its sources. The queries are visible on purpose: when an
answer is wrong it is usually the query that was wrong, and without
seeing it a bad search and a bad answer look identical.
Two failures found while testing against the live stack shaped the
design:
The model has no clock, so it falls back on its training cutoff and
writes that year into the query itself ("latest kernel ... 2025"),
poisoning the results before they are fetched. The current date now goes
into the system prompt whenever search is on, with an instruction not to
date its own queries.
A SearXNG whose engines are all rate-limited or CAPTCHA'd returns a
valid response with zero results. Reporting that as "no results" tells
the model the web is empty and invites a confident answer from stale
training data, so a search where every engine failed is now an error
naming the engines.
Results are attacker-influenced text entering the model's context. Only
title, url and the snippet survive, snippets are truncated, result text
is escaped on display, result links are never fetched automatically, and
a reply forging the search block's URL scheme has it defused as the
reasoning scheme already was. None of that stops a poisoned snippet from
influencing the answer, which is why the sources stay visible.
Adds eight test groups, 23 to 31, all hermetic behind a fake HTTP layer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'CHANGELOG.md')
| -rw-r--r-- | CHANGELOG.md | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3542e..7b3aad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Web search through a SearXNG instance. The model is offered a `web_search` + tool and decides for itself when a question needs current information; + llamachat runs the query and feeds the results back. Off by default, and + enabled only when both `search_enabled` and `search_url` are set, so an + upgrade never starts talking to the network on its own. +- Searches appear in the transcript as a collapsible block above the + thinking block, listing each query and its sources, with a status line + while a search runs. The queries are visible because a bad answer is + usually a bad query, and showing the sources is what lets a poisoned + result be recognised as one. +- Six configuration keys: `search_enabled`, `search_url`, `search_results`, + `search_snippet_chars`, `search_timeout` and `max_searches`. Searches are + capped per turn, after which the tool is withdrawn and the model must + answer. +- Searches are stored per message in a new `searches` column, so reopening a + conversation still shows what was looked up. They are deliberately kept + out of the full-text index, or web text nobody wrote would compete with + the user's own messages. +- The current date is added to the system prompt when search is on. A model + has no clock and falls back on its training cutoff, which it then writes + into the query itself ("latest kernel ... 2025"), poisoning the results + before they are fetched. It is also told not to date its own queries. +- A search where every engine failed is now reported as a failure naming + the engines, instead of as a successful search that found nothing. Rate + limits and CAPTCHAs are the normal way a self-hosted SearXNG stops + working, and "no results" invited a confident answer from stale training + data. + +### Security + +- Search results are untrusted text entering the model's context. Only + `title`, `url` and the snippet survive, snippets are truncated, all result + text is escaped on display, result links are shown but never fetched + automatically, and a reply forging the search toggle's URL scheme has it + defused the same way the reasoning scheme already was. A poisoned snippet + can still influence what the model says; the sources are shown so that it + can be judged. + ## [0.2.1] - 2026-07-31 ### Fixed |
