Tutorial: Erstellen eines Multi-Turn-Chat-Assistenten mit Foundry Local

In diesem Lernprogramm erstellen Sie einen interaktiven Chat-Assistenten, der vollständig auf Ihrem Gerät ausgeführt wird. Der Assistent verwaltet den Unterhaltungskontext über mehrere Interaktionen hinweg. Er kann sich also daran erinnern, was Sie früher in der Unterhaltung diskutiert haben. Sie verwenden das Foundry Local SDK, um ein Modell auszuwählen, eine Systemaufforderung zu definieren und die Antworten Token für Token im Datenstrom zu übertragen.

In diesem Tutorial erfahren Sie, wie:

  • Einrichten eines Projekts und Installieren des Foundry Local SDK
  • Durchsuchen des Modellkatalogs und Auswählen eines Modells
  • Definieren Sie eine Systemaufforderung zur Gestaltung des Verhaltens des Assistenten
  • Implementieren einer mehrteiligen Unterhaltung mit Nachrichtenverlauf
  • Streamingantworten für eine reagierende Benutzererfahrung
  • Bereinigen von Ressourcen, wenn die Unterhaltung endet

Voraussetzungen

  • Ein Windows, macOS oder Linux-Computer mit mindestens 8 GB RAM.

Beispielrepository

Der vollständige Beispielcode für diesen Artikel ist im Repository Foundry Local GitHub repository verfügbar. So klonen Sie das Repository, und navigieren Sie zur Beispielverwendung:

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

Pakete installieren

Wenn Sie Windows entwickeln oder versenden, wählen Sie die Registerkarte Windows aus. Das Windows-Paket ist in die Windows ML Laufzeit integriert. Es bietet den gleichen API-Oberflächenbereich mit einer breiteren Hardwarebeschleunigung.

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

Die C#-Beispiele im GitHub Repository sind vorkonfigurierte Projekte. Wenn Sie von Grund auf neu erstellen, sollten Sie die Referenz zum Foundry Local SDK lesen, um weitere Details zum Einrichten Ihres C#-Projekts mit Foundry Local zu erhalten.

Durchsuchen des Katalogs und Auswählen eines Modells

Das Foundry Local SDK stellt einen Modellkatalog bereit, der alle verfügbaren Modelle auflistet. In diesem Schritt initialisieren Sie das SDK und wählen ein Modell für Ihren Chat-Assistenten aus.

  • öffnen Sie Program.cs, und ersetzen Sie den Inhalt durch den folgenden Code, um das SDK zu initialisieren und ein Modell auszuwählen.

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

    Die Methode GetModelAsync akzeptiert einen Modellalias, bei dem es sich um einen kurzen Anzeigenamen handelt, der einem bestimmten Modell im Katalog zugeordnet ist. Die DownloadAsync Methode ruft die Modellgewichtung in Den lokalen Cache ab und LoadAsync macht das Modell zur Ableitung bereit.

Definieren einer Systemaufforderung

Eine Systemaufforderung legt die Persönlichkeit und das Verhalten des Assistenten fest. Es ist die erste Nachricht im Unterhaltungsverlauf, und das Modell verweist darauf während der gesamten Unterhaltung.

Fügen Sie eine Systemaufforderung hinzu, um zu gestalten, wie der Assistent reagiert:

// 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."
    }
};

Tipp

Experimentieren Sie mit verschiedenen Systemaufforderungen, um das Verhalten des Assistenten zu ändern. Sie können sie beispielsweise anweisen, als Piraten, Lehrer oder Domänenexperte zu reagieren.

Implementieren einer mehrteiligen Unterhaltung

Ein Chat-Assistent muss den Kontext über mehrere Austausch hinweg pflegen. Sie erreichen dies, indem Sie eine Liste aller Nachrichten (System, Benutzer und Assistent) beibehalten und die vollständige Liste mit jeder Anforderung senden. Das Modell verwendet diesen Verlauf, um kontextbezogene Antworten zu generieren.

Fügen Sie eine Unterhaltungsschleife hinzu, die:

  • Liest Benutzereingaben aus der Konsole.
  • Fügt die Benutzernachricht an den Verlauf an.
  • Sendet den vollständigen Verlauf an das Modell.
  • Fügt die Antwort des Assistenten für den nächsten Durchgang zum Verlauf hinzu.
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 });
}

Jeder Aufruf von CompleteChatAsync erhält den vollständigen Nachrichtenverlauf. Dies ist, wie das Modell "merkt sich" vorherige Wendungen – es speichert den Zustand nicht zwischen Aufrufen.

Hinzufügen von Streaming-Antworten

Streaming druckt jedes Token bei seiner Generierung, wodurch der Assistent einen reaktionsfähigeren Eindruck erweckt. Ersetzen Sie den Aufruf CompleteChatAsync durch CompleteChatStreamingAsync, um das Antworttoken nach und nach zu streamen.

Aktualisieren Sie die Gesprächsschleife zur Nutzung von Streaming.

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

Die gestreamte Version sammelt die vollständige Antwort, damit sie nach Abschluss des Streams dem Gesprächsverlauf hinzugefügt werden kann.

Vollständiger Code

Ersetzen Sie den Inhalt von Program.cs durch den folgenden vollständigen Code:

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

Führen Sie den Chat-Assistenten aus:

dotnet run

Es wird eine ähnliche Ausgabe angezeigt wie:

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!

Beachten Sie, wie sich der Assistent aus früheren Wendungen an den Kontext erinnert – wenn Sie fragen, "Warum ist es wichtig für andere Lebewesen?", weiß es, dass Sie immer noch über Photosynthese sprechen.

Beispielrepository

Der vollständige Beispielcode für diesen Artikel ist im Repository Foundry Local GitHub repository verfügbar. So klonen Sie das Repository, und navigieren Sie zur Beispielverwendung:

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

Pakete installieren

Wenn Sie Windows entwickeln oder versenden, wählen Sie die Registerkarte Windows aus. Das Windows-Paket ist in die Windows ML Laufzeit integriert. Es bietet den gleichen API-Oberflächenbereich mit einer breiteren Hardwarebeschleunigung.

npm install foundry-local-sdk-winml openai

Durchsuchen des Katalogs und Auswählen eines Modells

Das Foundry Local SDK stellt einen Modellkatalog bereit, der alle verfügbaren Modelle auflistet. In diesem Schritt initialisieren Sie das SDK und wählen ein Modell für Ihren Chat-Assistenten aus.

  1. Erstellen Sie eine Datei namens index.js.

  2. Fügen Sie den folgenden Code hinzu, um das SDK zu initialisieren und ein Modell auszuwählen:

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

    Die Methode getModel akzeptiert einen Modellalias, bei dem es sich um einen kurzen Anzeigenamen handelt, der einem bestimmten Modell im Katalog zugeordnet ist. Die download Methode ruft die Modellgewichtung in Den lokalen Cache ab und load macht das Modell zur Ableitung bereit.

Definieren einer Systemaufforderung

Eine Systemaufforderung legt die Persönlichkeit und das Verhalten des Assistenten fest. Es ist die erste Nachricht im Unterhaltungsverlauf, und das Modell verweist darauf während der gesamten Unterhaltung.

Fügen Sie eine Systemaufforderung hinzu, um zu gestalten, wie der Assistent reagiert:

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

Tipp

Experimentieren Sie mit verschiedenen Systemaufforderungen, um das Verhalten des Assistenten zu ändern. Sie können sie beispielsweise anweisen, als Piraten, Lehrer oder Domänenexperte zu reagieren.

Implementieren einer mehrteiligen Unterhaltung

Ein Chat-Assistent muss den Kontext über mehrere Austausch hinweg pflegen. Sie erreichen dies, indem Sie eine Liste aller Nachrichten (System, Benutzer und Assistent) beibehalten und die vollständige Liste mit jeder Anforderung senden. Das Modell verwendet diesen Verlauf, um kontextbezogene Antworten zu generieren.

Fügen Sie eine Unterhaltungsschleife hinzu, die:

  • Liest Benutzereingaben aus der Konsole.
  • Fügt die Benutzernachricht an den Verlauf an.
  • Sendet den vollständigen Verlauf an das Modell.
  • Fügt die Antwort des Assistenten für den nächsten Durchgang zum Verlauf hinzu.
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 });
}

Jeder Aufruf von completeChat erhält den vollständigen Nachrichtenverlauf. Dies ist, wie das Modell "merkt sich" vorherige Wendungen – es speichert den Zustand nicht zwischen Aufrufen.

Hinzufügen von Streaming-Antworten

Streaming druckt jedes Token bei seiner Generierung, wodurch der Assistent einen reaktionsfähigeren Eindruck erweckt. Ersetzen Sie den Aufruf completeChat durch completeStreamingChat, um das Antworttoken nach und nach zu streamen.

Aktualisieren Sie die Gesprächsschleife zur Nutzung von Streaming.

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

Die gestreamte Version sammelt die vollständige Antwort, damit sie nach Abschluss des Streams dem Gesprächsverlauf hinzugefügt werden kann.

Vollständiger Code

Erstellen Sie eine Datei mit dem Namen index.js , und fügen Sie den folgenden vollständigen Code hinzu:

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) => rl.question(prompt, resolve));

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

Führen Sie den Chat-Assistenten aus:

node index.js

Es wird eine ähnliche Ausgabe angezeigt wie:

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!

Beachten Sie, wie sich der Assistent aus früheren Wendungen an den Kontext erinnert – wenn Sie fragen, "Warum ist es wichtig für andere Lebewesen?", weiß es, dass Sie immer noch über Photosynthese sprechen.

Beispielrepository

Der vollständige Beispielcode für diesen Artikel ist im Repository Foundry Local GitHub repository verfügbar. So klonen Sie das Repository, und navigieren Sie zur Beispielverwendung:

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

Pakete installieren

Wenn Sie Windows entwickeln oder versenden, wählen Sie die Registerkarte Windows aus. Das Windows-Paket ist in die Windows ML Laufzeit integriert. Es bietet den gleichen API-Oberflächenbereich mit einer breiteren Hardwarebeschleunigung.

pip install foundry-local-sdk-winml openai

Durchsuchen des Katalogs und Auswählen eines Modells

Das Foundry Local SDK stellt einen Modellkatalog bereit, der alle verfügbaren Modelle auflistet. In diesem Schritt initialisieren Sie das SDK und wählen ein Modell für Ihren Chat-Assistenten aus.

  1. Erstellen Sie eine Datei namens main.py.

  2. Fügen Sie den folgenden Code hinzu, um das SDK zu initialisieren und ein Modell auszuwählen:

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

    Die Methode get_model akzeptiert einen Modellalias, bei dem es sich um einen kurzen Anzeigenamen handelt, der einem bestimmten Modell im Katalog zugeordnet ist. Die download Methode ruft die Modellgewichtung in Den lokalen Cache ab und load macht das Modell zur Ableitung bereit.

Definieren einer Systemaufforderung

Eine Systemaufforderung legt die Persönlichkeit und das Verhalten des Assistenten fest. Es ist die erste Nachricht im Unterhaltungsverlauf, und das Modell verweist darauf während der gesamten Unterhaltung.

Fügen Sie eine Systemaufforderung hinzu, um zu gestalten, wie der Assistent reagiert:

# 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."
    }
]

Tipp

Experimentieren Sie mit verschiedenen Systemaufforderungen, um das Verhalten des Assistenten zu ändern. Sie können sie beispielsweise anweisen, als Piraten, Lehrer oder Domänenexperte zu reagieren.

Implementieren einer mehrteiligen Unterhaltung

Ein Chat-Assistent muss den Kontext über mehrere Austausch hinweg pflegen. Sie erreichen dies, indem Sie eine Liste aller Nachrichten (System, Benutzer und Assistent) beibehalten und die vollständige Liste mit jeder Anforderung senden. Das Modell verwendet diesen Verlauf, um kontextbezogene Antworten zu generieren.

Fügen Sie eine Unterhaltungsschleife hinzu, die:

  • Liest Benutzereingaben aus der Konsole.
  • Fügt die Benutzernachricht an den Verlauf an.
  • Sendet den vollständigen Verlauf an das Modell.
  • Fügt die Antwort des Assistenten für den nächsten Durchgang zum Verlauf hinzu.
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):
        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})

Jeder Aufruf von complete_chat erhält den vollständigen Nachrichtenverlauf. Dies ist, wie das Modell "merkt sich" vorherige Wendungen – es speichert den Zustand nicht zwischen Aufrufen.

Hinzufügen von Streaming-Antworten

Streaming druckt jedes Token bei seiner Generierung, wodurch der Assistent einen reaktionsfähigeren Eindruck erweckt. Ersetzen Sie den Aufruf complete_chat durch complete_streaming_chat, um das Antworttoken nach und nach zu streamen.

Aktualisieren Sie die Gesprächsschleife zur Nutzung von Streaming.

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

Die gestreamte Version sammelt die vollständige Antwort, damit sie nach Abschluss des Streams dem Gesprächsverlauf hinzugefügt werden kann.

Vollständiger Code

Erstellen Sie eine Datei mit dem Namen main.py , und fügen Sie den folgenden vollständigen Code hinzu:

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

Führen Sie den Chat-Assistenten aus:

python main.py

Es wird eine ähnliche Ausgabe angezeigt wie:

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!

Beachten Sie, wie sich der Assistent aus früheren Wendungen an den Kontext erinnert – wenn Sie fragen, "Warum ist es wichtig für andere Lebewesen?", weiß es, dass Sie immer noch über Photosynthese sprechen.

Beispielrepository

Der vollständige Beispielcode für diesen Artikel ist im Repository Foundry Local GitHub repository verfügbar. So klonen Sie das Repository, und navigieren Sie zur Beispielverwendung:

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

Pakete installieren

Wenn Sie Windows entwickeln oder versenden, wählen Sie die Registerkarte Windows aus. Das Windows-Paket ist in die Windows ML Laufzeit integriert. Es bietet den gleichen API-Oberflächenbereich mit einer breiteren Hardwarebeschleunigung.

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

Durchsuchen des Katalogs und Auswählen eines Modells

Das Foundry Local SDK stellt einen Modellkatalog bereit, der alle verfügbaren Modelle auflistet. In diesem Schritt initialisieren Sie das SDK und wählen ein Modell für Ihren Chat-Assistenten aus.

  • öffnen Sie src/main.rs, und ersetzen Sie den Inhalt durch den folgenden Code, um das SDK zu initialisieren und ein Modell auszuwählen.

    // Initialize the Foundry Local SDK
    let manager = FoundryLocalManager::create(FoundryLocalConfig::new("chat-assistant"))?;
    
    // 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);
    

    Die Methode get_model akzeptiert einen Modellalias, bei dem es sich um einen kurzen Anzeigenamen handelt, der einem bestimmten Modell im Katalog zugeordnet ist. Die download Methode ruft die Modellgewichtung in Den lokalen Cache ab und load macht das Modell zur Ableitung bereit.

Definieren einer Systemaufforderung

Eine Systemaufforderung legt die Persönlichkeit und das Verhalten des Assistenten fest. Es ist die erste Nachricht im Unterhaltungsverlauf, und das Modell verweist darauf während der gesamten Unterhaltung.

Fügen Sie eine Systemaufforderung hinzu, um zu gestalten, wie der Assistent reagiert:

// 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(),
];

Tipp

Experimentieren Sie mit verschiedenen Systemaufforderungen, um das Verhalten des Assistenten zu ändern. Sie können sie beispielsweise anweisen, als Piraten, Lehrer oder Domänenexperte zu reagieren.

Implementieren einer mehrteiligen Unterhaltung

Ein Chat-Assistent muss den Kontext über mehrere Austausch hinweg pflegen. Sie erreichen dies, indem Sie einen Vektor aller Nachrichten (System, Benutzer und Assistent) beibehalten und die vollständige Liste mit jeder Anforderung senden. Das Modell verwendet diesen Verlauf, um kontextbezogene Antworten zu generieren.

Fügen Sie eine Unterhaltungsschleife hinzu, die:

  • Liest Benutzereingaben aus der Konsole.
  • Fügt die Benutzernachricht an den Verlauf an.
  • Sendet den vollständigen Verlauf an das Modell.
  • Fügt die Antwort des Assistenten für den nächsten Durchgang zum Verlauf hinzu.
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);
}

Jeder Aufruf von complete_chat erhält den vollständigen Nachrichtenverlauf. Dies ist, wie das Modell "merkt sich" vorherige Wendungen – es speichert den Zustand nicht zwischen Aufrufen.

Hinzufügen von Streaming-Antworten

Streaming druckt jedes Token bei seiner Generierung, wodurch der Assistent einen reaktionsfähigeren Eindruck erweckt. Ersetzen Sie den Aufruf complete_chat durch complete_streaming_chat, um das Antworttoken nach und nach zu streamen.

Aktualisieren Sie die Gesprächsschleife zur Nutzung von Streaming.

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

Die gestreamte Version sammelt die vollständige Antwort, damit sie nach Abschluss des Streams dem Gesprächsverlauf hinzugefügt werden kann.

Vollständiger Code

Ersetzen Sie den Inhalt von src/main.rs durch den folgenden vollständigen Code:

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("chat-assistant"))?;

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

Führen Sie den Chat-Assistenten aus:

cargo run

Es wird eine ähnliche Ausgabe angezeigt wie:

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!

Beachten Sie, wie sich der Assistent aus früheren Wendungen an den Kontext erinnert – wenn Sie fragen, "Warum ist es wichtig für andere Lebewesen?", weiß es, dass Sie immer noch über Photosynthese sprechen.

Bereinigen von Ressourcen

Die Gewichtungen des Modells verbleiben im lokalen Cache, nachdem Sie ein Modell entladen haben. Dies bedeutet, dass beim nächsten Ausführen der Anwendung der Downloadschritt übersprungen wird und das Modell schneller geladen wird. Es ist keine zusätzliche Bereinigung erforderlich, es sei denn, Sie möchten Speicherplatz freigeben.