Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Utwórz aplikację, która odczytuje pliki tekstowe i generuje zwięzłe podsumowania — w całości na urządzeniu. Jest to przydatne, gdy trzeba szybko zrozumieć zawartość dokumentów bez ich pełnego odczytania, a dokumenty zawierają poufne informacje, które nie powinny opuszczać komputera.
W tym poradniku nauczysz się, jak:
- Konfigurowanie projektu i instalowanie lokalnego zestawu SDK rozwiązania Foundry
- Odczytywanie dokumentu tekstowego z systemu plików
- Ładowanie modelu i generowanie podsumowania
- Kontrolowanie wyjściowych podsumowań za pomocą monitów systemowych
- Przetwarzanie wielu dokumentów zbiorczo
- Uprzątnij zasoby
Wymagania wstępne
- Komputer Windows, macOS lub Linux z co najmniej 8 GB pamięci RAM.
- .NET 8.0 SDK lub nowsza wersja zainstalowana.
Repozytorium przykładów
Pełny kod przykładowy do tego artykułu można znaleźć w repozytorium GitHub przykładów platformy Foundry. Aby sklonować repozytorium i przejść do przykładu, użyj następującego polecenia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/csharp/foundry-local/tutorial-document-summarizer
Instalowanie pakietów
dotnet add package Microsoft.AI.Foundry.Local
dotnet add package OpenAI
Przykłady języka C# w repozytorium GitHub to wstępnie skonfigurowane projekty. Jeśli tworzysz od podstaw, zapoznaj się z dokumentacją zestawu SDK lokalnego rozwiązania Foundry , aby uzyskać więcej informacji na temat sposobu konfigurowania projektu w języku C# przy użyciu rozwiązania Foundry Local.
Odczytywanie dokumentu tekstowego
Zanim podsumujesz wszystko, musisz pracować z przykładowym dokumentem. Utwórz plik o nazwie document.txt w katalogu projektu i dodaj następującą zawartość:
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.
Teraz otwórz Program.cs i dodaj następujący kod, aby odczytać dokument:
var target = args.Length > 0 ? args[0] : "document.txt";
Kod akceptuje opcjonalną ścieżkę pliku jako argument wiersza polecenia i domyślnie korzysta z document.txt, jeśli nie podano żadnej.
Generowanie podsumowania
Zainicjuj lokalny zestaw SDK usługi Foundry, załaduj model i wyślij zawartość dokumentu wraz z monitem systemowym, który nakazuje modelowi podsumowanie.
Zastąp zawartość Program.cs następującym kodem:
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(model, target, systemPrompt, ct);
}
else
{
Console.WriteLine($"--- {Path.GetFileName(target)} ---");
await SummarizeFileAsync(model, target, systemPrompt, ct);
}
Metoda GetModelAsync akceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metoda DownloadAsync pobiera wagi modelu do lokalnej pamięci podręcznej (i pomija pobieranie, jeśli są już buforowane) i LoadAsync sprawia, że model jest gotowy do wnioskowania. Monit systemowy informuje model o utworzeniu podsumowań punktów punktowych skoncentrowanych na kluczowych pomysłach.
Dane wyjściowe podsumowania kontroli
Różne sytuacje wymagają różnych stylów podsumowania. Możesz zmienić monit systemowy, aby kontrolować sposób tworzenia struktury danych wyjściowych modelu. Oto trzy przydatne odmiany:
Punkty punktorowe (wartość domyślna z poprzedniego kroku):
var systemPrompt =
"Summarize the following document into concise bullet points. " +
"Focus on the key points and main ideas.";
Podsumowanie jednego akapitu:
var systemPrompt =
"Summarize the following document in a single, concise paragraph. " +
"Capture the main argument and supporting points.";
Najważniejsze wnioski:
var systemPrompt =
"Extract the three most important takeaways from the following document. " +
"Number each takeaway and keep each to one or two sentences.";
Aby wypróbować inny styl, zastąp Content wartość w komunikacie systemowym jednym z monitów. Model realizuje instrukcje w systemie, aby ukształtować format i głębokość podsumowania.
Przetwarzanie wielu dokumentów
Rozszerz aplikację, aby podsumować każdy .txt plik w katalogu. Jest to przydatne, gdy masz folder dokumentów, które wymagają podsumowań.
Poniższa metoda iteruje wszystkie .txt pliki w danym katalogu i podsumowuje każdy z nich:
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();
}
}
Każdy plik jest odczytywany, sparowany z tym samym monitem systemowym i wysyłany niezależnie do modelu. Model nie przenosi kontekstu między plikami, więc każde podsumowanie jest samodzielne.
Kompletny kod
Zastąp zawartość Program.cs następującym kompletnym kodem:
using Microsoft.AI.Foundry.Local;
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");
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(model, target, systemPrompt, ct);
}
else
{
Console.WriteLine($"--- {Path.GetFileName(target)} ---");
await SummarizeFileAsync(model, target, systemPrompt, ct);
}
// Clean up
await model.UnloadAsync();
Console.WriteLine("\nModel unloaded. Done!");
async Task SummarizeFileAsync(
IModel loadedModel,
string filePath,
string prompt,
CancellationToken token)
{
var fileContent = await File.ReadAllTextAsync(filePath, token);
using var session = new ChatSession(loadedModel);
using var request = new Request();
request.AddItem(MessageItem.System(prompt));
request.AddItem(MessageItem.User(fileContent));
using var response = await session.ProcessRequestAsync(request, token);
Console.WriteLine(string.Concat(
response.OfType<MessageItem>()
.SelectMany(message => message.Parts)
.OfType<TextItem>()
.Where(text => text.Type == TextItemType.Default)
.Select(text => text.Text)));
}
async Task SummarizeDirectoryAsync(
IModel loadedModel,
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(loadedModel, txtFile, prompt, token);
Console.WriteLine();
}
}
Podsumowywanie pojedynczego pliku:
dotnet run -- document.txt
Lub podsumuj każdy .txt plik w katalogu:
dotnet run -- ./docs
Zobaczysz dane wyjściowe podobne do:
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!
- Node.js 20 lub nowszy zainstalowany.
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/javascript/foundry-local/tutorial-document-summarizer
Instalowanie pakietów
npm install foundry-local-sdk openai
Odczytywanie dokumentu tekstowego
Zanim podsumujesz wszystko, musisz pracować z przykładowym dokumentem. Utwórz plik o nazwie document.txt w katalogu projektu i dodaj następującą zawartość:
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.
Teraz utwórz plik o nazwie index.js i dodaj następujący kod, aby odczytać dokument:
const target = process.argv[2] || 'document.txt';
Skrypt akceptuje opcjonalną ścieżkę pliku jako argument wiersza polecenia i wraca do document.txt, jeśli żadna nie została podana.
Generowanie podsumowania
Zainicjuj lokalny zestaw SDK usługi Foundry, załaduj model i wyślij zawartość dokumentu wraz z monitem systemowym, który nakazuje modelowi podsumowanie.
Zastąp zawartość index.js następującym kodem:
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(model, target, systemPrompt);
} else {
console.log(`--- ${basename(target)} ---`);
const session = new ChatSession(model);
await summarizeFile(session, target, systemPrompt);
session.dispose();
}
} catch {
console.log(`--- ${basename(target)} ---`);
const session = new ChatSession(model);
await summarizeFile(session, target, systemPrompt);
session.dispose();
}
Metoda getModel akceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metoda download pobiera wagi modelu do lokalnej pamięci podręcznej (i pomija pobieranie, jeśli są już buforowane) i load sprawia, że model jest gotowy do wnioskowania. Monit systemowy informuje model o utworzeniu podsumowań punktów punktowych skoncentrowanych na kluczowych pomysłach.
Dane wyjściowe podsumowania kontroli
Różne sytuacje wymagają różnych stylów podsumowania. Możesz zmienić monit systemowy, aby kontrolować sposób tworzenia struktury danych wyjściowych modelu. Oto trzy przydatne odmiany:
Punkty punktorowe (wartość domyślna z poprzedniego kroku):
const systemPrompt =
'Summarize the following document into concise bullet points. ' +
'Focus on the key points and main ideas.';
Podsumowanie jednego akapitu:
const systemPrompt =
'Summarize the following document in a single, concise paragraph. ' +
'Capture the main argument and supporting points.';
Najważniejsze wnioski:
const systemPrompt =
'Extract the three most important takeaways from the following document. ' +
'Number each takeaway and keep each to one or two sentences.';
Aby wypróbować inny styl, zastąp content wartość w komunikacie systemowym jednym z monitów. Model realizuje instrukcje w systemie, aby ukształtować format i głębokość podsumowania.
Przetwarzanie wielu dokumentów
Rozszerz aplikację, aby podsumować każdy .txt plik w katalogu. Jest to przydatne, gdy masz folder dokumentów, które wymagają podsumowań.
Następująca funkcja iteruje wszystkie .txt pliki w danym katalogu i podsumowuje każdy z nich:
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();
}
}
Każdy plik jest odczytywany, sparowany z tym samym monitem systemowym i wysyłany niezależnie do modelu. Model nie przenosi kontekstu między plikami, więc każde podsumowanie jest samodzielne.
Kompletny kod
Utwórz plik o nazwie index.js i dodaj następujący kompletny kod:
import {
ChatSession,
FoundryLocalManager,
Item,
Request
} from 'foundry-local-sdk';
import { readFileSync, readdirSync, statSync } from 'fs';
import { join, basename } from 'path';
async function summarizeFile(chatSession, filePath, systemPrompt) {
const content = readFileSync(filePath, 'utf-8');
const request = new Request()
.addItem(Item.systemMessage(systemPrompt))
.addItem(Item.userMessage(content));
const response = await chatSession.processRequest(request);
const text = response.output
.map((item) => item.type === 'message'
? item.content ?? item.parts?.map((part) => part.text ?? '').join('') ?? ''
: item.type === 'text' ? item.text : '')
.join('');
console.log(text);
}
async function summarizeDirectory(model, 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} ---`);
const session = new ChatSession(model);
await summarizeFile(session, join(directory, fileName), systemPrompt);
session.dispose();
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');
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(model, target, systemPrompt);
} else {
console.log(`--- ${basename(target)} ---`);
const session = new ChatSession(model);
await summarizeFile(session, target, systemPrompt);
session.dispose();
}
} catch {
console.log(`--- ${basename(target)} ---`);
const session = new ChatSession(model);
await summarizeFile(session, target, systemPrompt);
session.dispose();
}
// Clean up
await model.unload();
console.log('\nModel unloaded. Done!');
Podsumowywanie pojedynczego pliku:
node index.js document.txt
Lub podsumuj każdy .txt plik w katalogu:
node index.js ./docs
Zobaczysz dane wyjściowe podobne do:
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!
- Python 3.11 lub nowszy.
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/python/foundry-local/tutorial-document-summarizer
Instalowanie pakietów
pip install foundry-local-sdk openai
Odczytywanie dokumentu tekstowego
Zanim podsumujesz wszystko, musisz pracować z przykładowym dokumentem. Utwórz plik o nazwie document.txt w katalogu projektu i dodaj następującą zawartość:
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.
Teraz utwórz plik o nazwie main.py i dodaj następujący kod, aby odczytać dokument:
target = sys.argv[1] if len(sys.argv) > 1 else "document.txt"
target_path = Path(target)
Skrypt akceptuje opcjonalną ścieżkę pliku jako argument wiersza polecenia i wraca do document.txt, jeśli żadna nie została podana. Metoda Path.read_text odczytuje cały plik do ciągu znaków.
Generowanie podsumowania
Zainicjuj lokalny zestaw SDK usługi Foundry, załaduj model i wyślij zawartość dokumentu wraz z monitem systemowym, który nakazuje modelowi podsumowanie.
Zastąp zawartość main.py następującym kodem:
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(model, target_path, system_prompt)
else:
print(f"--- {target_path.name} ---")
summarize_file(model, target_path, system_prompt)
Metoda get_model akceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metoda download pobiera wagi modelu do lokalnej pamięci podręcznej (i pomija pobieranie, jeśli są już buforowane) i load sprawia, że model jest gotowy do wnioskowania. Monit systemowy informuje model o utworzeniu podsumowań punktów punktowych skoncentrowanych na kluczowych pomysłach.
Dane wyjściowe podsumowania kontroli
Różne sytuacje wymagają różnych stylów podsumowania. Możesz zmienić monit systemowy, aby kontrolować sposób tworzenia struktury danych wyjściowych modelu. Oto trzy przydatne odmiany:
Punkty punktorowe (wartość domyślna z poprzedniego kroku):
system_prompt = (
"Summarize the following document into concise bullet points. "
"Focus on the key points and main ideas."
)
Podsumowanie jednego akapitu:
system_prompt = (
"Summarize the following document in a single, concise paragraph. "
"Capture the main argument and supporting points."
)
Najważniejsze wnioski:
system_prompt = (
"Extract the three most important takeaways from the following document. "
"Number each takeaway and keep each to one or two sentences."
)
Aby wypróbować inny styl, zastąp "content" wartość w komunikacie systemowym jednym z monitów. Model realizuje instrukcje w systemie, aby ukształtować format i głębokość podsumowania.
Przetwarzanie wielu dokumentów
Rozszerz aplikację, aby podsumować każdy .txt plik w katalogu. Jest to przydatne, gdy masz folder dokumentów, które wymagają podsumowań.
Następująca funkcja iteruje wszystkie .txt pliki w danym katalogu i podsumowuje każdy z nich:
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()
Każdy plik jest odczytywany, sparowany z tym samym monitem systemowym i wysyłany niezależnie do modelu. Model nie przenosi kontekstu między plikami, więc każde podsumowanie jest samodzielne.
Kompletny kod
Utwórz plik o nazwie main.py i dodaj następujący kompletny kod:
import sys
from pathlib import Path
from foundry_local_sdk import (
ChatSession,
Configuration,
FoundryLocalManager,
MessageItem,
Request,
RequestOptions,
SearchOptions,
TextItem,
)
def get_text(response):
parts = []
for item in response:
if isinstance(item, TextItem):
parts.append(item.text)
elif isinstance(item, MessageItem):
parts.extend(part.text for part in item.parts if isinstance(part, TextItem))
return "".join(parts)
def summarize_file(model, file_path, system_prompt):
"""Summarize a single file and print the result."""
content = Path(file_path).read_text(encoding="utf-8")
with ChatSession(model) as session:
session.set_options(
RequestOptions(
search=SearchOptions(temperature=0.2, max_output_tokens=512)
)
)
with (
Request()
.add_item(MessageItem.system(system_prompt))
.add_item(MessageItem.user(content)) as request
):
with session.process_request(request) as response:
print(get_text(response))
def summarize_directory(model, 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(model, 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
# Discover and register execution providers that are not already available.
missing_eps = [ep.name for ep in manager.discover_eps() if not ep.is_registered]
if missing_eps:
manager.download_and_register_eps(missing_eps)
# Select and load a model from the catalog.
model = manager.catalog.get_model("qwen2.5-0.5b")
if model is None:
raise RuntimeError("Model 'qwen2.5-0.5b' was not found in the catalog.")
if not model.is_cached:
model.download(
lambda progress: print(
f"\rDownloading model: {progress:.2f}%", end="", flush=True
)
)
print()
model.load()
print("Model loaded and ready.\n")
try:
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(model, target_path, system_prompt)
else:
print(f"--- {target_path.name} ---")
summarize_file(model, target_path, system_prompt)
finally:
# Clean up
if model.is_loaded:
model.unload()
manager.close()
print("\nModel unloaded. Done!")
if __name__ == "__main__":
main()
Podsumowywanie pojedynczego pliku:
python main.py document.txt
Lub podsumuj każdy .txt plik w katalogu:
python main.py ./docs
Zobaczysz dane wyjściowe podobne do:
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!
- Zainstalowano oprogramowanie Rust i Cargo (Rust 1.70.0 lub nowszy).
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/rust/foundry-local/tutorial-document-summarizer
Instalowanie pakietów
cargo add foundry-local-sdk
cargo add tokio --features full
cargo add tokio-stream anyhow
Odczytywanie dokumentu tekstowego
Zanim podsumujesz wszystko, musisz pracować z przykładowym dokumentem. Utwórz plik o nazwie document.txt w katalogu projektu i dodaj następującą zawartość:
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.
Teraz otwórz src/main.rs i dodaj następujący kod, aby odczytać dokument:
let target = env::args()
.nth(1)
.unwrap_or_else(|| "document.txt".to_string());
let target_path = Path::new(&target);
Kod akceptuje opcjonalną ścieżkę pliku jako argument wiersza polecenia i domyślnie korzysta z document.txt, jeśli nie podano żadnej.
Generowanie podsumowania
Zainicjuj lokalny zestaw SDK usługi Foundry, załaduj model i wyślij zawartość dokumentu wraz z monitem systemowym, który nakazuje modelowi podsumowanie.
Zastąp zawartość src/main.rs następującym kodem:
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(&model, target_path, system_prompt).await?;
} else {
let file_name = target_path
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_else(|| target.clone());
println!("--- {file_name} ---");
summarize_file(&model, target_path, system_prompt).await?;
}
Metoda get_model akceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metoda download pobiera wagi modelu do lokalnej pamięci podręcznej (i pomija pobieranie, jeśli są już buforowane) i load sprawia, że model jest gotowy do wnioskowania. Monit systemowy informuje model o utworzeniu podsumowań punktów punktowych skoncentrowanych na kluczowych pomysłach.
Dane wyjściowe podsumowania kontroli
Różne sytuacje wymagają różnych stylów podsumowania. Możesz zmienić monit systemowy, aby kontrolować sposób tworzenia struktury danych wyjściowych modelu. Oto trzy przydatne odmiany:
Punkty punktorowe (wartość domyślna z poprzedniego kroku):
let system_prompt =
"Summarize the following document into concise bullet points. \
Focus on the key points and main ideas.";
Podsumowanie jednego akapitu:
let system_prompt =
"Summarize the following document in a single, concise paragraph. \
Capture the main argument and supporting points.";
Najważniejsze wnioski:
let system_prompt =
"Extract the three most important takeaways from the following document. \
Number each takeaway and keep each to one or two sentences.";
Aby wypróbować inny styl, zastąp zawartość komunikatów systemowych jednym z monitów. Model realizuje instrukcje w systemie, aby ukształtować format i głębokość podsumowania.
Przetwarzanie wielu dokumentów
Rozszerz aplikację, aby podsumować każdy .txt plik w katalogu. Jest to przydatne, gdy masz folder dokumentów, które wymagają podsumowań.
Następująca funkcja iteruje wszystkie .txt pliki w danym katalogu i podsumowuje każdy z nich:
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(())
}
Każdy plik jest odczytywany, sparowany z tym samym monitem systemowym i wysyłany niezależnie do modelu. Model nie przenosi kontekstu między plikami, więc każde podsumowanie jest samodzielne.
Kompletny kod
Zastąp zawartość src/main.rs następującym kompletnym kodem:
use foundry_local_sdk::{
ChatSession, FoundryLocalConfig, FoundryLocalManager, Item, Model, Request, RequestOptions,
SearchOptions,
};
use std::io::{self, Write};
use std::path::Path;
use std::{env, fs};
async fn summarize_file(
model: &Model,
file_path: &Path,
system_prompt: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let content = fs::read_to_string(file_path)?;
let session = ChatSession::new(model).await?;
session
.set_options(RequestOptions {
search: SearchOptions {
temperature: Some(0.7),
max_output_tokens: Some(512),
..Default::default()
},
..Default::default()
})
.await?;
let response = session
.process_request(Request::from_items(vec![
Item::system_message(vec![Item::text(system_prompt)]),
Item::user_message(vec![Item::text(content)]),
]))
.await?;
for item in &response.items {
if let Some(message) = item.as_message() {
print!("{}", message.text());
} else if let Some(text) = item.as_text() {
print!("{text}");
}
}
println!();
drop(session);
Ok(())
}
async fn summarize_directory(
model: &Model,
directory: &Path,
system_prompt: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mut txt_files: Vec<_> = fs::read_dir(directory)?
.filter_map(Result::ok)
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "txt"))
.collect();
txt_files.sort_by_key(|entry| entry.path());
if txt_files.is_empty() {
println!("No .txt files found in {}", directory.display());
return Ok(());
}
for entry in &txt_files {
println!("--- {} ---", entry.file_name().to_string_lossy());
summarize_file(model, &entry.path(), system_prompt).await?;
println!();
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the Foundry Local SDK
let manager = FoundryLocalManager::create(FoundryLocalConfig::new("foundry_local_samples"))?;
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 {ep_name:<30} {percent:5.1}%");
io::stdout().flush().ok();
}
})
.await?;
println!();
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");
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(&model, target_path, system_prompt).await?;
} else {
let file_name = target_path
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_else(|| target.clone());
println!("--- {file_name} ---");
summarize_file(&model, target_path, system_prompt).await?;
}
model.unload().await?;
println!("\nModel unloaded. Done!");
Ok(())
}
Podsumowywanie pojedynczego pliku:
cargo run -- document.txt
Lub podsumuj każdy .txt plik w katalogu:
cargo run -- ./docs
Zobaczysz dane wyjściowe podobne do:
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!
Repozytorium przykładów
Sklonuj repozytorium GitHub z przykładami usługi Foundry i przejdź do przykładu języka C++:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/cpp/foundry-local/tutorial-document-summarizer
Instalowanie pakietów
Przykłady języka C++ wymagają:
Skonfiguruj i skompiluj przykład:
cmake -S . -B build
cmake --build build --config Release
Podczas konfiguracji przykład przywraca pakiet NuGet Microsoft.AI.Foundry.Local.Runtime w wersji 2.2.1 do build/_packages. Pakiet zawiera nagłówki języka C++ i biblioteki natywne dla Windows, Linux i macOS. Nie potrzebujesz oddzielnego pakietu WinML.
Konfiguracja narzędzia CMake wybiera zasoby środowiska uruchomieniowego dla Twojego systemu operacyjnego i architektury oraz kopiuje wymagane biblioteki natywne obok pliku wykonywalnego. W systemach macOS i Linux uruchom plik wykonywalny z build. W systemie Windows uruchom plik .exe z build.
Odczytywanie dokumentu tekstowego
Odczytaj plik tekstowy ze ścieżki wiersza polecenia:
const std::string path = argc > 1 ? argv[1] : "document.txt";
std::ifstream input(path);
if (!input) {
throw std::runtime_error("Could not open document: " + path);
}
const std::string document{
std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>(),
};
if (document.empty()) {
throw std::runtime_error("The document is empty.");
}
Generowanie podsumowania
Załaduj model czatu i poproś go o podsumowanie dokumentu:
{
ChatSession session(*model);
Request request{
SystemMessage(
"Summarize the supplied document using a short overview and three bullet points."),
UserMessage(document),
};
request.SetOptions({
.search = {.temperature = 0.2F, .max_output_tokens = 512, .seed = 42},
});
std::cout << ResponseText(session.ProcessRequest(request)) << "\n";
}
Kompletny kod
#include <foundry_local/foundry_local_cpp.h>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
using namespace foundry_local;
std::string ResponseText(const Response& response) {
std::string text;
for (const Item& item : response.GetItems()) {
if (item.GetType() != FOUNDRY_LOCAL_ITEM_MESSAGE) {
continue;
}
for (const Item& part : item.GetMessage().parts) {
if (part.GetType() == FOUNDRY_LOCAL_ITEM_TEXT) {
text.append(part.GetText().text);
}
}
}
return text;
}
int main(int argc, char* argv[]) {
try {
Configuration config("foundry_local_cpp_document_summarizer");
Manager manager(std::move(config));
auto model = manager.GetCatalog().GetModel("qwen2.5-0.5b");
if (!model) {
throw std::runtime_error("Model 'qwen2.5-0.5b' was not found in the catalog.");
}
if (!model->IsCached()) {
model->Download([](float progress) {
std::cout << "\rDownloading model: " << static_cast<int>(progress) << "%" << std::flush;
return 0;
});
std::cout << "\n";
}
model->Load();
const std::string path = argc > 1 ? argv[1] : "document.txt";
std::ifstream input(path);
if (!input) {
throw std::runtime_error("Could not open document: " + path);
}
const std::string document{
std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>(),
};
if (document.empty()) {
throw std::runtime_error("The document is empty.");
}
{
ChatSession session(*model);
Request request{
SystemMessage(
"Summarize the supplied document using a short overview and three bullet points."),
UserMessage(document),
};
request.SetOptions({
.search = {.temperature = 0.2F, .max_output_tokens = 512, .seed = 42},
});
std::cout << ResponseText(session.ProcessRequest(request)) << "\n";
}
model->Unload();
manager.Shutdown();
} catch (const Error& error) {
std::cerr << "Foundry Local error [" << error.Code() << "]: " << error.what() << "\n";
return 1;
} catch (const std::exception& error) {
std::cerr << "Error: " << error.what() << "\n";
return 1;
}
return 0;
}
Podsumuj plik:
./build/tutorial-document-summarizer document.txt
Uprzątnij zasoby
Wagi modelu są przechowywane w Twojej lokalnej pamięci podręcznej po rozładowaniu modelu. Oznacza to, że przy następnym uruchomieniu aplikacji krok pobierania zostanie pominięty, a model ładuje się szybciej. Nie jest wymagane żadne dodatkowe czyszczenie, chyba że chcesz odzyskać miejsce na dysku.