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
dotnet add package Microsoft.AI.Foundry.Local
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."); // Create one stateful native chat session for the conversation. using var chatSession = new ChatSession(model); chatSession.SetStreaming(true);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 systemPrompt = "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;
}
using var request = new Request();
if (chatSession.TurnCount == 0)
{
request.AddItem(MessageItem.System(systemPrompt));
}
request.AddItem(MessageItem.User(userInput));
// Stream the response token by token
Console.Write("Assistant: ");
await using var streamingResponse = chatSession.ProcessStreamingRequestAsync(request, ct);
await foreach (var item in streamingResponse)
{
using (item)
{
if (item is TextItem { Type: TextItemType.Default } text)
{
Console.Write(text.Text);
Console.Out.Flush();
}
}
}
using var finalResponse = await streamingResponse.FinalResponse;
Console.WriteLine("\n");
}
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: ");
await using var streamingResponse = chatSession.ProcessStreamingRequestAsync(request, ct);
await foreach (var item in streamingResponse)
{
using (item)
{
if (item is TextItem { Type: TextItemType.Default } text)
{
Console.Write(text.Text);
Console.Out.Flush();
}
}
}
using var finalResponse = await streamingResponse.FinalResponse;
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 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.");
// Create one stateful native chat session for the conversation.
using var chatSession = new ChatSession(model);
chatSession.SetStreaming(true);
// Start the conversation with a system prompt
var systemPrompt = "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;
}
using var request = new Request();
if (chatSession.TurnCount == 0)
{
request.AddItem(MessageItem.System(systemPrompt));
}
request.AddItem(MessageItem.User(userInput));
// Stream the response token by token
Console.Write("Assistant: ");
await using var streamingResponse = chatSession.ProcessStreamingRequestAsync(request, ct);
await foreach (var item in streamingResponse)
{
using (item)
{
if (item is TextItem { Type: TextItemType.Default } text)
{
Console.Write(text.Text);
Console.Out.Flush();
}
}
}
using var finalResponse = await streamingResponse.FinalResponse;
Console.WriteLine("\n");
}
// Clean up - unload the model
chatSession.Dispose();
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
npm install foundry-local-sdk 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 stateful native chat session const chatSession = new ChatSession(model); chatSession.setOptions({ search: { temperature: 0.7, maxOutputTokens: 512 } });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 systemPrompt =
'You are a helpful, friendly assistant. Keep your responses ' +
'concise and conversational. If you don\'t know something, say so.';
let firstTurn = true;
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;
}
const request = new Request();
if (firstTurn) {
request.addItem(Item.systemMessage(systemPrompt));
firstTurn = false;
}
request.addItem(Item.userMessage(userInput));
// Stream the response token by token
process.stdout.write('Assistant: ');
const stream = chatSession.processStreamingRequest(request);
for await (const item of stream) {
if (item.type === 'text' && item.textType === 'default') {
process.stdout.write(item.text);
}
}
await stream.response;
console.log('\n');
}
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: ');
const stream = chatSession.processStreamingRequest(request);
for await (const item of stream) {
if (item.type === 'text' && item.textType === 'default') {
process.stdout.write(item.text);
}
}
await stream.response;
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 {
ChatSession,
FoundryLocalManager,
Item,
Request
} 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 stateful native chat session
const chatSession = new ChatSession(model);
chatSession.setOptions({
search: { temperature: 0.7, maxOutputTokens: 512 }
});
// Start the conversation with a system prompt
const systemPrompt =
'You are a helpful, friendly assistant. Keep your responses ' +
'concise and conversational. If you don\'t know something, say so.';
let firstTurn = true;
// 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;
}
const request = new Request();
if (firstTurn) {
request.addItem(Item.systemMessage(systemPrompt));
firstTurn = false;
}
request.addItem(Item.userMessage(userInput));
// Stream the response token by token
process.stdout.write('Assistant: ');
const stream = chatSession.processStreamingRequest(request);
for await (const item of stream) {
if (item.type === 'text' && item.textType === 'default') {
process.stdout.write(item.text);
}
}
await stream.response;
console.log('\n');
}
// Clean up - unload the model
chatSession.dispose();
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
pip install foundry-local-sdk 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 # Discover and register execution providers that are not already available. missing_eps = [ep.name for ep in manager.discover_eps() if not ep.is_registered] if missing_eps: manager.download_and_register_eps(missing_eps) # Select and load a model from the catalog. model = manager.catalog.get_model("qwen2.5-0.5b") if model is None: raise RuntimeError("Model 'qwen2.5-0.5b' was not found in the catalog.") if not model.is_cached: model.download( lambda progress: print( f"\rDownloading model: {progress:.2f}%", end="", flush=True ) ) print() model.load() print("Model loaded and ready.")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.
system_prompt = (
"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
# The stateful session retains earlier turns automatically.
with Request() as request:
if first_turn:
request.add_item(MessageItem.system(system_prompt))
first_turn = False
request.add_item(MessageItem.user(user_input))
print("Assistant: ", end="", flush=True)
with session.process_streaming_request(request) as stream:
for item in stream:
if (
isinstance(item, TextItem)
and item.type == TextItemType.DEFAULT
):
print(item.text, end="", flush=True)
print("\n")
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:
# The stateful session retains earlier turns automatically.
with Request() as request:
if first_turn:
request.add_item(MessageItem.system(system_prompt))
first_turn = False
request.add_item(MessageItem.user(user_input))
print("Assistant: ", end="", flush=True)
with session.process_streaming_request(request) as stream:
for item in stream:
if (
isinstance(item, TextItem)
and item.type == TextItemType.DEFAULT
):
print(item.text, end="", flush=True)
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 (
ChatSession,
Configuration,
FoundryLocalManager,
MessageItem,
Request,
RequestOptions,
SearchOptions,
TextItem,
TextItemType,
)
def main():
# Initialize the Foundry Local SDK
config = Configuration(app_name="foundry_local_samples")
FoundryLocalManager.initialize(config)
manager = FoundryLocalManager.instance
# Discover and register execution providers that are not already available.
missing_eps = [ep.name for ep in manager.discover_eps() if not ep.is_registered]
if missing_eps:
manager.download_and_register_eps(missing_eps)
# Select and load a model from the catalog.
model = manager.catalog.get_model("qwen2.5-0.5b")
if model is None:
raise RuntimeError("Model 'qwen2.5-0.5b' was not found in the catalog.")
if not model.is_cached:
model.download(
lambda progress: print(
f"\rDownloading model: {progress:.2f}%", end="", flush=True
)
)
print()
model.load()
print("Model loaded and ready.")
try:
# Start the conversation with a system prompt.
system_prompt = (
"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")
with ChatSession(model) as session:
session.set_options(
RequestOptions(
search=SearchOptions(temperature=0.7, max_output_tokens=512)
)
)
session.set_streaming(True)
first_turn = True
while True:
user_input = input("You: ")
if user_input.strip().lower() in ("quit", "exit"):
break
# The stateful session retains earlier turns automatically.
with Request() as request:
if first_turn:
request.add_item(MessageItem.system(system_prompt))
first_turn = False
request.add_item(MessageItem.user(user_input))
print("Assistant: ", end="", flush=True)
with session.process_streaming_request(request) as stream:
for item in stream:
if (
isinstance(item, TextItem)
and item.type == TextItemType.DEFAULT
):
print(item.text, end="", flush=True)
print("\n")
finally:
# Clean up - unload the model
if model.is_loaded:
model.unload()
manager.close()
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
cargo add foundry-local-sdk
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"))?; 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 {ep_name:<30} {percent:5.1}%"); io::stdout().flush().ok(); } }) .await?; println!(); 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?; let session = ChatSession::new(&model).await?; session .set_options(RequestOptions { search: SearchOptions { temperature: Some(0.7), max_output_tokens: Some(512), ..Default::default() }, ..Default::default() }) .await?; println!("Model loaded and ready.");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:
let system_prompt = "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ąć, 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;
}
if input.is_empty() {
continue;
}
let mut items = Vec::new();
if session.turn_count() == 0 {
items.push(Item::system_message(vec![Item::text(system_prompt)]));
}
items.push(Item::user_message(vec![Item::text(input)]));
print!("Assistant: ");
io::stdout().flush()?;
let mut stream = session.process_streaming_request(Request::from_items(items));
while let Some(item) = stream.next().await {
if let Item::Text {
text,
kind: TextKind::Default,
} = item?
{
print!("{text}");
io::stdout().flush()?;
}
}
println!("\n");
}
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:
print!("Assistant: ");
io::stdout().flush()?;
let mut stream = session.process_streaming_request(Request::from_items(items));
while let Some(item) = stream.next().await {
if let Item::Text {
text,
kind: TextKind::Default,
} = item?
{
print!("{text}");
io::stdout().flush()?;
}
}
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::{
ChatSession, FoundryLocalConfig, FoundryLocalManager, Item, Request, RequestOptions,
SearchOptions, TextKind,
};
use std::io::{self, BufRead, Write};
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the Foundry Local SDK
let manager = FoundryLocalManager::create(FoundryLocalConfig::new("foundry_local_samples"))?;
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 {ep_name:<30} {percent:5.1}%");
io::stdout().flush().ok();
}
})
.await?;
println!();
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?;
let session = ChatSession::new(&model).await?;
session
.set_options(RequestOptions {
search: SearchOptions {
temperature: Some(0.7),
max_output_tokens: Some(512),
..Default::default()
},
..Default::default()
})
.await?;
println!("Model loaded and ready.");
let system_prompt = "You are a helpful, friendly assistant. Keep your responses concise and \
conversational. If you don't know something, say so.";
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;
}
if input.is_empty() {
continue;
}
let mut items = Vec::new();
if session.turn_count() == 0 {
items.push(Item::system_message(vec![Item::text(system_prompt)]));
}
items.push(Item::user_message(vec![Item::text(input)]));
print!("Assistant: ");
io::stdout().flush()?;
let mut stream = session.process_streaming_request(Request::from_items(items));
while let Some(item) = stream.next().await {
if let Item::Text {
text,
kind: TextKind::Default,
} = item?
{
print!("{text}");
io::stdout().flush()?;
}
}
println!("\n");
}
drop(session);
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.
Repozytorium przykładów
Sklonuj repozytorium GitHub z przykładami Foundry i przejdź do przykładu języka C++:
git clone https://github.com/microsoft-foundry/foundry-samples.git
cd foundry-samples/samples/cpp/foundry-local/tutorial-chat-assistant
Instalowanie pakietów
Przykłady języka C++ wymagają:
Skonfiguruj i skompiluj przykład:
cmake -S . -B build
cmake --build build --config Release
Podczas konfiguracji przykład powoduje przywrócenie pakietu NuGet Microsoft.AI.Foundry.Local.Runtime w wersji 2.2.1 do build/_packages. Pakiet zawiera nagłówki języka C++ i biblioteki natywne dla Windows, Linux i macOS. Nie potrzebujesz oddzielnego pakietu WinML.
Konfiguracja narzędzia CMake wybiera zasoby środowiska uruchomieniowego dla Twojego systemu operacyjnego i architektury oraz kopiuje wymagane biblioteki natywne obok pliku wykonywalnego. W systemach macOS i Linux uruchom plik wykonywalny z build. W systemie Windows uruchom plik .exe z build.
Przeglądanie wykazu i wybieranie modelu
Zainicjuj zestaw SDK, wybierz model z katalogu i pobierz i załaduj go:
Configuration config("foundry_local_cpp_tutorial_chat");
Manager manager(std::move(config));
auto model = SelectChatModel(manager.GetCatalog());
if (!model) {
throw std::runtime_error("No supported CPU chat model was found.");
}
if (!model->IsCached()) {
model->Download([](float progress) {
std::cout << "\rDownloading model: " << static_cast<int>(progress) << "%" << std::flush;
return 0;
});
std::cout << "\n";
}
model->Load();
Definiowanie monitu systemowego
Dodaj monit systemowy, aby ukształtować sposób reagowania asystenta:
Request system_prompt{
SystemMessage(
"You are a concise, friendly local assistant. State when you are unsure."),
};
session.ProcessRequest(system_prompt);
Implementowanie konwersacji wieloełowej
Zachowaj historię konwersacji i wyślij ją przy użyciu każdego żądania:
std::cout << "Enter a message, or type 'exit'.\n";
for (std::string input; std::cout << "You: " && std::getline(std::cin, input);) {
if (input == "exit" || input == "quit") {
break;
}
if (input.empty()) {
continue;
}
EnableStreaming(session);
Request request{UserMessage(input)};
std::cout << "Assistant: ";
session.ProcessRequest(request);
std::cout << "\n";
}
Dodawanie odpowiedzi przesyłania strumieniowego
Przesyłanie strumieniowe tekstu odpowiedzi podczas generowania go przez model:
void EnableStreaming(ChatSession& session) {
session.SetStreamingCallback([](Item item) {
if (item.GetType() == FOUNDRY_LOCAL_ITEM_TEXT &&
item.GetText().type == FOUNDRY_LOCAL_TEXT_ITEM_TYPE_DEFAULT) {
std::cout << item.GetText().text << std::flush;
}
return 0;
});
}
Kompletny kod
#include <foundry_local/foundry_local_cpp.h>
#include <iostream>
#include <memory>
#include <string>
using namespace foundry_local;
std::unique_ptr<IModel> SelectChatModel(ICatalog& catalog) {
for (const std::string alias : {"qwen2.5-0.5b", "qwen3.5-0.8b"}) {
auto model = catalog.GetModel(alias);
if (!model) {
continue;
}
for (const auto& variant : model->GetVariants()) {
const auto info = variant->GetInfo();
if (info.DeviceType() == FOUNDRY_LOCAL_DEVICE_CPU &&
(info.Task() == "chat-completion" || info.Task() == "coding")) {
model->SelectVariant(*variant);
return model;
}
}
}
return nullptr;
}
void EnableStreaming(ChatSession& session);
int main() {
try {
Configuration config("foundry_local_cpp_tutorial_chat");
Manager manager(std::move(config));
auto model = SelectChatModel(manager.GetCatalog());
if (!model) {
throw std::runtime_error("No supported CPU chat model was found.");
}
if (!model->IsCached()) {
model->Download([](float progress) {
std::cout << "\rDownloading model: " << static_cast<int>(progress) << "%" << std::flush;
return 0;
});
std::cout << "\n";
}
model->Load();
{
ChatSession session(*model);
session.SetOptions({
.search = {.temperature = 0.2F, .max_output_tokens = 512, .seed = 42},
});
Request system_prompt{
SystemMessage(
"You are a concise, friendly local assistant. State when you are unsure."),
};
session.ProcessRequest(system_prompt);
std::cout << "Enter a message, or type 'exit'.\n";
for (std::string input; std::cout << "You: " && std::getline(std::cin, input);) {
if (input == "exit" || input == "quit") {
break;
}
if (input.empty()) {
continue;
}
EnableStreaming(session);
Request request{UserMessage(input)};
std::cout << "Assistant: ";
session.ProcessRequest(request);
std::cout << "\n";
}
}
model->Unload();
manager.Shutdown();
} catch (const Error& error) {
std::cerr << "Foundry Local error [" << error.Code() << "]: " << error.what() << "\n";
return 1;
} catch (const std::exception& error) {
std::cerr << "Error: " << error.what() << "\n";
return 1;
}
return 0;
}
void EnableStreaming(ChatSession& session) {
session.SetStreamingCallback([](Item item) {
if (item.GetType() == FOUNDRY_LOCAL_ITEM_TEXT &&
item.GetText().type == FOUNDRY_LOCAL_TEXT_ITEM_TYPE_DEFAULT) {
std::cout << item.GetText().text << std::flush;
}
return 0;
});
}
Uruchom asystenta czatu:
./build/tutorial-chat-assistant
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.