Návod: Sestavte vícetahového chatovacího asistenta pomocí Foundry Local

V tomto kurzu vytvoříte interaktivního chatovacího asistenta, který běží zcela na vašem zařízení. Asistent udržuje kontext konverzace napříč několika výměnami, takže si pamatuje, co jste probírali dříve v konverzaci. Pomocí sady Foundry Local SDK můžete vybrat model, definovat systémový prompt a streamovat odpovědi token po tokenu.

V tomto návodu se naučíte, jak:

  • Nastavení projektu a instalace místní sady SDK Foundry
  • Procházení katalogu modelů a výběr modelu
  • Definujte systémový požadavek k formování chování asistenta
  • Implementace vícekrokové konverzace s historií zpráv
  • Odpovědi streamu pro responzivní prostředí
  • Uvolněte prostředky po ukončení konverzace

Předpoklady

  • Počítač s Windows, macOS nebo Linuxem s minimálně 8 GB paměti RAM.

Úložiště ukázek

Kompletní vzorový kód pro tento článek je k dispozici v úložišti foundry-samples GitHub. Pokud chcete naklonovat úložiště a přejít na ukázku, použijte:

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

Instalace balíčků

Pokud vyvíjíte nebo vydáváte aplikace na Windows, vyberte kartu Windows. Balíček Windows se integruje s runtime modulem Windows ML — poskytuje stejné rozhraní API a nabízí širší možnosti hardwarové akcelerace.

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

Ukázky jazyka C# v úložišti GitHub jsou předkonfigurované projekty. Pokud vytváříte úplně od začátku, měli byste si přečíst referenční informace k místní sadě SDK Foundry , kde najdete další podrobnosti o tom, jak nastavit projekt jazyka C# pomocí Foundry Local.

Procházení katalogu a výběr modelu

Sada Foundry Local SDK poskytuje katalog modelů, který obsahuje seznam všech dostupných modelů. V tomto kroku inicializujete sadu SDK a vyberete model pro chatovacího asistenta.

  • Otevřete Program.cs a nahraďte jeho obsah následujícím kódem, který inicializuje sadu SDK a vybere model:

    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.");
    
    // Get a chat client
    var chatClient = await model.GetChatClientAsync();
    

    Metoda GetModelAsync přijímá alias modelu, což je krátký přátelský název, který odpovídá konkrétnímu modelu v katalogu. Metoda DownloadAsync načte váhy modelu do místní mezipaměti a LoadAsync připraví model k odvozování.

Definování systémové výzvy

Systémová výzva nastaví osobnost a chování asistenta. Jedná se o první zprávu v historii konverzací a model ji odkazuje v celé konverzaci.

Přidejte systémovou výzvu k tvarování způsobu reakce asistenta:

// Start the conversation with a system prompt
var messages = new List<ChatMessage>
{
    new ChatMessage
    {
        Role = "system",
        Content = "You are a helpful, friendly assistant. Keep your responses " +
                  "concise and conversational. If you don't know something, say so."
    }
};

Návod

Experimentujte s různými výzvami systému ke změně chování asistenta. Můžete například dát pokyn, aby odpověděl jako pirát, učitel nebo odborník na doménu.

Implementace vícekrokové konverzace

Chatovací asistent musí udržovat kontext napříč několika výměnami. Toho dosáhnete tak, že si necháte seznam všech zpráv (systém, uživatel a asistent) a odešlete úplný seznam s každou žádostí. Model používá tuto historii ke generování kontextově relevantních odpovědí.

Přidejte smyčku dialogu, která:

  • Načte uživatelský vstup z konzoly.
  • Připojí zprávu uživatele k historii.
  • Odešle do modelu úplnou historii.
  • Připojí odpověď asistenta k historii dalšího turnu.
while (true)
{
    Console.Write("You: ");
    var userInput = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(userInput) ||
        userInput.Equals("quit", StringComparison.OrdinalIgnoreCase) ||
        userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
    {
        break;
    }

    // Add the user's message to conversation history
    messages.Add(new ChatMessage { Role = "user", Content = userInput });

    // Stream the response token by token
    Console.Write("Assistant: ");
    var fullResponse = string.Empty;
    var streamingResponse = chatClient.CompleteChatStreamingAsync(messages, ct);
    await foreach (var chunk in streamingResponse)
    {
        var content = chunk.Choices[0].Message.Content;
        if (!string.IsNullOrEmpty(content))
        {
            Console.Write(content);
            Console.Out.Flush();
            fullResponse += content;
        }
    }
    Console.WriteLine("\n");

    // Add the complete response to conversation history
    messages.Add(new ChatMessage { Role = "assistant", Content = fullResponse });
}

Každé volání CompleteChatAsync obdrží celou historii zpráv. Takto si model pamatuje předchozí dialogy – neukládá stav mezi vyvoláními.

Přidat streamované odpovědi

Streamování vypisuje každý token při jeho generování, čímž je asistent vnímán jako pohotovější. Nahraďte volání CompleteChatAsync voláním CompleteChatStreamingAsync, abyste mohli streamovat odpověď po jednotlivých tokenech.

Aktualizujte smyčku konverzace tak, aby používala streamování:

// Stream the response token by token
Console.Write("Assistant: ");
var fullResponse = string.Empty;
var streamingResponse = chatClient.CompleteChatStreamingAsync(messages, ct);
await foreach (var chunk in streamingResponse)
{
    var content = chunk.Choices[0].Message.Content;
    if (!string.IsNullOrEmpty(content))
    {
        Console.Write(content);
        Console.Out.Flush();
        fullResponse += content;
    }
}
Console.WriteLine("\n");

Verze streamování shromažďuje úplnou odpověď, aby se po dokončení streamu přidala do historie konverzací.

Kompletní kód

Nahraďte obsah Program.cs následujícím úplným kódem:

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.");

// Get a chat client
var chatClient = await model.GetChatClientAsync();

// Start the conversation with a system prompt
var messages = new List<ChatMessage>
{
    new ChatMessage
    {
        Role = "system",
        Content = "You are a helpful, friendly assistant. Keep your responses " +
                  "concise and conversational. If you don't know something, say so."
    }
};

Console.WriteLine("\nChat assistant ready! Type 'quit' to exit.\n");

while (true)
{
    Console.Write("You: ");
    var userInput = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(userInput) ||
        userInput.Equals("quit", StringComparison.OrdinalIgnoreCase) ||
        userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
    {
        break;
    }

    // Add the user's message to conversation history
    messages.Add(new ChatMessage { Role = "user", Content = userInput });

    // Stream the response token by token
    Console.Write("Assistant: ");
    var fullResponse = string.Empty;
    var streamingResponse = chatClient.CompleteChatStreamingAsync(messages, ct);
    await foreach (var chunk in streamingResponse)
    {
        var content = chunk.Choices[0].Message.Content;
        if (!string.IsNullOrEmpty(content))
        {
            Console.Write(content);
            Console.Out.Flush();
            fullResponse += content;
        }
    }
    Console.WriteLine("\n");

    // Add the complete response to conversation history
    messages.Add(new ChatMessage { Role = "assistant", Content = fullResponse });
}

// Clean up - unload the model
await model.UnloadAsync();
Console.WriteLine("Model unloaded. Goodbye!");

Spusťte chatovacího asistenta:

dotnet run

Zobrazí se výstup podobný následujícímu:

Downloading model: 100.00%
Model loaded and ready.

Chat assistant ready! Type 'quit' to exit.

You: What is photosynthesis?
Assistant: Photosynthesis is the process plants use to convert sunlight, water, and carbon
dioxide into glucose and oxygen. It mainly happens in the leaves, inside structures
called chloroplasts.

You: Why is it important for other living things?
Assistant: It's essential because photosynthesis produces the oxygen that most living things
breathe. It also forms the base of the food chain — animals eat plants or eat other
animals that depend on plants for energy.

You: quit
Model unloaded. Goodbye!

Všimněte si, jak si asistent pamatuje kontext z předchozích kroků – když se zeptáte "Proč je důležité pro ostatní živé věci?", ví, že pořád mluvíte o fotosyntéze.

Úložiště ukázek

Kompletní vzorový kód pro tento článek je k dispozici v úložišti foundry-samples GitHub. Pokud chcete naklonovat úložiště a přejít na ukázku, použijte:

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

Instalace balíčků

Pokud vyvíjíte nebo vydáváte aplikace na Windows, vyberte kartu Windows. Balíček Windows se integruje s runtime modulem Windows ML — poskytuje stejné rozhraní API a nabízí širší možnosti hardwarové akcelerace.

npm install foundry-local-sdk-winml openai

Procházení katalogu a výběr modelu

Sada Foundry Local SDK poskytuje katalog modelů, který obsahuje seznam všech dostupných modelů. V tomto kroku inicializujete sadu SDK a vyberete model pro chatovacího asistenta.

  1. Vytvořte soubor s názvem index.js.

  2. Přidejte následující kód pro inicializaci sady SDK a vyberte model:

    // 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.');
    
    // Create a chat client
    const chatClient = model.createChatClient();
    

    Metoda getModel přijímá alias modelu, což je krátký přátelský název, který odpovídá konkrétnímu modelu v katalogu. Metoda download načte váhy modelu do místní mezipaměti a load připraví model k odvozování.

Definování systémové výzvy

Systémová výzva nastaví osobnost a chování asistenta. Jedná se o první zprávu v historii konverzací a model ji odkazuje v celé konverzaci.

Přidejte systémovou výzvu k tvarování způsobu reakce asistenta:

// Start the conversation with a system prompt
const messages = [
    {
        role: 'system',
        content: 'You are a helpful, friendly assistant. Keep your responses ' +
                 'concise and conversational. If you don\'t know something, say so.'
    }
];

Návod

Experimentujte s různými výzvami systému ke změně chování asistenta. Můžete například dát pokyn, aby odpověděl jako pirát, učitel nebo odborník na doménu.

Implementace vícekrokové konverzace

Chatovací asistent musí udržovat kontext napříč několika výměnami. Toho dosáhnete tak, že si necháte seznam všech zpráv (systém, uživatel a asistent) a odešlete úplný seznam s každou žádostí. Model používá tuto historii ke generování kontextově relevantních odpovědí.

Přidejte smyčku dialogu, která:

  • Načte uživatelský vstup z konzoly.
  • Připojí zprávu uživatele k historii.
  • Odešle do modelu úplnou historii.
  • Připojí odpověď asistenta k historii dalšího turnu.
while (true) {
    const userInput = await askQuestion('You: ');
    if (userInput.trim().toLowerCase() === 'quit' ||
        userInput.trim().toLowerCase() === 'exit') {
        break;
    }

    // Add the user's message to conversation history
    messages.push({ role: 'user', content: userInput });

    // Stream the response token by token
    process.stdout.write('Assistant: ');
    let fullResponse = '';
    for await (const chunk of chatClient.completeStreamingChat(messages)) {
        const content = chunk.choices?.[0]?.delta?.content;
        if (content) {
            process.stdout.write(content);
            fullResponse += content;
        }
    }
    console.log('\n');

    // Add the complete response to conversation history
    messages.push({ role: 'assistant', content: fullResponse });
}

Každé volání completeChat obdrží celou historii zpráv. Takto si model pamatuje předchozí dialogy – neukládá stav mezi vyvoláními.

Přidat streamované odpovědi

Streamování vypisuje každý token při jeho generování, čímž je asistent vnímán jako pohotovější. Nahraďte volání completeChat voláním completeStreamingChat, abyste mohli streamovat odpověď po jednotlivých tokenech.

Aktualizujte smyčku konverzace tak, aby používala streamování:

// Stream the response token by token
process.stdout.write('Assistant: ');
let fullResponse = '';
for await (const chunk of chatClient.completeStreamingChat(messages)) {
    const content = chunk.choices?.[0]?.delta?.content;
    if (content) {
        process.stdout.write(content);
        fullResponse += content;
    }
}
console.log('\n');

Verze streamování shromažďuje úplnou odpověď, aby se po dokončení streamu přidala do historie konverzací.

Kompletní kód

Vytvořte soubor s názvem index.js a přidejte následující úplný kód:

import { FoundryLocalManager } from 'foundry-local-sdk';
import * as readline from 'readline';

// 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.');

// Create a chat client
const chatClient = model.createChatClient();

// Start the conversation with a system prompt
const messages = [
    {
        role: 'system',
        content: 'You are a helpful, friendly assistant. Keep your responses ' +
                 'concise and conversational. If you don\'t know something, say so.'
    }
];

// Set up readline for console input
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const askQuestion = (prompt) => new Promise((resolve) => {
    if (rl.closed) return resolve('quit');
    const onClose = () => resolve('quit');
    rl.once('close', onClose);
    try {
        rl.question(prompt, (answer) => {
            rl.off('close', onClose);
            resolve(answer);
        });
    } catch {
        rl.off('close', onClose);
        resolve('quit');
    }
});

console.log('\nChat assistant ready! Type \'quit\' to exit.\n');

while (true) {
    const userInput = await askQuestion('You: ');
    if (userInput.trim().toLowerCase() === 'quit' ||
        userInput.trim().toLowerCase() === 'exit') {
        break;
    }

    // Add the user's message to conversation history
    messages.push({ role: 'user', content: userInput });

    // Stream the response token by token
    process.stdout.write('Assistant: ');
    let fullResponse = '';
    for await (const chunk of chatClient.completeStreamingChat(messages)) {
        const content = chunk.choices?.[0]?.delta?.content;
        if (content) {
            process.stdout.write(content);
            fullResponse += content;
        }
    }
    console.log('\n');

    // Add the complete response to conversation history
    messages.push({ role: 'assistant', content: fullResponse });
}

// Clean up - unload the model
await model.unload();
console.log('Model unloaded. Goodbye!');
rl.close();

Spusťte chatovacího asistenta:

node index.js

Zobrazí se výstup podobný následujícímu:

Downloading model: 100.00%
Model downloaded.
Model loaded and ready.

Chat assistant ready! Type 'quit' to exit.

You: What is photosynthesis?
Assistant: Photosynthesis is the process plants use to convert sunlight, water, and carbon
dioxide into glucose and oxygen. It mainly happens in the leaves, inside structures
called chloroplasts.

You: Why is it important for other living things?
Assistant: It's essential because photosynthesis produces the oxygen that most living things
breathe. It also forms the base of the food chain — animals eat plants or eat other
animals that depend on plants for energy.

You: quit
Model unloaded. Goodbye!

Všimněte si, jak si asistent pamatuje kontext z předchozích kroků – když se zeptáte "Proč je důležité pro ostatní živé věci?", ví, že pořád mluvíte o fotosyntéze.

Úložiště ukázek

Kompletní vzorový kód pro tento článek je k dispozici v úložišti foundry-samples GitHub. Pokud chcete naklonovat úložiště a přejít na ukázku, použijte:

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

Instalace balíčků

Pokud vyvíjíte nebo vydáváte aplikace na Windows, vyberte kartu Windows. Balíček Windows se integruje s runtime modulem Windows ML — poskytuje stejné rozhraní API a nabízí širší možnosti hardwarové akcelerace.

pip install foundry-local-sdk-winml openai

Procházení katalogu a výběr modelu

Sada Foundry Local SDK poskytuje katalog modelů, který obsahuje seznam všech dostupných modelů. V tomto kroku inicializujete sadu SDK a vyberete model pro chatovacího asistenta.

  1. Vytvořte soubor s názvem main.py.

  2. Přidejte následující kód pro inicializaci sady SDK a vyberte model:

    # 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 progress: print(
            f"\rDownloading model: {progress:.2f}%", end="", flush=True
        )
    )
    print()
    model.load()
    print("Model loaded and ready.")
    
    # Get a chat client
    client = model.get_chat_client()
    

    Metoda get_model přijímá alias modelu, což je krátký přátelský název, který odpovídá konkrétnímu modelu v katalogu. Metoda download načte váhy modelu do místní mezipaměti a load připraví model k odvozování.

Definování systémové výzvy

Systémová výzva nastaví osobnost a chování asistenta. Jedná se o první zprávu v historii konverzací a model ji odkazuje v celé konverzaci.

Přidejte systémovou výzvu k tvarování způsobu reakce asistenta:

# Start the conversation with a system prompt
messages = [
    {
        "role": "system",
        "content": "You are a helpful, friendly assistant. Keep your responses "
        "concise and conversational. If you don't know something, say so.",
    }
]

Návod

Experimentujte s různými výzvami systému ke změně chování asistenta. Můžete například dát pokyn, aby odpověděl jako pirát, učitel nebo odborník na doménu.

Implementace vícekrokové konverzace

Chatovací asistent musí udržovat kontext napříč několika výměnami. Toho dosáhnete tak, že si necháte seznam všech zpráv (systém, uživatel a asistent) a odešlete úplný seznam s každou žádostí. Model používá tuto historii ke generování kontextově relevantních odpovědí.

Přidejte smyčku dialogu, která:

  • Načte uživatelský vstup z konzoly.
  • Připojí zprávu uživatele k historii.
  • Odešle do modelu úplnou historii.
  • Připojí odpověď asistenta k historii dalšího turnu.
while True:
    user_input = input("You: ")
    if user_input.strip().lower() in ("quit", "exit"):
        break

    # Add the user's message to conversation history
    messages.append({"role": "user", "content": user_input})

    # Stream the response token by token
    print("Assistant: ", end="", flush=True)
    full_response = ""
    for chunk in client.complete_streaming_chat(messages):
        if not chunk.choices:
            continue
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)
            full_response += content
    print("\n")

    # Add the complete response to conversation history
    messages.append({"role": "assistant", "content": full_response})

Každé volání complete_chat obdrží celou historii zpráv. Takto si model pamatuje předchozí dialogy – neukládá stav mezi vyvoláními.

Přidat streamované odpovědi

Streamování vypisuje každý token při jeho generování, čímž je asistent vnímán jako pohotovější. Nahraďte volání complete_chat voláním complete_streaming_chat, abyste mohli streamovat odpověď po jednotlivých tokenech.

Aktualizujte smyčku konverzace tak, aby používala streamování:

# Stream the response token by token
print("Assistant: ", end="", flush=True)
full_response = ""
for chunk in client.complete_streaming_chat(messages):
    if not chunk.choices:
        continue
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)
        full_response += content
print("\n")

Verze streamování shromažďuje úplnou odpověď, aby se po dokončení streamu přidala do historie konverzací.

Kompletní kód

Vytvořte soubor s názvem main.py a přidejte následující úplný kód:

from foundry_local_sdk import Configuration, FoundryLocalManager



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

    # Download and register all execution providers.
    current_ep = ""

    def ep_progress(ep_name: str, percent: float):
        nonlocal current_ep
        if ep_name != current_ep:
            if current_ep:
                print()
            current_ep = ep_name
        print(f"\r  {ep_name:<30}  {percent:5.1f}%", end="", flush=True)

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

    # Select and load a model from the catalog
    model = manager.catalog.get_model("qwen2.5-0.5b")
    model.download(
        lambda progress: print(
            f"\rDownloading model: {progress:.2f}%", end="", flush=True
        )
    )
    print()
    model.load()
    print("Model loaded and ready.")

    # Get a chat client
    client = model.get_chat_client()

    # Start the conversation with a system prompt
    messages = [
        {
            "role": "system",
            "content": "You are a helpful, friendly assistant. Keep your responses "
            "concise and conversational. If you don't know something, say so.",
        }
    ]

    print("\nChat assistant ready! Type 'quit' to exit.\n")

    while True:
        user_input = input("You: ")
        if user_input.strip().lower() in ("quit", "exit"):
            break

        # Add the user's message to conversation history
        messages.append({"role": "user", "content": user_input})

        # Stream the response token by token
        print("Assistant: ", end="", flush=True)
        full_response = ""
        for chunk in client.complete_streaming_chat(messages):
            if not chunk.choices:
                continue
            content = chunk.choices[0].delta.content
            if content:
                print(content, end="", flush=True)
                full_response += content
        print("\n")

        # Add the complete response to conversation history
        messages.append({"role": "assistant", "content": full_response})

    # Clean up - unload the model
    model.unload()
    print("Model unloaded. Goodbye!")


if __name__ == "__main__":
    main()

Spusťte chatovacího asistenta:

python main.py

Zobrazí se výstup podobný následujícímu:

Downloading model: 100.00%
Model loaded and ready.

Chat assistant ready! Type 'quit' to exit.

You: What is photosynthesis?
Assistant: Photosynthesis is the process plants use to convert sunlight, water, and carbon
dioxide into glucose and oxygen. It mainly happens in the leaves, inside structures
called chloroplasts.

You: Why is it important for other living things?
Assistant: It's essential because photosynthesis produces the oxygen that most living things
breathe. It also forms the base of the food chain — animals eat plants or eat other
animals that depend on plants for energy.

You: quit
Model unloaded. Goodbye!

Všimněte si, jak si asistent pamatuje kontext z předchozích kroků – když se zeptáte "Proč je důležité pro ostatní živé věci?", ví, že pořád mluvíte o fotosyntéze.

Úložiště ukázek

Kompletní vzorový kód pro tento článek je k dispozici v úložišti foundry-samples GitHub. Pokud chcete naklonovat úložiště a přejít na ukázku, použijte:

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

Instalace balíčků

Pokud vyvíjíte nebo vydáváte aplikace na Windows, vyberte kartu Windows. Balíček Windows se integruje s runtime modulem Windows ML — poskytuje stejné rozhraní API a nabízí širší možnosti hardwarové akcelerace.

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

Procházení katalogu a výběr modelu

Sada Foundry Local SDK poskytuje katalog modelů, který obsahuje seznam všech dostupných modelů. V tomto kroku inicializujete sadu SDK a vyberete model pro chatovacího asistenta.

  • Otevřete src/main.rs a nahraďte jeho obsah následujícím kódem, který inicializuje sadu SDK a vybere model:

    // 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.");
    
    // Create a chat client
    let client = model.create_chat_client().temperature(0.7).max_tokens(512);
    

    Metoda get_model přijímá alias modelu, což je krátký přátelský název, který odpovídá konkrétnímu modelu v katalogu. Metoda download načte váhy modelu do místní mezipaměti a load připraví model k odvozování.

Definování systémové výzvy

Systémová výzva nastaví osobnost a chování asistenta. Jedná se o první zprávu v historii konverzací a model ji odkazuje v celé konverzaci.

Přidejte systémovou výzvu k tvarování způsobu reakce asistenta:

// Start the conversation with a system prompt
let mut messages: Vec<ChatCompletionRequestMessage> = vec![
    ChatCompletionRequestSystemMessage::from(
        "You are a helpful, friendly assistant. Keep your responses \
         concise and conversational. If you don't know something, say so.",
    )
    .into(),
];

Návod

Experimentujte s různými výzvami systému ke změně chování asistenta. Můžete například dát pokyn, aby odpověděl jako pirát, učitel nebo odborník na doménu.

Implementace vícekrokové konverzace

Chatovací asistent musí udržovat kontext napříč několika výměnami. Toho dosáhnete tak, že zachováte vektor všech zpráv (systém, uživatel a asistent) a odešlete úplný seznam s každou žádostí. Model používá tuto historii ke generování kontextově relevantních odpovědí.

Přidejte smyčku dialogu, která:

  • Načte uživatelský vstup z konzoly.
  • Připojí zprávu uživatele k historii.
  • Odešle do modelu úplnou historii.
  • Připojí odpověď asistenta k historii dalšího turnu.
loop {
    print!("You: ");
    io::stdout().flush()?;

    let mut input = String::new();
    stdin.lock().read_line(&mut input)?;
    let input = input.trim();

    if input.eq_ignore_ascii_case("quit") || input.eq_ignore_ascii_case("exit") {
        break;
    }

    // Add the user's message to conversation history
    messages.push(ChatCompletionRequestUserMessage::from(input).into());

    // Stream the response token by token
    print!("Assistant: ");
    io::stdout().flush()?;
    let mut full_response = String::new();
    let mut stream = client.complete_streaming_chat(&messages, None).await?;
    while let Some(chunk) = stream.next().await {
        let chunk = chunk?;
        if let Some(choice) = chunk.choices.first() {
            if let Some(ref content) = choice.delta.content {
                print!("{content}");
                io::stdout().flush()?;
                full_response.push_str(content);
            }
        }
    }
    println!("\n");

    // Add the complete response to conversation history
    let assistant_msg: ChatCompletionRequestMessage = serde_json::from_value(
        serde_json::json!({"role": "assistant", "content": full_response}),
    )?;
    messages.push(assistant_msg);
}

Každé volání complete_chat obdrží celou historii zpráv. Takto si model pamatuje předchozí dialogy – neukládá stav mezi vyvoláními.

Přidat streamované odpovědi

Streamování vypisuje každý token při jeho generování, čímž je asistent vnímán jako pohotovější. Nahraďte volání complete_chat voláním complete_streaming_chat, abyste mohli streamovat odpověď po jednotlivých tokenech.

Aktualizujte smyčku konverzace tak, aby používala streamování:

// Stream the response token by token
print!("Assistant: ");
io::stdout().flush()?;
let mut full_response = String::new();
let mut stream = client.complete_streaming_chat(&messages, None).await?;
while let Some(chunk) = stream.next().await {
    let chunk = chunk?;
    if let Some(choice) = chunk.choices.first() {
        if let Some(ref content) = choice.delta.content {
            print!("{content}");
            io::stdout().flush()?;
            full_response.push_str(content);
        }
    }
}
println!("\n");

Verze streamování shromažďuje úplnou odpověď, aby se po dokončení streamu přidala do historie konverzací.

Kompletní kód

Nahraďte obsah src/main.rs následujícím úplným kódem:

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

#[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.");

    // Create a chat client
    let client = model.create_chat_client().temperature(0.7).max_tokens(512);

    // Start the conversation with a system prompt
    let mut messages: Vec<ChatCompletionRequestMessage> = vec![
        ChatCompletionRequestSystemMessage::from(
            "You are a helpful, friendly assistant. Keep your responses \
             concise and conversational. If you don't know something, say so.",
        )
        .into(),
    ];

    println!("\nChat assistant ready! Type 'quit' to exit.\n");

    let stdin = io::stdin();
    loop {
        print!("You: ");
        io::stdout().flush()?;

        let mut input = String::new();
        stdin.lock().read_line(&mut input)?;
        let input = input.trim();

        if input.eq_ignore_ascii_case("quit") || input.eq_ignore_ascii_case("exit") {
            break;
        }

        // Add the user's message to conversation history
        messages.push(ChatCompletionRequestUserMessage::from(input).into());

        // Stream the response token by token
        print!("Assistant: ");
        io::stdout().flush()?;
        let mut full_response = String::new();
        let mut stream = client.complete_streaming_chat(&messages, None).await?;
        while let Some(chunk) = stream.next().await {
            let chunk = chunk?;
            if let Some(choice) = chunk.choices.first() {
                if let Some(ref content) = choice.delta.content {
                    print!("{content}");
                    io::stdout().flush()?;
                    full_response.push_str(content);
                }
            }
        }
        println!("\n");

        // Add the complete response to conversation history
        let assistant_msg: ChatCompletionRequestMessage = serde_json::from_value(
            serde_json::json!({"role": "assistant", "content": full_response}),
        )?;
        messages.push(assistant_msg);
    }

    // Clean up - unload the model
    model.unload().await?;
    println!("Model unloaded. Goodbye!");

    Ok(())
}

Spusťte chatovacího asistenta:

cargo run

Zobrazí se výstup podobný následujícímu:

Downloading model: 100.00%
Model loaded and ready.

Chat assistant ready! Type 'quit' to exit.

You: What is photosynthesis?
Assistant: Photosynthesis is the process plants use to convert sunlight, water, and carbon
dioxide into glucose and oxygen. It mainly happens in the leaves, inside structures
called chloroplasts.

You: Why is it important for other living things?
Assistant: It's essential because photosynthesis produces the oxygen that most living things
breathe. It also forms the base of the food chain — animals eat plants or eat other
animals that depend on plants for energy.

You: quit
Model unloaded. Goodbye!

Všimněte si, jak si asistent pamatuje kontext z předchozích kroků – když se zeptáte "Proč je důležité pro ostatní živé věci?", ví, že pořád mluvíte o fotosyntéze.

Vyčistěte zdroje

Váhy modelu zůstanou v místní mezipaměti po uvolnění modelu. To znamená, že při příštím spuštění aplikace se krok stahování přeskočí a model se načte rychleji. Pokud nechcete uvolnit místo na disku, není potřeba žádné další vyčištění.