Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Cree una aplicación que lea archivos de texto y genere resúmenes concisos, completamente en el dispositivo. Esto resulta útil cuando necesita comprender rápidamente el contenido de los documentos sin leerlos en su totalidad y cuando los documentos contienen información confidencial que no debe salir de la máquina.
En este tutorial aprenderá a:
- Configuración de un proyecto e instalación del SDK local de Foundry
- Leer un documento de texto del sistema de archivos
- Carga de un modelo y generación de un resumen
- Controlar la salida de resumen con indicaciones del sistema
- Procesamiento de varios documentos en un lote
- Limpieza de recursos
Prerrequisitos
- Un equipo Windows, macOS o Linux con al menos 8 GB de RAM.
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. Para clonar el repositorio y dirigirte al ejemplo:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/cs/tutorial-document-summarizer
Si estás desarrollando o implementando en Windows, selecciona la pestaña Windows. El paquete de Windows se integra con el runtime Windows ML y ofrece la misma superficie de API con una mayor gama de aceleración por 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.
Leer un documento de texto
Antes de resumir cualquier cosa, necesita un documento de ejemplo con el que trabajar. Cree un archivo llamado document.txt en el directorio del proyecto y agregue el siguiente contenido:
Automated testing is a practice in software development where tests are written and executed
by specialized tools rather than performed manually. There are several categories of automated
tests, including unit tests, integration tests, and end-to-end tests. Unit tests verify that
individual functions or methods behave correctly in isolation. Integration tests check that
multiple components work together as expected. End-to-end tests simulate real user workflows
across the entire application.
Adopting automated testing brings measurable benefits to a development team. It catches
regressions early, before they reach production. It reduces the time spent on repetitive
manual verification after each code change. It serves as living documentation of expected
behavior, which helps new team members understand the codebase. Continuous integration
pipelines rely on automated tests to gate deployments and maintain release quality.
Effective test suites follow a few guiding principles. Tests should be deterministic, meaning
they produce the same result every time they run. Tests should be independent, so that one
failing test does not cascade into false failures elsewhere. Tests should run fast, because
slow tests discourage developers from running them frequently. Finally, tests should be
maintained alongside production code so they stay accurate as the application evolves.
Ahora abra Program.cs y agregue el código siguiente para leer el documento:
var target = args.Length > 0 ? args[0] : "document.txt";
El código acepta una ruta de acceso de archivo opcional como argumento de línea de comandos y vuelve a document.txt si no se proporciona ninguna.
Generación de un resumen
Inicialice el SDK Foundry Local, cargue un modelo y envíe el contenido del documento junto con una instrucción de sistema que pida al modelo que haga un resumen.
Reemplace el contenido de Program.cs por el código siguiente:
var systemPrompt =
"Summarize the following document into concise bullet points. " +
"Focus on the key points and main ideas.";
var target = args.Length > 0 ? args[0] : "document.txt";
if (Directory.Exists(target))
{
await SummarizeDirectoryAsync(chatClient, target, systemPrompt, ct);
}
else
{
Console.WriteLine($"--- {Path.GetFileName(target)} ---");
await SummarizeFileAsync(chatClient, target, systemPrompt, ct);
}
El GetModelAsync método acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. El DownloadAsync método captura los pesos del modelo en la memoria caché local (y omite la descarga si ya están almacenados en caché) y LoadAsync hace que el modelo esté listo para la inferencia. La solicitud del sistema indica al modelo que genere resúmenes en formato de viñetas centrados en las ideas clave.
Salida de resumen de control
Las diferentes situaciones llaman a diferentes estilos de resumen. Puede cambiar el mensaje del sistema para controlar cómo el modelo organiza su salida. Estas son tres variaciones útiles:
Puntos de lista (predeterminado en el paso anterior):
var systemPrompt =
"Summarize the following document into concise bullet points. " +
"Focus on the key points and main ideas.";
Resumen de un párrafo:
var systemPrompt =
"Summarize the following document in a single, concise paragraph. " +
"Capture the main argument and supporting points.";
Puntos clave:
var systemPrompt =
"Extract the three most important takeaways from the following document. " +
"Number each takeaway and keep each to one or two sentences.";
Para probar un estilo diferente, reemplace el valor Content del mensaje del sistema por una de las instrucciones. El modelo sigue las instrucciones del mensaje del sistema para dar forma al formato y la profundidad del resumen.
Procesar varios documentos
Extienda la aplicación para resumir todos los .txt archivos de un directorio. Esto resulta útil cuando tiene una carpeta de documentos que necesitan resúmenes.
El método siguiente recorre en iteración todos los .txt archivos de un directorio determinado y resume cada uno de ellos:
async Task SummarizeDirectoryAsync(
dynamic chatClient,
string directory,
string systemPrompt,
CancellationToken ct)
{
var txtFiles = Directory.GetFiles(directory, "*.txt")
.OrderBy(f => f)
.ToArray();
if (txtFiles.Length == 0)
{
Console.WriteLine($"No .txt files found in {directory}");
return;
}
foreach (var txtFile in txtFiles)
{
var fileContent = await File.ReadAllTextAsync(txtFile, ct);
var msgs = new List<ChatMessage>
{
new ChatMessage { Role = "system", Content = systemPrompt },
new ChatMessage { Role = "user", Content = fileContent }
};
Console.WriteLine($"--- {Path.GetFileName(txtFile)} ---");
var resp = await chatClient.CompleteChatAsync(msgs, ct);
Console.WriteLine(resp.Choices[0].Message.Content);
Console.WriteLine();
}
}
** Cada archivo se lee, se empareja con el mismo mensaje del sistema y se envía al modelo independientemente. El modelo no lleva contexto entre archivos, por lo que cada resumen es independiente.
Código completo
Reemplace el contenido de Program.cs por el código completo siguiente:
using Microsoft.AI.Foundry.Local;
using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels;
using Microsoft.Extensions.Logging;
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();
// Select and load a model from the catalog
var catalog = await mgr.GetCatalogAsync();
var model = await catalog.GetModelAsync("qwen2.5-0.5b")
?? throw new Exception("Model not found");
await model.DownloadAsync(progress =>
{
Console.Write($"\rDownloading model: {progress:F2}%");
if (progress >= 100f) Console.WriteLine();
});
await model.LoadAsync();
Console.WriteLine("Model loaded and ready.\n");
// Get a chat client
var chatClient = await model.GetChatClientAsync();
var systemPrompt =
"Summarize the following document into concise bullet points. " +
"Focus on the key points and main ideas.";
var target = args.Length > 0 ? args[0] : "document.txt";
if (Directory.Exists(target))
{
await SummarizeDirectoryAsync(chatClient, target, systemPrompt, ct);
}
else
{
Console.WriteLine($"--- {Path.GetFileName(target)} ---");
await SummarizeFileAsync(chatClient, target, systemPrompt, ct);
}
// Clean up
await model.UnloadAsync();
Console.WriteLine("\nModel unloaded. Done!");
async Task SummarizeFileAsync(
dynamic client,
string filePath,
string prompt,
CancellationToken token)
{
var fileContent = await File.ReadAllTextAsync(filePath, token);
var messages = new List<ChatMessage>
{
new ChatMessage { Role = "system", Content = prompt },
new ChatMessage { Role = "user", Content = fileContent }
};
var response = await client.CompleteChatAsync(messages, token);
Console.WriteLine(response.Choices[0].Message.Content);
}
async Task SummarizeDirectoryAsync(
dynamic client,
string directory,
string prompt,
CancellationToken token)
{
var txtFiles = Directory.GetFiles(directory, "*.txt")
.OrderBy(f => f)
.ToArray();
if (txtFiles.Length == 0)
{
Console.WriteLine($"No .txt files found in {directory}");
return;
}
foreach (var txtFile in txtFiles)
{
Console.WriteLine($"--- {Path.GetFileName(txtFile)} ---");
await SummarizeFileAsync(client, txtFile, prompt, token);
Console.WriteLine();
}
}
Resumir un único archivo:
dotnet run -- document.txt
O bien, resuma todos los .txt archivos de un directorio:
dotnet run -- ./docs
Verá una salida similar a la siguiente:
Downloading model: 100.00%
Model loaded and ready.
--- document.txt ---
- Automated testing uses specialized tools to execute tests instead of manual verification.
- Tests fall into three main categories: unit tests (individual functions), integration tests
(component interactions), and end-to-end tests (full user workflows).
- Key benefits include catching regressions early, reducing manual effort, serving as living
documentation, and gating deployments through continuous integration pipelines.
- Effective test suites should be deterministic, independent, fast, and maintained alongside
production code.
Model unloaded. Done!
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-document-summarizer
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
Leer un documento de texto
Antes de resumir cualquier cosa, necesita un documento de ejemplo con el que trabajar. Cree un archivo llamado document.txt en el directorio del proyecto y agregue el siguiente contenido:
Automated testing is a practice in software development where tests are written and executed
by specialized tools rather than performed manually. There are several categories of automated
tests, including unit tests, integration tests, and end-to-end tests. Unit tests verify that
individual functions or methods behave correctly in isolation. Integration tests check that
multiple components work together as expected. End-to-end tests simulate real user workflows
across the entire application.
Adopting automated testing brings measurable benefits to a development team. It catches
regressions early, before they reach production. It reduces the time spent on repetitive
manual verification after each code change. It serves as living documentation of expected
behavior, which helps new team members understand the codebase. Continuous integration
pipelines rely on automated tests to gate deployments and maintain release quality.
Effective test suites follow a few guiding principles. Tests should be deterministic, meaning
they produce the same result every time they run. Tests should be independent, so that one
failing test does not cascade into false failures elsewhere. Tests should run fast, because
slow tests discourage developers from running them frequently. Finally, tests should be
maintained alongside production code so they stay accurate as the application evolves.
Ahora cree un archivo llamado index.js y agregue el código siguiente para leer el documento:
const target = process.argv[2] || 'document.txt';
El script acepta una ruta de archivo opcional como argumento de línea de comandos y pasa a document.txt si no se indica ninguna.
Generación de un resumen
Inicialice el SDK Foundry Local, cargue un modelo y envíe el contenido del documento junto con una instrucción de sistema que pida al modelo que haga un resumen.
Reemplace el contenido de index.js por el código siguiente:
const systemPrompt =
'Summarize the following document into concise bullet points. ' +
'Focus on the key points and main ideas.';
const target = process.argv[2] || 'document.txt';
try {
const stats = statSync(target);
if (stats.isDirectory()) {
await summarizeDirectory(chatClient, target, systemPrompt);
} else {
console.log(`--- ${basename(target)} ---`);
await summarizeFile(chatClient, target, systemPrompt);
}
} catch {
console.log(`--- ${basename(target)} ---`);
await summarizeFile(chatClient, target, systemPrompt);
}
El getModel método acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. El download método captura los pesos del modelo en la memoria caché local (y omite la descarga si ya están almacenados en caché) y load hace que el modelo esté listo para la inferencia. La solicitud del sistema indica al modelo que genere resúmenes en formato de viñetas centrados en las ideas clave.
Salida de resumen de control
Las diferentes situaciones llaman a diferentes estilos de resumen. Puede cambiar el mensaje del sistema para controlar cómo el modelo organiza su salida. Estas son tres variaciones útiles:
Puntos de lista (predeterminado en el paso anterior):
const systemPrompt =
'Summarize the following document into concise bullet points. ' +
'Focus on the key points and main ideas.';
Resumen de un párrafo:
const systemPrompt =
'Summarize the following document in a single, concise paragraph. ' +
'Capture the main argument and supporting points.';
Puntos clave:
const systemPrompt =
'Extract the three most important takeaways from the following document. ' +
'Number each takeaway and keep each to one or two sentences.';
Para probar un estilo diferente, reemplace el valor content del mensaje del sistema por una de las instrucciones. El modelo sigue las instrucciones del mensaje del sistema para dar forma al formato y la profundidad del resumen.
Procesar varios documentos
Extienda la aplicación para resumir todos los .txt archivos de un directorio. Esto resulta útil cuando tiene una carpeta de documentos que necesitan resúmenes.
La función siguiente recorre en iteración todos los .txt archivos de un directorio determinado y resume cada uno de ellos:
import { readdirSync } from 'fs';
import { join, basename } from 'path';
async function summarizeDirectory(chatClient, directory, systemPrompt) {
const txtFiles = readdirSync(directory)
.filter(f => f.endsWith('.txt'))
.sort();
if (txtFiles.length === 0) {
console.log(`No .txt files found in ${directory}`);
return;
}
for (const fileName of txtFiles) {
const fileContent = readFileSync(join(directory, fileName), 'utf-8');
const msgs = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: fileContent }
];
console.log(`--- ${fileName} ---`);
const resp = await chatClient.completeChat(msgs);
console.log(resp.choices[0]?.message?.content);
console.log();
}
}
** Cada archivo se lee, se empareja con el mismo mensaje del sistema y se envía al modelo independientemente. El modelo no lleva contexto entre archivos, por lo que cada resumen es independiente.
Código completo
Cree un archivo denominado index.js y agregue el siguiente código completo:
import { FoundryLocalManager } from 'foundry-local-sdk';
import { readFileSync, readdirSync, statSync } from 'fs';
import { join, basename } from 'path';
async function summarizeFile(chatClient, filePath, systemPrompt) {
const content = readFileSync(filePath, 'utf-8');
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: content }
];
const response = await chatClient.completeChat(messages);
console.log(response.choices[0]?.message?.content);
}
async function summarizeDirectory(chatClient, directory, systemPrompt) {
const txtFiles = readdirSync(directory)
.filter(f => f.endsWith('.txt'))
.sort();
if (txtFiles.length === 0) {
console.log(`No .txt files found in ${directory}`);
return;
}
for (const fileName of txtFiles) {
console.log(`--- ${fileName} ---`);
await summarizeFile(chatClient, join(directory, fileName), systemPrompt);
console.log();
}
}
// 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');
// Select and load a model from the catalog
const model = await manager.catalog.getModel('qwen2.5-0.5b');
await model.download((progress) => {
process.stdout.write(`\rDownloading model: ${progress.toFixed(2)}%`);
});
console.log('\nModel downloaded.');
await model.load();
console.log('Model loaded and ready.\n');
// Create a chat client
const chatClient = model.createChatClient();
const systemPrompt =
'Summarize the following document into concise bullet points. ' +
'Focus on the key points and main ideas.';
const target = process.argv[2] || 'document.txt';
try {
const stats = statSync(target);
if (stats.isDirectory()) {
await summarizeDirectory(chatClient, target, systemPrompt);
} else {
console.log(`--- ${basename(target)} ---`);
await summarizeFile(chatClient, target, systemPrompt);
}
} catch {
console.log(`--- ${basename(target)} ---`);
await summarizeFile(chatClient, target, systemPrompt);
}
// Clean up
await model.unload();
console.log('\nModel unloaded. Done!');
Resumir un único archivo:
node index.js document.txt
O bien, resuma todos los .txt archivos de un directorio:
node index.js ./docs
Verá una salida similar a la siguiente:
Downloading model: 100.00%
Model downloaded.
Model loaded and ready.
--- document.txt ---
- Automated testing uses specialized tools to execute tests instead of manual verification.
- Tests fall into three main categories: unit tests (individual functions), integration tests
(component interactions), and end-to-end tests (full user workflows).
- Key benefits include catching regressions early, reducing manual effort, serving as living
documentation, and gating deployments through continuous integration pipelines.
- Effective test suites should be deterministic, independent, fast, and maintained alongside
production code.
Model unloaded. Done!
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-document-summarizer
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
Leer un documento de texto
Antes de resumir cualquier cosa, necesita un documento de ejemplo con el que trabajar. Cree un archivo llamado document.txt en el directorio del proyecto y agregue el siguiente contenido:
Automated testing is a practice in software development where tests are written and executed
by specialized tools rather than performed manually. There are several categories of automated
tests, including unit tests, integration tests, and end-to-end tests. Unit tests verify that
individual functions or methods behave correctly in isolation. Integration tests check that
multiple components work together as expected. End-to-end tests simulate real user workflows
across the entire application.
Adopting automated testing brings measurable benefits to a development team. It catches
regressions early, before they reach production. It reduces the time spent on repetitive
manual verification after each code change. It serves as living documentation of expected
behavior, which helps new team members understand the codebase. Continuous integration
pipelines rely on automated tests to gate deployments and maintain release quality.
Effective test suites follow a few guiding principles. Tests should be deterministic, meaning
they produce the same result every time they run. Tests should be independent, so that one
failing test does not cascade into false failures elsewhere. Tests should run fast, because
slow tests discourage developers from running them frequently. Finally, tests should be
maintained alongside production code so they stay accurate as the application evolves.
Ahora cree un archivo llamado main.py y agregue el código siguiente para leer el documento:
target = sys.argv[1] if len(sys.argv) > 1 else "document.txt"
target_path = Path(target)
El script acepta una ruta de archivo opcional como argumento de línea de comandos y pasa a document.txt si no se indica ninguna. El Path.read_text método lee todo el archivo en una cadena.
Generación de un resumen
Inicialice el SDK Foundry Local, cargue un modelo y envíe el contenido del documento junto con una instrucción de sistema que pida al modelo que haga un resumen.
Reemplace el contenido de main.py por el código siguiente:
system_prompt = (
"Summarize the following document into concise bullet points. "
"Focus on the key points and main ideas."
)
target = sys.argv[1] if len(sys.argv) > 1 else "document.txt"
target_path = Path(target)
if target_path.is_dir():
summarize_directory(client, target_path, system_prompt)
else:
print(f"--- {target_path.name} ---")
summarize_file(client, target_path, system_prompt)
El get_model método acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. El download método captura los pesos del modelo en la memoria caché local (y omite la descarga si ya están almacenados en caché) y load hace que el modelo esté listo para la inferencia. La solicitud del sistema indica al modelo que genere resúmenes en formato de viñetas centrados en las ideas clave.
Salida de resumen de control
Las diferentes situaciones llaman a diferentes estilos de resumen. Puede cambiar el mensaje del sistema para controlar cómo el modelo organiza su salida. Estas son tres variaciones útiles:
Puntos de lista (predeterminado en el paso anterior):
system_prompt = (
"Summarize the following document into concise bullet points. "
"Focus on the key points and main ideas."
)
Resumen de un párrafo:
system_prompt = (
"Summarize the following document in a single, concise paragraph. "
"Capture the main argument and supporting points."
)
Puntos clave:
system_prompt = (
"Extract the three most important takeaways from the following document. "
"Number each takeaway and keep each to one or two sentences."
)
Para probar un estilo diferente, reemplace el valor "content" del mensaje del sistema por una de las instrucciones. El modelo sigue las instrucciones del mensaje del sistema para dar forma al formato y la profundidad del resumen.
Procesar varios documentos
Extienda la aplicación para resumir todos los .txt archivos de un directorio. Esto resulta útil cuando tiene una carpeta de documentos que necesitan resúmenes.
La función siguiente recorre en iteración todos los .txt archivos de un directorio determinado y resume cada uno de ellos:
async def summarize_directory(client, directory):
txt_files = sorted(Path(directory).glob("*.txt"))
if not txt_files:
print(f"No .txt files found in {directory}")
return
for txt_file in txt_files:
content = txt_file.read_text(encoding="utf-8")
messages = [
{
"role": "system",
"content": "Summarize the following document into concise bullet points. "
"Focus on the key points and main ideas."
},
{"role": "user", "content": content}
]
print(f"--- {txt_file.name} ---")
response = client.complete_chat(messages)
print(response.choices[0].message.content)
print()
Cada archivo se lee, se empareja con la misma indicación del sistema y se envía al modelo de forma independiente. El modelo no lleva contexto entre archivos, por lo que cada resumen es independiente.
Código completo
Cree un archivo denominado main.py y agregue el siguiente código completo:
import sys
from pathlib import Path
from foundry_local_sdk import Configuration, FoundryLocalManager
def summarize_file(client, file_path, system_prompt):
"""Summarize a single file and print the result."""
content = Path(file_path).read_text(encoding="utf-8")
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": content}
]
response = client.complete_chat(messages)
print(response.choices[0].message.content)
def summarize_directory(client, directory, system_prompt):
"""Summarize all .txt files in a directory."""
txt_files = sorted(Path(directory).glob("*.txt"))
if not txt_files:
print(f"No .txt files found in {directory}")
return
for txt_file in txt_files:
print(f"--- {txt_file.name} ---")
summarize_file(client, txt_file, system_prompt)
print()
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()
# Select and load a model from the catalog
model = manager.catalog.get_model("qwen2.5-0.5b")
model.download(lambda p: print(f"\rDownloading model: {p:.2f}%", end="", flush=True))
print()
model.load()
print("Model loaded and ready.\n")
# Get a chat client
client = model.get_chat_client()
system_prompt = (
"Summarize the following document into concise bullet points. "
"Focus on the key points and main ideas."
)
target = sys.argv[1] if len(sys.argv) > 1 else "document.txt"
target_path = Path(target)
if target_path.is_dir():
summarize_directory(client, target_path, system_prompt)
else:
print(f"--- {target_path.name} ---")
summarize_file(client, target_path, system_prompt)
# Clean up
model.unload()
print("\nModel unloaded. Done!")
if __name__ == "__main__":
main()
Resumir un único archivo:
python main.py document.txt
O bien, resuma todos los .txt archivos de un directorio:
python main.py ./docs
Verá una salida similar a la siguiente:
Downloading model: 100.00%
Model loaded and ready.
--- document.txt ---
- Automated testing uses specialized tools to execute tests instead of manual verification.
- Tests fall into three main categories: unit tests (individual functions), integration tests
(component interactions), and end-to-end tests (full user workflows).
- Key benefits include catching regressions early, reducing manual effort, serving as living
documentation, and gating deployments through continuous integration pipelines.
- Effective test suites should be deterministic, independent, fast, and maintained alongside
production code.
Model unloaded. Done!
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-document-summarizer
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
Leer un documento de texto
Antes de resumir cualquier cosa, necesita un documento de ejemplo con el que trabajar. Cree un archivo llamado document.txt en el directorio del proyecto y agregue el siguiente contenido:
Automated testing is a practice in software development where tests are written and executed
by specialized tools rather than performed manually. There are several categories of automated
tests, including unit tests, integration tests, and end-to-end tests. Unit tests verify that
individual functions or methods behave correctly in isolation. Integration tests check that
multiple components work together as expected. End-to-end tests simulate real user workflows
across the entire application.
Adopting automated testing brings measurable benefits to a development team. It catches
regressions early, before they reach production. It reduces the time spent on repetitive
manual verification after each code change. It serves as living documentation of expected
behavior, which helps new team members understand the codebase. Continuous integration
pipelines rely on automated tests to gate deployments and maintain release quality.
Effective test suites follow a few guiding principles. Tests should be deterministic, meaning
they produce the same result every time they run. Tests should be independent, so that one
failing test does not cascade into false failures elsewhere. Tests should run fast, because
slow tests discourage developers from running them frequently. Finally, tests should be
maintained alongside production code so they stay accurate as the application evolves.
Ahora abra src/main.rs y agregue el código siguiente para leer el documento:
let target = env::args()
.nth(1)
.unwrap_or_else(|| "document.txt".to_string());
let target_path = Path::new(&target);
El código acepta una ruta de acceso de archivo opcional como argumento de línea de comandos y vuelve a document.txt si no se proporciona ninguna.
Generación de un resumen
Inicialice el SDK local de Foundry, cargue un modelo y envíe el contenido del documento junto con una instrucción al sistema que indique al modelo resumir el contenido.
Reemplace el contenido de src/main.rs por el código siguiente:
let system_prompt = "Summarize the following document \
into concise bullet points. Focus on the key \
points and main ideas.";
let target = env::args()
.nth(1)
.unwrap_or_else(|| "document.txt".to_string());
let target_path = Path::new(&target);
if target_path.is_dir() {
summarize_directory(
&client,
target_path,
system_prompt,
)
.await?;
} else {
let file_name = target_path
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| target.clone());
println!("--- {} ---", file_name);
summarize_file(
&client,
target_path,
system_prompt,
)
.await?;
}
El get_model método acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. El download método captura los pesos del modelo en la memoria caché local (y omite la descarga si ya están almacenados en caché) y load hace que el modelo esté listo para la inferencia. La indicación del sistema le dice al modelo que genere resúmenes en viñetas enfocados en las ideas clave.
Salida de resumen de control
Las diferentes situaciones llaman a diferentes estilos de resumen. Puede cambiar el indicador del sistema para controlar cómo el modelo organiza su respuesta. Estas son tres variaciones útiles:
Puntos de lista (predeterminado en el paso anterior):
let system_prompt =
"Summarize the following document into concise bullet points. \
Focus on the key points and main ideas.";
Resumen de un párrafo:
let system_prompt =
"Summarize the following document in a single, concise paragraph. \
Capture the main argument and supporting points.";
Puntos clave:
let system_prompt =
"Extract the three most important takeaways from the following document. \
Number each takeaway and keep each to one or two sentences.";
Para probar un estilo diferente, sustituya el contenido del mensaje del sistema por uno de los avisos. El modelo sigue las indicaciones de la instrucción de sistema para ajustar el formato y los detalles del resumen.
Procesar varios documentos
Extienda la aplicación para resumir todos los .txt archivos de un directorio. Esto resulta útil cuando tiene una carpeta de documentos que necesitan resúmenes.
La función siguiente recorre en iteración todos los .txt archivos de un directorio determinado y resume cada uno de ellos:
use std::path::Path;
async fn summarize_directory(
client: &foundry_local_sdk::ChatClient,
directory: &Path,
system_prompt: &str,
) -> anyhow::Result<()> {
let mut txt_files: Vec<_> = fs::read_dir(directory)?
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry.path().extension()
.map(|ext| ext == "txt")
.unwrap_or(false)
})
.collect();
txt_files.sort_by_key(|e| e.path());
if txt_files.is_empty() {
println!("No .txt files found in {}", directory.display());
return Ok(());
}
for entry in &txt_files {
let file_content = fs::read_to_string(entry.path())?;
let messages: Vec<ChatCompletionRequestMessage> = vec![
ChatCompletionRequestSystemMessage::new(system_prompt).into(),
ChatCompletionRequestUserMessage::new(&file_content).into(),
];
let file_name = entry.file_name();
println!("--- {} ---", file_name.to_string_lossy());
let resp = client.complete_chat(&messages, None).await?;
let text = resp.choices[0]
.message
.content
.as_deref()
.unwrap_or("");
println!("{}\n", text);
}
Ok(())
}
Cada archivo se lee, se empareja con la misma sugerencia del sistema y se envía al modelo de forma independiente. El modelo no lleva contexto entre archivos, por lo que cada resumen es independiente.
Código completo
Reemplace el contenido de src/main.rs por el código completo siguiente:
use foundry_local_sdk::{
ChatCompletionRequestMessage,
ChatCompletionRequestSystemMessage,
ChatCompletionRequestUserMessage, FoundryLocalConfig,
FoundryLocalManager,
};
use std::io::{self, Write};
use std::path::Path;
use std::{env, fs};
async fn summarize_file(
client: &foundry_local_sdk::openai::ChatClient,
file_path: &Path,
system_prompt: &str,
) -> anyhow::Result<()> {
let content = fs::read_to_string(file_path)?;
let messages: Vec<ChatCompletionRequestMessage> = vec![
ChatCompletionRequestSystemMessage::from(system_prompt)
.into(),
ChatCompletionRequestUserMessage::from(content.as_str())
.into(),
];
let response =
client.complete_chat(&messages, None).await?;
let summary = response.choices[0]
.message
.content
.as_deref()
.unwrap_or("");
println!("{}", summary);
Ok(())
}
async fn summarize_directory(
client: &foundry_local_sdk::openai::ChatClient,
directory: &Path,
system_prompt: &str,
) -> anyhow::Result<()> {
let mut txt_files: Vec<_> = fs::read_dir(directory)?
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry
.path()
.extension()
.map(|ext| ext == "txt")
.unwrap_or(false)
})
.collect();
txt_files.sort_by_key(|e| e.path());
if txt_files.is_empty() {
println!(
"No .txt files found in {}",
directory.display()
);
return Ok(());
}
for entry in &txt_files {
let file_name = entry.file_name();
println!(
"--- {} ---",
file_name.to_string_lossy()
);
summarize_file(
client,
&entry.path(),
system_prompt,
)
.await?;
println!();
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize the Foundry Local SDK
let manager = FoundryLocalManager::create(
FoundryLocalConfig::new("doc-summarizer"),
)?;
// 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!();
// Select and load a model from the catalog
let model = manager
.catalog()
.get_model("qwen2.5-0.5b")
.await?;
if !model.is_cached().await? {
println!("Downloading model...");
model
.download(Some(|progress: f64| {
print!("\r {progress:.1}%");
io::stdout().flush().ok();
}))
.await?;
println!();
}
model.load().await?;
println!("Model loaded and ready.\n");
// Create a chat client
let client = model
.create_chat_client()
.temperature(0.7)
.max_tokens(512);
let system_prompt = "Summarize the following document \
into concise bullet points. Focus on the key \
points and main ideas.";
let target = env::args()
.nth(1)
.unwrap_or_else(|| "document.txt".to_string());
let target_path = Path::new(&target);
if target_path.is_dir() {
summarize_directory(
&client,
target_path,
system_prompt,
)
.await?;
} else {
let file_name = target_path
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| target.clone());
println!("--- {} ---", file_name);
summarize_file(
&client,
target_path,
system_prompt,
)
.await?;
}
// Clean up
model.unload().await?;
println!("\nModel unloaded. Done!");
Ok(())
}
Resumir un único archivo:
cargo run -- document.txt
O bien, resuma todos los .txt archivos de un directorio:
cargo run -- ./docs
Verá una salida similar a la siguiente:
Downloading model: 100.00%
Model loaded and ready.
--- document.txt ---
- Automated testing uses specialized tools to execute tests instead of manual verification.
- Tests fall into three main categories: unit tests (individual functions), integration tests
(component interactions), and end-to-end tests (full user workflows).
- Key benefits include catching regressions early, reducing manual effort, serving as living
documentation, and gating deployments through continuous integration pipelines.
- Effective test suites should be deterministic, independent, fast, and maintained alongside
production code.
Model unloaded. Done!
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.