Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym samouczku utworzysz interaktywnego asystenta czatu działającego w całości na urządzeniu. Asystent utrzymuje kontekst konwersacji w wielu wymianach, dlatego pamięta to, co omówiliśmy wcześniej w konwersacji. Lokalny zestaw SDK usługi Foundry służy do wybierania modelu, definiowania monitu systemowego i przesyłania odpowiedzi strumieniowo, token po tokenie.
W tym poradniku nauczysz się, jak:
- Konfigurowanie projektu i instalowanie lokalnego zestawu SDK rozwiązania Foundry
- Przeglądanie wykazu modeli i wybieranie modelu
- Zdefiniuj systemowy monit dla kształtowania zachowania asystenta
- Implementowanie wieloełowej konwersacji z historią komunikatów
- Przesyłanie strumieniowe odpowiedzi dla responsywnego doświadczenia
- Czyszczenie zasobów po zakończeniu konwersacji
Wymagania wstępne
- Komputer Windows, macOS lub Linux z co najmniej 8 GB pamięci RAM.
Repozytorium przykładów
Pełny kod przykładowy do tego artykułu można znaleźć w repozytorium GitHub przykładów platformy Foundry. Aby sklonować repozytorium i przejść do przykładu, użyj następującego polecenia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/csharp/foundry-local/tutorial-chat-assistant
Instalowanie pakietów
Jeśli programujesz lub wysyłasz na Windows, wybierz kartę Windows. Pakiet Windows integruje się z środowiskiem uruchomieniowym Windows ML — zapewnia ten sam obszar powierzchni interfejsu API z szerszym zakresem przyspieszania sprzętowego.
dotnet add package Microsoft.AI.Foundry.Local.WinML
dotnet add package OpenAI
Przykłady języka C# w repozytorium GitHub to wstępnie skonfigurowane projekty. Jeśli tworzysz od podstaw, zapoznaj się z dokumentacją zestawu SDK lokalnego rozwiązania Foundry , aby uzyskać więcej informacji na temat sposobu konfigurowania projektu w języku C# przy użyciu rozwiązania Foundry Local.
Przeglądanie wykazu i wybieranie modelu
Lokalny zestaw SDK usługi Foundry udostępnia wykaz modeli, który zawiera listę wszystkich dostępnych modeli. W tym kroku zainicjujesz zestaw SDK i wybierzesz model asystenta czatu.
Otwórz
Program.csi zastąp jego zawartość następującym kodem, aby zainicjować zestaw SDK i wybrać 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
GetModelAsyncakceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. MetodaDownloadAsyncpobiera wagi modelu do lokalnej pamięci podręcznej iLoadAsyncprzygotowuje model do wnioskowania.
Definiowanie monitu systemowego
Monit systemowy ustawia osobowość i zachowanie asystenta. Jest to pierwsza wiadomość w historii konwersacji i model odwołuje się do niej w całej konwersacji.
Dodaj monit systemowy, aby ukształtować sposób reagowania asystenta:
// 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."
}
};
Wskazówka
Poeksperymentuj z różnymi monitami systemowymi o zmianę zachowania asystenta. Możesz na przykład poinstruować go, aby odpowiedział jako pirat, nauczyciel lub ekspert domeny.
Implementowanie konwersacji wieloełowej
Asystent czatu musi zachować kontekst w wielu wymianach. Można to osiągnąć, utrzymując listę wszystkich komunikatów (system, użytkownika i asystenta) i wysyłając pełną listę z każdym żądaniem. Model używa tej historii do generowania kontekstowo odpowiednich odpowiedzi.
Dodaj pętlę konwersacji, która:
- Odczytuje dane wejściowe użytkownika z konsoli.
- Dołącza komunikat użytkownika do historii.
- Wysyła pełną historię do modelu.
- Dołącza odpowiedź asystenta do historii na następną turę.
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żde wywołanie CompleteChatAsync otrzymuje pełną historię komunikatów. W ten sposób model "zapamiętuje" poprzednie interakcje — nie przechowuje stanu między wywołaniami.
Dodawanie odpowiedzi przesyłania strumieniowego
Przesyłanie strumieniowe wyświetla każdy token w miarę jego generowania, co sprawia, że asystent wydaje się bardziej responsywny. Zastąp wywołanie CompleteChatAsync na CompleteChatStreamingAsync, aby przesyłać strumieniowo odpowiedź, token po tokenie.
Zaktualizuj pętlę konwersacji, aby używać przesyłania strumieniowego:
// 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");
Wersja przesyłania strumieniowego gromadzi pełną odpowiedź, aby można ją było dodać do historii konwersacji po zakończeniu transmisji strumieniowej.
Kompletny kod
Zastąp zawartość Program.cs następującym kompletnym kodem:
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!");
Uruchom asystenta czatu:
dotnet run
Zobaczysz dane wyjściowe podobne do:
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!
Zwróć uwagę, że asystent zapamiętuje kontekst z poprzednich rozmów — gdy pytasz "Dlaczego to jest ważne dla innych organizmów żywych?", wie, że nadal mówisz o fotosyntezie.
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/javascript/foundry-local/tutorial-chat-assistant
Instalowanie pakietów
Jeśli programujesz lub wysyłasz na Windows, wybierz kartę Windows. Pakiet Windows integruje się z środowiskiem uruchomieniowym Windows ML — zapewnia ten sam obszar powierzchni interfejsu API z szerszym zakresem przyspieszania sprzętowego.
npm install foundry-local-sdk-winml openai
Przeglądanie wykazu i wybieranie modelu
Lokalny zestaw SDK usługi Foundry udostępnia wykaz modeli, który zawiera listę wszystkich dostępnych modeli. W tym kroku zainicjujesz zestaw SDK i wybierzesz model asystenta czatu.
Utwórz plik o nazwie
index.js.Dodaj następujący kod, aby zainicjować zestaw SDK i wybrać 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
getModelakceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metodadownloadpobiera wagi modelu do lokalnej pamięci podręcznej iloadprzygotowuje model do wnioskowania.
Definiowanie monitu systemowego
Monit systemowy ustawia osobowość i zachowanie asystenta. Jest to pierwsza wiadomość w historii konwersacji i model odwołuje się do niej w całej konwersacji.
Dodaj monit systemowy, aby ukształtować sposób reagowania asystenta:
// 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.'
}
];
Wskazówka
Poeksperymentuj z różnymi monitami systemowymi o zmianę zachowania asystenta. Możesz na przykład poinstruować go, aby odpowiedział jako pirat, nauczyciel lub ekspert domeny.
Implementowanie konwersacji wieloełowej
Asystent czatu musi zachować kontekst w wielu wymianach. Można to osiągnąć, utrzymując listę wszystkich komunikatów (system, użytkownika i asystenta) i wysyłając pełną listę z każdym żądaniem. Model używa tej historii do generowania kontekstowo odpowiednich odpowiedzi.
Dodaj pętlę konwersacji, która:
- Odczytuje dane wejściowe użytkownika z konsoli.
- Dołącza komunikat użytkownika do historii.
- Wysyła pełną historię do modelu.
- Dołącza odpowiedź asystenta do historii na następną turę.
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żde wywołanie completeChat otrzymuje pełną historię komunikatów. W ten sposób model "zapamiętuje" poprzednie interakcje — nie przechowuje stanu między wywołaniami.
Dodawanie odpowiedzi przesyłania strumieniowego
Przesyłanie strumieniowe wyświetla każdy token w miarę jego generowania, co sprawia, że asystent wydaje się bardziej responsywny. Zastąp wywołanie completeChat na completeStreamingChat, aby przesyłać strumieniowo odpowiedź, token po tokenie.
Zaktualizuj pętlę konwersacji, aby używać przesyłania strumieniowego:
// 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');
Wersja przesyłania strumieniowego gromadzi pełną odpowiedź, aby można ją było dodać do historii konwersacji po zakończeniu transmisji strumieniowej.
Kompletny kod
Utwórz plik o nazwie index.js i dodaj następujący kompletny kod:
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();
Uruchom asystenta czatu:
node index.js
Zobaczysz dane wyjściowe podobne do:
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!
Zwróć uwagę, że asystent zapamiętuje kontekst z poprzednich rozmów — gdy pytasz "Dlaczego to jest ważne dla innych organizmów żywych?", wie, że nadal mówisz o fotosyntezie.
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/python/foundry-local/tutorial-chat-assistant
Instalowanie pakietów
Jeśli programujesz lub wysyłasz na Windows, wybierz kartę Windows. Pakiet Windows integruje się z środowiskiem uruchomieniowym Windows ML — zapewnia ten sam obszar powierzchni interfejsu API z szerszym zakresem przyspieszania sprzętowego.
pip install foundry-local-sdk-winml openai
Przeglądanie wykazu i wybieranie modelu
Lokalny zestaw SDK usługi Foundry udostępnia wykaz modeli, który zawiera listę wszystkich dostępnych modeli. W tym kroku zainicjujesz zestaw SDK i wybierzesz model asystenta czatu.
Utwórz plik o nazwie
main.py.Dodaj następujący kod, aby zainicjować zestaw SDK i wybrać 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_modelakceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metodadownloadpobiera wagi modelu do lokalnej pamięci podręcznej iloadprzygotowuje model do wnioskowania.
Definiowanie monitu systemowego
Monit systemowy ustawia osobowość i zachowanie asystenta. Jest to pierwsza wiadomość w historii konwersacji i model odwołuje się do niej w całej konwersacji.
Dodaj monit systemowy, aby ukształtować sposób reagowania asystenta:
# 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.",
}
]
Wskazówka
Poeksperymentuj z różnymi monitami systemowymi o zmianę zachowania asystenta. Możesz na przykład poinstruować go, aby odpowiedział jako pirat, nauczyciel lub ekspert domeny.
Implementowanie konwersacji wieloełowej
Asystent czatu musi zachować kontekst w wielu wymianach. Można to osiągnąć, utrzymując listę wszystkich komunikatów (system, użytkownika i asystenta) i wysyłając pełną listę z każdym żądaniem. Model używa tej historii do generowania kontekstowo odpowiednich odpowiedzi.
Dodaj pętlę konwersacji, która:
- Odczytuje dane wejściowe użytkownika z konsoli.
- Dołącza komunikat użytkownika do historii.
- Wysyła pełną historię do modelu.
- Dołącza odpowiedź asystenta do historii na następną turę.
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żde wywołanie complete_chat otrzymuje pełną historię komunikatów. W ten sposób model "zapamiętuje" poprzednie interakcje — nie przechowuje stanu między wywołaniami.
Dodawanie odpowiedzi przesyłania strumieniowego
Przesyłanie strumieniowe wyświetla każdy token w miarę jego generowania, co sprawia, że asystent wydaje się bardziej responsywny. Zastąp wywołanie complete_chat na complete_streaming_chat, aby przesyłać strumieniowo odpowiedź, token po tokenie.
Zaktualizuj pętlę konwersacji, aby używać przesyłania strumieniowego:
# 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")
Wersja przesyłania strumieniowego gromadzi pełną odpowiedź, aby można ją było dodać do historii konwersacji po zakończeniu transmisji strumieniowej.
Kompletny kod
Utwórz plik o nazwie main.py i dodaj następujący kompletny kod:
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()
Uruchom asystenta czatu:
python main.py
Zobaczysz dane wyjściowe podobne do:
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!
Zwróć uwagę, że asystent zapamiętuje kontekst z poprzednich rozmów — gdy pytasz "Dlaczego to jest ważne dla innych organizmów żywych?", wie, że nadal mówisz o fotosyntezie.
Repozytorium przykładów
Kompletny przykładowy kod dla tego artykułu jest dostępny w repozytorium foundry-samples GitHub. Aby sklonować repozytorium i przejść do przykładowego użycia:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/rust/foundry-local/tutorial-chat-assistant
Instalowanie pakietów
Jeśli programujesz lub wysyłasz na Windows, wybierz kartę Windows. Pakiet Windows integruje się z środowiskiem uruchomieniowym Windows ML — zapewnia ten sam obszar powierzchni interfejsu API z szerszym zakresem przyspieszania sprzętowego.
cargo add foundry-local-sdk --features winml
cargo add tokio --features full
cargo add tokio-stream anyhow
Przeglądanie wykazu i wybieranie modelu
Lokalny zestaw SDK usługi Foundry udostępnia wykaz modeli, który zawiera listę wszystkich dostępnych modeli. W tym kroku zainicjujesz zestaw SDK i wybierzesz model asystenta czatu.
Otwórz
src/main.rsi zastąp jego zawartość następującym kodem, aby zainicjować zestaw SDK i wybrać 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_modelakceptuje alias modelu, który jest krótką, przyjazną nazwą odnoszącą się do określonego modelu w wykazie. Metodadownloadpobiera wagi modelu do lokalnej pamięci podręcznej iloadprzygotowuje model do wnioskowania.
Definiowanie monitu systemowego
Monit systemowy ustawia osobowość i zachowanie asystenta. Jest to pierwsza wiadomość w historii konwersacji i model odwołuje się do niej w całej konwersacji.
Dodaj monit systemowy, aby ukształtować sposób reagowania asystenta:
// 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(),
];
Wskazówka
Poeksperymentuj z różnymi monitami systemowymi o zmianę zachowania asystenta. Możesz na przykład poinstruować go, aby odpowiedział jako pirat, nauczyciel lub ekspert domeny.
Implementowanie konwersacji wieloełowej
Asystent czatu musi zachować kontekst w wielu wymianach. Można to osiągnąć, zachowując wektor wszystkich komunikatów (system, użytkownik i asystent) i wysyłając pełną listę przy każdym żądaniu. Model używa tej historii do generowania kontekstowo odpowiednich odpowiedzi.
Dodaj pętlę konwersacji, która:
- Odczytuje dane wejściowe użytkownika z konsoli.
- Dołącza komunikat użytkownika do historii.
- Wysyła pełną historię do modelu.
- Dołącza odpowiedź asystenta do historii na następną turę.
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żde wywołanie complete_chat otrzymuje pełną historię komunikatów. W ten sposób model "zapamiętuje" poprzednie interakcje — nie przechowuje stanu między wywołaniami.
Dodawanie odpowiedzi przesyłania strumieniowego
Przesyłanie strumieniowe wyświetla każdy token w miarę jego generowania, co sprawia, że asystent wydaje się bardziej responsywny. Zastąp wywołanie complete_chat na complete_streaming_chat, aby przesyłać strumieniowo odpowiedź, token po tokenie.
Zaktualizuj pętlę konwersacji, aby używać przesyłania strumieniowego:
// 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");
Wersja przesyłania strumieniowego gromadzi pełną odpowiedź, aby można ją było dodać do historii konwersacji po zakończeniu transmisji strumieniowej.
Kompletny kod
Zastąp zawartość src/main.rs następującym kompletnym kodem:
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(())
}
Uruchom asystenta czatu:
cargo run
Zobaczysz dane wyjściowe podobne do:
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!
Zwróć uwagę, że asystent zapamiętuje kontekst z poprzednich rozmów — gdy pytasz "Dlaczego to jest ważne dla innych organizmów żywych?", wie, że nadal mówisz o fotosyntezie.
Uprzątnij zasoby
Wagi modelu są przechowywane w Twojej lokalnej pamięci podręcznej po rozładowaniu modelu. Oznacza to, że przy następnym uruchomieniu aplikacji krok pobierania zostanie pominięty, a model ładuje się szybciej. Nie jest wymagane żadne dodatkowe czyszczenie, chyba że chcesz odzyskać miejsce na dysku.