Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questa esercitazione si crea un assistente chat interattivo che viene eseguito interamente nel dispositivo. L'assistente gestisce il contesto della conversazione tra più scambi, quindi ricorda ciò che hai discusso in precedenza nella conversazione. Si usa Foundry Local SDK per selezionare un modello, definire una richiesta di sistema e trasmettere le risposte token per token.
In questa esercitazione apprenderai a:
- Configurare un progetto e installare Foundry Local SDK
- Esplorare il catalogo dei modelli e selezionare un modello
- Definire un prompt di sistema per modellare il comportamento dell'assistente
- Implementare una conversazione a più turni con la cronologia dei messaggi
- Trasmettere risposte per un'esperienza reattiva
- Ripulire le risorse al termine della conversazione
Prerequisiti
- Un computer Windows, macOS o Linux con almeno 8 GB di RAM.
Repository di esempi
Il codice di esempio completo per questo articolo è disponibile nel repository GitHub Foundry Local. Per clonare il repository e navigare all'esempio:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/cs/tutorial-chat-assistant
Installare i pacchetti
Se si sviluppa o si esegue la spedizione in Windows, selezionare la scheda Windows. Il pacchetto Windows si integra con il runtime Windows ML, che fornisce la stessa area di attacco API con un'ampiezza più ampia di accelerazione hardware.
dotnet add package Microsoft.AI.Foundry.Local.WinML
dotnet add package OpenAI
Gli esempi C# nel repository GitHub sono progetti preconfigurati. Se stai creando da zero, dovresti leggere il riferimento del Foundry Local SDK per ulteriori dettagli su come configurare il tuo progetto C# con Foundry Local.
Esplorare il catalogo e selezionare un modello
Foundry Local SDK fornisce un catalogo di modelli che elenca tutti i modelli disponibili. In questo passaggio si inizializza l'SDK e si seleziona un modello per l'assistente chat.
Aprire
Program.cse sostituire il relativo contenuto con il codice seguente per inizializzare l'SDK e selezionare un modello: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();Il
GetModelAsyncmetodo accetta un alias del modello, cioè un nome breve e amichevole che corrisponde a un modello specifico nel catalogo. IlDownloadAsyncmetodo recupera i pesi del modello nella cache locale eLoadAsyncrende il modello pronto per l'inferenza.
Definire un prompt di sistema
Una richiesta di sistema imposta la personalità e il comportamento dell'assistente. Si tratta del primo messaggio nella cronologia delle conversazioni e il modello vi fa riferimento in tutta la conversazione.
Aggiungere una richiesta di sistema per modellare la risposta dell'assistente:
// 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."
}
};
Suggerimento
Sperimentare con richieste di sistema diverse per modificare il comportamento dell'assistente. Ad esempio, è possibile indicare di rispondere come pirata, insegnante o esperto di dominio.
Implementare una conversazione a più turni
Un assistente chat deve mantenere il contesto tra più scambi. A tale scopo, mantenere un elenco di tutti i messaggi (sistema, utente e assistente) e inviare l'elenco completo con ogni richiesta. Il modello usa questa cronologia per generare risposte contestualmente pertinenti.
Aggiungere un ciclo di conversazione che:
- Legge l'input dell'utente dalla console.
- Aggiunge il messaggio utente alla cronologia.
- Invia la cronologia completa al modello.
- Aggiunge la risposta dell'assistente alla cronologia per il turno successivo.
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 });
}
Ogni chiamata a CompleteChatAsync riceve la cronologia completa dei messaggi. Questo è il modo in cui il modello "ricorda" i turni precedenti: non archivia lo stato tra le chiamate.
Aggiungere risposte di streaming
Lo streaming stampa ogni token man mano che viene generato, il che rende l'assistente più reattivo. Sostituire la CompleteChatAsync chiamata con CompleteChatStreamingAsync per trasmettere il token di risposta uno per uno.
Aggiornare il ciclo di conversazione per usare lo 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");
La versione di streaming accumula la risposta completa in modo che possa essere aggiunta alla cronologia della conversazione al termine del flusso.
Codice completo
Sostituire il contenuto di Program.cs con il codice completo seguente:
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!");
Avvia l'assistente chat:
dotnet run
Viene visualizzato un output simile al seguente:
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!
Si noti che l'assistente ricorda il contesto dei turni precedenti: quando chiedi "Perché è importante per altre cose viventi?", sa che stai ancora parlando di fotosintesi.
Repository di esempi
Il codice di esempio completo per questo articolo è disponibile nel repository GitHub Foundry Local. Per clonare il repository e navigare all'esempio:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/js/tutorial-chat-assistant
Installare i pacchetti
Se si sviluppa o si esegue la spedizione in Windows, selezionare la scheda Windows. Il pacchetto Windows si integra con il runtime Windows ML, che fornisce la stessa area di attacco API con un'ampiezza più ampia di accelerazione hardware.
npm install foundry-local-sdk-winml openai
Esplorare il catalogo e selezionare un modello
Foundry Local SDK fornisce un catalogo di modelli che elenca tutti i modelli disponibili. In questo passaggio si inizializza l'SDK e si seleziona un modello per l'assistente chat.
Creare un file denominato
index.js.Aggiungere il codice seguente per inizializzare l'SDK e selezionare un modello:
// 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();Il
getModelmetodo accetta un alias del modello, cioè un nome breve e amichevole che corrisponde a un modello specifico nel catalogo. Ildownloadmetodo recupera i pesi del modello nella cache locale eloadrende il modello pronto per l'inferenza.
Definire un prompt di sistema
Una richiesta di sistema imposta la personalità e il comportamento dell'assistente. Si tratta del primo messaggio nella cronologia delle conversazioni e il modello vi fa riferimento in tutta la conversazione.
Aggiungere una richiesta di sistema per modellare la risposta dell'assistente:
// 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.'
}
];
Suggerimento
Sperimentare con richieste di sistema diverse per modificare il comportamento dell'assistente. Ad esempio, è possibile indicare di rispondere come pirata, insegnante o esperto di dominio.
Implementare una conversazione a più turni
Un assistente chat deve mantenere il contesto tra più scambi. A tale scopo, mantenere un elenco di tutti i messaggi (sistema, utente e assistente) e inviare l'elenco completo con ogni richiesta. Il modello usa questa cronologia per generare risposte contestualmente pertinenti.
Aggiungere un ciclo di conversazione che:
- Legge l'input dell'utente dalla console.
- Aggiunge il messaggio utente alla cronologia.
- Invia la cronologia completa al modello.
- Aggiunge la risposta dell'assistente alla cronologia per il turno successivo.
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 });
}
Ogni chiamata a completeChat riceve la cronologia completa dei messaggi. Questo è il modo in cui il modello "ricorda" i turni precedenti: non archivia lo stato tra le chiamate.
Aggiungere risposte di streaming
Lo streaming stampa ogni token man mano che viene generato, il che rende l'assistente più reattivo. Sostituire la completeChat chiamata con completeStreamingChat per trasmettere il token di risposta uno per uno.
Aggiornare il ciclo di conversazione per usare lo 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');
La versione di streaming accumula la risposta completa in modo che possa essere aggiunta alla cronologia della conversazione al termine del flusso.
Codice completo
Creare un file denominato index.js e aggiungere il codice completo seguente:
import { FoundryLocalManager } from 'foundry-local-sdk';
import * as readline from 'readline';
// Initialize the Foundry Local SDK
const manager = FoundryLocalManager.create({
appName: 'foundry_local_samples',
logLevel: 'info'
});
// Download and register all execution providers.
let currentEp = '';
await manager.downloadAndRegisterEps((epName, percent) => {
if (epName !== currentEp) {
if (currentEp !== '') process.stdout.write('\n');
currentEp = epName;
}
process.stdout.write(`\r ${epName.padEnd(30)} ${percent.toFixed(1).padStart(5)}%`);
});
if (currentEp !== '') process.stdout.write('\n');
// Select and load a model from the catalog
const model = await manager.catalog.getModel('qwen2.5-0.5b');
await model.download((progress) => {
process.stdout.write(`\rDownloading model: ${progress.toFixed(2)}%`);
});
console.log('\nModel downloaded.');
await model.load();
console.log('Model loaded and ready.');
// Create a chat client
const chatClient = model.createChatClient();
// Start the conversation with a system prompt
const messages = [
{
role: 'system',
content: 'You are a helpful, friendly assistant. Keep your responses ' +
'concise and conversational. If you don\'t know something, say so.'
}
];
// Set up readline for console input
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const askQuestion = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
console.log('\nChat assistant ready! Type \'quit\' to exit.\n');
while (true) {
const userInput = await askQuestion('You: ');
if (userInput.trim().toLowerCase() === 'quit' ||
userInput.trim().toLowerCase() === 'exit') {
break;
}
// Add the user's message to conversation history
messages.push({ role: 'user', content: userInput });
// Stream the response token by token
process.stdout.write('Assistant: ');
let fullResponse = '';
for await (const chunk of chatClient.completeStreamingChat(messages)) {
const content = chunk.choices?.[0]?.delta?.content;
if (content) {
process.stdout.write(content);
fullResponse += content;
}
}
console.log('\n');
// Add the complete response to conversation history
messages.push({ role: 'assistant', content: fullResponse });
}
// Clean up - unload the model
await model.unload();
console.log('Model unloaded. Goodbye!');
rl.close();
Avvia l'assistente chat:
node index.js
Viene visualizzato un output simile al seguente:
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!
Si noti che l'assistente ricorda il contesto dei turni precedenti: quando chiedi "Perché è importante per altre cose viventi?", sa che stai ancora parlando di fotosintesi.
Repository di esempi
Il codice di esempio completo per questo articolo è disponibile nel repository GitHub Foundry Local. Per clonare il repository e navigare all'esempio:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/python/tutorial-chat-assistant
Installare i pacchetti
Se si sviluppa o si esegue la spedizione in Windows, selezionare la scheda Windows. Il pacchetto Windows si integra con il runtime Windows ML, che fornisce la stessa area di attacco API con un'ampiezza più ampia di accelerazione hardware.
pip install foundry-local-sdk-winml openai
Esplorare il catalogo e selezionare un modello
Foundry Local SDK fornisce un catalogo di modelli che elenca tutti i modelli disponibili. In questo passaggio si inizializza l'SDK e si seleziona un modello per l'assistente chat.
Creare un file denominato
main.py.Aggiungere il codice seguente per inizializzare l'SDK e selezionare un modello:
# 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()Il
get_modelmetodo accetta un alias del modello, cioè un nome breve e amichevole che corrisponde a un modello specifico nel catalogo. Ildownloadmetodo recupera i pesi del modello nella cache locale eloadrende il modello pronto per l'inferenza.
Definire un prompt di sistema
Una richiesta di sistema imposta la personalità e il comportamento dell'assistente. Si tratta del primo messaggio nella cronologia delle conversazioni e il modello vi fa riferimento in tutta la conversazione.
Aggiungere una richiesta di sistema per modellare la risposta dell'assistente:
# 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."
}
]
Suggerimento
Sperimentare con richieste di sistema diverse per modificare il comportamento dell'assistente. Ad esempio, è possibile indicare di rispondere come pirata, insegnante o esperto di dominio.
Implementare una conversazione a più turni
Un assistente chat deve mantenere il contesto tra più scambi. A tale scopo, mantenere un elenco di tutti i messaggi (sistema, utente e assistente) e inviare l'elenco completo con ogni richiesta. Il modello usa questa cronologia per generare risposte contestualmente pertinenti.
Aggiungere un ciclo di conversazione che:
- Legge l'input dell'utente dalla console.
- Aggiunge il messaggio utente alla cronologia.
- Invia la cronologia completa al modello.
- Aggiunge la risposta dell'assistente alla cronologia per il turno successivo.
while True:
user_input = input("You: ")
if user_input.strip().lower() in ("quit", "exit"):
break
# Add the user's message to conversation history
messages.append({"role": "user", "content": user_input})
# Stream the response token by token
print("Assistant: ", end="", flush=True)
full_response = ""
for chunk in client.complete_streaming_chat(messages):
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
full_response += content
print("\n")
# Add the complete response to conversation history
messages.append({"role": "assistant", "content": full_response})
Ogni chiamata a complete_chat riceve la cronologia completa dei messaggi. Questo è il modo in cui il modello "ricorda" i turni precedenti: non archivia lo stato tra le chiamate.
Aggiungere risposte di streaming
Lo streaming stampa ogni token man mano che viene generato, il che rende l'assistente più reattivo. Sostituire la complete_chat chiamata con complete_streaming_chat per trasmettere il token di risposta uno per uno.
Aggiornare il ciclo di conversazione per usare lo streaming:
# Stream the response token by token
print("Assistant: ", end="", flush=True)
full_response = ""
for chunk in client.complete_streaming_chat(messages):
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
full_response += content
print("\n")
La versione di streaming accumula la risposta completa in modo che possa essere aggiunta alla cronologia della conversazione al termine del flusso.
Codice completo
Creare un file denominato main.py e aggiungere il codice completo seguente:
from foundry_local_sdk import Configuration, FoundryLocalManager
def main():
# Initialize the Foundry Local SDK
config = Configuration(app_name="foundry_local_samples")
FoundryLocalManager.initialize(config)
manager = FoundryLocalManager.instance
# Download and register all execution providers.
current_ep = ""
def ep_progress(ep_name: str, percent: float):
nonlocal current_ep
if ep_name != current_ep:
if current_ep:
print()
current_ep = ep_name
print(f"\r {ep_name:<30} {percent:5.1f}%", end="", flush=True)
manager.download_and_register_eps(progress_callback=ep_progress)
if current_ep:
print()
# Select and load a model from the catalog
model = manager.catalog.get_model("qwen2.5-0.5b")
model.download(lambda progress: print(f"\rDownloading model: {progress:.2f}%", end="", flush=True))
print()
model.load()
print("Model loaded and ready.")
# Get a chat client
client = model.get_chat_client()
# Start the conversation with a system prompt
messages = [
{
"role": "system",
"content": "You are a helpful, friendly assistant. Keep your responses "
"concise and conversational. If you don't know something, say so."
}
]
print("\nChat assistant ready! Type 'quit' to exit.\n")
while True:
user_input = input("You: ")
if user_input.strip().lower() in ("quit", "exit"):
break
# Add the user's message to conversation history
messages.append({"role": "user", "content": user_input})
# Stream the response token by token
print("Assistant: ", end="", flush=True)
full_response = ""
for chunk in client.complete_streaming_chat(messages):
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
full_response += content
print("\n")
# Add the complete response to conversation history
messages.append({"role": "assistant", "content": full_response})
# Clean up - unload the model
model.unload()
print("Model unloaded. Goodbye!")
if __name__ == "__main__":
main()
Avvia l'assistente chat:
python main.py
Viene visualizzato un output simile al seguente:
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!
Si noti che l'assistente ricorda il contesto dei turni precedenti: quando chiedi "Perché è importante per altre cose viventi?", sa che stai ancora parlando di fotosintesi.
Repository di esempi
Il codice di esempio completo per questo articolo è disponibile nel repository GitHub Foundry Local. Per clonare il repository e navigare all'esempio:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/rust/tutorial-chat-assistant
Installare i pacchetti
Se si sviluppa o si esegue la spedizione in Windows, selezionare la scheda Windows. Il pacchetto Windows si integra con il runtime Windows ML, che fornisce la stessa area di attacco API con un'ampiezza più ampia di accelerazione hardware.
cargo add foundry-local-sdk --features winml
cargo add tokio --features full
cargo add tokio-stream anyhow
Esplorare il catalogo e selezionare un modello
Foundry Local SDK fornisce un catalogo di modelli che elenca tutti i modelli disponibili. In questo passaggio si inizializza l'SDK e si seleziona un modello per l'assistente chat.
Aprire
src/main.rse sostituire il relativo contenuto con il codice seguente per inizializzare l'SDK e selezionare un modello:// Initialize the Foundry Local SDK let manager = FoundryLocalManager::create(FoundryLocalConfig::new("chat-assistant"))?; // Download and register all execution providers. manager .download_and_register_eps_with_progress(None, { let mut current_ep = String::new(); move |ep_name: &str, percent: f64| { if ep_name != current_ep { if !current_ep.is_empty() { println!(); } current_ep = ep_name.to_string(); } print!("\r {:<30} {:5.1}%", ep_name, percent); io::stdout().flush().ok(); } }) .await?; println!(); // Select and load a model from the catalog let model = manager.catalog().get_model("qwen2.5-0.5b").await?; if !model.is_cached().await? { println!("Downloading model..."); model .download(Some(|progress: f64| { print!("\r {progress:.1}%"); io::stdout().flush().ok(); })) .await?; println!(); } model.load().await?; println!("Model loaded and ready."); // Create a chat client let client = model.create_chat_client().temperature(0.7).max_tokens(512);Il
get_modelmetodo accetta un alias del modello, cioè un nome breve e amichevole che corrisponde a un modello specifico nel catalogo. Ildownloadmetodo recupera i pesi del modello nella cache locale eloadrende il modello pronto per l'inferenza.
Definire un prompt di sistema
Una richiesta di sistema imposta la personalità e il comportamento dell'assistente. Si tratta del primo messaggio nella cronologia delle conversazioni e il modello vi fa riferimento in tutta la conversazione.
Aggiungere una richiesta di sistema per modellare la risposta dell'assistente:
// 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(),
];
Suggerimento
Sperimentare con richieste di sistema diverse per modificare il comportamento dell'assistente. Ad esempio, è possibile indicare di rispondere come pirata, insegnante o esperto di dominio.
Implementare una conversazione a più turni
Un assistente chat deve mantenere il contesto tra più scambi. A tale scopo, mantenere un vettore di tutti i messaggi (sistema, utente e assistente) e inviare l'elenco completo con ogni richiesta. Il modello usa questa cronologia per generare risposte contestualmente pertinenti.
Aggiungere un ciclo di conversazione che:
- Legge l'input dell'utente dalla console.
- Aggiunge il messaggio utente alla cronologia.
- Invia la cronologia completa al modello.
- Aggiunge la risposta dell'assistente alla cronologia per il turno successivo.
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);
}
Ogni chiamata a complete_chat riceve la cronologia completa dei messaggi. Questo è il modo in cui il modello "ricorda" i turni precedenti: non archivia lo stato tra le chiamate.
Aggiungere risposte di streaming
Lo streaming stampa ogni token man mano che viene generato, il che rende l'assistente più reattivo. Sostituire la complete_chat chiamata con complete_streaming_chat per trasmettere il token di risposta uno per uno.
Aggiornare il ciclo di conversazione per usare lo 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");
La versione di streaming accumula la risposta completa in modo che possa essere aggiunta alla cronologia della conversazione al termine del flusso.
Codice completo
Sostituire il contenuto di src/main.rs con il codice completo seguente:
use foundry_local_sdk::{
ChatCompletionRequestMessage,
ChatCompletionRequestSystemMessage, ChatCompletionRequestUserMessage,
FoundryLocalConfig, FoundryLocalManager,
};
use std::io::{self, BufRead, Write};
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize the Foundry Local SDK
let manager = FoundryLocalManager::create(FoundryLocalConfig::new("chat-assistant"))?;
// Download and register all execution providers.
manager
.download_and_register_eps_with_progress(None, {
let mut current_ep = String::new();
move |ep_name: &str, percent: f64| {
if ep_name != current_ep {
if !current_ep.is_empty() {
println!();
}
current_ep = ep_name.to_string();
}
print!("\r {:<30} {:5.1}%", ep_name, percent);
io::stdout().flush().ok();
}
})
.await?;
println!();
// Select and load a model from the catalog
let model = manager.catalog().get_model("qwen2.5-0.5b").await?;
if !model.is_cached().await? {
println!("Downloading model...");
model
.download(Some(|progress: f64| {
print!("\r {progress:.1}%");
io::stdout().flush().ok();
}))
.await?;
println!();
}
model.load().await?;
println!("Model loaded and ready.");
// Create a chat client
let client = model.create_chat_client().temperature(0.7).max_tokens(512);
// Start the conversation with a system prompt
let mut messages: Vec<ChatCompletionRequestMessage> = vec![
ChatCompletionRequestSystemMessage::from(
"You are a helpful, friendly assistant. Keep your responses \
concise and conversational. If you don't know something, say so.",
)
.into(),
];
println!("\nChat assistant ready! Type 'quit' to exit.\n");
let stdin = io::stdin();
loop {
print!("You: ");
io::stdout().flush()?;
let mut input = String::new();
stdin.lock().read_line(&mut input)?;
let input = input.trim();
if input.eq_ignore_ascii_case("quit") || input.eq_ignore_ascii_case("exit") {
break;
}
// Add the user's message to conversation history
messages.push(ChatCompletionRequestUserMessage::from(input).into());
// Stream the response token by token
print!("Assistant: ");
io::stdout().flush()?;
let mut full_response = String::new();
let mut stream = client.complete_streaming_chat(&messages, None).await?;
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
if let Some(choice) = chunk.choices.first() {
if let Some(ref content) = choice.delta.content {
print!("{content}");
io::stdout().flush()?;
full_response.push_str(content);
}
}
}
println!("\n");
// Add the complete response to conversation history
let assistant_msg: ChatCompletionRequestMessage = serde_json::from_value(
serde_json::json!({"role": "assistant", "content": full_response}),
)?;
messages.push(assistant_msg);
}
// Clean up - unload the model
model.unload().await?;
println!("Model unloaded. Goodbye!");
Ok(())
}
Avvia l'assistente chat:
cargo run
Viene visualizzato un output simile al seguente:
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!
Si noti che l'assistente ricorda il contesto dei turni precedenti: quando chiedi "Perché è importante per altre cose viventi?", sa che stai ancora parlando di fotosintesi.
Pulire le risorse
I pesi del modello rimangono nella cache locale dopo lo scaricamento di un modello. Ciò significa che la volta successiva che si esegue l'applicazione, il passaggio di download viene ignorato e il modello viene caricato più velocemente. Non è necessaria alcuna pulizia aggiuntiva, a meno che non si voglia recuperare spazio su disco.