Povestea începe cu un proiect pentru a crearea unei mașini robot minimaliste care a declanșat o călătorie inovatoare în domeniul inteligenței artificiale și al roboticii. Mașina robot urma să detecteze incendii și trebuia să mă asiste în demo-urile pe care le făceam pentru diverse evenimente. Construită pe un Raspberry Pi echipat cu senzori, mașina robot putea detecta pericolul și în ciuda faptului că API-ul său suporta mișcările de bază: întoarcere, înainte, înapoi și oprire, îmi crea probleme când trebuia să execute mișcări complexe, pentru că trebuia să implementez eu acea secvență de mișcări.
Provocarea de a traduce comenzile umane abstracte în instrucțiuni API structurate într-un mod automat s-a dovedit a fi prea complexă pentru a fi implementată eficient la acea vreme.
Ani mai târziu, apariția inteligenței artificiale generative mi-a reaprins interesul. Experimentele cu ChatGPT au arătat că modelele mari de limbaj (LLM) pot descompune cu mult succes instrucțiunile complexe în pași simpli, executabili, pe care API-ul mașinii robot îi înțelege. Această descoperire a redus decalajul dintre comenzile umane și acțiunile mașinii.
Acum, valorificând .NET și tehnicile de inginerie a prompturilor specifice modelelor largi de limbaj (LLM), haideți să creăm un copilot interactiv care transformă instrucțiunile umane complexe de navigare în comenzi executabile de mașină în 10 pași simpli, utilizând Microsoft Semantic Kernel și C#.
Pentru început, creați o nouă aplicație consolă .NET și instalați pachetele Semantic Kernel necesare. Gândiți-vă la acest lucru ca la punerea bazelor pentru "creierul" mașinii dvs. robot, chiar dacă roțile nu sunt încă atașate.
Rulați aceste comenzi în terminal:
dotnet new console -n
RobotCarAssistant
cd RobotCarAssistant
dotnet add package
Microsoft.SemanticKernel
dotnet add package
Microsoft.Extensions.Configuration
.UserSecrets
Aceste comenzi configurează proiectul cu toate dependențele necesare pentru a lucra cu Microsoft Semantic Kernel și pentru a gestiona în siguranță datele sensibile, secrete, din timpul dezvoltării.
API-urile sunt instrumente puternice, dar odată cu o mare putere vine și o mare responsabilitate, mai ales atunci când este implicat buzunarul propriu. Pentru a vă păstra cheile API în siguranță, utilizați variabile de mediu sau .NET Secret Manager în timpul dezvoltării.
Opțiunea 1: Utilizarea variabilelor de mediu
Setați o variabilă de mediu pentru cheia API:
setx OPENAI_API_KEY "YOUR_API_KEY"
Accesați-o în codul dvs. astfel:
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
Alternativ, vă puteți stoca secretele local utilizând .NET Secret Manager:
dotnet user-secrets init
dotnet user-secrets set "OpenAI:ApiKey" "your API key"
Extrageți cheia API din codul dvs. după cum urmează:
var configuration =
new ConfigurationBuilder()
.AddUserSecrets()
.Build();
var apiKey = configuration["OpenAI:ApiKey"];
De asemenea, puteți utiliza această abordare pentru a gestiona alte date sensibile, cum ar fi punctele finale sau ID-urile de model. Acest lucru asigură faptul că acreditările dvs. rămân sigure și accesibile numai utilizatorilor autorizați.
Eficacitatea copilotului dvs. depinde de cât de bine îi proiectați instrucțiunile. Înainte de a vă afunda în cod, experimentați tehnici de inginerie a promptului pentru a crea instrucțiuni clare și structurate. De exemplu, iată o interogare ChatGPT neprelucrată pe care ați putea-o încerca:
User Prompt:
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop. You have to break down the complex command "There is a tree directly in front of the car. Avoid it and then return to the original path." into basic moves you know.
Și să vedem răspunsul:
ChatGPT Response:
Sure! Let's break down the complex command into a series of basic moves:
1. Stop: If the car is moving, first make it stop.
2. Turn Left: Turn the car left to start moving around the tree.
3. Move Forward: Move forward a bit to clear the tree.
4. Turn Right: Turn the car right to straighten out.
5. Move Forward: Continue moving forward to clear the tree completely.
6. Turn Right: Turn the car right again to start moving back towards the original path.
7. Move Forward: Move forward a bit to align the car with the original path.
8. Turn Left: Turn the car left to return to the original path.
9. Move Forward: Continue moving forward along the original path
This way, the car can avoid the tree and get back on its original path!
If you have any specific parameters like distances or turning angles, I can refine the steps further.
Dacă rulăm interogarea de câteva ori, observăm că, indiferent dacă modelul decide să înceapă prin a vira la dreapta sau la stânga, manevra (secvența de pași) este destul de asemănătoare. Desigur, prin aceasta, vedem cum LLM are variațiile sale aleatorii în generarea răspunsului.
Știți ce? Uneori, este mult mai ușor să înțelegem acești pași atunci când îi putem vizualiza, așa că am creat o diagramă simplă. Consultați figura 1 pentru a obține o imagine mai clară a mișcării mașinii robot în jurul copacului.
Răspunsul nu este rău, dar, cu siguranță, nu arată ca un mod de comunicare de la mașină la mașină. Soluția este de a rafina acel prompt folosind tehnici de inginerie a promptului. Pentru a crea promptere eficiente, eu folosesc cadrul PACT, care este adaptarea mea a cadrului Role-Task-Format (RTF). PACT este deosebit de potrivit pentru Semantic Kernel, deoarece se aliniază cu abordarea sa modulară și structurată..
PACT ajută la structurarea prompterelor prin abordarea a patru elemente cheie:
Figura 1: LLM generează un răspuns textual care detaliază mișcările robotului auto în jurul copacului, dar vizualizarea acestei secvențe sub forma unei diagrame oferă o înțelegere mai intuitivă a ocolului
Persona (Cine): Definește rolul sau identitatea pe care modelul ar trebui să și-o asume atunci când generează un răspuns. De exemplu, ați putea cere modelului să acționeze ca un profesor, jurnalist sau asistent AI.
Action (Ce): Specificați sarcina sau acțiunea pe care doriți ca modelul să o efectueze. Instrucțiunile clare ghidează comportamentul modelului (de exemplu, "scrieți un articol" sau "descompuneți o comandă").
Context (cu ce): Furnizează informații de fundal relevante sau detalii care ajută modelul să genereze răspunsuri precise și adecvate contextului.
Template (Cum): Definește structura sau formatul dorit al răspunsului, cum ar fi matrici JSON, tabele sau text simplu.
Abordarea structurată oferită de PACT este ușor de utilizat și extrem de eficientă pentru crearea de prompturi precise și fiabile. Pentru scenarii mai complexe, PACT poate fi îmbogățit cu tehnici de învățare (few-shot learning) sau cu zero-shot chain-of-thought (lanț de gândire fără exemple). Vă garantez că aceste două tehnici sună mai sofisticat decât sunt în realitate.
Haideți să extragem părțile prompterului PACT din prompterul brut anterior:
Persona: Sunteți un asistent AI care controlează o mașină robot capabilă să efectueze mișcări de bază: înainte, înapoi, la stânga, la dreapta și oprire.
Action: Trebuie să descompuneți comanda complexă [...] în mișcări de bază pe care le cunoașteți.
Context: Există un copac chiar în fața mașinii. Evită copacul și apoi revino la calea inițială.
Template: (nu avem încă un șablon declara în promptul nostru!)
Observăm că nu avem un șablon (template), dar este o ocazie foarte bună de a adăuga unul. După cum vom afla în curând, partea de șablon este crucială pentru formatarea răspunsului. Ce spuneți de "Use a JSON array for the response"?
Solicitarea nou structurată arată astfel (observați etichetele care marchează clar părțile PACT din prompt):
User Prompt:
### Persona
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop.
### Action
You have to break down the provided complex commands into the basic moves you know.
### Context
Complex command: "There is a tree directly in front of the car. Avoid it and then return to the original path."
### Template
Use a JSON array for the response.
Nu este nimic rău în păstrarea etichetelor părților PACT în promptul nostru. De fapt, dimpotrivă, acest lucru ajută LLM să înțeleagă mai bine structura promptului. Cu toate acestea, există situații în care am putea prefera un format mai compact. Pentru moment, să le păstrăm.
Iată ce ar putea genera ChatGPT:
ChatGPT Response:
Here's the breakdown of the complex command into basic moves for the robot car:
[
{"move": "turn left"},
{"move": "forward"},
{"move": "turn right"},
{"move": "forward"},
{"move": "turn right"},
{"move": "forward"},
{"move": "turn left"}
]
These steps should help the robot car avoid the tree and return to the original path.
JSON-ul ar fi fost suficient. Dar că să mai folosim o ajustare, putem să cerem doar secvența de mișcări fără comentarii, adăugând la șablon "Răspundeți doar cu mișcările, fără explicații suplimentare.":
User Prompt:
### Persona
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop.
### Action
You have to break down the provided complex commands into the basic moves you know.
### Context
Complex command: "There is a tree directly in front of the car. Avoid it and then return to the original path."
### Template
Use a JSON array for the response.
Respond only with the moves, without any additional explanations.
Și iată cum arată un răspuns rafinat:
ChatGPT Response:
[
{"action": "stop"},
{"action": "turn right"},
{"action": "forward"},
{"action": "turn left"},
{"action": "forward"},
{"action": "turn left"},
{"action": "forward"},
{"action": "turn right"}
]
Chiar și cu prompturi structurate, LLM-urile pot genera formate variate, inconsecvente, din cauza caracterului aleatoriu al răspunsurilor. Pentru a rezolva această problemă, să includem în partea cu șablonul un exemplu de rezultat pe care îl așteptăm de la LLM (această tehnică se numește one-shot learning):
"Use a JSON array like [move1, move2, move3] for the response".
Rulați acest prompt actualizat de mai multe ori pentru a verifica coerența răspunsului:
ChatGPT Response:
["turn right", "forward", "turn left", "forward", "turn left", "forward", "turn right"]
Cu rezultatele constante obținute prin rularea prompturilor de mai multe ori, putem spune că am creat un prompt bine optimizat, gata de integrare în aplicația Semantic Kernel.
Sunteți pregătiți să trecem la cod?
Acum este timpul să punem totul cap la cap și să implementăm prima aplicație Semantic Kernel. În acest pas, vom crea o aplicație simplă de consolă C# care trimite un prompt bine elaborat către API-ul OpenAI utilizând Semantic Kernel.
Iată codul:
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace RobotCarAssistant;
internal class Program
{
static async Task Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddUserSecrets()
.Build();
var textGeneration = new OpenAIChatCompletionService(
modelId: configuration["OpenAI:ModelId"]!,
apiKey: configuration["OpenAI:ApiKey"]!);
var prompt = """
### Persona
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop.
### Action
You have to break down the provided complex commands into basic moves you know.
### Context
Complex command: "There is a tree directly in front of the car. Avoid it and then come back to the original path."
### Template
Use JSON array like [move1, move2, move3] for response.
Respond only with the moves, without any additional explanations.
""";
var response = await textGeneration.GetChatMessageContentAsync(prompt);
Console.WriteLine(response);
}
}
Atunci când executăm acest cod, el trimite interogarea structurată (promptul) către API-ul OpenAI prin Semantic Kernel. Răspunsul va fi o expresie JSON care reprezintă secvența mișcărilor mașinii robot. De exemplu:
["turn right", "forward", "turn left", "forward", "turn left", "forward", "turn right"]
Acest răspuns este ușor de procesat și poate fi utilizat direct de către alte sisteme. robot.
Pentru un răspuns mai determinist (și cu mai puține variații creative), ajustăm setările, reducând temperatura la 0,1.
Acest lucru asigură faptul că modelul generează rezultate coerente, ceea ce este esențial pentru comunicarea mașină-mașină.
În plus, pentru o mai bună reutilizare a codului, este o bună practică să separăm promptul sistemului (partea statică a promptului) de promptul utilizatorului (partea dinamică a promptului). Această separare vă permite să inițializăm interogarea cu un context predefinit, păstrând în același timp informația specifică utilizatorului modulară și flexibilă.
Iată cum putem implementa acest lucru:
var executionSettings = new
OpenAIPromptExecutionSettings
{
Temperature = 0.1,
// lower temperature makes the response
// more deterministic
ChatSystemPrompt = """
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop.
You have to break down the provided complex commands into basic moves you know.
Use JSON array like [move1, move2, move3] for response.
Respond only with the moves, without any additional explanations.
"""
};
var userPrompt = """
There is a tree directly in front of the car. Avoid it and then come back to the original path.
""";
var response = await textGeneration
.GetChatMessageContentAsync(userPrompt,
executionSettings);
Separarea promptului sistemului (partea statică) de promptul utilizatorului (partea dinamică) îmbunătățește modularitatea, flexibilitatea și interacțiunea. Promptul sistemului definește rolul, capacitățile și formatul de răspuns al modelului, în timp ce promptul utilizatorului se concentrează pe sarcinile specifice, așa cum sunt replicile dintr-un dialog. Această structură permite reutilizarea contextul sistemului pentru fiecare din interacțiunile următoare cu modelul. Astfel, se menține claritatea și se asigură o comunicare fără întreruperi cu modelul.
În etapa anterioară, am anticipat necesitatea de a "discuta" cu modelul. Cu toate acestea, un chatbot trebuie să facă mai mult decât să răspundă, trebuie să își amintească interacțiunile anterioare. În acest pas, vom transforma aplicația într-un chatbot interactiv care memorează istoricul conversațiilor.
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: configuration["OpenAI:ModelId"]!,
apiKey: configuration["OpenAI:ApiKey"]!)
.Build();
var executionSettings = new
OpenAIPromptExecutionSettings
{
Temperature = 0.1,
};
var chat = kernel
.GetRequiredService();
var history = new ChatHistory();
history.AddSystemMessage("""
You are an AI assistant controlling a robot car capable of performing basic moves: forward, backward, turn left, turn right, and stop.
You have to break down the provided complex commands into basic moves you know.
Use JSON array like [move1, move2, move3] for response.
Respond only with the moves, without any additional explanations.
""");
while (true)
{
Console.Write(" User >>> ");
var prompt = Console.ReadLine();
if (string.IsNullOrEmpty(prompt)) break;
history.AddUserMessage(prompt);
var response = await chat
.GetChatMessageContentAsync(history,
executionSettings);
Console.WriteLine($" Bot >>> {response.Content}");
history.Add(response);
}
Când executăm acest cod, copilotul își va aminti fiecare interacțiune anterioară. De exemplu:
User >>> There is a tree directly in front of the car. Avoid it and return to the original path.
Bot >>> ["turn right", "forward", "turn left", "forward", "turn left", "forward", "turn right"]
User >>> Now move forward 10 meters.
Bot >>> ["forward"]
Promptul sistemului începe să pară încărcat. În loc să păstrăm aspectele specifice ale comportamentul promptului înăuntrul său, am putea decupla aceste informații utilizând pluginurile Semantic Kernel. Pluginurile permit gestionarea modulară a prompturilor, făcând aplicația mai structurată și reutilizabilă.
Să creăm un plugin semantic CommandsPlugin care descompune comenzile complexe.
Un plugin constă din două componente principale: un fișier de configurare (config.json) și un fișier prompt (skprompt.txt). Începem cu fișierul de configurare (config.json) pentru a defini funcția semantică:
{
"schema": 1,
"type": "completion",
"description": "It breaks down the given complex command into a step-by-step sequence of basic moves.",
"completion": {
"max_tokens": 500,
"temperature": 0.1
},
"input_variables": [
{
"name": "input",
"description": "The complex command to break down into a step-by-step sequence of basic moves.",
"is_required": true,
"default": ""
}
]
}
Apoi, definim comportamentul pluginului într-un fișier prompt (skprompt.txt):
Your task is to break down complex commands into a sequence of these basic moves.
The permitted basic moves are: forward, backward, turn left, turn right, and stop.
[COMPLEX COMMAND START]
{{$input}}
[COMPLEX COMMAND END]
Provide only the sequence of the basic movements, without any additional explanations.
Commands:
În cele din urmă, importăm pluginul în kernel:
var pluginPath = Path.Combine(Directory.GetCurrentDirectory(), "Plugins", "CommandsPlugin");
kernel.ImportPluginFromPromptDirectory(pluginPath, "CommandsPlugin");
Pentru a ne asigura că funcția semantică este invocată automat atunci când este necesar, ajustăm setările de execuție prin scăderea temperaturii și prin activarea comportamentului de selecție automată a funcției:
var executionSettings = new OpenAIPromptExecutionSettings
{
Temperature = 0.1,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
Mecanismul de selecție automată a funcției (cunoscut sub denumirea de function calling) instruiește LLM să apeleze funcțiile pluginului atunci când semnătura lor semantică corespunde nevoilor deduse din solicitarea și contextul utilizatorului. Spre deosebire de semnăturile funcțiilor sau metodelor tradiționale, în care totul se învârte în jurul sintaxei, semnăturile semantice oferă o descriere detaliată, bazată pe semnificație, a funcției și a parametrilor acesteia. În acest mod, modelul detectează în mod automat situațiile în care invocarea uneia sau mai multor funcții poate furniza datele necesare sau poate efectua acțiuni specifice pentru a îmbunătăți răspunsul, reducând în mod eficient neajunsurile dintre înțelegerea limbajului natural și operațiunile directe, acționabile, cum ar fi apelarea funcțiilor semantice sau native (codul tradițional). Acest mecanism permite modelului să integreze perfect capacitățile externe (sistem, API-uri) prin selectarea funcțiilor ale căror intrări și ieșiri declarate se aliniază cu sarcina în cauză, asigurându-se că răspunsul modelului valorifică atât cunoștințele sale interne (dobândite la momentul antrenării modelului), cât și cunoștințele specializate din pluginurile disponibile.
Prin utilizarea pluginurilor, copilotul dvs. devine mai structurat și mai adaptabil, deschizând calea pentru aplicații avansate bazate pe inteligență artificială.
Este timpul ca chatbotul nostru să meargă dincolo de simple conversații, acum este gata să controleze mașina robot! Pentru a realiza acest lucru, veți crea un plugin nativ (de exemplu, RobotCarPlugin) cu funcții C# care se referă direct la mișcările de bază ale mașinii robot. Aceste funcții vor acționa ca o interfață între asistentul dvs. AI și API-ul de control al motorului mașinii. De exemplu:
[Description("Robot car plugin.")]
public class RobotCarPlugin
{
[KernelFunction, Description("Basic command: Moves the robot car backward.")]
public static async Task Backward([Description("The distance (in meters) to move the robot car.")] int distance)
{
// TODO call car motor API, backward endpoint
Console.WriteLine($"Backward: {distance}m");
return await Task.FromResult($"moving backward for {distance} meters...");
}
[KernelFunction, Description("Basic command: Moves the robot car forward.")]
public static async Task Forward([Description("The distance (in meters) to move the robot car.")] int distance)
{
// TODO call car motor API, forward endpoint
Console.WriteLine($"Forward: {distance}m");
return await Task.FromResult($"moving forward for {distance} meters...");
}
[KernelFunction, Description("Basic command: Stops the robot car.")]
public static async Task Stop()
{
// TODO call car motor API, stop endpoint
Console.WriteLine("Stop");
return await Task.FromResult("stopping...");
}
[KernelFunction, Description("Basic command: Turns the robot car anticlockwise.")]
public static async Task TurnLeft([Description("The angle (in °degrees) to turn the robot car.")] int angle)
{
// TODO call car motor API, turn left endpoint
Console.WriteLine($"TurnLeft: {angle}°");
return await Task.FromResult($"turning anticlockwise {angle}°...");
}
[KernelFunction, Description("Basic command: Turns the robot car clockwise.")]
public static async Task TurnRight([Description("The angle (in ° degrees) to turn the robot car.")] int angle)
{
// TODO call car motor API, turn right endpoint
Console.WriteLine($"TurnRight: {angle}°");
return await Task.FromResult($"turning clockwise {angle}°...");
}
}
Apoi, încărcați-l în kernel cu:
kernel.ImportPluginFromType();
Prin integrarea funcțiilor native în aplicația Semantic Kernel, îi permiteți chatbotului dvs. să execute acțiuni din lumea reală. De exemplu, atunci când un utilizator emite o comandă precum "Move forward 10 meters", AI-ul poate invoca funcția Forward din pluginul RobotCarPlugin, care va interacționa cu sistemul de control al motorului robotului.
Acum chatbotul nostru nu numai că știe ce să spună, dar poate controla roțile robotului API, așa că sper ca nu greșim dacă îl numim copilot.
Acum este momentul să combinăm tot ceea ce am construit, pluginuri, istoricul conversațiilor și setările de execuție, într-un copilot coerent și robust. Această implementare finală va permite asistentului nostru AI să proceseze comenzile utilizatorului, să invoce pluginuri și să mențină contextul conversației fără probleme. Iată codul complet:
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel;
using Plugins;
using Microsoft.SemanticKernel.ChatCompletion;
namespace RobotCarAssistant;
internal class Program
{
static async Task Main(string[] args)
{
var configuration = new ConfigurationBuilder().AddUserSecrets().Build();
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: configuration["OpenAI:ModelId"]!,
apiKey: configuration["OpenAI:ApiKey"]!)
.Build();
var commandsPluginPath = Path.Combine(Directory.GetCurrentDirectory(), "Plugins", "CommandsPlugin");
kernel.ImportPluginFromPromptDirectory(commandsPluginPath, "CommandsPlugin");
var chat = kernel.GetRequiredService();
var history = new ChatHistory();
history.AddSystemMessage("""
You are an AI assistant controlling a robot car.
"""
);
#pragma warning disable SKEXP0001 // FunctionChoiceBehavior is experimental, and it needs to be enabled explicitly
var executionSettings = new OpenAIPromptExecutionSettings
{
Temperature = 0.1,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
while (true)
{
Console.Write(" User >>> ");
var prompt = Console.ReadLine();
if (string.IsNullOrEmpty(prompt)) break;
history.AddUserMessage(prompt);
var response = await chat.GetChatMessageContentAsync(history, executionSettings, kernel);
Console.WriteLine($" Bot >>> {response}");
history.Add(response);
}
}
}
Copilotul este activ și așteaptă instrucțiuni.
Am construit un copilot care înțelege, răspunde și chiar trasează traseul mașinii robot în jurul obstacolelor. Pe parcurs, am folosit ingineria prompturilor, am explorat puterea Semantic Kernel, am gestionat în siguranță cheile API, am modularizat codul cu pluginuri și am integrat funcții native pentru a conecta inteligența artificială cu acțiunile API-ului din lumea reală.
Dar acesta este doar începutul. La fel ca primele mișcări prudente ale mașinii robot, există o lume vastă de funcționalități avansate de explorat.
Un alt aspect practic ar fi streamingul răspunsurilor. Atunci când modelul generează un răspuns mai lung, latența poate cauza întârzieri. Streamingul atenuează acest aspect prin furnizarea în timp real a unor fragmente de răspunsuri parțial generate, permițând luarea mai rapidă a deciziilor pe baza unor informații parțiale.
Cu toate acestea, rețineți că răspunsurile cu formate structurate, cum ar fi JSON sau XML servite prin streaming pot introduce probleme de validare structurală dacă datele ajung incomplete. Pentru a rezolva această problemă, luați în considerare utilizarea unor formate compatibile cu streamingul sau gestionarea cu atenție a răspunsurilor parțiale.
Iată cum putem implementa streamingul prin înlocuirea buclei while a copilotului:
while (true)
{
Console.Write(" User >>> ");
var prompt = Console.ReadLine(); // There is a tree directly in front of the car. Avoid it and then come back to the original path.
if (string.IsNullOrEmpty(prompt)) break;
history.AddUserMessage(prompt);
Console.Write($" Bot >>> ");
string fullMessage = string.Empty;
await foreach (var messageChunk in chat.GetStreamingChatMessageContentsAsync(history, executionSettings, kernel))
{
Console.Write(messageChunk.Content);
fullMessage += messageChunk.Content;
}
Console.WriteLine();
history.AddAssistantMessage(fullMessage);
}
Odată ce copilotul nostru este funcțional, încercați comenzi creative precum "To the moonwalk" sau "Move like a jellyfish". Deși încă nu este perfect, acesta reprezintă un salt semnificativ către aplicații bazate pe inteligență artificială mai eficiente și mai autonome.
Acest proiect demonstrează modul în care un copilot alimentat cu inteligență artificială poate face legătura între intenția umană și capacitățile mașinii. Indiferent dacă navigați cu mașini robotizate sau construiți alte sisteme bazate pe inteligență artificială, abilitățile pe care le-ați dobândit aici vă vor servi drept bază pentru inovații viitoare.
Călătorie plăcută în aventura Semantic Kernel!
[1] https://www.manning.com/books/microsoft-semantic-kernel-in-action
[2] https://learn.microsoft.com/en-us/semantic-kernel/overview/