Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tutorial, creará un asistente de chat interactivo que se ejecuta completamente en el dispositivo. El asistente mantiene el contexto de la conversación a lo largo de varios intercambios, así que recuerda lo que discutieron anteriormente. Use el SDK Local de Foundry para seleccionar un modelo, definir un mensaje del sistema y transmitir las respuestas token por token.
En este tutorial aprenderá a:
- Configuración de un proyecto e instalación del SDK local de Foundry
- Examinar el catálogo de modelos y seleccionar un modelo
- Definir un mensaje del sistema para configurar el comportamiento del asistente
- Implemente conversaciones de varios turnos con historial de mensajes
- Transmitir respuestas para una experiencia receptiva
- Limpie los recursos cuando finalice la conversación
Prerrequisitos
- Un equipo Windows, macOS o Linux con al menos 8 GB de RAM.
Repositorio de ejemplos
El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/cs/tutorial-chat-assistant
Instalación de paquetes
Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.
dotnet add package Microsoft.AI.Foundry.Local.WinML
dotnet add package OpenAI
Los ejemplos de C# del repositorio de GitHub son proyectos preconfigurados. Si va a compilar desde cero, debe leer la referencia del SDK local de Foundry para obtener más detalles sobre cómo configurar el proyecto de C# con Foundry Local.
Examinar el catálogo y seleccionar un modelo
El SDK local de Foundry proporciona un catálogo de modelos que muestra todos los modelos disponibles. En este paso, inicializa el SDK y selecciona un modelo para el asistente de chat.
Abra
Program.csy reemplace su contenido por el código siguiente para inicializar el SDK y seleccionar un modelo: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();El
GetModelAsyncmétodo acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. ElDownloadAsyncmétodo captura los pesos del modelo en la memoria caché local yLoadAsynchace que el modelo esté listo para la inferencia.
Definir un mensaje del sistema
Un mensaje del sistema establece la personalidad y el comportamiento del asistente. Es el primer mensaje del historial de conversaciones y el modelo hace referencia a él a lo largo de la conversación.
Agregue un símbolo del sistema para dar forma a cómo responde el asistente:
// 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."
}
};
Sugerencia
Experimente con diferentes avisos del sistema para cambiar el comportamiento del asistente. Por ejemplo, puede indicarle que responda como un pirata, un profesor o un experto en dominio.
Implementar una conversación multiturno
Un asistente de chat debe mantener el contexto en varios intercambios. Para ello, mantenga una lista de todos los mensajes (sistema, usuario y asistente) y envíe la lista completa con cada solicitud. El modelo usa este historial para generar respuestas contextualmente relevantes.
Agregue un bucle de conversación que:
- Lee la entrada del usuario desde la consola.
- Anexa el mensaje de usuario al historial.
- Envía el historial completo al modelo.
- Anexa la respuesta del asistente al historial para el siguiente turno.
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 });
}
Cada llamada a CompleteChatAsync recibe el historial de mensajes completo. Así es como el modelo "recuerda" los turnos anteriores, no almacena el estado entre las llamadas.
Agrega respuestas de streaming
El streaming muestra cada token a medida que se genera, lo que hace que el asistente parezca más receptivo. Reemplazar la llamada CompleteChatAsync con CompleteChatStreamingAsync para transmitir la respuesta, token por token.
Actualice el bucle de conversación para usar el 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 versión de streaming acumula la respuesta completa para que se pueda agregar al historial de conversaciones una vez completada la secuencia.
Código completo
Reemplace el contenido de Program.cs por el código completo siguiente:
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!");
Ejecute el asistente de chat:
dotnet run
Verá una salida similar a la siguiente:
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!
Observe cómo el asistente recuerda el contexto de los turnos anteriores, cuando se pregunta "¿Por qué es importante para otras cosas vivientes?", sabe que sigue hablando de fotosynthesis.
Repositorio de ejemplos
El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/js/tutorial-chat-assistant
Instalación de paquetes
Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.
npm install foundry-local-sdk-winml openai
Examinar el catálogo y seleccionar un modelo
El SDK local de Foundry proporciona un catálogo de modelos que muestra todos los modelos disponibles. En este paso, inicializa el SDK y selecciona un modelo para el asistente de chat.
Cree un archivo denominado
index.js.Agregue el código siguiente para inicializar el SDK y seleccionar un modelo:
// 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();El
getModelmétodo acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. Eldownloadmétodo captura los pesos del modelo en la memoria caché local yloadhace que el modelo esté listo para la inferencia.
Definir un mensaje del sistema
Un mensaje del sistema establece la personalidad y el comportamiento del asistente. Es el primer mensaje del historial de conversaciones y el modelo hace referencia a él a lo largo de la conversación.
Agregue un símbolo del sistema para dar forma a cómo responde el asistente:
// 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.'
}
];
Sugerencia
Experimente con diferentes avisos del sistema para cambiar el comportamiento del asistente. Por ejemplo, puede indicarle que responda como un pirata, un profesor o un experto en dominio.
Implementar una conversación multiturno
Un asistente de chat debe mantener el contexto en varios intercambios. Para ello, mantenga una lista de todos los mensajes (sistema, usuario y asistente) y envíe la lista completa con cada solicitud. El modelo usa este historial para generar respuestas contextualmente relevantes.
Agregue un bucle de conversación que:
- Lee la entrada del usuario desde la consola.
- Anexa el mensaje de usuario al historial.
- Envía el historial completo al modelo.
- Anexa la respuesta del asistente al historial para el siguiente turno.
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 });
}
Cada llamada a completeChat recibe el historial de mensajes completo. Así es como el modelo "recuerda" los turnos anteriores, no almacena el estado entre las llamadas.
Agrega respuestas de streaming
El streaming muestra cada token a medida que se genera, lo que hace que el asistente parezca más receptivo. Reemplazar la llamada completeChat con completeStreamingChat para transmitir la respuesta, token por token.
Actualice el bucle de conversación para usar el 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 versión de streaming acumula la respuesta completa para que se pueda agregar al historial de conversaciones una vez completada la secuencia.
Código completo
Cree un archivo denominado index.js y agregue el siguiente código completo:
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();
Ejecute el asistente de chat:
node index.js
Verá una salida similar a la siguiente:
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!
Observe cómo el asistente recuerda el contexto de los turnos anteriores, cuando se pregunta "¿Por qué es importante para otras cosas vivientes?", sabe que sigue hablando de fotosynthesis.
Repositorio de ejemplos
El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/python/tutorial-chat-assistant
Instalación de paquetes
Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.
pip install foundry-local-sdk-winml openai
Examinar el catálogo y seleccionar un modelo
El SDK local de Foundry proporciona un catálogo de modelos que muestra todos los modelos disponibles. En este paso, inicializa el SDK y selecciona un modelo para el asistente de chat.
Cree un archivo denominado
main.py.Agregue el código siguiente para inicializar el SDK y seleccionar un modelo:
# 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()El
get_modelmétodo acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. Eldownloadmétodo captura los pesos del modelo en la memoria caché local yloadhace que el modelo esté listo para la inferencia.
Definir un mensaje del sistema
Un mensaje del sistema establece la personalidad y el comportamiento del asistente. Es el primer mensaje del historial de conversaciones y el modelo hace referencia a él a lo largo de la conversación.
Agregue un símbolo del sistema para dar forma a cómo responde el asistente:
# 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."
}
]
Sugerencia
Experimente con diferentes avisos del sistema para cambiar el comportamiento del asistente. Por ejemplo, puede indicarle que responda como un pirata, un profesor o un experto en dominio.
Implementar una conversación multiturno
Un asistente de chat debe mantener el contexto en varios intercambios. Para ello, mantenga una lista de todos los mensajes (sistema, usuario y asistente) y envíe la lista completa con cada solicitud. El modelo usa este historial para generar respuestas contextualmente relevantes.
Agregue un bucle de conversación que:
- Lee la entrada del usuario desde la consola.
- Anexa el mensaje de usuario al historial.
- Envía el historial completo al modelo.
- Anexa la respuesta del asistente al historial para el siguiente turno.
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})
Cada llamada a complete_chat recibe el historial de mensajes completo. Así es como el modelo "recuerda" los turnos anteriores, no almacena el estado entre las llamadas.
Agrega respuestas de streaming
El streaming muestra cada token a medida que se genera, lo que hace que el asistente parezca más receptivo. Reemplazar la llamada complete_chat con complete_streaming_chat para transmitir la respuesta, token por token.
Actualice el bucle de conversación para usar el 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 versión de streaming acumula la respuesta completa para que se pueda agregar al historial de conversaciones una vez completada la secuencia.
Código completo
Cree un archivo denominado main.py y agregue el siguiente código completo:
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()
Ejecute el asistente de chat:
python main.py
Verá una salida similar a la siguiente:
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!
Observe cómo el asistente recuerda el contexto de los turnos anteriores, cuando se pregunta "¿Por qué es importante para otras cosas vivientes?", sabe que sigue hablando de fotosynthesis.
Repositorio de ejemplos
El código de ejemplo completo de este artículo está disponible en el repositorio de Foundry Local GitHub. Cómo clonar el repositorio y acceder al directorio de ejemplo:
git clone https://github.com/microsoft/Foundry-Local.git
cd Foundry-Local/samples/rust/tutorial-chat-assistant
Instalación de paquetes
Si está desarrollando o distribuyendo en Windows, seleccione la pestaña Windows. El paquete de Windows se integra con el entorno de ejecución Windows ML y ofrece la misma superficie de API con una gama más amplia de aceleración de hardware.
cargo add foundry-local-sdk --features winml
cargo add tokio --features full
cargo add tokio-stream anyhow
Examinar el catálogo y seleccionar un modelo
El SDK local de Foundry proporciona un catálogo de modelos que muestra todos los modelos disponibles. En este paso, inicializa el SDK y selecciona un modelo para el asistente de chat.
Abra
src/main.rsy reemplace su contenido por el código siguiente para inicializar el SDK y seleccionar un modelo:// 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);El
get_modelmétodo acepta un alias de modelo, que es un nombre descriptivo corto que se asigna a un modelo específico del catálogo. Eldownloadmétodo captura los pesos del modelo en la memoria caché local yloadhace que el modelo esté listo para la inferencia.
Defina un mensaje de sistema
Un mensaje de sistema establece la personalidad y el comportamiento del asistente. Es el primer mensaje del historial de conversaciones y el modelo hace referencia a él a lo largo de la conversación.
Agregue un mensaje de sistema para configurar cómo responde el asistente.
// 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(),
];
Sugerencia
Experimente con diferentes avisos del sistema para cambiar el comportamiento del asistente. Por ejemplo, puede indicarle que responda como un pirata, un profesor o un experto en dominio.
Implementar una conversación multiturno
Un asistente de chat debe mantener el contexto en varios intercambios. Para ello, mantenga un vector de todos los mensajes (sistema, usuario y asistente) y envíe la lista completa con cada solicitud. El modelo usa este historial para generar respuestas contextualmente relevantes.
Agregue un bucle de conversación que:
- Lee la entrada del usuario desde la consola.
- Anexa el mensaje de usuario al historial.
- Envía el historial completo al modelo.
- Anexa la respuesta del asistente al historial para el siguiente turno.
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);
}
Cada llamada a complete_chat recibe el historial de mensajes completo. Así es como el modelo "recuerda" los turnos anteriores, no almacena el estado entre las llamadas.
Agrega respuestas de streaming
El streaming muestra cada token a medida que se genera, lo que hace que el asistente parezca más receptivo. Reemplazar la llamada complete_chat con complete_streaming_chat para transmitir la respuesta, token por token.
Actualice el bucle de conversación para usar el 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 versión de streaming acumula la respuesta completa para que se pueda agregar al historial de conversaciones una vez completada la secuencia.
Código completo
Reemplace el contenido de src/main.rs por el código completo siguiente:
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(())
}
Ejecute el asistente de chat:
cargo run
Verá una salida similar a la siguiente:
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!
Observe cómo el asistente recuerda el contexto de los turnos anteriores, cuando se pregunta "¿Por qué es importante para otras cosas vivientes?", sabe que sigue hablando de fotosynthesis.
Limpieza de recursos
Los parámetros de ponderación del modelo se quedan almacenados en la memoria caché local después de descargar un modelo. Esto significa que la próxima vez que ejecute la aplicación, se omite el paso de descarga y el modelo se carga más rápido. No se necesita ninguna limpieza adicional a menos que quiera reclamar espacio en disco.