Compartir a través de


Tutorial: Creación de un tomador de notas de voz a texto

Cree una aplicación que convierta audio hablado en notas organizadas, completamente en el dispositivo. La aplicación transcribe primero un archivo de audio mediante un modelo de conversión de voz a texto y, a continuación, usa un modelo de chat para resumir y organizar la transcripción en notas limpias.

En este tutorial aprenderá a:

  • Configuración de un proyecto e instalación del SDK local de Foundry
  • Cargar un modelo de voz a texto y transcribir un archivo de audio
  • Carga de un modelo de chat y resumen de la transcripción
  • Combinar transcripción y resumen en una aplicación completa
  • Limpieza de recursos

Prerrequisitos

  • Un equipo Windows, macOS o Linux con al menos 8 GB de RAM.
  • Un .wav archivo de audio para transcribir (el tutorial usa un archivo de ejemplo).

Instalación de paquetes

Repositorio de ejemplos

El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/cs/tutorial-voice-to-text

Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.

dotnet add package Microsoft.AI.Foundry.Local.WinML
dotnet add package OpenAI

Los ejemplos de C# del repositorio de GitHub son proyectos preconfigurados. Si va a compilar desde cero, debe leer la referencia del SDK local de Foundry para obtener más detalles sobre cómo configurar el proyecto de C# con Foundry Local.

Transcribir un archivo de audio

En este paso, cargará un modelo de reconocimiento de voz y transcribirá un archivo de audio. El SDK local de Foundry utiliza el alias de modelo whisper para seleccionar la mejor variante de Whisper para su hardware.

  • Abra Program.cs y reemplace su contenido por el código siguiente para inicializar el SDK, cargar el modelo de voz y transcribir un archivo de audio:

    // Load the speech-to-text model
    var speechModel = await catalog.GetModelAsync("whisper-tiny")
        ?? throw new Exception("Speech model not found");
    
    await speechModel.DownloadAsync(progress =>
    {
        Console.Write($"\rDownloading speech model: {progress:F2}%");
        if (progress >= 100f) Console.WriteLine();
    });
    
    await speechModel.LoadAsync();
    Console.WriteLine("Speech model loaded.");
    
    // Transcribe the audio file
    var audioClient = await speechModel.GetAudioClientAsync();
    var transcriptionText = new StringBuilder();
    
    Console.WriteLine("\nTranscription:");
    var audioResponse = audioClient
        .TranscribeAudioStreamingAsync("meeting-notes.wav", ct);
    await foreach (var chunk in audioResponse)
    {
        Console.Write(chunk.Text);
        transcriptionText.Append(chunk.Text);
    }
    Console.WriteLine();
    
    // Unload the speech model to free memory
    await speechModel.UnloadAsync();
    

    El GetAudioClientAsync método devuelve un cliente para las operaciones de audio. El TranscribeAudioStreamingAsync método transmite fragmentos de transcripción a medida que están disponibles. Acumula el texto para que pueda pasarlo al modelo de chat en el paso siguiente.

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Resumen de la transcripción

Ahora use un modelo de chat para organizar la transcripción sin procesar en notas estructuradas. Cargue el modelo qwen2.5-0.5b y envíe la transcripción como contexto con un mensaje del sistema que indique al modelo que genere notas limpias y resumidas.

Agregue el código siguiente después del paso de transcripción:

// Load the chat model for summarization
var chatModel = await catalog.GetModelAsync("qwen2.5-0.5b")
    ?? throw new Exception("Chat model not found");

await chatModel.DownloadAsync(progress =>
{
    Console.Write($"\rDownloading chat model: {progress:F2}%");
    if (progress >= 100f) Console.WriteLine();
});

await chatModel.LoadAsync();
Console.WriteLine("Chat model loaded.");

// Summarize the transcription into organized notes
var chatClient = await chatModel.GetChatClientAsync();
var messages = new List<ChatMessage>
{
    new ChatMessage
    {
        Role = "system",
        Content = "You are a note-taking assistant. Summarize " +
                  "the following transcription into organized, " +
                  "concise notes with bullet points."
    },
    new ChatMessage
    {
        Role = "user",
        Content = transcriptionText.ToString()
    }
};

var chatResponse = await chatClient.CompleteChatAsync(messages, ct);
var summary = chatResponse.Choices[0].Message.Content;
Console.WriteLine($"\nSummary:\n{summary}");

// Clean up
await chatModel.UnloadAsync();
Console.WriteLine("\nDone. Models unloaded.");

La solicitud del sistema da forma al formato de salida del modelo. Al indicarle que genere "notas organizadas y concisas con viñetas", obtendrá contenido estructurado en lugar de una paráfrasis sin procesar.

Combinar en una aplicación completa

Reemplace el contenido de Program.cs por el siguiente código completo que transcriba un archivo de audio y resume la transcripción:

using Microsoft.AI.Foundry.Local;
using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels;
using Microsoft.Extensions.Logging;
using System.Text;

CancellationToken ct = CancellationToken.None;

var config = new Configuration
{
    AppName = "foundry_local_samples",
    LogLevel = Microsoft.AI.Foundry.Local.LogLevel.Information
};

using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.SetMinimumLevel(
        Microsoft.Extensions.Logging.LogLevel.Information
    );
});
var logger = loggerFactory.CreateLogger<Program>();

// Initialize the singleton instance
await FoundryLocalManager.CreateAsync(config, logger);
var mgr = FoundryLocalManager.Instance;

// Download and register all execution providers.
var currentEp = "";
await mgr.DownloadAndRegisterEpsAsync((epName, percent) =>
{
    if (epName != currentEp)
    {
        if (currentEp != "") Console.WriteLine();
        currentEp = epName;
    }
    Console.Write($"\r  {epName.PadRight(30)}  {percent,6:F1}%");
});
if (currentEp != "") Console.WriteLine();

var catalog = await mgr.GetCatalogAsync();

// Load the speech-to-text model
var speechModel = await catalog.GetModelAsync("whisper-tiny")
    ?? throw new Exception("Speech model not found");

await speechModel.DownloadAsync(progress =>
{
    Console.Write($"\rDownloading speech model: {progress:F2}%");
    if (progress >= 100f) Console.WriteLine();
});

await speechModel.LoadAsync();
Console.WriteLine("Speech model loaded.");

// Transcribe the audio file
var audioClient = await speechModel.GetAudioClientAsync();
var transcriptionText = new StringBuilder();

Console.WriteLine("\nTranscription:");
var audioResponse = audioClient
    .TranscribeAudioStreamingAsync("meeting-notes.wav", ct);
await foreach (var chunk in audioResponse)
{
    Console.Write(chunk.Text);
    transcriptionText.Append(chunk.Text);
}
Console.WriteLine();

// Unload the speech model to free memory
await speechModel.UnloadAsync();

// Load the chat model for summarization
var chatModel = await catalog.GetModelAsync("qwen2.5-0.5b")
    ?? throw new Exception("Chat model not found");

await chatModel.DownloadAsync(progress =>
{
    Console.Write($"\rDownloading chat model: {progress:F2}%");
    if (progress >= 100f) Console.WriteLine();
});

await chatModel.LoadAsync();
Console.WriteLine("Chat model loaded.");

// Summarize the transcription into organized notes
var chatClient = await chatModel.GetChatClientAsync();
var messages = new List<ChatMessage>
{
    new ChatMessage
    {
        Role = "system",
        Content = "You are a note-taking assistant. Summarize " +
                  "the following transcription into organized, " +
                  "concise notes with bullet points."
    },
    new ChatMessage
    {
        Role = "user",
        Content = transcriptionText.ToString()
    }
};

var chatResponse = await chatClient.CompleteChatAsync(messages, ct);
var summary = chatResponse.Choices[0].Message.Content;
Console.WriteLine($"\nSummary:\n{summary}");

// Clean up
await chatModel.UnloadAsync();
Console.WriteLine("\nDone. Models unloaded.");

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Ejecute el tomador de notas:

dotnet run

Verá una salida similar a la siguiente:

Downloading speech model: 100.00%
Speech model loaded.

Transcription:
OK so let's get started with the weekly sync. First, the backend
API is nearly done. Sarah finished the authentication endpoints
yesterday. We still need to add rate limiting before we go to
staging. On the frontend, the dashboard redesign is about seventy
percent complete. Jake, can you walk us through the new layout?
Great. The charts look good. I think we should add a filter for
date range though. For testing, we have about eighty percent code
coverage on the API. We need to write integration tests for the
new auth flow before Friday. Let's plan to do a full regression
test next Tuesday before the release. Any blockers? OK, sounds
like we are in good shape. Let's wrap up.

Downloading chat model: 100.00%
Chat model loaded.

Summary:
- **Backend API**: Authentication endpoints complete. Rate limiting
  still needed before staging deployment.
- **Frontend**: Dashboard redesign 70% complete. New chart layout
  reviewed. Action item: add a date range filter.
- **Testing**: API code coverage at 80%. Integration tests for the
  auth flow due Friday. Full regression test scheduled for next
  Tuesday before release.
- **Status**: No blockers reported. Team is on track.

Done. Models unloaded.

La aplicación transcribe primero el contenido de audio con salida de streaming y, a continuación, pasa el texto acumulado a un modelo de chat que extrae puntos clave y los organiza en notas estructuradas.

Instalación de paquetes

Repositorio de ejemplos

El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/js/tutorial-voice-to-text

Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.

npm install foundry-local-sdk-winml openai

Transcribir un archivo de audio

En este paso, cargará un modelo de reconocimiento de voz y transcribirá un archivo de audio. El SDK Local de The Foundry usa el alias de modelo whisper para seleccionar la mejor variante de Whisper para tu hardware.

  1. Cree un archivo denominado app.js.

  2. Agregue el código siguiente para inicializar el SDK, cargar el modelo de voz y transcribir un archivo de audio:

    // Load the speech-to-text model
    const speechModel = await manager.catalog.getModel('whisper-tiny');
    await speechModel.download((progress) => {
        process.stdout.write(
            `\rDownloading speech model: ${progress.toFixed(2)}%`
        );
    });
    console.log('\nSpeech model downloaded.');
    
    await speechModel.load();
    console.log('Speech model loaded.');
    
    // Transcribe the audio file
    const audioClient = speechModel.createAudioClient();
    const transcription = await audioClient.transcribe(
        path.join(__dirname, 'meeting-notes.wav')
    );
    console.log(`\nTranscription:\n${transcription.text}`);
    
    // Unload the speech model to free memory
    await speechModel.unload();
    

    El createAudioClient método devuelve un cliente para las operaciones de audio. El transcribe método acepta una ruta de acceso de archivo y devuelve un objeto con una text propiedad que contiene el contenido transcrito.

Nota:

Reemplace './meeting-notes.wav' por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Resumen de la transcripción

Ahora use un modelo de chat para organizar la transcripción sin procesar en notas estructuradas. Cargue el modelo qwen2.5-0.5b y envíe la transcripción como contexto con un mensaje del sistema que indique al modelo que genere notas limpias y resumidas.

Agregue el código siguiente después del paso de transcripción:

// Load the chat model for summarization
const chatModel = await manager.catalog.getModel('qwen2.5-0.5b');
await chatModel.download((progress) => {
    process.stdout.write(
        `\rDownloading chat model: ${progress.toFixed(2)}%`
    );
});
console.log('\nChat model downloaded.');

await chatModel.load();
console.log('Chat model loaded.');

// Summarize the transcription into organized notes
const chatClient = chatModel.createChatClient();
const messages = [
    {
        role: 'system',
        content: 'You are a note-taking assistant. Summarize ' +
                 'the following transcription into organized, ' +
                 'concise notes with bullet points.'
    },
    {
        role: 'user',
        content: transcription.text
    }
];

const response = await chatClient.completeChat(messages);
const summary = response.choices[0]?.message?.content;
console.log(`\nSummary:\n${summary}`);

// Clean up
await chatModel.unload();
console.log('\nDone. Models unloaded.');

La solicitud del sistema da forma al formato de salida del modelo. Al indicarle que genere "notas organizadas y concisas con viñetas", obtendrá contenido estructurado en lugar de una paráfrasis sin procesar.

Combinar en una aplicación completa

Cree un archivo denominado app.js y agregue el siguiente código completo que transcriba un archivo de audio y resuma la transcripción:

import { FoundryLocalManager } from 'foundry-local-sdk';
import { fileURLToPath } from 'url';
import path from 'path';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Initialize the Foundry Local SDK
const manager = FoundryLocalManager.create({
    appName: 'foundry_local_samples',
    logLevel: 'info'
});

// Download and register all execution providers.
let currentEp = '';
await manager.downloadAndRegisterEps((epName, percent) => {
    if (epName !== currentEp) {
        if (currentEp !== '') process.stdout.write('\n');
        currentEp = epName;
    }
    process.stdout.write(`\r  ${epName.padEnd(30)}  ${percent.toFixed(1).padStart(5)}%`);
});
if (currentEp !== '') process.stdout.write('\n');

// Load the speech-to-text model
const speechModel = await manager.catalog.getModel('whisper-tiny');
await speechModel.download((progress) => {
    process.stdout.write(
        `\rDownloading speech model: ${progress.toFixed(2)}%`
    );
});
console.log('\nSpeech model downloaded.');

await speechModel.load();
console.log('Speech model loaded.');

// Transcribe the audio file
const audioClient = speechModel.createAudioClient();
const transcription = await audioClient.transcribe(
    path.join(__dirname, 'meeting-notes.wav')
);
console.log(`\nTranscription:\n${transcription.text}`);

// Unload the speech model to free memory
await speechModel.unload();

// Load the chat model for summarization
const chatModel = await manager.catalog.getModel('qwen2.5-0.5b');
await chatModel.download((progress) => {
    process.stdout.write(
        `\rDownloading chat model: ${progress.toFixed(2)}%`
    );
});
console.log('\nChat model downloaded.');

await chatModel.load();
console.log('Chat model loaded.');

// Summarize the transcription into organized notes
const chatClient = chatModel.createChatClient();
const messages = [
    {
        role: 'system',
        content: 'You are a note-taking assistant. Summarize ' +
                 'the following transcription into organized, ' +
                 'concise notes with bullet points.'
    },
    {
        role: 'user',
        content: transcription.text
    }
];

const response = await chatClient.completeChat(messages);
const summary = response.choices[0]?.message?.content;
console.log(`\nSummary:\n${summary}`);

// Clean up
await chatModel.unload();
console.log('\nDone. Models unloaded.');

Nota:

Reemplace './meeting-notes.wav' por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Ejecute el tomador de notas:

node app.js

Verá una salida similar a la siguiente:

Downloading speech model: 100.00%
Speech model downloaded.
Speech model loaded.

Transcription:
OK so let's get started with the weekly sync. First, the backend
API is nearly done. Sarah finished the authentication endpoints
yesterday. We still need to add rate limiting before we go to
staging. On the frontend, the dashboard redesign is about seventy
percent complete. Jake, can you walk us through the new layout?
Great. The charts look good. I think we should add a filter for
date range though. For testing, we have about eighty percent code
coverage on the API. We need to write integration tests for the
new auth flow before Friday. Let's plan to do a full regression
test next Tuesday before the release. Any blockers? OK, sounds
like we are in good shape. Let's wrap up.

Downloading chat model: 100.00%
Chat model downloaded.
Chat model loaded.

Summary:
- **Backend API**: Authentication endpoints complete. Rate limiting
  still needed before staging deployment.
- **Frontend**: Dashboard redesign 70% complete. New chart layout
  reviewed. Action item: add a date range filter.
- **Testing**: API code coverage at 80%. Integration tests for the
  auth flow due Friday. Full regression test scheduled for next
  Tuesday before release.
- **Status**: No blockers reported. Team is on track.

Done. Models unloaded.

La aplicación transcribe primero el contenido de audio y, a continuación, pasa ese texto a un modelo de chat que extrae puntos clave y los organiza en notas estructuradas.

Instalación de paquetes

Repositorio de ejemplos

El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/python/tutorial-voice-to-text

Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.

pip install foundry-local-sdk-winml openai

Transcribir un archivo de audio

En este paso, cargará un modelo de reconocimiento de voz y transcribirá un archivo de audio. El SDK local de Foundry usa el alias de modelo whisper para seleccionar la mejor variante de Whisper para tu hardware.

  1. Cree un archivo denominado app.py.

  2. Agregue el código siguiente para inicializar el SDK, cargar el modelo de voz y transcribir un archivo de audio:

    # Load the speech-to-text model
    speech_model = manager.catalog.get_model("whisper-tiny")
    speech_model.download(
        lambda progress: print(
            f"\rDownloading speech model: {progress:.2f}%",
            end="",
            flush=True,
        )
    )
    print()
    speech_model.load()
    print("Speech model loaded.")
    
    # Transcribe the audio file
    audio_client = speech_model.get_audio_client()
    transcription = audio_client.transcribe("meeting-notes.wav")
    print(f"\nTranscription:\n{transcription.text}")
    
    # Unload the speech model to free memory
    speech_model.unload()
    

    El get_audio_client método devuelve un cliente para las operaciones de audio. El transcribe método acepta una ruta de acceso de archivo y devuelve un objeto con una text propiedad que contiene el contenido transcrito.

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Resumen de la transcripción

Ahora use un modelo de chat para organizar la transcripción sin procesar en notas estructuradas. Cargue el modelo qwen2.5-0.5b y envíe la transcripción como contexto con una instrucción que indique al modelo que genere notas claras y resumidas.

Agregue el código siguiente después del paso de transcripción:

# Load the chat model for summarization
chat_model = manager.catalog.get_model("qwen2.5-0.5b")
chat_model.download(
    lambda progress: print(
        f"\rDownloading chat model: {progress:.2f}%",
        end="",
        flush=True,
    )
)
print()
chat_model.load()
print("Chat model loaded.")

# Summarize the transcription into organized notes
client = chat_model.get_chat_client()
messages = [
    {
        "role": "system",
        "content": "You are a note-taking assistant. "
                   "Summarize the following transcription "
                   "into organized, concise notes with "
                   "bullet points.",
    },
    {"role": "user", "content": transcription.text},
]

response = client.complete_chat(messages)
summary = response.choices[0].message.content
print(f"\nSummary:\n{summary}")

# Clean up
chat_model.unload()
print("\nDone. Models unloaded.")

La solicitud del sistema da forma al formato de salida del modelo. Al indicarle que genere "notas organizadas y concisas con viñetas", obtendrá contenido estructurado en lugar de un parafraseo sin procesar.

Combinar en una aplicación completa

Cree un archivo denominado app.py y agregue el siguiente código completo que transcriba un archivo de audio y resuma la transcripción:

from foundry_local_sdk import Configuration, FoundryLocalManager


def main():
    # Initialize the Foundry Local SDK
    config = Configuration(app_name="foundry_local_samples")
    FoundryLocalManager.initialize(config)
    manager = FoundryLocalManager.instance

    # Download and register all execution providers.
    current_ep = ""
    def ep_progress(ep_name: str, percent: float):
        nonlocal current_ep
        if ep_name != current_ep:
            if current_ep:
                print()
            current_ep = ep_name
        print(f"\r  {ep_name:<30}  {percent:5.1f}%", end="", flush=True)

    manager.download_and_register_eps(progress_callback=ep_progress)
    if current_ep:
        print()

    # Load the speech-to-text model
    speech_model = manager.catalog.get_model("whisper-tiny")
    speech_model.download(
        lambda progress: print(
            f"\rDownloading speech model: {progress:.2f}%",
            end="",
            flush=True,
        )
    )
    print()
    speech_model.load()
    print("Speech model loaded.")

    # Transcribe the audio file
    audio_client = speech_model.get_audio_client()
    transcription = audio_client.transcribe("meeting-notes.wav")
    print(f"\nTranscription:\n{transcription.text}")

    # Unload the speech model to free memory
    speech_model.unload()

    # Load the chat model for summarization
    chat_model = manager.catalog.get_model("qwen2.5-0.5b")
    chat_model.download(
        lambda progress: print(
            f"\rDownloading chat model: {progress:.2f}%",
            end="",
            flush=True,
        )
    )
    print()
    chat_model.load()
    print("Chat model loaded.")

    # Summarize the transcription into organized notes
    client = chat_model.get_chat_client()
    messages = [
        {
            "role": "system",
            "content": "You are a note-taking assistant. "
                       "Summarize the following transcription "
                       "into organized, concise notes with "
                       "bullet points.",
        },
        {"role": "user", "content": transcription.text},
    ]

    response = client.complete_chat(messages)
    summary = response.choices[0].message.content
    print(f"\nSummary:\n{summary}")

    # Clean up
    chat_model.unload()
    print("\nDone. Models unloaded.")


if __name__ == "__main__":
    main()

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Ejecute el tomador de notas:

python app.py

Verá una salida similar a la siguiente:

Downloading speech model: 100.00%
Speech model loaded.

Transcription:
OK so let's get started with the weekly sync. First, the backend
API is nearly done. Sarah finished the authentication endpoints
yesterday. We still need to add rate limiting before we go to
staging. On the frontend, the dashboard redesign is about seventy
percent complete. Jake, can you walk us through the new layout?
Great. The charts look good. I think we should add a filter for
date range though. For testing, we have about eighty percent code
coverage on the API. We need to write integration tests for the
new auth flow before Friday. Let's plan to do a full regression
test next Tuesday before the release. Any blockers? OK, sounds
like we are in good shape. Let's wrap up.

Downloading chat model: 100.00%
Chat model loaded.

Summary:
- **Backend API**: Authentication endpoints complete. Rate limiting
  still needed before staging deployment.
- **Frontend**: Dashboard redesign 70% complete. New chart layout
  reviewed. Action item: add a date range filter.
- **Testing**: API code coverage at 80%. Integration tests for the
  auth flow due Friday. Full regression test scheduled for next
  Tuesday before release.
- **Status**: No blockers reported. Team is on track.

Done. Models unloaded.

La aplicación transcribe primero el contenido de audio y, a continuación, pasa ese texto a un modelo de chat que extrae puntos clave y los organiza en notas estructuradas.

Instalación de paquetes

Repositorio de ejemplos

El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/rust/tutorial-voice-to-text

Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.

cargo add foundry-local-sdk --features winml
cargo add tokio --features full
cargo add tokio-stream anyhow

Transcribir un archivo de audio

En este paso, cargará un modelo de reconocimiento de voz y transcribirá un archivo de audio. El SDK Local de Foundry utiliza el alias del modelo whisper para seleccionar la mejor variante de Whisper para el hardware.

  • Abra src/main.rs y reemplace su contenido por el código siguiente para inicializar el SDK, cargar el modelo de voz y transcribir un archivo de audio:

    // Load the speech-to-text model
    let speech_model = manager
        .catalog()
        .get_model("whisper-tiny")
        .await?;
    
    if !speech_model.is_cached().await? {
        println!("Downloading speech model...");
        speech_model
            .download(Some(|progress: f64| {
                print!("\r  {progress:.1}%");
                io::stdout().flush().ok();
            }))
            .await?;
        println!();
    }
    
    speech_model.load().await?;
    println!("Speech model loaded.");
    
    // Transcribe the audio file
    let audio_client = speech_model.create_audio_client();
    let transcription = audio_client
        .transcribe("meeting-notes.wav")
        .await?;
    println!("\nTranscription:\n{}", transcription.text);
    
    // Unload the speech model to free memory
    speech_model.unload().await?;
    

    El create_audio_client método devuelve un cliente para las operaciones de audio. El transcribe método acepta una ruta de acceso de archivo y devuelve un objeto con un text campo que contiene el contenido transcrito.

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Resumen de la transcripción

Ahora use un modelo de chat para organizar la transcripción sin procesar en notas estructuradas. Cargue el modelo qwen2.5-0.5b y envíe la transcripción como contexto con un comando del sistema que indique al modelo que genere notas limpias y resumidas.

Agregue el código siguiente después del paso de transcripción, dentro de la main función :

// Load the chat model for summarization
let chat_model = manager
    .catalog()
    .get_model("qwen2.5-0.5b")
    .await?;

if !chat_model.is_cached().await? {
    println!("Downloading chat model...");
    chat_model
        .download(Some(|progress: f64| {
            print!("\r  {progress:.1}%");
            io::stdout().flush().ok();
        }))
        .await?;
    println!();
}

chat_model.load().await?;
println!("Chat model loaded.");

// Summarize the transcription into organized notes
let client = chat_model
    .create_chat_client()
    .temperature(0.7)
    .max_tokens(512);

let messages: Vec<ChatCompletionRequestMessage> = vec![
    ChatCompletionRequestSystemMessage::from(
        "You are a note-taking assistant. Summarize \
         the following transcription into organized, \
         concise notes with bullet points.",
    )
    .into(),
    ChatCompletionRequestUserMessage::from(
        transcription.text.as_str(),
    )
    .into(),
];

let response = client
    .complete_chat(&messages, None)
    .await?;
let summary = response.choices[0]
    .message
    .content
    .as_deref()
    .unwrap_or("");
println!("\nSummary:\n{}", summary);

// Clean up
chat_model.unload().await?;
println!("\nDone. Models unloaded.");

La solicitud del sistema da forma al formato de salida del modelo. Al indicarle que genere "notas organizadas y concisas con viñetas", obtendrá contenido estructurado en lugar de una paráfrasis sin procesar.

Combinar en una aplicación completa

Reemplace el contenido de src/main.rs por el siguiente código completo que transcriba un archivo de audio y resume la transcripción:

use foundry_local_sdk::{
    ChatCompletionRequestMessage,
    ChatCompletionRequestSystemMessage,
    ChatCompletionRequestUserMessage,
    FoundryLocalConfig, FoundryLocalManager,
};
use std::io::{self, Write};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize the Foundry Local SDK
    let manager = FoundryLocalManager::create(
        FoundryLocalConfig::new("note-taker"),
    )?;

    // Download and register all execution providers.
    manager
        .download_and_register_eps_with_progress(None, {
            let mut current_ep = String::new();
            move |ep_name: &str, percent: f64| {
                if ep_name != current_ep {
                    if !current_ep.is_empty() {
                        println!();
                    }
                    current_ep = ep_name.to_string();
                }
                print!("\r  {:<30}  {:5.1}%", ep_name, percent);
                io::stdout().flush().ok();
            }
        })
        .await?;
    println!();

    // Load the speech-to-text model
    let speech_model = manager
        .catalog()
        .get_model("whisper-tiny")
        .await?;

    if !speech_model.is_cached().await? {
        println!("Downloading speech model...");
        speech_model
            .download(Some(|progress: f64| {
                print!("\r  {progress:.1}%");
                io::stdout().flush().ok();
            }))
            .await?;
        println!();
    }

    speech_model.load().await?;
    println!("Speech model loaded.");

    // Transcribe the audio file
    let audio_client = speech_model.create_audio_client();
    let transcription = audio_client
        .transcribe("meeting-notes.wav")
        .await?;
    println!("\nTranscription:\n{}", transcription.text);

    // Unload the speech model to free memory
    speech_model.unload().await?;

    // Load the chat model for summarization
    let chat_model = manager
        .catalog()
        .get_model("qwen2.5-0.5b")
        .await?;

    if !chat_model.is_cached().await? {
        println!("Downloading chat model...");
        chat_model
            .download(Some(|progress: f64| {
                print!("\r  {progress:.1}%");
                io::stdout().flush().ok();
            }))
            .await?;
        println!();
    }

    chat_model.load().await?;
    println!("Chat model loaded.");

    // Summarize the transcription into organized notes
    let client = chat_model
        .create_chat_client()
        .temperature(0.7)
        .max_tokens(512);

    let messages: Vec<ChatCompletionRequestMessage> = vec![
        ChatCompletionRequestSystemMessage::from(
            "You are a note-taking assistant. Summarize \
             the following transcription into organized, \
             concise notes with bullet points.",
        )
        .into(),
        ChatCompletionRequestUserMessage::from(
            transcription.text.as_str(),
        )
        .into(),
    ];

    let response = client
        .complete_chat(&messages, None)
        .await?;
    let summary = response.choices[0]
        .message
        .content
        .as_deref()
        .unwrap_or("");
    println!("\nSummary:\n{}", summary);

    // Clean up
    chat_model.unload().await?;
    println!("\nDone. Models unloaded.");

    Ok(())
}

Nota:

Reemplace "meeting-notes.wav" por la ruta de acceso al archivo de audio. Los formatos admitidos incluyen WAV, MP3 y FLAC.

Ejecute el tomador de notas:

cargo run

Verá una salida similar a la siguiente:

Downloading speech model: 100.00%
Speech model loaded.

Transcription:
OK so let's get started with the weekly sync. First, the backend
API is nearly done. Sarah finished the authentication endpoints
yesterday. We still need to add rate limiting before we go to
staging. On the frontend, the dashboard redesign is about seventy
percent complete. Jake, can you walk us through the new layout?
Great. The charts look good. I think we should add a filter for
date range though. For testing, we have about eighty percent code
coverage on the API. We need to write integration tests for the
new auth flow before Friday. Let's plan to do a full regression
test next Tuesday before the release. Any blockers? OK, sounds
like we are in good shape. Let's wrap up.

Downloading chat model: 100.00%
Chat model loaded.

Summary:
- **Backend API**: Authentication endpoints complete. Rate limiting
  still needed before staging deployment.
- **Frontend**: Dashboard redesign 70% complete. New chart layout
  reviewed. Action item: add a date range filter.
- **Testing**: API code coverage at 80%. Integration tests for the
  auth flow due Friday. Full regression test scheduled for next
  Tuesday before release.
- **Status**: No blockers reported. Team is on track.

Done. Models unloaded.

La aplicación transcribe primero el contenido de audio y, a continuación, pasa ese texto a un modelo de chat que extrae puntos clave y los organiza en notas estructuradas.

Limpieza de recursos

Los parámetros de ponderación del modelo se quedan almacenados en la memoria caché local después de descargar un modelo. Esto significa que la próxima vez que ejecute la aplicación, se omite el paso de descarga y el modelo se carga más rápido. No se necesita ninguna limpieza adicional a menos que quiera reclamar espacio en disco.