Panduan: Membangun asisten percakapan bergiliran dengan Foundry Local

Dalam tutorial ini, Anda membangun asisten obrolan interaktif yang berjalan sepenuhnya di perangkat Anda. Asisten mempertahankan konteks percakapan di berbagai pertukaran dialog, sehingga mengingat apa yang Anda bahas sebelumnya dalam dialog tersebut. Anda menggunakan Foundry Local SDK untuk memilih model, menetapkan prompt sistem, dan mengalirkan respons secara berurutan satu per satu.

Dalam tutorial ini, Anda akan belajar cara:

  • Menyiapkan proyek dan menginstal SDK Lokal Foundry
  • Telusuri katalog model dan pilih model
  • Menentukan permintaan sistem untuk membentuk perilaku asisten
  • Menerapkan percakapan multi-giliran dengan riwayat pesan
  • Memproses respons untuk pengalaman yang responsif
  • Membersihkan sumber daya saat percakapan berakhir

Prasyarat

  • Komputer Windows, macOS, atau Linux dengan RAM minimal 8 GB.

Repositori Penampung Sampel

Kode sampel lengkap untuk artikel ini tersedia di repositori GitHub foundry-samples. Untuk mengkloning repositori dan menavigasi ke penggunaan sampel:

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

Memasang paket

Jika Anda mengembangkan atau mengirim di Windows, pilih tab Windows. Paket Windows terintegrasi dengan runtime Windows ML — ini menyediakan area permukaan API yang sama dengan luas akselerasi perangkat keras yang lebih luas.

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

Sampel C# di repositori GitHub adalah proyek yang telah dikonfigurasi sebelumnya. Jika Anda membangun dari awal, Anda harus membaca referensi Foundry Local SDK untuk detail selengkapnya tentang cara menyiapkan proyek C# Anda dengan Foundry Local.

Telusuri katalog dan pilih model

Foundry Local SDK menyediakan katalog model yang mencantumkan semua model yang tersedia. Dalam langkah ini, Anda menginisialisasi SDK dan memilih model untuk asisten obrolan Anda.

  • Buka Program.cs dan ganti kontennya dengan kode berikut untuk menginisialisasi SDK dan pilih 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();
    

    Metode GetModelAsync menerima alias model, yaitu nama pendek yang mudah diingat dan mewakili model tertentu dalam katalog. Metode ini DownloadAsync mengambil bobot model ke cache lokal Anda, dan LoadAsync membuat model siap untuk inferensi.

Tentukan perintah sistem

Permintaan sistem mengatur kepribadian dan perilaku asisten. Ini adalah pesan pertama dalam riwayat percakapan dan model mereferensikannya di seluruh percakapan.

Tambahkan permintaan sistem untuk membentuk bagaimana asisten merespons:

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

Tip

Bereksperimenlah dengan permintaan sistem yang berbeda untuk mengubah perilaku asisten. Misalnya, Anda dapat menginstruksikannya untuk merespons sebagai bajak laut, guru, atau pakar domain.

Menerapkan percakapan bergiliran banyak

Asisten obrolan perlu mempertahankan konteks di beberapa pertukaran. Anda mencapai ini dengan menyimpan daftar semua pesan (sistem, pengguna, dan asisten) dan mengirim daftar lengkap dengan setiap permintaan. Model ini menggunakan riwayat ini untuk menghasilkan respons yang relevan secara kontekstual.

Tambahkan perulangan percakapan yang:

  • Membaca input pengguna dari konsol.
  • Menambahkan pesan pengguna ke riwayat.
  • Mengirim riwayat lengkap ke model.
  • Menambahkan respons asisten ke riwayat untuk putaran berikutnya.
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 });
}

Setiap panggilan untuk CompleteChatAsync menerima riwayat pesan lengkap. Inilah bagaimana model "mengingat" interaksi sebelumnya — model tersebut tidak menyimpan status di antara setiap panggilan.

Tambahkan respons streaming

Streaming mencetak setiap token saat dihasilkan, yang membuat asisten merasa lebih responsif. Ganti panggilan CompleteChatAsync dengan CompleteChatStreamingAsync untuk mengalirkan token respons secara token demi token.

Perbarui loop percakapan agar menggunakan 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");

Versi streaming mengakumulasi respons penuh sehingga dapat ditambahkan ke riwayat percakapan setelah streaming selesai.

Kode lengkap

Ganti konten Program.cs dengan kode lengkap berikut:

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

Jalankan asisten obrolan:

dotnet run

Anda melihat output yang mirip dengan:

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!

Perhatikan bagaimana asisten mengingat konteks dari giliran sebelumnya - ketika Anda bertanya "Mengapa penting untuk makhluk hidup lainnya?", itu tahu Anda masih berbicara tentang fotosintesis.

Repositori Penampung Sampel

Kode sampel lengkap untuk artikel ini tersedia di repositori GitHub foundry-samples. Untuk mengkloning repositori dan menavigasi ke penggunaan sampel:

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

Memasang paket

Jika Anda mengembangkan atau mengirim di Windows, pilih tab Windows. Paket Windows terintegrasi dengan runtime Windows ML — ini menyediakan area permukaan API yang sama dengan luas akselerasi perangkat keras yang lebih luas.

npm install foundry-local-sdk-winml openai

Telusuri katalog dan pilih model

Foundry Local SDK menyediakan katalog model yang mencantumkan semua model yang tersedia. Dalam langkah ini, Anda menginisialisasi SDK dan memilih model untuk asisten obrolan Anda.

  1. Buat file yang disebut index.js.

  2. Tambahkan kode berikut untuk menginisialisasi SDK dan pilih 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();
    

    Metode getModel menerima alias model, yaitu nama pendek yang mudah diingat dan mewakili model tertentu dalam katalog. Metode ini download mengambil bobot model ke cache lokal Anda, dan load membuat model siap untuk inferensi.

Tentukan perintah sistem

Permintaan sistem mengatur kepribadian dan perilaku asisten. Ini adalah pesan pertama dalam riwayat percakapan dan model mereferensikannya di seluruh percakapan.

Tambahkan permintaan sistem untuk membentuk bagaimana asisten merespons:

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

Tip

Bereksperimenlah dengan permintaan sistem yang berbeda untuk mengubah perilaku asisten. Misalnya, Anda dapat menginstruksikannya untuk merespons sebagai bajak laut, guru, atau pakar domain.

Menerapkan percakapan bergiliran banyak

Asisten obrolan perlu mempertahankan konteks di beberapa pertukaran. Anda mencapai ini dengan menyimpan daftar semua pesan (sistem, pengguna, dan asisten) dan mengirim daftar lengkap dengan setiap permintaan. Model ini menggunakan riwayat ini untuk menghasilkan respons yang relevan secara kontekstual.

Tambahkan perulangan percakapan yang:

  • Membaca input pengguna dari konsol.
  • Menambahkan pesan pengguna ke riwayat.
  • Mengirim riwayat lengkap ke model.
  • Menambahkan respons asisten ke riwayat untuk putaran berikutnya.
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 });
}

Setiap panggilan untuk completeChat menerima riwayat pesan lengkap. Inilah bagaimana model "mengingat" interaksi sebelumnya — model tersebut tidak menyimpan status di antara setiap panggilan.

Tambahkan respons streaming

Streaming mencetak setiap token saat dihasilkan, yang membuat asisten merasa lebih responsif. Ganti panggilan completeChat dengan completeStreamingChat untuk mengalirkan token respons secara token demi token.

Perbarui loop percakapan agar menggunakan 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');

Versi streaming mengakumulasi respons penuh sehingga dapat ditambahkan ke riwayat percakapan setelah streaming selesai.

Kode lengkap

Buat file bernama index.js dan tambahkan kode lengkap berikut:

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

Jalankan asisten obrolan:

node index.js

Anda melihat output yang mirip dengan:

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!

Perhatikan bagaimana asisten mengingat konteks dari giliran sebelumnya - ketika Anda bertanya "Mengapa penting untuk makhluk hidup lainnya?", itu tahu Anda masih berbicara tentang fotosintesis.

Repositori Penampung Sampel

Kode sampel lengkap untuk artikel ini tersedia di repositori GitHub foundry-samples. Untuk mengkloning repositori dan menavigasi ke penggunaan sampel:

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

Memasang paket

Jika Anda mengembangkan atau mengirim di Windows, pilih tab Windows. Paket Windows terintegrasi dengan runtime Windows ML — ini menyediakan area permukaan API yang sama dengan luas akselerasi perangkat keras yang lebih luas.

pip install foundry-local-sdk-winml openai

Telusuri katalog dan pilih model

Foundry Local SDK menyediakan katalog model yang mencantumkan semua model yang tersedia. Dalam langkah ini, Anda menginisialisasi SDK dan memilih model untuk asisten obrolan Anda.

  1. Buat file yang disebut main.py.

  2. Tambahkan kode berikut untuk menginisialisasi SDK dan pilih 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()
    

    Metode get_model menerima alias model, yaitu nama pendek yang mudah diingat dan mewakili model tertentu dalam katalog. Metode ini download mengambil bobot model ke cache lokal Anda, dan load membuat model siap untuk inferensi.

Tentukan perintah sistem

Permintaan sistem mengatur kepribadian dan perilaku asisten. Ini adalah pesan pertama dalam riwayat percakapan dan model mereferensikannya di seluruh percakapan.

Tambahkan permintaan sistem untuk membentuk bagaimana asisten merespons:

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

Tip

Bereksperimenlah dengan permintaan sistem yang berbeda untuk mengubah perilaku asisten. Misalnya, Anda dapat menginstruksikannya untuk merespons sebagai bajak laut, guru, atau pakar domain.

Menerapkan percakapan bergiliran banyak

Asisten obrolan perlu mempertahankan konteks di beberapa pertukaran. Anda mencapai ini dengan menyimpan daftar semua pesan (sistem, pengguna, dan asisten) dan mengirim daftar lengkap dengan setiap permintaan. Model ini menggunakan riwayat ini untuk menghasilkan respons yang relevan secara kontekstual.

Tambahkan perulangan percakapan yang:

  • Membaca input pengguna dari konsol.
  • Menambahkan pesan pengguna ke riwayat.
  • Mengirim riwayat lengkap ke model.
  • Menambahkan respons asisten ke riwayat untuk putaran berikutnya.
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})

Setiap panggilan untuk complete_chat menerima riwayat pesan lengkap. Inilah bagaimana model "mengingat" interaksi sebelumnya — model tersebut tidak menyimpan status di antara setiap panggilan.

Tambahkan respons streaming

Streaming mencetak setiap token saat dihasilkan, yang membuat asisten merasa lebih responsif. Ganti panggilan complete_chat dengan complete_streaming_chat untuk mengalirkan token respons secara token demi token.

Perbarui loop percakapan agar menggunakan streaming.

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

Versi streaming mengakumulasi respons penuh sehingga dapat ditambahkan ke riwayat percakapan setelah streaming selesai.

Kode lengkap

Buat file bernama main.py dan tambahkan kode lengkap berikut:

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

Jalankan asisten obrolan:

python main.py

Anda melihat output yang mirip dengan:

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!

Perhatikan bagaimana asisten mengingat konteks dari giliran sebelumnya - ketika Anda bertanya "Mengapa penting untuk makhluk hidup lainnya?", itu tahu Anda masih berbicara tentang fotosintesis.

Repositori Penampung Sampel

Kode sampel lengkap untuk artikel ini tersedia di repositori GitHub foundry-samples. Untuk mengkloning repositori dan menavigasi ke penggunaan sampel:

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

Memasang paket

Jika Anda mengembangkan atau mengirim di Windows, pilih tab Windows. Paket Windows terintegrasi dengan runtime Windows ML — ini menyediakan area permukaan API yang sama dengan luas akselerasi perangkat keras yang lebih luas.

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

Telusuri katalog dan pilih model

Foundry Local SDK menyediakan katalog model yang mencantumkan semua model yang tersedia. Dalam langkah ini, Anda menginisialisasi SDK dan memilih model untuk asisten obrolan Anda.

  • Buka src/main.rs dan ganti kontennya dengan kode berikut untuk menginisialisasi SDK dan pilih 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);
    

    Metode get_model menerima alias model, yaitu nama pendek yang mudah diingat dan mewakili model tertentu dalam katalog. Metode ini download mengambil bobot model ke cache lokal Anda, dan load membuat model siap untuk inferensi.

Tentukan perintah sistem

Permintaan sistem mengatur kepribadian dan perilaku asisten. Ini adalah pesan pertama dalam riwayat percakapan dan model mereferensikannya di seluruh percakapan.

Tambahkan permintaan sistem untuk membentuk bagaimana asisten merespons:

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

Tip

Bereksperimenlah dengan permintaan sistem yang berbeda untuk mengubah perilaku asisten. Misalnya, Anda dapat menginstruksikannya untuk merespons sebagai bajak laut, guru, atau pakar domain.

Menerapkan percakapan bergiliran banyak

Asisten obrolan perlu mempertahankan konteks di beberapa pertukaran. Anda mencapai ini dengan menyimpan vektor semua pesan (sistem, pengguna, dan asisten) dan mengirim daftar lengkap dengan setiap permintaan. Model ini menggunakan riwayat ini untuk menghasilkan respons yang relevan secara kontekstual.

Tambahkan perulangan percakapan yang:

  • Membaca input pengguna dari konsol.
  • Menambahkan pesan pengguna ke riwayat.
  • Mengirim riwayat lengkap ke model.
  • Menambahkan respons asisten ke riwayat untuk putaran berikutnya.
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);
}

Setiap panggilan untuk complete_chat menerima riwayat pesan lengkap. Inilah bagaimana model "mengingat" interaksi sebelumnya — model tersebut tidak menyimpan status di antara setiap panggilan.

Tambahkan respons streaming

Streaming mencetak setiap token saat dihasilkan, yang membuat asisten merasa lebih responsif. Ganti panggilan complete_chat dengan complete_streaming_chat untuk mengalirkan token respons secara token demi token.

Perbarui loop percakapan agar menggunakan 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");

Versi streaming mengakumulasi respons penuh sehingga dapat ditambahkan ke riwayat percakapan setelah streaming selesai.

Kode lengkap

Ganti konten src/main.rs dengan kode lengkap berikut:

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

Jalankan asisten obrolan:

cargo run

Anda melihat output yang mirip dengan:

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!

Perhatikan bagaimana asisten mengingat konteks dari giliran sebelumnya - ketika Anda bertanya "Mengapa penting untuk makhluk hidup lainnya?", itu tahu Anda masih berbicara tentang fotosintesis.

Membersihkan sumber daya

Bobot model akan tetap berada di cache lokal Anda setelah Anda menghapus model. Ini berarti lain kali Anda menjalankan aplikasi, langkah unduhan dilewati dan model dimuat lebih cepat. Tidak diperlukan pembersihan tambahan kecuali Anda ingin mengklaim kembali ruang disk.