Notice when the question is about their own machine

The other detector recognises named components, so its complement is unbounded;
this one reads grammar instead, which is not.
This commit is contained in:
Gmer4Lfe
2026-08-12 18:08:50 -04:00
parent a18f0cfc84
commit a04368e9fc
4 changed files with 76 additions and 1 deletions
+13
View File
@@ -1909,6 +1909,19 @@
AI_WEB_SEARCH_RESULTS=4 AI_WEB_SEARCH_RESULTS=4
AI_WEB_SEARCH_TIMEOUT=6 AI_WEB_SEARCH_TIMEOUT=6
# ━━━ AI Chat Caution ━━━
# General Chat cannot see this installation, and web search gives it confident-sounding material
# about the outside world. The danger is the overlap: a question about THIS machine, answered
# from a general page about Unraid, reads exactly like an answer about this machine.
#
# Detection is grammatical rather than topical — possessives, "this box", state questions, "what
# happened last night" — because the topics are unbounded and the grammar is not. When it fires,
# chat keeps its caution and defers instead of answering from the web.
#
# Pipe-separated extra phrases for what grammar misses: a nickname for the box, a share name,
# anything that in practice means "mine". Matched as literal text, not as patterns.
AI_CHAT_MY_SYSTEM_PHRASES=""
# ━━━ AI Conf Write Access ━━━ # ━━━ AI Conf Write Access ━━━
# Separate switch from AI_ENABLED, off by default, and an explicit key whitelist. Never paths, # Separate switch from AI_ENABLED, off by default, and an explicit key whitelist. Never paths,
# never credentials, never a container name. An empty whitelist means no writes regardless of # never credentials, never a container name. An empty whitelist means no writes regardless of
+1 -1
View File
@@ -855,7 +855,7 @@ if ($profile === 'chat' && vv_ai_chat_needs_varaverk($question)) {
// //
// Deterministic on both sides: this fires only when detection says there is nothing Varaverk in // Deterministic on both sides: this fires only when detection says there is nothing Varaverk in
// the question, so the cautious branch above keeps every question it would have caught. // the question, so the cautious branch above keeps every question it would have caught.
} elseif ($profile === 'chat' && $webHave) { } elseif ($profile === 'chat' && $webHave && !vv_ai_asks_about_this_system($question)) {
// Removes a false prohibition. It does NOT classify the question, and the first version of // Removes a false prohibition. It does NOT classify the question, and the first version of
// this did: it told the model that nothing named a Varaverk component so this was a question // this did: it told the model that nothing named a Varaverk component so this was a question
// about the wider world. That inference is not the detector's to make. The detector finds // about the wider world. That inference is not the detector's to make. The detector finds
+52
View File
@@ -112,6 +112,58 @@ function vv_ai_chat_needs_varaverk(string $question): bool {
|| vv_ai_resolve_run_target($question) !== ''; || vv_ai_resolve_run_target($question) !== '';
} }
// Is the operator asking about THEIR OWN machine, without naming anything on it?
//
// A different axis from vv_ai_chat_needs_varaverk(), and the reason that one cannot be extended
// to cover this. That detector recognises Varaverk-shaped tokens — a .sh file, a SCREAMING_CASE
// key, a run target — so its complement is every question that names none, which is unbounded.
// "why is my array so slow tonight", "how much space do I have left" and "is everything healthy"
// are all about this machine and contain nothing to detect. Enumerating that class topic by topic
// is endless; the operator referring to their own system is not, because it is grammar rather
// than subject matter. Possessives, this-machine deictics, state questions and questions about
// what happened here cover most of it in about a dozen patterns.
//
// Used to decide when NOT to relax the chat profile's caution. Wrong in the safe direction: a
// false positive costs a redirect to a profile that can actually see the machine, a false
// negative risks a general web page answered as though it described this installation.
function vv_ai_asks_about_this_system(string $question): bool {
$q = ' ' . mb_strtolower(trim($question)) . ' ';
$builtin = [
// Theirs, or the box in front of them.
'/\b(my|our|mine)\b/',
'/\bthis (box|server|machine|system|install(ation)?|setup|rig|host|array|pool|cache|node)\b/',
// State of something here. "is everything healthy", "are the containers up".
'/\b(is|are|was|were)\b[^.?!]{0,40}\b(health(y|ier)?|ok|okay|fine|up|down|running|stopped|'
. 'slow|fast|full|empty|broken|failing|stuck|degraded|offline|online)\b/',
// What happened here, and when.
'/\bwhat (happened|went wrong|is (going on|happening))\b/',
'/\b(last night|this morning|yesterday|today|tonight|right now|currently|at the moment)\b/',
// Capacity and load, which are only ever about the machine being asked from.
'/\bhow (much|many)\b[^.?!]{0,30}\b(space|room|ram|memory|disk|storage|free|left|used)\b/',
'/\bdo i have\b/',
// Ownership of running things.
'/\b(my|the) (containers?|dockers?|shares?|disks?|drives?|pools?|arrays?)\b/',
];
foreach ($builtin as $re) if (preg_match($re, $q)) return true;
// Operator-supplied phrases, for the ones grammar does not reach — a nickname for the box, a
// share name, whatever turns up in practice. Pipe-separated rather than a conf array because
// vv_conf_vars() reads scalars and hands back "(" for an array, and this needs to be readable
// at question time from a settings field the operator can edit.
//
// Matched as literal text, never as patterns. The field is editable from the settings UI, and
// a regex arriving from there would run against every question asked.
$extra = trim((string) (vv_conf_vars()['AI_CHAT_MY_SYSTEM_PHRASES'] ?? ''));
if ($extra !== '') {
foreach (explode('|', $extra) as $phrase) {
$phrase = mb_strtolower(trim($phrase));
if ($phrase !== '' && mb_strpos($q, $phrase) !== false) return true;
}
}
return false;
}
// Asking for a script to be written, as opposed to asking about one that exists. The verb is // Asking for a script to be written, as opposed to asking about one that exists. The verb is
// required: "what does arr_sync.sh do" names a script and wants documentation, "write me a script // required: "what does arr_sync.sh do" names a script and wants documentation, "write me a script
// that does X" names none and wants code. Getting that backwards in either direction is the whole // that does X" names none and wants code. Getting that backwards in either direction is the whole
+10
View File
@@ -434,6 +434,16 @@ Saved into `host1.conf`, which does not need to be opened by hand.
|---|---|---|---| |---|---|---|---|
| `HOST1_ZFS_REPORT_IGNORE_POOLS` | a list, one entry per line | in this section | — | | `HOST1_ZFS_REPORT_IGNORE_POOLS` | a list, one entry per line | in this section | — |
## AI Chat Caution
Route: AI tab → Settings → Configuration → *AI Chat Caution*
Saved into `master.conf`, which does not need to be opened by hand.
| Setting | Control | Where | What it does |
|---|---|---|---|
| `AI_CHAT_MY_SYSTEM_PHRASES` | a text box | in this section | Pipe-separated extra phrases for what grammar misses: a nickname for the box, a share name, anything that in practice means "mine". Matched as literal text, not as patterns. |
## AI Conf Write Access ## AI Conf Write Access
Route: AI tab → Settings → Configuration → *AI Conf Write Access* Route: AI tab → Settings → Configuration → *AI Conf Write Access*