Let the assistant propose what to remember, and the operator decide what is kept
This commit is contained in:
@@ -96,6 +96,7 @@ if (PHP_SAPI !== 'cli') {
|
||||
}
|
||||
|
||||
require_once dirname(__DIR__) . '/include/ai.php';
|
||||
require_once dirname(__DIR__) . '/include/ai_memory_learn.php';
|
||||
|
||||
// ── explain mode ─────────────────────────────────────────────────────────────────────────────
|
||||
// Answers "what would this question be given, and why" without asking the model anything. Every
|
||||
@@ -761,6 +762,22 @@ if (trim($mem['assisted']) !== '') {
|
||||
. trim($mem['assisted']) . "\n\n";
|
||||
}
|
||||
|
||||
// Asking for the candidate inside the same call rather than making a second one. A follow-up
|
||||
// "was anything here worth remembering" would cost another 25-75s on every question to answer
|
||||
// "no" most of the time. The marker is stripped from the answer before it is shown, so the
|
||||
// mechanism never appears in the transcript.
|
||||
if (vv_ai_mem_learn_enabled()) {
|
||||
$system .= "REMEMBERING SOMETHING\n"
|
||||
. "If this exchange established a durable, non-obvious fact about THIS installation — "
|
||||
. "a hardware quirk, a deliberate setting, something the operator corrected you on — "
|
||||
. "then after your answer, on its own final line, write:\n"
|
||||
. "MEMORY: <one short sentence>\n"
|
||||
. "Rules: one line, under 200 characters, stated as fact with no hedging. Not a "
|
||||
. "summary of your answer, not a restatement of the question, not anything already "
|
||||
. "written above in what you know. Most exchanges warrant nothing — when in doubt, "
|
||||
. "leave the line out entirely.\n\n";
|
||||
}
|
||||
|
||||
if (trim($mem['learned']) !== '') {
|
||||
$system .= "NOTES YOU WROTE EARLIER, KEPT BY THE OPERATOR\n"
|
||||
. "These were proposed by you on previous turns and approved for keeping. They are "
|
||||
@@ -932,6 +949,27 @@ fclose($fh);
|
||||
$answer = trim($answer);
|
||||
$thinking = trim($thinking);
|
||||
|
||||
// Pull the candidate out before anything else looks at the answer — the code scan, the transcript
|
||||
// and the token ledger all see the text without it. Matched only at the very end, so a MEMORY:
|
||||
// mentioned mid-answer while explaining this feature is not mistaken for one being filed.
|
||||
if (vv_ai_mem_learn_enabled() && $answer !== '') {
|
||||
if (preg_match('/\n[ \t]*MEMORY:[ \t]*(.+?)[ \t]*$/s', "\n" . $answer, $mm)) {
|
||||
$candidate = trim(preg_replace('/\s+/', ' ', $mm[1]));
|
||||
$answer = trim(preg_replace('/\n[ \t]*MEMORY:[ \t]*.+?[ \t]*$/s', '', "\n" . $answer));
|
||||
|
||||
if ($candidate !== '') {
|
||||
$r = vv_ai_mem_propose($candidate, [
|
||||
'profile' => $profile,
|
||||
'asked' => $question,
|
||||
]);
|
||||
wlog(sprintf('memory candidate %s: %s',
|
||||
$r['ok'] ? ($r['state'] === 'accepted' ? 'auto-accepted' : 'filed')
|
||||
: ('rejected (' . ($r['error'] ?? '?') . ')'),
|
||||
mb_substr($candidate, 0, 80)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($answer === '') {
|
||||
jw($jobFile, ['status' => 'error',
|
||||
'error' => 'The model returned no answer' . ($thinking !== '' ? ' (only reasoning)' : ''),
|
||||
|
||||
Reference in New Issue
Block a user