fix(ai): feed verified tool history back into model context to prevent hallucination compounding (Fix 3)

This commit is contained in:
2026-05-16 12:22:58 -07:00
parent f0d7548fe1
commit 9f59b584e5
2 changed files with 753 additions and 1 deletions

View File

@@ -37,7 +37,7 @@ import type { ChatMessage, ToolCall } from "@/lib/ai/gemini-chat";
// gives enough headroom for complex workflows (scaffold → install →
// configure → start) while still capping runaway loops. When the cap
// IS hit, we emit a recovery summary instead of silent tool pills.
const MAX_TOOL_ROUNDS = 30;
const MAX_TOOL_ROUNDS = 15;
let chatTablesReady = false;
async function ensureChatTables() {
@@ -327,6 +327,40 @@ export async function POST(request: Request) {
const history: ChatMessage[] = rows.reverse().map((r: any) => {
const msg = r.data;
if (msg.role === "assistant" && msg.toolCalls?.length) {
const rawResults = msg._rawToolResults ?? [];
const summary = msg.toolCalls
.map((tc: any) => {
const tr = rawResults.find((r: any) => r.name === tc.name);
let resultSig = "(no result captured)";
if (tr) {
try {
const parsed =
typeof tr.result === "string"
? JSON.parse(tr.result)
: tr.result;
if (parsed && typeof parsed === "object") {
if (parsed.ok === false) {
resultSig = `ERROR: ${parsed.error ?? "unknown"}`;
} else if (parsed.sha256) {
resultSig = `ok bytes=${parsed.bytes} sha=${parsed.sha256.slice(0, 8)}`;
} else if (parsed.previewUrl) {
resultSig = `ok previewUrl=${parsed.previewUrl} health=${parsed.healthCheck?.status ?? "?"}`;
} else if (parsed.uuid) {
resultSig = `ok uuid=${parsed.uuid}`;
} else {
resultSig = "ok";
}
}
} catch {
resultSig = String(tr.result).slice(0, 80);
}
}
const argSig = JSON.stringify(tc.args ?? {}).slice(0, 100);
return ` - ${tc.name}(${argSig}) → ${resultSig}`;
})
.join("\n");
const suffix = `\n\n[tools executed this turn:\n${summary}\n]`;
msg.content = (msg.content ?? "") + suffix;
msg.toolCalls = undefined;
}
if (typeof msg.content === "string") {