feat(vocale): interpretazione come analista inter-agent in JSON + guida Perplexity
L'interpretazione del prompt vocale ora usa un framing inter-agent: Gemini è un
analista tecnico/middleware che scrive un briefing per l'orchestratore (NON
risponde all'utente), in JSON strutturato.
- Nuovo prompt: ruolo analista, destinatario = solo l'agente orchestratore,
nessun testo rivolto all'utente, plan-first (nessuna esecuzione).
- Contratto JSON: {trascrizione_corretta, intent_analisi, note_per_agent,
azioni_raccomandate, prompt_utente_pulito, ricerca_necessaria, suggerimenti_ricerca}.
- extractVoiceBriefing(): estrae prompt_utente_pulito (-> finalText) e se
ricerca_necessaria=true aggiunge al prompt finale la nota di usare la ricerca
web (Perplexity) per best practices/versioni/documentazione.
- Testato: parser JSON (valido, con markdown attorno, fallback su testo grezzo).
- docs/vocal-workflow.md aggiornato.
This commit is contained in:
@@ -83,6 +83,27 @@ Con `vocalPlanningMode=true` (default) il flusso di interpretazione vocale è
|
||||
d'azione sintetico** (nessuna esecuzione). Questo evita che l'interpretazione
|
||||
avvii autonomamente loop agentici o modifiche a sorpresa a causa di errori di STT.
|
||||
|
||||
L'interpretazione usa un **framing inter-agent**: Gemini agisce come **analista
|
||||
tecnico/middleware** che scrive un briefing per l'orchestratore (NON risponde
|
||||
all'utente), in **JSON strutturato**:
|
||||
|
||||
```json
|
||||
{
|
||||
"trascrizione_corretta": "...",
|
||||
"intent_analisi": "...",
|
||||
"note_per_agent": "...",
|
||||
"azioni_raccomandate": ["..."],
|
||||
"prompt_utente_pulito": "...",
|
||||
"ricerca_necessaria": true/false,
|
||||
"suggerimenti_ricerca": ["..."]
|
||||
}
|
||||
```
|
||||
|
||||
Il campo `prompt_utente_pulito` diventa il prompt finale per l'orchestratore. Se
|
||||
`ricerca_necessaria=true`, al prompt finale viene aggiunta una **nota che
|
||||
indica all'agente successivo di usare la ricerca web (Perplexity)** per
|
||||
verificare best practices, versioni o documentazione aggiornate.
|
||||
|
||||
Poi un **overlay TUI** mostra trascrizione + piano e attende la conferma:
|
||||
|
||||
```
|
||||
|
||||
+49
-5
@@ -715,6 +715,31 @@ function stageExternalFiles(
|
||||
return { prompt: newPrompt, filePaths: stagedPaths, dirs: [] };
|
||||
}
|
||||
|
||||
// Estrae dal briefing JSON prodotto dall'interpretazione vocale i campi per
|
||||
// l'orchestratore: prompt pulito, se serve ricerca web e suggerimenti.
|
||||
function extractVoiceBriefing(text: string): {
|
||||
cleanPrompt: string;
|
||||
needsSearch: boolean;
|
||||
searchHints: string[];
|
||||
} {
|
||||
const result = { cleanPrompt: "", needsSearch: false, searchHints: [] as string[] };
|
||||
try {
|
||||
const start = text.indexOf("{");
|
||||
const end = text.lastIndexOf("}");
|
||||
if (start >= 0 && end > start) {
|
||||
const obj = JSON.parse(text.slice(start, end + 1));
|
||||
if (typeof obj.prompt_utente_pulito === "string") result.cleanPrompt = obj.prompt_utente_pulito.trim();
|
||||
result.needsSearch = String(obj.ricerca_necessaria).toLowerCase() === "true";
|
||||
if (Array.isArray(obj.suggerimenti_ricerca)) {
|
||||
result.searchHints = obj.suggerimenti_ricerca.map(String).filter(Boolean);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* JSON non parsato: si usa il testo grezzo come fallback */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function executeAgy(opts: AgyExecOptions) {
|
||||
// Staging dei file esterni (workaround accesso agy a file fuori dal workspace)
|
||||
const staged = stageExternalFiles(opts.prompt, opts.filePaths ?? []);
|
||||
@@ -2154,25 +2179,44 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
// e lo combina con la trascrizione audio (comportamento "multimodale").
|
||||
const editorText = (ctx.ui.getEditorText?.() ?? "").trim();
|
||||
|
||||
// Interpreta con Gemini (testo) — direttiva plan-first: solo piano, no esecuzione
|
||||
// Interpreta con Gemini (testo) — framing inter-agent: Gemini è un analista
|
||||
// che scrive un briefing per l'orchestratore (NON risponde all'utente),
|
||||
// in JSON strutturato, con direttiva plan-first (nessuna esecuzione).
|
||||
ctx.ui.notify("Interpretazione con Gemini...", "info");
|
||||
const context = getConversationContext(ctx);
|
||||
const res = await executeAgy({
|
||||
prompt:
|
||||
`Ecco la trascrizione di un messaggio vocale dell'utente:\n\n${transcript}` +
|
||||
`[CONTESTO INTERNO — COMUNICAZIONE TRA AGENTI]\n` +
|
||||
`Sei un analista tecnico/middleware per un agente AI orchestratore. NON rispondere all'utente: il tuo output sarà letto SOLO dall'orchestratore, che poi risponderà all'utente.` +
|
||||
`\n\nAnalizza la richiesta vocale (e l'eventuale testo dell'editor) e produci un briefing strutturato per l'orchestratore.` +
|
||||
`\n\nTrascrizione vocale:\n${transcript}` +
|
||||
(editorText
|
||||
? `\n\nTesto scritto dall'utente nel campo di input (da combinare con la voce):\n${editorText}`
|
||||
: "") +
|
||||
`\n\nContesto della conversazione:\n${context || "(nessuno)"}` +
|
||||
`\n\nProduce SOLO: 1) la trascrizione corretta, 2) un piano d'azione sintetico.` +
|
||||
`\nIMPORTANTE: NON eseguire alcuno strumento o azione. Attendi la conferma dell'utente.`,
|
||||
`\n\nRestituisci UN SOLO oggetto JSON (nessun testo aggiuntivo) con questi campi:` +
|
||||
`\n{"trascrizione_corretta":"...","intent_analisi":"...","note_per_agent":"...","azioni_raccomandate":["..."],"prompt_utente_pulito":"...","ricerca_necessaria":true/false,"suggerimenti_ricerca":["..."]}` +
|
||||
`\nRegole:` +
|
||||
`\n- NON eseguire alcuno strumento o azione; solo analisi e briefing.` +
|
||||
`\n- Nessun saluto o testo rivolto all'utente: solo JSON tecnico per l'orchestratore.` +
|
||||
`\n- prompt_utente_pulito = la richiesta rielaborata che l'orchestratore userà come prompt verso l'utente.` +
|
||||
`\n- ricerca_necessaria=true se serve verificare best practices, versioni, documentazione o dati aggiornati.` +
|
||||
`\n- suggerimenti_ricerca: se ricerca_necessaria, indica all'orchestratore di usare la ricerca web (Perplexity) e su cosa.`,
|
||||
stateless: true,
|
||||
model: "Gemini 3.6 Flash (Medium)",
|
||||
yolo: true,
|
||||
});
|
||||
ctx.ui.setStatus("agy-rec", "");
|
||||
|
||||
let finalText = res.text.trim() || transcript;
|
||||
// Estrae il briefing JSON; fallback sul testo grezzo se non parsato
|
||||
const briefing = extractVoiceBriefing(res.text);
|
||||
let finalText = briefing.cleanPrompt || res.text.trim() || transcript;
|
||||
|
||||
// Guida per l'agente successivo: usa la ricerca web/Perplexity se necessario
|
||||
if (briefing.needsSearch) {
|
||||
const hints = briefing.searchHints.length ? ` Suggerimenti: ${briefing.searchHints.join("; ")}` : "";
|
||||
finalText += `\n\n[Nota per l'agente: per rispondere correttamente, usa la ricerca web (Perplexity) per verificare best practices/versioni/documentazione aggiornate.${hints}]`;
|
||||
}
|
||||
|
||||
// Il testo dell'editor è stato consumato: lo svuota per evitare reinvii duplicati.
|
||||
if (editorText) {
|
||||
|
||||
Reference in New Issue
Block a user