Oktatóanyag: Dokumentumösszegző létrehozása

Olyan alkalmazás létrehozása, amely szövegfájlokat olvas be, és tömör összegzéseket hoz létre – teljes egészében az eszközön. Ez akkor hasznos, ha gyorsan meg kell értenie a dokumentumok tartalmát anélkül, hogy teljes mértékben elolvassa őket, és ha a dokumentumok olyan bizalmas információkat tartalmaznak, amelyek nem hagyhatják el a gépet.

Ebben az oktatóanyagban a következőket sajátíthatja el:

  • Projekt beállítása és az Foundry Helyi SDK telepítése
  • Szövegdokumentum beolvasása a fájlrendszerből
  • Modell betöltése és összegzés létrehozása
  • Összesítő kimenet szabályozása rendszerkérésekkel
  • Több dokumentum feldolgozása kötegben
  • Erőforrások tisztítása

Előfeltételek

  • Legalább 8 GB RAM-mal rendelkező Windows, macOS vagy Linux rendszerű számítógép.

Mintaadattár

A cikk teljes mintakódját az Öntödei minták GitHub adattárban találja. Az adattár klónozásához és a mintához való navigáláshoz használja a következőt:

git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/csharp/foundry-local/tutorial-document-summarizer

Csomagok telepítése

Ha Windows fejleszt vagy szállít, válassza a Windows lapot. A Windows csomag integrálható a Windows ML futtatókörnyezettel – ugyanazt az API-felületet biztosítja a hardveres gyorsítás szélesebb körű használatával.

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

A GitHub adattár C#-mintái előre konfigurált projektek. Ha az alapoktól épít, olvassa el a Foundry Local SDK-referenciáját , amelyből megtudhatja, hogyan állíthatja be a C#-projektet a Foundry Local használatával.

Szövegdokumentum olvasása

Mielőtt bármit összefoglalna, szüksége lesz egy mintadokumentumra, amellyel dolgozhat. Hozzon létre egy fájl nevűt document.txt a projektkönyvtárban, és adja hozzá a következő tartalmat:

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.

Most nyissa meg Program.cs , és adja hozzá a következő kódot a dokumentum olvasásához:

var target = args.Length > 0 ? args[0] : "document.txt";

A kód opcionális parancssori argumentumként elfogad egy fájl elérési útját, és az alapértelmezett document.txt értéket használja, ha nincs megadva.

Összegzés létrehozása

Inicializálja az Foundry Local SDK-t, töltsön be egy modellt, és küldje el a dokumentum tartalmát egy rendszerkéréssel együtt, amely a modell összegzésére utasítja.

Cserélje le a Program.cs tartalmát a következő kódra:

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);
}

A GetModelAsync metódus egy modell aliasát fogadja el, amely egy rövid felhasználóbarát név, amely leképezi a katalógus egy adott modelljét. A DownloadAsync metódus lekéri a modell súlyait a helyi gyorsítótárba (és kihagyja a letöltést, ha már gyorsítótárazott), és LoadAsync a modellt készen állítja a következtetéshez. A rendszerkérés arra utasítja a modellt, hogy készítsen listajeles összefoglalókat a fő ötletekre összpontosítva.

Összesítő kimenet szabályozása

A különböző helyzetek különböző összefoglaló stílusokat követelnek meg. A rendszerkérés módosításával szabályozhatja, hogy a modell hogyan strukturálja a kimenetét. Íme három hasznos változat:

Felsorolásjelek (alapértelmezett az előző lépésben):

var systemPrompt =
    "Summarize the following document into concise bullet points. " +
    "Focus on the key points and main ideas.";

Egy bekezdéses összefoglalás:

var systemPrompt =
    "Summarize the following document in a single, concise paragraph. " +
    "Capture the main argument and supporting points.";

Főbb tanulságok:

var systemPrompt =
    "Extract the three most important takeaways from the following document. " +
    "Number each takeaway and keep each to one or two sentences.";

Ha másik stílust szeretne kipróbálni, cserélje le a Content rendszerüzenetben szereplő értéket az egyik utasításra. A modell a rendszerkérés utasításait követve formázza az összegzés formátumát és mélységét.

Több dokumentum feldolgozása

Bővítse ki az alkalmazást egy könyvtár összes .txt fájljának összegzéséhez. Ez akkor hasznos, ha olyan dokumentummappával rendelkezik, amely mindegyiknek összegzésre van szüksége.

Az alábbi módszer bejárja az adott könyvtár összes .txt fájlját, és összefoglalja mindegyiket:

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();
    }
}

A rendszer minden fájlt beolvas, ugyanazzal a rendszerkéréssel párosít, és egymástól függetlenül küldi el a modellnek. A modell nem hordoz kontextust a fájlok között, ezért minden összegzés önálló.

Teljes kód

Cserélje le a következő teljes kód tartalmát Program.cs :

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();
    }
}

Egyetlen fájl összegzése:

dotnet run -- document.txt

Vagy összegezze egy könyvtár összes .txt fájlját:

dotnet run -- ./docs

A következőhöz hasonló kimenet jelenik meg:

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!

Mintaadattár

A cikk teljes mintakódja az öntödei minták GitHub adattárban érhető el. Az adattár klónozásához és a mintahasználathoz való navigáláshoz:

git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/javascript/foundry-local/tutorial-document-summarizer

Csomagok telepítése

Ha Windows fejleszt vagy szállít, válassza a Windows lapot. A Windows csomag integrálható a Windows ML futtatókörnyezettel – ugyanazt az API-felületet biztosítja a hardveres gyorsítás szélesebb körű használatával.

npm install foundry-local-sdk-winml openai

Szövegdokumentum olvasása

Mielőtt bármit összefoglalna, szüksége lesz egy mintadokumentumra, amellyel dolgozhat. Hozzon létre egy fájl nevűt document.txt a projektkönyvtárban, és adja hozzá a következő tartalmat:

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.

Most hozzon létre egy hívott index.js fájlt, és adja hozzá a következő kódot a dokumentum olvasásához:

const target = process.argv[2] || 'document.txt';

A szkript parancssori argumentumként elfogadja az opcionális fájl elérési útját, és visszaesik a document.txt-ra, ha nincs megadva semmi.

Összegzés létrehozása

Inicializálja az Foundry Local SDK-t, töltsön be egy modellt, és küldje el a dokumentum tartalmát egy rendszerkéréssel együtt, amely a modell összegzésére utasítja.

Cserélje le a index.js tartalmát a következő kódra:

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);
}

A getModel metódus egy modell aliasát fogadja el, amely egy rövid felhasználóbarát név, amely leképezi a katalógus egy adott modelljét. A download metódus lekéri a modell súlyait a helyi gyorsítótárba (és kihagyja a letöltést, ha már gyorsítótárazott), és load a modellt készen állítja a következtetéshez. A rendszerkérés arra utasítja a modellt, hogy készítsen listajeles összefoglalókat a fő ötletekre összpontosítva.

Összesítő kimenet szabályozása

A különböző helyzetek különböző összefoglaló stílusokat követelnek meg. A rendszerkérés módosításával szabályozhatja, hogy a modell hogyan strukturálja a kimenetét. Íme három hasznos változat:

Felsorolásjelek (alapértelmezett az előző lépésben):

const systemPrompt =
    'Summarize the following document into concise bullet points. ' +
    'Focus on the key points and main ideas.';

Egy bekezdéses összefoglalás:

const systemPrompt =
    'Summarize the following document in a single, concise paragraph. ' +
    'Capture the main argument and supporting points.';

Főbb tanulságok:

const systemPrompt =
    'Extract the three most important takeaways from the following document. ' +
    'Number each takeaway and keep each to one or two sentences.';

Ha másik stílust szeretne kipróbálni, cserélje le a content rendszerüzenetben szereplő értéket az egyik utasításra. A modell a rendszerkérés utasításait követve formázza az összegzés formátumát és mélységét.

Több dokumentum feldolgozása

Bővítse ki az alkalmazást egy könyvtár összes .txt fájljának összegzéséhez. Ez akkor hasznos, ha olyan dokumentummappával rendelkezik, amely mindegyiknek összegzésre van szüksége.

Az alábbi függvény egy adott könyvtár összes .txt fájlját átfuttatva összegzi az egyes fájlokat:

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();
    }
}

A rendszer minden fájlt beolvas, ugyanazzal a rendszerkéréssel párosít, és egymástól függetlenül küldi el a modellnek. A modell nem hordoz kontextust a fájlok között, ezért minden összegzés önálló.

Teljes kód

Hozzon létre egy elnevezett index.js fájlt, és adja hozzá a következő teljes kódot:

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!');

Egyetlen fájl összegzése:

node index.js document.txt

Vagy összegezze egy könyvtár összes .txt fájlját:

node index.js ./docs

A következőhöz hasonló kimenet jelenik meg:

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!

Mintaadattár

A cikk teljes mintakódja az öntödei minták GitHub adattárban érhető el. Az adattár klónozásához és a mintahasználathoz való navigáláshoz:

git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/python/foundry-local/tutorial-document-summarizer

Csomagok telepítése

Ha Windows fejleszt vagy szállít, válassza a Windows lapot. A Windows csomag integrálható a Windows ML futtatókörnyezettel – ugyanazt az API-felületet biztosítja a hardveres gyorsítás szélesebb körű használatával.

pip install foundry-local-sdk-winml openai

Szövegdokumentum olvasása

Mielőtt bármit összefoglalna, szüksége lesz egy mintadokumentumra, amellyel dolgozhat. Hozzon létre egy fájl nevűt document.txt a projektkönyvtárban, és adja hozzá a következő tartalmat:

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.

Most hozzon létre egy hívott main.py fájlt, és adja hozzá a következő kódot a dokumentum olvasásához:

target = sys.argv[1] if len(sys.argv) > 1 else "document.txt"
target_path = Path(target)

A szkript parancssori argumentumként elfogadja az opcionális fájl elérési útját, és visszaesik a document.txt-ra, ha nincs megadva semmi. A Path.read_text metódus a teljes fájlt egy sztringbe olvassa be.

Összegzés létrehozása

Inicializálja az Foundry Local SDK-t, töltsön be egy modellt, és küldje el a dokumentum tartalmát egy rendszerkéréssel együtt, amely a modell összegzésére utasítja.

Cserélje le a main.py tartalmát a következő kódra:

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)

A get_model metódus egy modell aliasát fogadja el, amely egy rövid felhasználóbarát név, amely leképezi a katalógus egy adott modelljét. A download metódus lekéri a modell súlyait a helyi gyorsítótárba (és kihagyja a letöltést, ha már gyorsítótárazott), és load a modellt készen állítja a következtetéshez. A rendszerkérés arra utasítja a modellt, hogy készítsen listajeles összefoglalókat a fő ötletekre összpontosítva.

Összesítő kimenet szabályozása

A különböző helyzetek különböző összefoglaló stílusokat követelnek meg. A rendszerkérés módosításával szabályozhatja, hogy a modell hogyan strukturálja a kimenetét. Íme három hasznos változat:

Felsorolásjelek (alapértelmezett az előző lépésben):

system_prompt = (
    "Summarize the following document into concise bullet points. "
    "Focus on the key points and main ideas."
)

Egy bekezdéses összefoglalás:

system_prompt = (
    "Summarize the following document in a single, concise paragraph. "
    "Capture the main argument and supporting points."
)

Főbb tanulságok:

system_prompt = (
    "Extract the three most important takeaways from the following document. "
    "Number each takeaway and keep each to one or two sentences."
)

Ha másik stílust szeretne kipróbálni, cserélje le a "content" rendszerüzenetben szereplő értéket az egyik utasításra. A modell a rendszerkérés utasításait követve formázza az összegzés formátumát és mélységét.

Több dokumentum feldolgozása

Bővítse ki az alkalmazást egy könyvtár összes .txt fájljának összegzéséhez. Ez akkor hasznos, ha olyan dokumentummappával rendelkezik, amely mindegyiknek összegzésre van szüksége.

Az alábbi függvény egy adott könyvtár összes .txt fájlját átfuttatva összegzi az egyes fájlokat:

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()

A rendszer minden fájlt beolvas, ugyanazzal a rendszerkéréssel párosít, és egymástól függetlenül küldi el a modellnek. A modell nem hordoz kontextust a fájlok között, ezért minden összegzés önálló.

Teljes kód

Hozzon létre egy elnevezett main.py fájlt, és adja hozzá a következő teljes kódot:

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()

Egyetlen fájl összegzése:

python main.py document.txt

Vagy összegezze egy könyvtár összes .txt fájlját:

python main.py ./docs

A következőhöz hasonló kimenet jelenik meg:

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!

Mintaadattár

A cikk teljes mintakódja az öntödei minták GitHub adattárban érhető el. Az adattár klónozásához és a mintahasználathoz való navigáláshoz:

git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/rust/foundry-local/tutorial-document-summarizer

Csomagok telepítése

Ha Windows fejleszt vagy szállít, válassza a Windows lapot. A Windows csomag integrálható a Windows ML futtatókörnyezettel – ugyanazt az API-felületet biztosítja a hardveres gyorsítás szélesebb körű használatával.

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

Szövegdokumentum olvasása

Mielőtt bármit összefoglalna, szüksége lesz egy mintadokumentumra, amellyel dolgozhat. Hozzon létre egy fájl nevűt document.txt a projektkönyvtárban, és adja hozzá a következő tartalmat:

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.

Most nyissa meg src/main.rs , és adja hozzá a következő kódot a dokumentum olvasásához:

let target = env::args()
    .nth(1)
    .unwrap_or_else(|| "document.txt".to_string());
let target_path = Path::new(&target);

A kód opcionális parancssori argumentumként elfogad egy fájl elérési útját, és az alapértelmezett document.txt értéket használja, ha nincs megadva.

Összegzés létrehozása

Inicializálja az Foundry Local SDK-t, töltsön be egy modellt, és küldje el a dokumentum tartalmát egy rendszerkéréssel együtt, amely a modell összegzésére utasítja.

Cserélje le a src/main.rs tartalmát a következő kódra:

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?;
}

A get_model metódus egy modell aliasát fogadja el, amely egy rövid felhasználóbarát név, amely leképezi a katalógus egy adott modelljét. A download metódus lekéri a modell súlyait a helyi gyorsítótárba (és kihagyja a letöltést, ha már gyorsítótárazott), és load a modellt készen állítja a következtetéshez. A rendszerkérés arra utasítja a modellt, hogy készítsen listajeles összefoglalókat a fő ötletekre összpontosítva.

Összesítő kimenet szabályozása

A különböző helyzetek különböző összefoglaló stílusokat követelnek meg. A rendszerkérés módosításával szabályozhatja, hogy a modell hogyan strukturálja a kimenetét. Íme három hasznos változat:

Felsorolásjelek (alapértelmezett az előző lépésben):

let system_prompt =
    "Summarize the following document into concise bullet points. \
     Focus on the key points and main ideas.";

Egy bekezdéses összefoglalás:

let system_prompt =
    "Summarize the following document in a single, concise paragraph. \
     Capture the main argument and supporting points.";

Főbb tanulságok:

let system_prompt =
    "Extract the three most important takeaways from the following document. \
     Number each takeaway and keep each to one or two sentences.";

Egy másik stílus kipróbálásához cserélje le a rendszerüzenet tartalmát az egyik kérdésre. A modell a rendszerkérés utasításait követve formázza az összegzés formátumát és mélységét.

Több dokumentum feldolgozása

Bővítse ki az alkalmazást egy könyvtár összes .txt fájljának összegzéséhez. Ez akkor hasznos, ha olyan dokumentummappával rendelkezik, amely mindegyiknek összegzésre van szüksége.

Az alábbi függvény egy adott könyvtár összes .txt fájlját átfuttatva összegzi az egyes fájlokat:

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(())
}

A rendszer minden fájlt beolvas, ugyanazzal a rendszerkéréssel párosít, és egymástól függetlenül küldi el a modellnek. A modell nem hordoz kontextust a fájlok között, ezért minden összegzés önálló.

Teljes kód

Cserélje le a következő teljes kód tartalmát src/main.rs :

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("foundry_local_samples"),
    )?;

    // 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(())
}

Egyetlen fájl összegzése:

cargo run -- document.txt

Vagy összegezze egy könyvtár összes .txt fájlját:

cargo run -- ./docs

A következőhöz hasonló kimenet jelenik meg:

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!

Erőforrások tisztítása

A modell súlyai a modell kiürítése után a helyi gyorsítótárban maradnak. Ez azt jelenti, hogy az alkalmazás következő futtatásakor a rendszer kihagyja a letöltési lépést, és a modell gyorsabban betöltődik. Nincs szükség további törlésre, ha vissza szeretné igényelni a lemezterületet.