In cui si costruisce un sistema vocale [55] - [56] to [56] - [56] form system che doesn [56] ' [56] hand the keys to the AI Kingdom
In questo caso, la cosa relativa alle interfacce vocali è che tutti li vogliono, nessuno si fida di loro e loro non si fidano di loro.
La maggior parte delle forme vocali falliscono nel momento in cui il modello mishears una data o decide di "Helpfully "Helpfully skip available .Hai detto che "Maggio MRSK4Maggio MRSK5M e gli estratti LLM "Maggio MRSK7Maggio MRSK7M e avanza al campo successivo prima di poterlo correggere .M o peggio :M ha allucinazioni di fiducia e auto-MsK10Invia la tua forma con dati errati .M
Nel momento in cui si lascia un LLM di controllo LLM" di controllo " di flusso di forma , di flusso ha introdotto una scatola nera non - di Deterministic in quello che dovrebbe essere un prevedibile user experience . di esperienza
Ma cosa succederebbe se potessimo avere la nostra torta e mangiarla troppo? Codice deterministico per tutto ciò che effettivamente conta
Che il sistema di forma vocale di T' è conforme ai comandamenti di T" utilizzando il server Blazor [Blazor], [Whisper] locale per il discorso - di T- [Text] di T, [Text] e Ollama locale per l'estrazione di campo . No cloud dipendency . No imprevedibile AI overlords . Just clean architecture .
Prima di scrivere un qualsiasi codice [49], [49] lascia che [54]' [56] stabilisce le regole [56]: [56]
In questo caso, l'' è il modo in cui questo appare nella pratica [49]: [56]
flowchart LR
A[User Speaks] --> B[Whisper STT]
B --> C[Raw Transcript]
C --> D[Ollama LLM]
D --> E[Extracted Value]
E --> F{Validation}
F -->|Pass| G{Policy Check}
F -->|Fail| H[Show Error]
G -->|High Confidence| I[Auto-Confirm]
G -->|Low Confidence| J[Ask User]
J --> K{User Confirms?}
K -->|Yes| L[State Machine: Next Field]
K -->|No| M[Retry Recording]
I --> L
H --> M
style D stroke:#f96,stroke-width:2px
style F stroke:#6f6,stroke-width:2px
style G stroke:#ff9,stroke-width:2px
Notate cosa fa il LLM [47]: [56] traduce [47]. [56] che [56]' [56] che [57] la macchina statale decide il flusso [54]. [56] che la convalida decide la correttezza [54]. [56] il controllo della politica non è il LLM [56]- [56] decide se auto- [56]- [56]confermare o chiedere all'utente [56]. [56]
Lasciare che i ' siano chiari sull'ambito di applicazione
Questi vincoli sono caratteristiche che non sono bug, ma che rendono il sistema prevedibile e verificabile.
We > ' > Costruire un Blazor Server app > . > Qui > ' > L'architettura > : >
Mostlylucid.VoiceForm/
├── Config/
│ └── VoiceFormConfig.cs # Configuration binding
├── Models/
│ ├── FormSchema/ # Form definitions
│ ├── State/ # Session state
│ └── Extraction/ # LLM input/output
├── Services/
│ ├── Stt/ # Speech-to-text (Whisper)
│ ├── Extraction/ # Field extraction (Ollama)
│ ├── Validation/ # Type-based validators
│ ├── StateMachine/ # Form flow control
│ └── Orchestration/ # Coordinates everything
├── Components/
│ └── Pages/ # Blazor UI
└── wwwroot/js/
└── audio-recorder.js # Web Audio API capture
Nota architettonica Nessun servizio dipende da un altro S"Scegliere verso il basso S."Scheda L'estrattore doesn S'Sheet conosce la macchina statale SH.Sheet Il validatore doesn'Sheet conosce l'orchestratore SH.Sheet Questo rende ogni pezzo testabile indipendentemente SH.S
Prima di tutto Whisper e Ollama che funzionano localmente Aggiungi questi al tuo devdeps-docker-compose.yml:
services:
whisper:
image: onerahmet/openai-whisper-asr-webservice:latest
container_name: whisper-stt
ports:
- "9000:9000"
environment:
- ASR_MODEL=base.en
- ASR_ENGINE=faster_whisper
volumes:
- whisper-models:/root/.cache/huggingface
restart: unless-stopped
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
restart: unless-stopped
volumes:
whisper-models:
ollama-models:
Dopo la filatura verso l'alto, tirare un modello per Ollama
docker exec -it ollama ollama pull llama3.2:3b
Perché locale [50]? [50] Eseguire tutto ciò che localmente isn®'® circa il costo®-®it®'® circa la provabilità®, determinismo®, e data località®. La vostra pipeline CI può far girare gli stessi contenitori®. I vostri test hanno colpito gli stessi endpoint.Nessun limite di velocità API®,′ nessuna variabilità della rete®, nessun dato che lasci la vostra macchina®.
Nota [47]: [47] La base.en Il modello Whisper è veloce e preciso per l'inglese. small.en oppure medium.en per una migliore precisione [55]. [56]
I moduli sono definiti in JSON®.® Questa è l'unica fonte di verità per quali campi esistono e come si comportano.
{
"id": "customer-intake",
"name": "Customer Intake Form",
"fields": [
{
"id": "fullName",
"label": "Full Name",
"type": "Text",
"prompt": "Please say your full name.",
"required": true
},
{
"id": "dateOfBirth",
"label": "Date of Birth",
"type": "Date",
"prompt": "What is your date of birth?",
"required": true,
"confirmationPolicy": {
"alwaysConfirm": true
}
},
{
"id": "email",
"label": "Email Address",
"type": "Email",
"prompt": "What is your email address?",
"required": true
},
{
"id": "phone",
"label": "Phone Number",
"type": "Phone",
"prompt": "What is your phone number?",
"required": false
},
{
"id": "notes",
"label": "Additional Notes",
"type": "Text",
"prompt": "Any additional notes?",
"required": false
}
]
}
I modelli C.P.R.M.S.K.O. che rappresentano questo C.P.R.M.S.M.K.O.
public record FormDefinition(
string Id,
string Name,
List<FieldDefinition> Fields);
public record FieldDefinition(
string Id,
string Label,
FieldType Type,
string Prompt,
bool Required = true,
ConfirmationPolicy? ConfirmationPolicy = null);
public enum FieldType
{
Text,
Date,
Email,
Phone,
Choice // Constrained vocabulary - especially important for voice
}
public record ConfirmationPolicy(
bool AlwaysConfirm = false,
double ConfidenceThreshold = 0.85);
Poiché lo schema è dichiarativo aggiungere campi non richiede mai di toccare la macchina di statoIl modulo include automaticamente una nuova definizione di campo nel campo JSON, e il modulo lo include con un corretto controllo del flusso.
In questo caso, la valutazione di base è stata effettuata da [47]' [47] la macchina di stato doesn®'® usa AILa logica pura di C.P.R.M.M.S.K.2 che determina le transizioni basate su regole esplicite
public class FormStateMachine : IFormStateMachine
{
private FormSession _session = null!;
private int _currentFieldIndex;
public FormSession StartSession(FormDefinition form)
{
_session = new FormSession
{
Id = Guid.NewGuid().ToString(),
Form = form,
Status = FormStatus.InProgress,
StartedAt = DateTime.UtcNow,
FieldStates = form.Fields.ToDictionary(
f => f.Id,
f => new FieldState { FieldId = f.Id, Status = FieldStatus.Pending })
};
// First field starts in progress
if (form.Fields.Count > 0)
{
_session.FieldStates[form.Fields[0].Id].Status = FieldStatus.InProgress;
}
return _session;
}
public FieldDefinition? GetCurrentField()
{
if (_currentFieldIndex >= _session.Form.Fields.Count)
return null;
return _session.Form.Fields[_currentFieldIndex];
}
}
Le transizioni di stato sono esplicite e testabili
public StateTransitionResult ProcessExtraction(
ExtractionResponse extraction,
ValidationResult validation)
{
var currentField = GetCurrentField();
if (currentField == null)
return new StateTransitionResult(false, "No current field");
var fieldState = _session.FieldStates[currentField.Id];
fieldState.AttemptCount++;
// Validation failed? Stay on current field
if (!validation.IsValid)
{
return new StateTransitionResult(
false,
$"Validation failed: {validation.ErrorMessage}");
}
// Store the pending value
fieldState.PendingValue = extraction.Value;
fieldState.PendingConfidence = extraction.Confidence;
// Check confirmation policy - this is rules, not AI
var policy = currentField.ConfirmationPolicy
?? new ConfirmationPolicy();
var needsConfirmation = policy.AlwaysConfirm
|| extraction.Confidence < policy.ConfidenceThreshold;
if (needsConfirmation)
{
fieldState.Status = FieldStatus.AwaitingConfirmation;
return new StateTransitionResult(
true,
"Please confirm this value",
RequiresConfirmation: true);
}
// Auto-confirm high confidence values
return ConfirmValue();
}
Punto chiave [50]: [50] La _currentFieldIndex Solo avanzamenti alla conferma [49], [56] mai all'estrazione [56]. [56] Ciò significa un'estrazione non riuscita o una conferma respinta ti mantiene sullo stesso campo [56]. [56] L'utente rimane nel controllo [56]. [56]
Si noti che la logica di conferma [47]: [47] it [47] ha un semplice controllo politico [47], [56] non ha un ragionamento LLM [47] Elevata fiducia [56] + [56] no alwaysConfirm Auto-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
L'estrattore di Ollama ha un lavoro [47] : [47] trasformano discorso umano disordinato in valori di campo strutturati [47] . [47] Qui ' [47] ha contratto [47] : [47]
public interface IFieldExtractor
{
Task<ExtractionResponse> ExtractAsync(
ExtractionContext context,
CancellationToken ct = default);
}
public record ExtractionContext(
FieldDefinition Field,
string Prompt,
string Transcript);
public record ExtractionResponse(
string FieldId,
string? Value,
double Confidence,
bool NeedsConfirmation, // Suggestion only - policy has final say
string? Reason);
Importante [55]: [56] L'estrattore può suggerire NeedsConfirmation: trueIl parere di LLM®' è consultivo [47] non autorevole.
E l'attuazione [55]: [56]
public class OllamaFieldExtractor : IFieldExtractor
{
private readonly HttpClient _httpClient;
private readonly string _model;
public async Task<ExtractionResponse> ExtractAsync(
ExtractionContext context,
CancellationToken ct = default)
{
var systemPrompt = BuildSystemPrompt(context.Field);
var userPrompt = $"User said: \"{context.Transcript}\"";
var request = new
{
model = _model,
messages = new[]
{
new { role = "system", content = systemPrompt },
new { role = "user", content = userPrompt }
},
format = "json",
stream = false,
options = new { temperature = 0.1 } // Low = deterministic
};
var response = await _httpClient.PostAsJsonAsync(
"/api/chat", request, ct);
return ParseResponse(response, context.Field.Id);
}
}
Perché la temperatura di 0.1? Vogliamo che l'LLM sia noioso e prevedibile, dato lo stesso trascrizione... .., .. .. ..dovrebbe estrarre lo stesso valore ogni volta che la temperatura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Il prompt del sistema è esplicito in merito al ruolo limitato di LLM/' [47]
private string BuildSystemPrompt(FieldDefinition field)
{
return $"""
You are a data extraction assistant. Extract the {field.Label}
from the user's speech.
Field type: {field.Type}
Return JSON only:
{{
"fieldId": "{field.Id}",
"value": "<extracted value or null>",
"confidence": <0.0-1.0>,
"needsConfirmation": <true/false>,
"reason": "<brief explanation>"
}}
Rules:
- For dates, output ISO format (YYYY-MM-DD)
- For emails, output lowercase
- For phones, output digits only
- If you can't extract, set value to null
- Be conservative with confidence scores
DO NOT:
- Ask follow-up questions
- Suggest next steps
- Make assumptions beyond the transcript
""";
}
L'LLM estrae [47], [56] convalida il formato di tipo [56], [56] e segnala la fiducia [54]. [56] Esso doesn [56]' [56]t decide cosa succede dopo [56]. [56]
Il lato JavaScript cattura l'audio del microfono e lo converte in WAV®16SHz mono WAV®:S
window.voiceFormAudio = (function () {
let mediaRecorder = null;
let audioChunks = [];
let audioContext = null;
let dotNetRef = null;
async function startRecording() {
const stream = await navigator.mediaDevices
.getUserMedia({ audio: true });
audioContext = new AudioContext();
audioChunks = [];
const options = { mimeType: 'audio/webm;codecs=opus' };
mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = async () => {
stream.getTracks().forEach(track => track.stop());
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
const wavBlob = await convertToWav(audioBlob);
const wavBytes = await wavBlob.arrayBuffer();
// Send to Blazor
const uint8Array = new Uint8Array(wavBytes);
await dotNetRef.invokeMethodAsync(
'OnRecordingComplete',
Array.from(uint8Array));
};
mediaRecorder.start(100);
}
return { initialize, startRecording, stopRecording };
})();
Perché convertire in WAV Whisper accetta i vari formati [49], [47] ma il mono WAV è ottimale [54]- [54]it [54] è esattamente ciò che il modello è stato addestrato su [54]. [54] Nessuna transcodifica aerea sul server [49] risultati coerenti [56], [56] e la conversione è deterministica [54]( [56] lo stesso audio in [49] = [56] lo stesso bytes out [49]
La convertToWav ricampioni di funzione a S16SHZ-S'SystemsK2Systemsll vi risparmia l'header WAV bitS-Systemstwiddling S,S, ma sS'Ss nel S.S repoSMSK6S
L'interfaccia utente mostra il campo corrente prominente sulla sinistra con tutti i campi visibili in una barra laterale sulla destra La barra laterale conta per la fiduciaGli utilizzatori possono vedere dove si trovano nella forma [55], [56] ciò che hanno già risposto [56]' [56] e ciò che [56]' [56] che arriva dopo [56] Nessuna sorpresa [56]
graph LR
subgraph "Left Panel"
A[Current Field Prompt]
B[Record Button]
C[Transcript Display]
D[Confirmation Dialog]
end
subgraph "Right Sidebar"
E[Field 1: Full Name ✓]
F[Field 2: DOB - Active]
G[Field 3: Email - Pending]
H[Field 4: Phone - Pending]
end
style F stroke:#36f,stroke-width:2px
style E stroke:#6f6,stroke-width:2px
In questo caso, la struttura della pagina Blazor è la "Blazor" ("Blazor page structure") ("Blazor page structure") ("Blazor page structure") ("Blazor page structure") ("Blazor page structure") ("Blazor page structure").
@page "/voiceform/{FormId}"
@inject IFormOrchestrator Orchestrator
@rendermode InteractiveServer
<div class="top-bar">
<h1>Voice Form</h1>
<button class="theme-toggle" onclick="voiceFormTheme.toggle()">
Dark Mode
</button>
</div>
<main>
<div class="voice-form-layout">
<!-- Left: Active Field -->
<div class="active-field-panel">
@if (_currentField != null)
{
<div class="current-prompt">
<h2>@_currentField.Label</h2>
<p class="prompt-text">@_message</p>
</div>
<AudioRecorder OnAudioCaptured="HandleAudioCaptured"
IsRecording="_isRecording"
IsProcessing="_isProcessing" />
@if (!string.IsNullOrEmpty(_transcript))
{
<TranscriptDisplay Transcript="_transcript"
Confidence="_transcriptConfidence" />
}
@if (_showConfirmation)
{
<ConfirmationDialog ExtractedValue="_pendingValue"
OnConfirm="HandleConfirm"
OnReject="HandleReject" />
}
}
</div>
<!-- Right: Form Overview -->
<div class="form-sidebar">
<h3>@_session.Form.Name</h3>
<div class="form-fields-list">
@foreach (var field in _session.Form.Fields)
{
var state = _session.GetFieldState(field.Id);
<div class="form-field-item @GetFieldClass(field, state)">
<div class="field-status-icon">
@GetStatusIcon(state.Status)
</div>
<div class="field-info">
<div class="field-name">@field.Label</div>
<div class="field-value">
@(state.Value ?? "Waiting")
</div>
</div>
</div>
}
</div>
</div>
</div>
</main>
L'orchestratore coordina i servizi nessuna logica ramificante propriaIl servizio di chiamata di appena di chiamata di S- in sequenza e passa i risultati lungo il S: SRT project
public class FormOrchestrator : IFormOrchestrator
{
private readonly ISttService _sttService;
private readonly IFieldExtractor _extractor;
private readonly IFormValidator _validator;
private readonly IFormStateMachine _stateMachine;
private readonly IFormEventLog _eventLog;
public async Task<ProcessingResult> ProcessAudioAsync(byte[] audioData)
{
var currentField = _stateMachine.GetCurrentField();
if (currentField == null)
return ProcessingResult.Error("No current field");
// Step 1: Speech to text
var sttResult = await _sttService.TranscribeAsync(audioData);
await _eventLog.LogAsync(new TranscriptReceivedEvent(
currentField.Id, sttResult.Transcript, sttResult.Confidence));
// Step 2: Extract structured value
var context = new ExtractionContext(
currentField, currentField.Prompt, sttResult.Transcript);
var extraction = await _extractor.ExtractAsync(context);
await _eventLog.LogAsync(new ExtractionAttemptEvent(
currentField.Id, extraction));
// Step 3: Validate
var validation = _validator.Validate(currentField, extraction.Value);
// Step 4: State machine decides next step
var transition = _stateMachine.ProcessExtraction(extraction, validation);
return new ProcessingResult(
Success: transition.Success,
Message: transition.Message,
Session: _stateMachine.GetSession(),
RequiresConfirmation: transition.RequiresConfirmation,
PendingValue: extraction.Value);
}
}
Ogni passaggio è indipendente e testabile. non tocca mai la reteL'LLM non tocca mai lo stato.
La modalità scura è implementata con le proprietà personalizzate CSS
:root {
--bg-primary: #f8fafc;
--bg-secondary: #ffffff;
--text-primary: #1e293b;
--accent-blue: #3b82f6;
--accent-green: #22c55e;
}
[data-theme="dark"] {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--text-primary: #f1f5f9;
--accent-blue: #60a5fa;
--accent-green: #4ade80;
}
body {
background: var(--bg-primary);
color: var(--text-primary);
}
Il tema toggle persiste tramite localStorage [47]: [47]
window.voiceFormTheme = (function () {
const THEME_KEY = 'voiceform-theme';
function toggle() {
const current = document.documentElement
.getAttribute('data-theme') || 'light';
const newTheme = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem(THEME_KEY, newTheme);
}
// Initialize from saved preference or system preference
function init() {
const saved = localStorage.getItem(THEME_KEY);
const systemDark = window.matchMedia(
'(prefers-color-scheme: dark)').matches;
const theme = saved || (systemDark ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
}
return { toggle, init };
})();
L'architettura deterministica si ripaga nel testare il test [47]. [47] Qui [47]' [47] è un test per unità di macchina statale per il percorso felice [56]: [56]
[Fact]
public void ProcessExtraction_HighConfidence_AutoConfirms()
{
// Arrange
var form = CreateTestForm();
var stateMachine = new FormStateMachine();
stateMachine.StartSession(form);
var extraction = new ExtractionResponse(
FieldId: "fullName",
Value: "John Smith",
Confidence: 0.95, // High confidence
NeedsConfirmation: false,
Reason: "Clear speech");
var validation = ValidationResult.Success();
// Act
var result = stateMachine.ProcessExtraction(extraction, validation);
// Assert
result.Success.Should().BeTrue();
result.RequiresConfirmation.Should().BeFalse();
var fieldState = stateMachine.GetSession()
.GetFieldState("fullName");
fieldState.Status.Should().Be(FieldStatus.Confirmed);
fieldState.Value.Should().Be("John Smith");
}
E in questo caso il test negativo è il test del test del test del test del test del test del test del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del controllo del veicolo. forze conferma indipendentemente da ciò che la LLM suggerisce
[Fact]
public void ProcessExtraction_LowConfidence_RequiresConfirmation()
{
// Arrange
var form = CreateTestForm();
var stateMachine = new FormStateMachine();
stateMachine.StartSession(form);
var extraction = new ExtractionResponse(
FieldId: "fullName",
Value: "John Smith",
Confidence: 0.65, // Below threshold
NeedsConfirmation: false, // LLM says no, but policy overrides
Reason: "Noisy audio");
var validation = ValidationResult.Success();
// Act
var result = stateMachine.ProcessExtraction(extraction, validation);
// Assert
result.RequiresConfirmation.Should().BeTrue(
"Policy threshold (0.85) should override LLM suggestion");
var fieldState = stateMachine.GetSession()
.GetFieldState("fullName");
fieldState.Status.Should().Be(FieldStatus.AwaitingConfirmation);
}
E un test del browser PuppeteerSharp che fa clic sulle cose
[Fact]
public async Task HomePage_ThemeToggle_ShouldSwitchTheme()
{
await _page!.GoToAsync(BaseUrl);
await _page.WaitForSelectorAsync(".theme-toggle");
var initialTheme = await _page.EvaluateFunctionAsync<string?>(
"() => document.documentElement.getAttribute('data-theme')");
// Click toggle
await _page.ClickAsync(".theme-toggle");
await Task.Delay(100);
var newTheme = await _page.EvaluateFunctionAsync<string?>(
"() => document.documentElement.getAttribute('data-theme')");
newTheme.Should().NotBe(initialTheme);
}
[Fact]
public async Task VoiceFormPage_Sidebar_ShouldShowAllFields()
{
await _page!.GoToAsync($"{BaseUrl}/voiceform/customer-intake");
await _page.WaitForSelectorAsync(".form-fields-list");
var fieldItems = await _page.QuerySelectorAllAsync(".form-field-item");
fieldItems.Should().HaveCount(5, "Customer intake form has 5 fields");
}
La suite di test completa esegue i test delle unità di test di M98 per la logica aziendale , per i test di integrazione del browser di 22 per l'interfaccia utente. per i test di integrazione del browser
Lasciate che gli ' possano rivisitare il motivo per cui l'abbiamo costruito in questo modo.
graph TD
subgraph "Traditional Voice Form"
A1[User Speaks] --> B1[LLM]
B1 --> C1[LLM decides field]
C1 --> D1[LLM validates]
D1 --> E1[LLM confirms]
E1 --> F1[LLM advances form]
end
subgraph "Ten Commandments Approach"
A2[User Speaks] --> B2[Whisper STT]
B2 --> C2[Ollama Extract]
C2 --> D2[C# Validator]
D2 --> E2[Policy Check]
E2 --> F2[State Machine]
end
style B1 stroke:#f96,stroke-width:2px
style C1 stroke:#f96,stroke-width:2px
style D1 stroke:#f96,stroke-width:2px
style E1 stroke:#f96,stroke-width:2px
style F1 stroke:#f96,stroke-width:2px
style C2 stroke:#f96,stroke-width:2px
style D2 stroke:#6f6,stroke-width:2px
style E2 stroke:#6f6,stroke-width:2px
style F2 stroke:#6f6,stroke-width:2px
Nell'approccio tradizionale [55], [56] tutto è il LLMOgni insetto è un insetto che a volte fa che l'"- non sia riproducibile ed inspiegabile.
Nel nostro approccio [49], [49] la LLM fa una cosaTutto il resto è deterministico C.I.M.M.M.K.I.M.K.I.M.K.I.M.K.I.M.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.K.I.J.K.I.K.I.I.K.I.K.I.K.K.I.K.I.K.I.K.I.K.K.I.K.I.K.K.K.I.K.K.I.K.K.I.K.K.K.I.K.K.I.K.K.K.K.I.K.K.K.K.K.K.I.K.K.K.K.K.K.K.K.K.K.K.J.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.
Questo è Comandi S2,S S3,S e S4S in azione S:S LLM traduce S,S stato SMSK4S controllo del flusso delle macchine S,S la politica decide conferma S.S
docker compose -f devdeps-docker-compose.yml up -d
docker exec -it ollama ollama pull llama3.2:3b
cd Mostlylucid.VoiceForm
dotnet run
Vai a http://localhost:5000
Clicca su Clicca su Clicca su Client Intake e inizia a parlare Clicca su Client Intake
Interfacce vocali don®'®T devono essere scatole nere®.® Trattando il discorso come solo un altro metodo di input®,® LLMs come traduttori e mantenendo il controllo del flusso nel codice deterministico®,® si ottiene [49]:®
I Comandamenti di Ten di S" sono di S' di SMSK2 di essere antiS- di S. di SMSK4 di S' di SMSK5 di circa mettere l'IA al suo postoUn potente strumento che si traduce tra la confusione umana e la precisione del computer non è un oracolo che prende decisioni per noi
Ora vai a costruire qualcosa in cui si [55]- [56]non il modello [56]- [56]decide cosa succede dopo [56]. [56]
In questo momento, l'utente legge i prompt e parla le risposte ma se il sistema potesse *Rispondi.*La parte del testo aggiungerà il testo - a - "Speech feedback" , trasformandolo in una risposta vocale interattiva completa "IVR" ( "Interactive Voice Response" ) "System" "System" : "Speech Response"
L'architettura è già preparata... ..:... ..la macchina di stato emette eventi... ..,... ..l'orchestratrice coordina i servizi... ..L'aggiunta dell'uscita vocale è solo un altro servizio che ascolta quegli eventi... .. .....
In arrivo
Il codice sorgente completo è nel Mostlylucid.VoiceForm Progetto nell'ambito del piano di ristrutturazione per la ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della ristrutturazione della
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.