mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 05:43:40 +02:00
Six P2 items from the AI Workbench audit: #7 Prompt ↔ loop budget sync: System prompt now says "1 bis 5 Schritte pro Planungsrunde, bis zu 5 Planungsrunden" — matches MAX_REASONING_LOOP_ITERATIONS. Cross-ref comment added to runner.ts. #9 SceneHeader: useAgents() → useAgent(id): Only loads the single bound agent instead of the full agent list. Eliminates unnecessary Dexie churn on every scene header render. #10 Unified scope filter: New scope-filter.ts with filterByScopeTagMap() (batch, sync) and filterByScopeAsync() (per-record). Both scope-context.ts (AI) and scene-scope.svelte.ts (UI) now import from the shared module — zero duplicated filter logic. #11 Research dedup: Research input ID changed from `news-research-${Date.now()}` to `news-research-${mission.id}` — re-runs overwrite instead of appending duplicates. #12 Kontext injection policy clarified: loadAgentKontextAsResolvedInput no longer falls back to the global singleton. Comment + code aligned: kontext injection is explicit (via input picker), not auto. Dead loadKontextAsResolvedInput kept for potential future opt-in auto-inject feature. Audit doc updated with all items marked DONE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
/**
|
|
* Prompt builder for the Mission Planner.
|
|
*
|
|
* Produces a system + user message pair. The grammar we ask the model to
|
|
* return is deliberately small (JSON in a fenced block, one shape) — the
|
|
* parser is strict, and we'd rather the LLM mess up in a detectable way
|
|
* than produce half-valid output.
|
|
*
|
|
* Pure function: no ambient state. Used identically from the browser and
|
|
* the mana-ai Bun service.
|
|
*/
|
|
|
|
import type { AiPlanInput } from './types';
|
|
|
|
export interface PlannerMessages {
|
|
readonly system: string;
|
|
readonly user: string;
|
|
}
|
|
|
|
export function buildPlannerPrompt(input: AiPlanInput): PlannerMessages {
|
|
return {
|
|
system: buildSystemPrompt(input),
|
|
user: buildUserPrompt(input),
|
|
};
|
|
}
|
|
|
|
function buildSystemPrompt(input: AiPlanInput): string {
|
|
const toolBlock = input.availableTools
|
|
.map((t) => {
|
|
const params = t.parameters
|
|
.map((p) => {
|
|
const req = p.required ? ' (required)' : '';
|
|
const enumeration = p.enum ? ` [${p.enum.join('|')}]` : '';
|
|
return ` - ${p.name}: ${p.type}${enumeration}${req} — ${p.description}`;
|
|
})
|
|
.join('\n');
|
|
return ` • ${t.name} (${t.module}) — ${t.description}\n${params || ' (no parameters)'}`;
|
|
})
|
|
.join('\n');
|
|
|
|
return `Du bist eine KI, die im Auftrag des Nutzers an einer langlebigen Mission arbeitet.
|
|
|
|
Dein Job: aus dem aktuellen Mission-Kontext einen konkreten Plan ableiten — 1 bis 5 Schritte pro Planungsrunde, jeder ein Tool-Aufruf auf Nutzerdaten. Es gibt bis zu 5 Planungsrunden pro Iteration. Jeder Schritt MUSS eine Begründung haben (rationale), die der Nutzer in der Review-UI sieht.
|
|
|
|
Wichtige Regeln:
|
|
1. Nutze NUR Tools aus der Liste unten. Unbekannte Tools → Plan invalide.
|
|
2. Read-only Tools (z.B. list_*, get_*) laufen automatisch — ihre Ausgabe siehst du in der nächsten Planungsrunde als "Zwischenergebnisse" und kannst dann darauf aufbauend schreibende Tools vorschlagen. Write-Tools (create_*, update_*, add_tag_to_note, etc.) werden dem Nutzer zur Approval vorgelegt.
|
|
3. Wenn ein Batch-Job ansteht (z.B. "tagge alle Notizen"), gib alle Einzel-Calls in EINEM plan zurück — du kriegst nach propose-Tools keinen weiteren Turn.
|
|
4. Berücksichtige das Feedback aus vorherigen Iterationen (unten im User-Prompt). Wenn ein Vorschlag rejected wurde, wiederhole ihn nicht ohne Änderung.
|
|
5. Antworte AUSSCHLIESSLICH mit einem JSON-Block in folgendem Format, keine Prosa davor/danach:
|
|
|
|
\`\`\`json
|
|
{
|
|
"summary": "Ein Satz was du in dieser Iteration tust.",
|
|
"steps": [
|
|
{
|
|
"summary": "Kurzer Schritt-Titel",
|
|
"toolName": "create_task",
|
|
"params": { "title": "…" },
|
|
"rationale": "Warum genau jetzt, auf Basis welchen Inputs."
|
|
}
|
|
]
|
|
}
|
|
\`\`\`
|
|
|
|
Verfügbare Tools:
|
|
${toolBlock || ' (keine Tools verfügbar — gib leeren steps zurück)'}`;
|
|
}
|
|
|
|
function buildUserPrompt(input: AiPlanInput): string {
|
|
const { mission, resolvedInputs } = input;
|
|
|
|
const inputsBlock =
|
|
resolvedInputs.length === 0
|
|
? '_(keine verlinkten Inputs)_'
|
|
: resolvedInputs
|
|
.map((r) => {
|
|
const header = `### ${r.module}/${r.table}: ${r.title ?? r.id}`;
|
|
return `${header}\n${r.content}`;
|
|
})
|
|
.join('\n\n');
|
|
|
|
const iterationHistory =
|
|
mission.iterations.length === 0
|
|
? '_(erste Iteration)_'
|
|
: mission.iterations
|
|
.slice(-3)
|
|
.map((it) => {
|
|
const steps = it.plan.map((s) => ` - [${s.status}] ${s.summary}`).join('\n');
|
|
const feedback = it.userFeedback ? `\n Nutzer-Feedback: ${it.userFeedback}` : '';
|
|
const summary = it.summary ? `\n Summary: ${it.summary}` : '';
|
|
return `**${it.startedAt}** (${it.overallStatus}):${summary}\n${steps}${feedback}`;
|
|
})
|
|
.join('\n\n');
|
|
|
|
return `# Mission: ${mission.title}
|
|
|
|
## Konzept
|
|
${mission.conceptMarkdown || '_(leer)_'}
|
|
|
|
## Konkretes Ziel
|
|
${mission.objective}
|
|
|
|
## Verlinkte Inputs
|
|
${inputsBlock}
|
|
|
|
## Letzte Iterationen (max. 3)
|
|
${iterationHistory}
|
|
|
|
---
|
|
|
|
Erzeuge jetzt einen Plan für die nächste Iteration.`;
|
|
}
|