Skip to content

Commit

Permalink
.NET Agent example 3 (#34)
Browse files Browse the repository at this point in the history
New C# example with a basic conversational agent, showing:

* best practice for metaprompt guardrails
* integration with Azure Content Safety
* consume LLMs via Semantic Kernel
* show how to use SW notes and notices
  • Loading branch information
dluc authored Sep 5, 2024
1 parent ac1f56d commit 3ce5020
Show file tree
Hide file tree
Showing 43 changed files with 1,154 additions and 88 deletions.
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ facilitates experimentation, development, testing, and measurement of agent beha
Agents integrate with the workbench via a RESTful API, allowing for flexibility and broad applicability
in various development environments.

![Semantic Workbench architecture](docs/architecture-animation.gif)
![Semantic Workbench architecture](https://github.com/raw/microsoft/semanticworkbench/main/docs/architecture-animation.gif)

# Quick start (Recommended) - GitHub Codespaces for turn-key development environment

Expand Down Expand Up @@ -48,8 +48,9 @@ The repository contains a few examples that can be used to create custom agents:

- [Python Canonical Assistant](semantic-workbench/v1/service/semantic-workbench-assistant/semantic_workbench_assistant/canonical.py)
- [Python example 1](examples/python-example01/README.md): a simple assistant echoing text back.
- [.NET example 1](examples/dotnet-example01/README.md): a simple agent with echo and support for a basic `/say` command.
- [.NET example 2](examples/dotnet-example02/README.md): a simple agents showcasing Azure AI Content Safety integration and some workbench features like Mermaid graphs.
- [.NET example 1](examples/dotnet-01-echo-bot/README.md): a simple agent with echo and support for a basic `/say` command.
- [.NET example 2](examples/dotnet-02-message-types-demo/README.md): a simple agents showcasing Azure AI Content Safety integration and some workbench features like Mermaid graphs.
- [.NET example 3](examples/dotnet-03-simple-chatbot/README.md): a functional chatbot implementing metaprompt guardrails and content moderation.

![Mermaid graph example](examples/dotnet-example02/docs/mermaid.png)
![ABC music example](examples/dotnet-example02/docs/abc.png)
Expand All @@ -73,20 +74,20 @@ Enable long file paths on Windows.
Open the app in your browser at [`https://localhost:4000`](https://localhost:4000):

1. Click `Sign in`
1. Add and Assistant:
2. Add and Assistant:
1. Click +Add Assistant Button
1. Click Instance of Assistant
1. Give it a name.
1. Enter the assistant service URL in the combobox, e.g. `http://127.0.0.1:3010`.
1. Click Chat box icon.
1. Type a message and hit send.
1. If you see "Please set the OpenAI API key in the config."
2. Click Instance of Assistant
3. Give it a name.
4. Enter the assistant service URL in the combobox, e.g. `http://127.0.0.1:3010`.
5. Click Chat box icon.
6. Type a message and hit send.
7. If you see "Please set the OpenAI API key in the config."
1. Click Edit icon in upper right.
1. Paste in your OpenAI Key.
1. Paste in your OrgID.
1. Click Save.
1. Hit Back button in UI.
1. Type another message and hit send.
2. Paste in your OpenAI Key.
3. Paste in your OrgID.
4. Click Save.
5. Hit Back button in UI.
8. Type another message and hit send.

Expected: You get a response from your assistant!

Expand Down
15 changes: 12 additions & 3 deletions RESPONSIBLE_AI_FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

## What is/are Semantic Workbench’s intended use(s)?

- Semantic Workbench is designed for prototyping assistants, running conversations and testing assistants behavior.
- Semantic Workbench is designed for prototyping assistants, running conversations and testing assistants behavior in a test environment.
- Semantic Workbench is not intended to be run in a production environment. AI assistants and agents developed with the help of Semantic Workbench, should be deployed separately from the workbench environment, in dedicated environments with proper monitoring and safety protections.

## How was Semantic Workbench evaluated? What metrics are used to measure performance?

Expand All @@ -21,6 +22,10 @@ Developers can use any of preferred technology and connect their bots to Semanti

- Semantic Workbench is not an assistant in itself, it only allows to connect and test existing assistants.

- Semantic Workbench is not a container for Production assistants. Assistants and Agents are executed in the workbench environment only during development and test phases.

- Semantic Workbench does not monitor assistants behavior, it's only designed to make it easier for developers to observe the behavior. Developers are responsible for designing assistants and understanding if these are working properly.

- Intelligent assistants must be developed with usual IDEs and development tools like Semantic Kernel, Langchain, Autogen, following the best practices there recommended, for instance [Responsible AI and Semantic Kernel](https://learn.microsoft.com/semantic-kernel/when-to-use-ai/responsible-ai) and [LangSmith](https://www.langchain.com/langsmith).

- The workbench is unable to automatically discover agents: once the code for an agent is ready, some extra code needs to be added in order to connect the assistant to Semantic Workbench.
Expand All @@ -31,13 +36,17 @@ Developers can use any of preferred technology and connect their bots to Semanti

- Developers using Semantic Workbench can adopt a user-centric approach in designing applications, ensuring that users are well-informed and have the ability to approve any actions taken by the AI. Semantic Workbench exposes all the information provided by the connected assistants, so it's important that developers code these assistants to expose their rationale, prompts, and state.

- Additionally, intelligent assistants developers should implement mechanisms to monitor and filter any automatically generated information, if deemed necessary.
- Additionally, intelligent assistants developers should implement mechanisms to monitor and filter any automatically generated information, if deemed necessary. Some of these mechanisms include:
- moderating users' input and AI's output, for instance using [Azure AI Content Safety](https://azure.microsoft.com/products/ai-services/ai-content-safety).
- including metaprompt guardrails, instructing LLMs how to protect users and business logic. For instance see [this page](https://learn.microsoft.com/azure/ai-services/openai/concepts/system-message) for information and examples.

- By addressing responsible AI issues in this manner, developers can create assistants that are not only efficient and useful but also adhere to ethical guidelines and prioritize user trust and safety.

## What operational factors and settings allow for effective and responsible use of Semantic Workbench?

- First and foremost, developers using Semantic Workbench can precisely define user interactions and how user data is managed in the source code of their intelligent assistants.
- First and foremost, use Semantic Workbench to access your assistants only in private development environments, such as your localhost.

- Developers using Semantic Workbench can precisely define user interactions and how user data is managed in the source code of their intelligent assistants.

- If a prototype assistant runs a sequence of components, additional risks/failures may arise when using non-deterministic behavior. To mitigate this, developers can:

Expand Down
Binary file modified docs/readme1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 30 additions & 2 deletions dotnet/SemanticWorkbench.sln
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkbenchConnector", "WorkbenchConnector\WorkbenchConnector.csproj", "{F7DBFD56-5A7C-41D1-8F0A-B00E51477E19}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExample01", "..\examples\dotnet-example01\AgentExample01.csproj", "{3A6FE36E-B186-458C-984B-C1BBF4BFB440}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-01-echo-bot", "..\examples\dotnet-01-echo-bot\dotnet-01-echo-bot.csproj", "{3A6FE36E-B186-458C-984B-C1BBF4BFB440}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExample02", "..\examples\dotnet-example02\AgentExample02.csproj", "{46BC33EC-AA35-428D-A8B4-2C0E693C7C51}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-02-message-types-demo", "..\examples\dotnet-02-message-types-demo\dotnet-02-message-types-demo.csproj", "{46BC33EC-AA35-428D-A8B4-2C0E693C7C51}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-03-simple-chatbot", "..\examples\dotnet-03-simple-chatbot\dotnet-03-simple-chatbot.csproj", "{C6CA301B-11B3-4EF5-A18A-D5840F23115B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{5E645E57-0B7A-4EC2-B90C-03E387E7F124}"
ProjectSection(SolutionItems) = preProject
..\tools\reset-service-data.sh = ..\tools\reset-service-data.sh
..\tools\run-app.sh = ..\tools\run-app.sh
..\tools\run-canonical-agent.sh = ..\tools\run-canonical-agent.sh
..\tools\run-dotnet-example1.sh = ..\tools\run-dotnet-example1.sh
..\tools\run-dotnet-example2.sh = ..\tools\run-dotnet-example2.sh
..\tools\run-python-example1.sh = ..\tools\run-python-example1.sh
..\tools\run-service.sh = ..\tools\run-service.sh
..\tools\run-dotnet-example3.sh = ..\tools\run-dotnet-example3.sh
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{968FE485-6440-45CD-9DCA-E2FD42D2B765}"
ProjectSection(SolutionItems) = preProject
..\README.md = ..\README.md
..\RESPONSIBLE_AI_FAQ.md = ..\RESPONSIBLE_AI_FAQ.md
..\SECURITY.md = ..\SECURITY.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -24,5 +45,12 @@ Global
{46BC33EC-AA35-428D-A8B4-2C0E693C7C51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46BC33EC-AA35-428D-A8B4-2C0E693C7C51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46BC33EC-AA35-428D-A8B4-2C0E693C7C51}.Release|Any CPU.Build.0 = Release|Any CPU
{C6CA301B-11B3-4EF5-A18A-D5840F23115B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6CA301B-11B3-4EF5-A18A-D5840F23115B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6CA301B-11B3-4EF5-A18A-D5840F23115B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6CA301B-11B3-4EF5-A18A-D5840F23115B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5E645E57-0B7A-4EC2-B90C-03E387E7F124} = {968FE485-6440-45CD-9DCA-E2FD42D2B765}
EndGlobalSection
EndGlobal
4 changes: 3 additions & 1 deletion dotnet/SemanticWorkbench.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ABC/@EntryIndexedValue">ABC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CORS/@EntryIndexedValue">CORS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HTML/@EntryIndexedValue">HTML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSON/@EntryIndexedValue">JSON</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSON/@EntryIndexedValue">JSON</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String></wpf:ResourceDictionary>
24 changes: 24 additions & 0 deletions dotnet/WorkbenchConnector/ConfigUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.SemanticWorkbench.Connector;

public static class ConfigUtils
{
// Use "text area" instead of default "input box"
public static void UseTextAreaFor(string propertyName, Dictionary<string, object> uiSchema)
{
uiSchema[propertyName] = new Dictionary<string, object>
{
{ "ui:widget", "textarea" }
};
}

// Use "list of radio buttons" instead of default "select box"
public static void UseRadioButtonsFor(string propertyName, Dictionary<string, object> uiSchema)
{
uiSchema[propertyName] = new Dictionary<string, object>
{
{ "ui:widget", "radio" }
};
}
}
4 changes: 4 additions & 0 deletions dotnet/WorkbenchConnector/Models/DebugInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ namespace Microsoft.SemanticWorkbench.Connector;

public class DebugInfo : Dictionary<string, object?>
{
public DebugInfo()
{
}

public DebugInfo(string key, object? info)
{
this.Add(key, info);
Expand Down
44 changes: 44 additions & 0 deletions dotnet/WorkbenchConnector/Models/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,48 @@ public static Message CreateChatMessage(

return result;
}

public static Message CreateNotice(
string agentId,
string content,
object? debug = null,
string contentType = "text/plain")
{
var result = CreateChatMessage(agentId: agentId, content: content, debug: debug, contentType: contentType);
result.MessageType = "notice";
return result;
}

public static Message CreateNote(
string agentId,
string content,
object? debug = null,
string contentType = "text/plain")
{
var result = CreateChatMessage(agentId: agentId, content: content, debug: debug, contentType: contentType);
result.MessageType = "note";
return result;
}

public static Message CreateCommand(
string agentId,
string content,
object? debug = null,
string contentType = "text/plain")
{
var result = CreateChatMessage(agentId: agentId, content: content, debug: debug, contentType: contentType);
result.MessageType = "command";
return result;
}

public static Message CreateCommandResponse(
string agentId,
string content,
object? debug = null,
string contentType = "text/plain")
{
var result = CreateChatMessage(agentId: agentId, content: content, debug: debug, contentType: contentType);
result.MessageType = "command-response";
return result;
}
}
4 changes: 3 additions & 1 deletion dotnet/WorkbenchConnector/Storage/AgentServiceStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ public AgentServiceStorage(
{
this._log = logFactory.CreateLogger<AgentServiceStorage>();

this._path = appConfig.GetSection("Workbench").GetValue<string>(
var connectorId = appConfig.GetSection("Workbench").GetValue<string>("ConnectorId") ?? "undefined";
var tmpPath = appConfig.GetSection("Workbench").GetValue<string>(
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "StoragePathWindows"
: "StoragePathLinux") ?? string.Empty;
this._path = Path.Join(tmpPath, connectorId);

if (this._path.Contains("$tmp"))
{
Expand Down
Loading

0 comments on commit 3ce5020

Please sign in to comment.