مشاركة عبر


الدرس التعليمي: بناء ملخص المستندات

ابن تطبيقا يقرأ ملفات نصية ويولد ملخصات موجزة — بالكامل على جهازك. هذا مفيد عندما تحتاج إلى فهم محتوى المستندات بسرعة دون قراءتها بالكامل، وعندما تحتوي المستندات على معلومات حساسة لا ينبغي أن تغادر جهازك.

في هذا البرنامج التعليمي، تتعلم كيفية:

  • قم بإعداد مشروع وتثبيت حزمة تطوير البرمجيات المحلية للعبة Foundry
  • اقرأ مستند نصي من نظام الملفات
  • قم بتحميل نموذج وإنشاء ملخص
  • التحكم في ملخص الأوامر باستخدام أوامر النظام
  • معالجة عدة مستندات في دفعة واحدة
  • تنظيف الموارد

المتطلبات المسبقه

  • جهاز كمبيوتر يعمل بنظام Windows أو macOS أو Linux يحتوي على ذاكرة RAM لا تقل عن 8 جيجابايت.

تثبيت الحزم

مستودع العينات

الكود النموذجي الكامل لهذا المقال متوفر في مستودع Foundry Local GitHub repository. لاستنساخ المستودع والانتقال إلى العينة، استخدم:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/cs/tutorial-document-summarizer

إذا كنت تطور أو تشحن على Windows، اختر تبويب Windows. تتكامل حزمة Windows مع وقت التشغيل Windows ML — حيث توفر نفس مساحة واجهة برمجة التطبيقات مع تسريع أوسع في الأجهزة.

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

عينات C# في مستودع GitHub هي مشاريع مهيأة مسبقا. إذا كنت تبني من الصفر، يجب عليك قراءة مرجع Foundry Local SDK لمزيد من التفاصيل حول كيفية إعداد مشروع C# الخاص بك مع Foundry Local.

اقرأ وثيقة نصية

قبل أن تلخص أي شيء، تحتاج إلى نموذج مستند للعمل عليه. أنشئ ملفا يسمى document.txt في دليل مشروعك وأضف المحتوى التالي:

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.

الآن افتح Program.cs وأضف الكود التالي لقراءة المستند:

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

يقبل الكود مسار ملف اختياري كوسيط سطر أوامر ويعود إلى document.txt إذا لم يتم توفيره.

أنشئ ملخصا

قم بتهيئة حزمة تطوير البرمجيات المحلية للصاهر، وتحميل نموذج، وأرسل محتوى المستند مع تنبيه النظام الذي يوجه النموذج للتلخيص.

استبدل محتويات Program.cs بالتعليمة البرمجية التالية:

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

GetModelAsync تقبل الطريقة اسم مستعار للنموذج، وهو اسم ودود قصير يربط بنموذج معين في الكتالوج. تقوم الطريقة DownloadAsync بجلب أوزان النماذج إلى ذاكرة التخزين المؤقت المحلية (وتتجاوز التحميل إذا كانت مخبأة بالفعل)، وتجعل LoadAsync النموذج جاهزا للاستنتاج. يخبر التوجيه النظام النموذج بإنتاج ملخصات نقاط تركز على الأفكار الرئيسية.

مخرجات ملخص التحكم

المواقف المختلفة تتطلب أساليب ملخص مختلفة. يمكنك تغيير موجه النظام للتحكم في كيفية هيكلة النموذج لإنتاجه. إليك ثلاث تنويعات مفيدة:

نقاط الرصاص (الإعداد الافتراضي من الخطوة السابقة):

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

ملخص من فقرة واحدة:

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

المسارات الرئيسية:

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

لتجربة أسلوب مختلف، استبدل القيمة Content في رسالة النظام بأحد التوجيهات. يتبع النموذج التعليمات في موجه النظام لتشكيل تنسيق وعمق الملخص.

معالجة عدة مستندات

قم بتوسيع التطبيق لتلخيص كل .txt ملف في دليل. وهذا مفيد عندما يكون لديك مجلد من المستندات يحتاج جميعها إلى ملخصات.

الطريقة التالية تكرر جميع .txt الملفات في الدليل المحدد وتلخص كل واحد منها:

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

يقرأ كل ملف، ويقرن بنفس موجه النظام، ويرسل إلى النموذج بشكل مستقل. النموذج لا يحمل السياق بين الملفات، لذا كل ملخص يكون مستقلا.

تعليمة برمجية كاملة

استبدل محتوى الرمز 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();
    }
}

تلخص ملف واحد:

dotnet run -- document.txt

أو تلخيص كل .txt ملف في دليل:

dotnet run -- ./docs

ترى مخرجا مشابها ل:

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!

تثبيت الحزم

مستودع العينات

الكود النموذجي الكامل لهذا المقال متوفر في مستودع Foundry Local GitHub repository. لاستنساخ المستودع والانتقال إلى العينة، استخدم:

git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/js/tutorial-document-summarizer

إذا كنت تطور أو تشحن على Windows، اختر تبويب Windows. تتكامل حزمة Windows مع وقت التشغيل Windows ML — حيث توفر نفس مساحة واجهة برمجة التطبيقات مع تسريع أوسع في الأجهزة.

npm install foundry-local-sdk-winml openai

اقرأ وثيقة نصية

قبل أن تلخص أي شيء، تحتاج إلى نموذج مستند للعمل عليه. أنشئ ملفا يسمى document.txt في دليل مشروعك وأضف المحتوى التالي:

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.

الآن أنشئ ملفا يسمى index.js وأضف الكود التالي لقراءة المستند:

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

يقبل السكربت مسار ملف اختياري كوسيط سطر أوامر ويعود إليه document.txt إذا لم يتم توفيره.

أنشئ ملخصا

قم بتهيئة حزمة تطوير البرمجيات المحلية للصاهر، وتحميل نموذج، وأرسل محتوى المستند مع تنبيه النظام الذي يوجه النموذج للتلخيص.

استبدل محتويات index.js بالتعليمة البرمجية التالية:

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

getModel تقبل الطريقة اسم مستعار للنموذج، وهو اسم ودود قصير يربط بنموذج معين في الكتالوج. تقوم الطريقة download بجلب أوزان النماذج إلى ذاكرة التخزين المؤقت المحلية (وتتجاوز التحميل إذا كانت مخبأة بالفعل)، وتجعل load النموذج جاهزا للاستنتاج. يخبر التوجيه النظام النموذج بإنتاج ملخصات نقاط تركز على الأفكار الرئيسية.

مخرجات ملخص التحكم

المواقف المختلفة تتطلب أساليب ملخص مختلفة. يمكنك تغيير موجه النظام للتحكم في كيفية هيكلة النموذج لإنتاجه. إليك ثلاث تنويعات مفيدة:

نقاط الرصاص (الإعداد الافتراضي من الخطوة السابقة):

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

ملخص من فقرة واحدة:

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

المسارات الرئيسية:

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

لتجربة أسلوب مختلف، استبدل القيمة content في رسالة النظام بأحد التوجيهات. يتبع النموذج التعليمات في موجه النظام لتشكيل تنسيق وعمق الملخص.

معالجة عدة مستندات

قم بتوسيع التطبيق لتلخيص كل .txt ملف في دليل. وهذا مفيد عندما يكون لديك مجلد من المستندات يحتاج جميعها إلى ملخصات.

الدالة التالية تكرر جميع .txt الملفات في الدليل المحدد وتلخص كل واحد منها:

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

يقرأ كل ملف، ويقرن بنفس موجه النظام، ويرسل إلى النموذج بشكل مستقل. النموذج لا يحمل السياق بين الملفات، لذا كل ملخص يكون مستقلا.

تعليمة برمجية كاملة

أنشئ ملفا مسمى index.js وأضف الكود الكامل التالي:

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

تلخص ملف واحد:

node index.js document.txt

أو تلخيص كل .txt ملف في دليل:

node index.js ./docs

ترى مخرجا مشابها ل:

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!

تثبيت الحزم

مستودع العينات

الكود النموذجي الكامل لهذا المقال متوفر في مستودع Foundry Local GitHub repository. لاستنساخ المستودع والانتقال إلى العينة، استخدم:

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

إذا كنت تطور أو تشحن على Windows، اختر تبويب Windows. تتكامل حزمة Windows مع وقت التشغيل Windows ML — حيث توفر نفس مساحة واجهة برمجة التطبيقات مع تسريع أوسع في الأجهزة.

pip install foundry-local-sdk-winml openai

اقرأ وثيقة نصية

قبل أن تلخص أي شيء، تحتاج إلى نموذج مستند للعمل عليه. أنشئ ملفا يسمى document.txt في دليل مشروعك وأضف المحتوى التالي:

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.

الآن أنشئ ملفا يسمى main.py وأضف الكود التالي لقراءة المستند:

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

يقبل السكربت مسار ملف اختياري كوسيط سطر أوامر ويعود إليه document.txt إذا لم يتم توفيره. تقرأ الطريقة Path.read_text الملف بأكمله في سلسلة نصية.

أنشئ ملخصا

قم بتهيئة حزمة تطوير البرمجيات المحلية للصاهر، وتحميل نموذج، وأرسل محتوى المستند مع تنبيه النظام الذي يوجه النموذج للتلخيص.

استبدل محتويات main.py بالتعليمة البرمجية التالية:

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)

get_model تقبل الطريقة اسم مستعار للنموذج، وهو اسم ودود قصير يربط بنموذج معين في الكتالوج. تقوم الطريقة download بجلب أوزان النماذج إلى ذاكرة التخزين المؤقت المحلية (وتتجاوز التحميل إذا كانت مخبأة بالفعل)، وتجعل load النموذج جاهزا للاستنتاج. يخبر التوجيه النظام النموذج بإنتاج ملخصات نقاط تركز على الأفكار الرئيسية.

مخرجات ملخص التحكم

المواقف المختلفة تتطلب أساليب ملخص مختلفة. يمكنك تغيير موجه النظام للتحكم في كيفية هيكلة النموذج لإنتاجه. إليك ثلاث تنويعات مفيدة:

نقاط الرصاص (الإعداد الافتراضي من الخطوة السابقة):

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

ملخص من فقرة واحدة:

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

المسارات الرئيسية:

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

لتجربة أسلوب مختلف، استبدل القيمة "content" في رسالة النظام بأحد التوجيهات. يتبع النموذج التعليمات في موجه النظام لتشكيل تنسيق وعمق الملخص.

معالجة عدة مستندات

قم بتوسيع التطبيق لتلخيص كل .txt ملف في دليل. وهذا مفيد عندما يكون لديك مجلد من المستندات يحتاج جميعها إلى ملخصات.

الدالة التالية تكرر جميع .txt الملفات في الدليل المحدد وتلخص كل واحد منها:

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

يقرأ كل ملف، ويقرن بنفس موجه النظام، ويرسل إلى النموذج بشكل مستقل. النموذج لا يحمل السياق بين الملفات، لذا كل ملخص يكون مستقلا.

تعليمة برمجية كاملة

أنشئ ملفا مسمى main.py وأضف الكود الكامل التالي:

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

تلخص ملف واحد:

python main.py document.txt

أو تلخيص كل .txt ملف في دليل:

python main.py ./docs

ترى مخرجا مشابها ل:

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!

تثبيت الحزم

مستودع العينات

الكود النموذجي الكامل لهذا المقال متوفر في مستودع Foundry Local GitHub repository. لاستنساخ المستودع والانتقال إلى العينة، استخدم:

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

إذا كنت تطور أو تشحن على Windows، اختر تبويب Windows. تتكامل حزمة Windows مع وقت التشغيل Windows ML — حيث توفر نفس مساحة واجهة برمجة التطبيقات مع تسريع أوسع في الأجهزة.

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

اقرأ وثيقة نصية

قبل أن تلخص أي شيء، تحتاج إلى نموذج مستند للعمل عليه. أنشئ ملفا يسمى document.txt في دليل مشروعك وأضف المحتوى التالي:

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.

الآن افتح src/main.rs وأضف الكود التالي لقراءة المستند:

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

يقبل الكود مسار ملف اختياري كوسيط سطر أوامر ويعود إلى document.txt إذا لم يتم توفيره.

أنشئ ملخصا

قم بتهيئة حزمة تطوير البرمجيات المحلية للصاهر، وتحميل نموذج، وأرسل محتوى المستند مع تنبيه النظام الذي يوجه النموذج للتلخيص.

استبدل محتويات src/main.rs بالتعليمة البرمجية التالية:

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

get_model تقبل الطريقة اسم مستعار للنموذج، وهو اسم ودود قصير يربط بنموذج معين في الكتالوج. تقوم الطريقة download بجلب أوزان النماذج إلى ذاكرة التخزين المؤقت المحلية (وتتجاوز التحميل إذا كانت مخبأة بالفعل)، وتجعل load النموذج جاهزا للاستنتاج. يخبر التوجيه النظام النموذج بإنتاج ملخصات نقاط تركز على الأفكار الرئيسية.

مخرجات ملخص التحكم

المواقف المختلفة تتطلب أساليب ملخص مختلفة. يمكنك تغيير موجه النظام للتحكم في كيفية هيكلة النموذج لإنتاجه. إليك ثلاث تنويعات مفيدة:

نقاط الرصاص (الإعداد الافتراضي من الخطوة السابقة):

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

ملخص من فقرة واحدة:

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

المسارات الرئيسية:

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

لتجربة أسلوب مختلف، استبدل محتوى رسالة النظام بأحد الطلبات. يتبع النموذج التعليمات في موجه النظام لتشكيل تنسيق وعمق الملخص.

معالجة عدة مستندات

قم بتوسيع التطبيق لتلخيص كل .txt ملف في دليل. وهذا مفيد عندما يكون لديك مجلد من المستندات يحتاج جميعها إلى ملخصات.

الدالة التالية تكرر جميع .txt الملفات في الدليل المحدد وتلخص كل واحد منها:

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

يقرأ كل ملف، ويقرن بنفس موجه النظام، ويرسل إلى النموذج بشكل مستقل. النموذج لا يحمل السياق بين الملفات، لذا كل ملخص يكون مستقلا.

تعليمة برمجية كاملة

استبدل محتوى الرمز 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("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(())
}

تلخص ملف واحد:

cargo run -- document.txt

أو تلخيص كل .txt ملف في دليل:

cargo run -- ./docs

ترى مخرجا مشابها ل:

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!

تنظيف الموارد

أوزان النماذج تبقى في ذاكرة التخزين المؤقت المحلية بعد تفريغ النموذج. هذا يعني أنه في المرة القادمة التي تشغل فيها التطبيق، يتم تخطي خطوة التحميل ويتم تحميل النموذج بشكل أسرع. لا حاجة لتنظيف إضافي إلا إذا أردت استعادة مساحة القرص.