Skip to content

Commit

Permalink
Merge pull request #125 from Azure-Samples/gk/64-auto-invest-using-pl…
Browse files Browse the repository at this point in the history
…anner
  • Loading branch information
thegovind committed Jul 18, 2023
2 parents 8528167 + 002823e commit ef167c8
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 178 deletions.
161 changes: 61 additions & 100 deletions sandbox/usecases/sentiment-analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
Expand All @@ -37,17 +37,7 @@
"kernelName": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.SemanticKernel, 0.17.230711.7-preview</span></li><li><span>Microsoft.SemanticKernel.Planning.StepwisePlanner, 0.17.230711.7-preview</span></li><li><span>Microsoft.SemanticKernel.Skills.Web, 0.17.230711.7-preview</span></li></ul></div></div>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"outputs": [],
"source": [
"#r \"nuget: Microsoft.SemanticKernel, 0.17.230711.7-preview\"\n",
"#r \"nuget: Microsoft.SemanticKernel.Planning.StepwisePlanner, 0.17.230711.7-preview\"\n",
Expand All @@ -72,7 +62,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
Expand All @@ -88,6 +78,8 @@
"var (_, model, azureEndpoint, apiKey, org, bingApiKey) = Settings.LoadFromFile();\n",
"builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
"\n",
"// TODO: Add embedding service for RaG\n",
"\n",
"IKernel kernel = builder.Build();"
]
},
Expand All @@ -100,7 +92,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
Expand All @@ -109,19 +101,10 @@
"kernelName": "csharp"
}
},
"outputs": [
{
"ename": "Error",
"evalue": "(34,17): error CS0103: The name 'kernel' does not exist in the current context",
"output_type": "error",
"traceback": [
"(34,17): error CS0103: The name 'kernel' does not exist in the current context"
]
}
],
"outputs": [],
"source": [
"string skPrompt = @\"\n",
"This is a chat between a classification system and human looking to rate their stock portfolio. Given a portfolio in valid JSON, respond back a sentiment in JSON for the attribute: sentiment. To determine this attribute, consider Web results from Bing and propietary data below.\n",
"This is a chat between a classification system that responds in valid JSON and human looking to rate their stock portfolio. Given a portfolio in valid JSON, respond back a sentiment in JSON for the attribute: sentiment. To determine this attribute, consider Web results from Bing and propietary data below.\n",
"\n",
"Example:\n",
"\"\"stocks\"\": [\n",
Expand Down Expand Up @@ -152,15 +135,17 @@
"{{$portfolio}}\n",
"\n",
"\n",
"Question: {{$input}}\n",
"\n",
"Context: \n",
"Web Results: {{$web}}\n",
"Propietary data: {{$propietary}}\n",
"\";\n",
"\n",
"// create semantic function\n",
"var textToSQL = kernel.CreateSemanticFunction(skPrompt, maxTokens: 1000, temperature: 0, topP: 0.5);\n",
"var sentimentClassifier = kernel.CreateSemanticFunction(\n",
" skPrompt,\n",
" skillName: \"SentimentClassifier\",\n",
" functionName: \"SentimentClassifer\",\n",
" description: \"Classify sentiment of a stock portfolio\",\n",
" maxTokens: 1000, temperature: 0, topP: 0.5);\n",
"Console.WriteLine(textToSQL.ToString());"
]
},
Expand All @@ -173,7 +158,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
Expand All @@ -184,15 +169,34 @@
},
"outputs": [],
"source": [
"var tableInfo = @\"\n",
" Users table has columns: user_id, first_name, last_name, and email.\n",
" Transactions table has columns: transaction_id, user_id, transaction_amount, transaction_date, and transaction_type.\n",
"var portfolio = @\"\n",
" \"\"stocks\"\": [\n",
" {\n",
" \"\"symbol\"\": \"\"AAPL\"\",\n",
" \"\"allocation\"\": 0.2,\n",
" \"\"sentiment\"\": \"\"\"\"\n",
" },\n",
" {\n",
" \"\"symbol\"\": \"\"NVDA\"\",\n",
" \"\"allocation\"\": 0.3,\n",
" \"\"sentiment\"\": \"\"\"\"\n",
" },\n",
" {\n",
" \"\"symbol\"\": \"\"TSLA\"\",\n",
" \"\"allocation\"\": 0.2,\n",
" \"\"sentiment\"\": \"\"\"\"\n",
" },\n",
" {\n",
" \"\"symbol\"\": \"\"T\"\",\n",
" \"\"allocation\"\": 0.3,\n",
" \"\"sentiment\"\": \"\"\"\"\n",
" }\n",
" ]\n",
"\";\n",
"\n",
"// context variables\n",
"var context = kernel.CreateNewContext();\n",
"context[\"dialect\"] = \"PostgreSQL\";\n",
"context[\"tableInfo\"] = tableInfo;"
"context[\"portfolio\"] = portfolio;"
]
},
{
Expand All @@ -204,7 +208,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
Expand All @@ -213,77 +217,34 @@
"kernelName": "csharp"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Question: What was the most expensive purchase made in the last year?\n",
"\n",
"SQLQuery: SELECT \n",
" t.transaction_id,\n",
" t.transaction_date,\n",
" t.transaction_amount,\n",
" t.transaction_type,\n",
" u.first_name,\n",
" u.last_name\n",
" FROM \n",
" transactions t\n",
" JOIN \n",
" users u ON t.user_id = u.user_id\n",
" WHERE \n",
" t.transaction_date >= DATE_TRUNC('year', CURRENT_DATE - INTERVAL '1 year')\n",
" AND t.transaction_date < DATE_TRUNC('year', CURRENT_DATE)\n",
" ORDER BY \n",
" t.transaction_amount DESC\n",
" LIMIT 1;\n",
"\n",
"Answer: The most expensive purchase made in the last year was {transaction_type} for {transaction_amount} by {first_name} {last_name} on {transaction_date}.\r\n"
]
}
],
"outputs": [],
"source": [
"context[\"input\"] = \"Most expensive purchase in the last year\";\n",
"var sql = await textToSQL.InvokeAsync(context);\n",
"Console.WriteLine(sql);\n",
"\n",
"var question = \"Determine the sentiment of my stock portfolio based on what the internet says about the stocks in my portfolio.\";\n",
"\n",
"using var bingConnector = new BingConnector(Env.Var(\"BING_API_KEY\"));\n",
" var webSearchEngineSkill = new WebSearchEngineSkill(bingConnector);\n",
"var bingConnector = new BingConnector(bingApiKey);\n",
"var webSearchEngineSkill = new WebSearchEngineSkill(bingConnector);\n",
"\n",
" kernel.ImportSkill(webSearchEngineSkill, \"WebSearch\");\n",
" kernel.ImportSkill(new LanguageCalculatorSkill(kernel), \"advancedCalculator\");\n",
" // kernel.ImportSkill(new SimpleCalculatorSkill(kernel), \"basicCalculator\");\n",
" kernel.ImportSkill(new TimeSkill(), \"time\");\n",
"kernel.ImportSkill(sentimentClassifier, \"SentimentClassifier\");\n",
"kernel.ImportSkill(webSearchEngineSkill, \"WebSearch\");\n",
"\n",
" Console.WriteLine(\"*****************************************************\");\n",
" Stopwatch sw = new();\n",
" Console.WriteLine(\"Question: \" + question);\n",
"var config = new Microsoft.SemanticKernel.Planning.Stepwise.StepwisePlannerConfig();\n",
"config.MinIterationTimeMs = 1500;\n",
"config.MaxTokens = 4000;\n",
"\n",
" var config = new Microsoft.SemanticKernel.Planning.Stepwise.StepwisePlannerConfig();\n",
" config.ExcludedFunctions.Add(\"TranslateMathProblem\");\n",
" config.MinIterationTimeMs = 1500;\n",
" config.MaxTokens = 4000;\n",
"StepwisePlanner planner = new(kernel, config);\n",
"var plan = planner.CreatePlan(question);\n",
"\n",
" StepwisePlanner planner = new(kernel, config);\n",
" sw.Start();\n",
" var plan = planner.CreatePlan(question);\n",
"\n",
" var result = await plan.InvokeAsync(kernel.CreateNewContext());\n",
" Console.WriteLine(\"Result: \" + result);\n",
" if (result.Variables.TryGetValue(\"stepCount\", out string? stepCount))\n",
" {\n",
" Console.WriteLine(\"Steps Taken: \" + stepCount);\n",
" }\n",
"\n",
" if (result.Variables.TryGetValue(\"skillCount\", out string? skillCount))\n",
" {\n",
" Console.WriteLine(\"Skills Used: \" + skillCount);\n",
" }\n",
"var result = await plan.InvokeAsync(kernel.CreateNewContext());\n",
"Console.WriteLine(\"Result: \" + result);\n",
"if (result.Variables.TryGetValue(\"stepCount\", out string? stepCount))\n",
"{\n",
" Console.WriteLine(\"Steps Taken: \" + stepCount);\n",
"}\n",
"\n",
" Console.WriteLine(\"Time Taken: \" + sw.Elapsed);\n",
" Console.WriteLine(\"*****************************************************\");\n",
" }"
"if (result.Variables.TryGetValue(\"skillCount\", out string? skillCount))\n",
"{\n",
" Console.WriteLine(\"Skills Used: \" + skillCount);\n",
"}\n"
]
}
],
Expand Down
7 changes: 2 additions & 5 deletions services/recommendation-service/dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,8 @@
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseSwagger();
app.UseSwaggerUI();

app.UseCors();
// app.UseHttpsRedirection(); // TODO: Issue with Next.js to use https redirection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ namespace GBB.Miyagi.RecommendationService.Controllers;
public class InvestmentsController : ControllerBase
{
private readonly IKernel _kernel;
private readonly WebSearchEngineSkill _webSearchEngineSkill;
private readonly string _memoryCollection = Env.Var("MEMORY_COLLECTION");
private readonly WebSearchEngineSkill _webSearchEngineSkill;

public InvestmentsController(IKernel kernel, WebSearchEngineSkill webSearchEngineSkill)
{
Expand Down Expand Up @@ -61,29 +61,50 @@ public async Task<IActionResult> GetRecommendations([FromBody] MiyagiContext miy
log?.LogDebug("Number of Tokens: {N}", numTokens);
log?.LogDebug("Context: {S}", context.ToString());

// ========= Prometheus - RaG with current data =========
// // TODO: Swap Bing Web Search with News Search.
_kernel.ImportSkill(_webSearchEngineSkill, "bing");
var question = "What is the current inflation rate?";
var bingResult = await _kernel.Func("bing", "search").InvokeAsync(question);
context.Set("bingResult", bingResult.Result);
log?.LogDebug("Bing Result: {S}", bingResult.Result);

var searchResults = _kernel.Memory.SearchAsync(_memoryCollection, "investment advise", 3, 0.8);

await foreach (var item in searchResults) log?.LogDebug(item.Metadata.Text + " : " + item.Relevance);

// ========= Orchestrate with LLM using context, connector, and memory =========
var result = await _kernel.RunAsync(
context,
userProfilePlugin["GetUserAge"],
userProfilePlugin["GetAnnualHouseholdIncome"],
advisorPlugin["InvestmentAdvise"]);
log?.LogDebug("Result: {S}", result.Result);
numTokens = GPT3Tokenizer.Encode(result.Result).Count;
log?.LogDebug("Number of Tokens: {N}", numTokens);
var output = result.Result.Replace("\n", "");
const int maxRetries = 2;
for (var currentRetry = 0; currentRetry < maxRetries; currentRetry++)
try
{
// ========= Prometheus - RaG with current data =========
_kernel.ImportSkill(_webSearchEngineSkill, "bing");
var question = "What is the current inflation rate?";
var bingResult = await _kernel.Func("bing", "search").InvokeAsync(question);
context.Set("bingResult", bingResult.Result);
log?.LogDebug("Bing Result: {S}", bingResult.Result);

var searchResults = _kernel.Memory.SearchAsync(_memoryCollection, "investment advise", 3, 0.8);

await foreach (var item in searchResults) log?.LogDebug(item.Metadata.Text + " : " + item.Relevance);

// ========= Orchestrate with LLM using context, connector, and memory =========
var result = await _kernel.RunAsync(
context,
userProfilePlugin["GetUserAge"],
userProfilePlugin["GetAnnualHouseholdIncome"],
advisorPlugin["InvestmentAdvise"]);
log?.LogDebug("Result: {S}", result.Result);
numTokens = GPT3Tokenizer.Encode(result.Result).Count;
log?.LogDebug("Number of Tokens: {N}", numTokens);
var output = result.Result.Replace("\n", "");

var jsonDocument = JsonDocument.Parse(output);

return new JsonResult(jsonDocument);
}
catch (JsonException ex)
{
if (currentRetry == maxRetries - 1)
{
// Handle error gracefully, e.g. return an error response
log?.LogError(ex, "Failed to parse JSON data");
return BadRequest(new { error = "Failed to parse JSON data after retrying investments" });
}

// Log the error and proceed to the next iteration to retry
log?.LogError(ex, $"Failed to parse JSON data, retry attempt {currentRetry + 1}");
}

return Content(output, "application/json");
log?.LogError("Failed to parse JSON data, returning 400");
return BadRequest(new { error = "Unexpected error occurred during processing investments" });
}
}

0 comments on commit ef167c8

Please sign in to comment.