{"id":3648,"date":"2026-03-17T17:11:47","date_gmt":"2026-03-17T17:11:47","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/03\/17\/rt-assistant-a-multi-agent-voice-bot-using-net-and-openai\/"},"modified":"2026-03-17T17:11:47","modified_gmt":"2026-03-17T17:11:47","slug":"rt-assistant-a-multi-agent-voice-bot-using-net-and-openai","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/03\/17\/rt-assistant-a-multi-agent-voice-bot-using-net-and-openai\/","title":{"rendered":"RT.Assistant: A Multi-Agent Voice Bot Using .NET and OpenAI"},"content":{"rendered":"<blockquote>\n<p>This is a guest post by <strong><a href=\"https:\/\/github.com\/fwaris\">Faisal Waris<\/a><\/strong>, an AI strategist in the telecom industry. Faisal built RT.Assistant to explore how .NET, F#, and the OpenAI Realtime API can come together in a production-style, multi-agent voice application.<\/p>\n<\/blockquote>\n<p>RT.Assistant is a voice-enabled, multi-agent assistant built entirely in .NET \u2014 combining the <strong>OpenAI Realtime API<\/strong> over <strong>WebRTC<\/strong> for low-latency, bidirectional voice; <strong>F#<\/strong> discriminated unions and async state machines for agent orchestration; <strong>.NET MAUI<\/strong> (via Fabulous) for cross-platform native UI on iOS, Android, macOS, and Windows; and <strong>Microsoft.Extensions.AI<\/strong> for portable LLM integration with both OpenAI and Anthropic models.<\/p>\n<p>Under the hood, a custom <strong>RTFlow<\/strong> framework hosts multiple specialized agents \u2014 a Voice Agent, a CodeGen Agent, a Query Agent, and an App Agent \u2014 that communicate over a strongly-typed async bus, while a deterministic state-machine (the \u201cFlow\u201d) keeps the non-deterministic LLM behavior in check. The sample also showcases an unconventional RAG approach: instead of vector search, user queries are translated into <strong>Prolog<\/strong> and executed against a logic-programming knowledge base embedded in a .NET MAUI HybridWebView, yielding precise, hallucination-resistant answers.<\/p>\n\n<div class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/youtu.be\/bSMByJvYLoY\" target=\"_blank\">YouTube: Overview<\/a><\/div>\n<div class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/youtu.be\/0ghPhQyzyaI\" target=\"_blank\">YouTube: Code Walkthrough<\/a><\/div>\n<div class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/readme.md\" target=\"_blank\">GitHub Repo<\/a><\/div>\n<div class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/www.linkedin.com\/posts\/activity-7410082754836725761-lquf?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAAbaagBCG-0LlGBjghxmo7KKzbEXRHmiZ0\" target=\"_blank\">LinkedIn<\/a><\/div>\n<h2>Why telecom plan selection?<\/h2>\n<p>Faisal works in the telecom industry, where one of the most common customer pain points is choosing the right phone plan. Carriers offer dozens of bundled plans that mix voice, data, hotspot, streaming, and promotional pricing in ways that are genuinely difficult to compare \u2014 even for the people selling them. It\u2019s the kind of domain where a conversational AI assistant can make a real difference: customers ask natural-language questions and get precise, verifiable answers instead of sifting through comparison matrices or waiting on hold.<\/p>\n<p>RT.Assistant uses this domain as a realistic proving ground. The application maintains a mocked \u2014 but representative \u2014 catalog of plans modeled after a major US carrier\u2019s actual offerings. Let\u2019s look at what makes these plans hard to compare in the first place.<\/p>\n<h3>Plan offerings<\/h3>\n<p>Phone plans (like many other offerings these days) are bundled products and services which makes it non-trivial to ascertain which plan will work best for one\u2019s needs. Consider the following components of a typical contemporary plan:<\/p>\n<ul>\n<li><em>Base plan<\/em> with voice, text and data rates &amp; limits<\/li>\n<li><em>Mobile hotspot<\/em> data rates &amp; limits (20gb, 50gb, 100gb?)<\/li>\n<li><em>Premium data<\/em> rates &amp; limits (with premium, one\u2019s data gets preference in a crowded\/overloaded network)<\/li>\n<li><em>Streaming services<\/em> e.g. Netflix, Hulu, Apple TV, etc.<\/li>\n<li><em>Taxes and fees<\/em> may or may not be included in the plan price<\/li>\n<li>Special discounts for <em>Military Veterans<\/em>, <em>First Responders<\/em>, <em>Seniors<\/em>, etc.<\/li>\n<li><em>In-flight<\/em> data rates &amp; limits<\/li>\n<li>Then there may be seasonal or campaign <em>promotions<\/em><\/li>\n<\/ul>\n<p>Additionally, the available features may be dependent on the number of lines (distinct phone numbers). For example, Netflix may be <em>excluded<\/em> for a single line but <em>included<\/em> for two or more lines.<\/p>\n<h3>Technologies Showcased<\/h3>\n<p>The system internally maintains a mocked\u2014but representative\u2014set of phone plans, modeled as Prolog facts, simulating offerings from a typical major telecom provider. From (a) capturing the voice input, to (b) querying the Prolog knowledge base, and finally (c) generating the results, multiple components work together seamlessly.<\/p>\n<p>This sample highlights the integration of the following frameworks and technologies:<\/p>\n<ul>\n<li><strong>RTFlow<\/strong>: Multi-agent framework for realtime GenAI applications \u2013 written in F#<\/li>\n<li><strong>RTOpenAI<\/strong>: F# library for interfacing with the OpenAI realtime API via the WebRTC protocol<\/li>\n<li><strong>Fabulous<\/strong> for .NET MAUI: F# library for building native mobile applications (IOS, Android, +) with Microsoft .NET MAUI<\/li>\n<li><strong>Tau<\/strong>: JavaScript-based Prolog engine and its use in a native mobile app via the .NET MAUI HybridWebView control.<\/li>\n<li>Integration with the OpenAI &amp; Anthropic \u2018chat\u2019 APIs for Prolog code generation with the <strong>Microsoft.Extensions.AI<\/strong> library<\/li>\n<\/ul>\n<h2>Overview<\/h2>\n<p>There is a lot going on here: <em>generative AI<\/em>; <em>old-school symbolic AI<\/em>; <em>multi-agents<\/em>; <em>realtime voice<\/em>; <em>cross-platform native mobile apps<\/em>; to name some. The following explains how these are all stitched together into a comprehensive system.<\/p>\n<ul>\n<li>Voice-enabled interaction: The assistant allows users to ask questions about phone plans through natural speech, making the experience conversational and intuitive.<\/li>\n<li>Structured knowledge base: Plan details are represented as logically consistent Prolog facts, ensuring clarity and eliminating ambiguity in feature\u2013price combinations.<\/li>\n<li>Internally, multiple specialized agents work together to process and respond to user queries:\n<ul>\n<li><strong>Voice Agent<\/strong> \u2013 Maintains a connection to the OpenAI realtime \u2018voice\u2019 API, enabling natural voice conversations about phone plans. It handles the steady stream of messages from the API, include tool calls containing natural language queries. These queries are then routed to other agents, and the resulting answers are returned to the voice model, which conveys them back to the user in audio form.<\/li>\n<li><strong>CodeGen Agent<\/strong> \u2013 Converts natural language queries into Prolog statements using another LLM API, then leverages the Tau Prolog engine to evaluate those statements against the knowledge base of facts.<\/li>\n<li><strong>Query Agent<\/strong> \u2013 Executes predefined (\u201ccanned\u201d) Prolog queries directly against the Prolog engine for quick, structured lookups.<\/li>\n<li><strong>App Agent<\/strong> \u2013 Oversees communication among agents and reports activity back to the user interface for transparency and monitoring.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>All agents are orchestrated by the <strong>RTFlow<\/strong> framework, which provides hosting and communication services. The diagram below illustrates the RTFlow agent arrangement for the RT.Assistant sample:<\/p>\n<p><img data-opt-id=2086034218  fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2026\/03\/RTflow.webp\" alt=\"RTFlow\" \/><\/p>\n<p>As there are multiple frameworks \/ technologies in play here. Let\u2019s briefly delve into each one of them \u2013 in the order of perceived importance.<\/p>\n<h2>1. RTFlow<\/h2>\n<p>RTFlow is a framework for building real-time, <em>agentic<\/em> applications. It is composed of three primary elements: <strong>Flow<\/strong>, <strong>Bus<\/strong>, and <strong>Agents<\/strong>.<\/p>\n<h3>Bus<\/h3>\n<p>The <strong>Bus<\/strong> provides the communication substrate that connects <strong>Agents<\/strong> to one another and to the <strong>Flow<\/strong>. It exposes two distinct logical channels:<\/p>\n<ul>\n<li><strong>Agent broadcast channel<\/strong><br \/>\nAll agent-intent messages published to this channel are broadcast to <em>all<\/em> agents.<\/li>\n<li><strong>Flow input channel<\/strong><br \/>\nMessages published to this channel are delivered exclusively to the <strong>Flow<\/strong>. Agents do not receive these messages.<\/li>\n<\/ul>\n<p>This separation allows agent collaboration to occur independently of system-level orchestration, while still enabling agents to explicitly signal the Flow when required.<\/p>\n<h3>Messages and State<\/h3>\n<p>Both <strong>Flow<\/strong> and <strong>Agents<\/strong> maintain private internal state and communicate exclusively via strongly typed, asynchronous messages. Message \u2018schemas\u2019 are defined as F# <a href=\"https:\/\/learn.microsoft.com\/dotnet\/fsharp\/language-reference\/discriminated-unions\"><em>discriminated unions (DUs)<\/em><\/a> types and are fixed at implementation time, providing:<\/p>\n<ul>\n<li>Compile-time exhaustiveness checking<\/li>\n<li>Explicit modeling of intent and system events<\/li>\n<li>Clear separation between agent-level and flow-level concerns<\/li>\n<\/ul>\n<h3>Flow<\/h3>\n<p>The <strong>Flow<\/strong> is an asynchronous, deterministic state machine. Its state transitions are triggered solely by messages arriving on the Flow input channel.<\/p>\n<p>Depending on application requirements, the Flow can range from minimal to highly directive:<\/p>\n<ul>\n<li><strong>Minimal control<\/strong><br \/>\nA simple lifecycle state machine (e.g., <code>Start \u2192 Run \u2192 Terminate<\/code>), where agents primarily interact with each other via the broadcast channel and the Flow plays a supervisory role.<\/li>\n<li><strong>Orchestrated control<\/strong><br \/>\nA more granular state machine in which agents primarily communicate with the Flow, and the Flow explicitly coordinates agent behavior based on its current state.<\/li>\n<\/ul>\n<p>This design allows system-level determinism and control to be introduced incrementally, without constraining agent autonomy where it is unnecessary.<\/p>\n<h3>Topology<\/h3>\n<p>From a multi-agent systems perspective, RTFlow employs a hybrid <strong>bus\u2013star<\/strong> <a href=\"https:\/\/vuir.vu.edu.au\/652\/1\/Zhang_H-TopologicalClassfication.pdf\">topology<\/a>:<\/p>\n<ul>\n<li>The <strong>Bus<\/strong> enables broadcast-based, peer-style agent communication.<\/li>\n<li>The <strong>Flow<\/strong> acts as a central coordinating node when orchestration is required.<\/li>\n<\/ul>\n<p>This hybrid model balances scalability and decoupling with deterministic system control.<\/p>\n<p>The F# language offers a clean way to model asynchronous state machines (or more precisely <a href=\"https:\/\/www.tutorialspoint.com\/automata_theory\/moore_and_mealy_machines.htm\">Mealy machines<\/a>) where the states are functions and transitions happen via pattern matching over messages (DUs) or with <a href=\"https:\/\/zetcode.com\/fsharp\/active-patterns\/\">\u2018active patterns\u2019<\/a>. In the snippet below <code>s_XXX<\/code> are functions as states and <code>M_xxx<\/code> are messages that arrive on the <code>Bus<\/code>. The structure <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/RTFlow\/Workflow.fs#L35\"><code>F<\/code><\/a> packages the next state along with any output messages to be sent to agents.<\/p>\n<pre><code class=\"language-fsharp\">let rec s_start msg = async {\r\n  match msg with \r\n  | M_Start -&gt; return F(s_run,[M_Started]) \/\/transition to run\r\n  | _       -&gt; return F(s_start,[]) \/\/stay in start state\r\n  }\r\n\r\nand s_run msg = async {\r\n  match msg with \r\n  | M_DoSomething -&gt; do! doSomething()\r\n                     return F(s_run,[M_DidSomething])                     \r\n  | M_Terminate   -&gt; return F(s_terminate,[])\r\n  | _             -&gt; return F(s_run,[])\r\n}\r\n\r\nand s_terminate msg = async {\r\n...<\/code><\/pre>\n<p>LLMs are inherently non-deterministic. RTFlow offers a way to control non-determinism to keep the overall system stable. As applications move from being human-centric to being more autonomous, we will need increasingly sophisticated methods to manage non-determinism. RTFlow\u2019s approach is to inject a deterministic state machine in the mix to effect such control.<\/p>\n<p>Given the relatively simple building blocks of RTFlow, we can construct rich agentic systems that can support many realtime needs with the ability to dial-in the desired degree of control, when needed.<\/p>\n<h2>2. RTOpenAI<\/h2>\n<p>RTOpenAI wraps the OpenAI realtime voice API for native mobile(+) apps. Its two key features are a) support for the <a href=\"https:\/\/webrtc.org\/\">WebRTC<\/a> protocol; and b) <strong>strongly-typed realtime protocol messages<\/strong>. These are discussed next.<\/p>\n<h3>WebRTC<\/h3>\n<p>The OpenAI voice API can be used via Web Sockets or WebRTC, where WebRTC has some key advantages over the other;<\/p>\n<ul>\n<li>Firstly, WebRTC was designed for bidirectional, realtime communication. It has built-in resiliency for minor network disconnects \u2013 which crucially Web Sockets does not.<\/li>\n<li>WebRTC has separate channels for voice and data. (Also video \u2013 which is not currently used). This means that typically the application only needs to handle the data channel explicitly. The in\/out audio channels are wired to the audio hardware by the underlying WebRTC libraries. In the case of Web Sockets, the application explicitly needs to handle in\/out audio as well as the data.<\/li>\n<li>WebRTC transmits audio via the OPUS codec that has excellent compression but also retains good audio quality. For Web Sockets multiple choices exist. High quality audio is sent as uncompressed 24KHz <a href=\"https:\/\/www.tutorialspoint.com\/digital_communication\/digital_communication_pulse_code_modulation.htm\">PCM<\/a> binary as base64 encoded strings. The bandwidth required is 10X that for OPUS. There are other telephony formats available but the audio quality drops significantly.<\/li>\n<\/ul>\n<h3>Strongly-Typed Event Handling<\/h3>\n<p>The <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/RTOpenAI.Events\/Events.fs#L925\">RTOpenAI.Events<\/a> library attempts to define F# types for all OpenAI realtime API protocol messages (that are currently documented).<\/p>\n<p>Additionally, the server (and client) messages are wrapped in DUs, which is convenient for consuming applications; incoming events can be handled with simple pattern matching. After the realtime connection is established, there is a steady flow of incoming events from the server that the application needs to accept and handle. The following snippet is an impressionistic version of how the <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Agents\/VoiceAgent.fs#L155\">Voice Agent<\/a> handles server events:<\/p>\n<pre><code class=\"language-fsharp\">let handleEvent (ev:ServerEvent) = async {\r\n  match ev with\r\n  | SessionCreated                                   -&gt; ...\r\n  | ResponseOutputItemDone ev when isFunctionCall ev -&gt; ...\r\n  | _                                                -&gt; ... \/\/choose to ignore\r\n}<\/code><\/pre>\n<p>The RTOpenAI library is a cross-platform <em>.NET MAUI<\/em> (see next) library and as such supports realtime voice applications for IOS, MacOS, Android and Windows.<\/p>\n<h2>3. Fabulous .NET MAUI Controls<\/h2>\n<p><a href=\"https:\/\/dotnet.microsoft.com\/apps\/maui\"><em>Microsoft .NET MAUI<\/em><\/a> is a technology for building cross-platform native apps. The F# library <a href=\"https:\/\/github.com\/fabulous-dev\/Fabulous.MauiControls\"><em>Fabulous.MauiControls<\/em><\/a> enables building of .NET MAUI apps in F#.<\/p>\n<p>Fabulous is a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Functional_reactive_programming\">functional-reactive<\/a> UI framework (influenced by <a href=\"https:\/\/elm-lang.org\/\">Elm<\/a> and <a href=\"https:\/\/react.dev\/\">React<\/a>).<\/p>\n<p>Fabulous is a joy to use. UI\u2019s can be defined declaratively in simple and understandable F#. UI \u2018events\u2019 are messages, which again are F# DU types that are \u2018handled\u2019 with pattern matching. In the simplest case, <em>events<\/em> update the application state, which is then rendered by Fabulous on to the screen.<\/p>\n<p>Fabulous for .NET MAUI has a rich feature set, which cannot be fully covered here but the <em>Counter App<\/em> sample is replicated below to provide some sense of how the library works:<\/p>\n<pre><code class=\"language-fsharp\">\/\/\/ A simple Counter app\r\n\r\ntype Model =         \/\/application state\r\n    { Count: int }\r\n\r\ntype Msg =           \/\/DU message types\r\n    | Increment\r\n    | Decrement\r\n\r\nlet init () =\r\n    { Count = 0 }\r\n\r\nlet update msg model = \/\/function to handle UI events\/messages\r\n    match msg with\r\n    | Increment -&gt; { model with Count = model.Count + 1 }\r\n    | Decrement -&gt; { model with Count = model.Count - 1 }\r\n\r\nlet view model =  \r\n    Application(\r\n        ContentPage(\r\n            VStack(spacing = 16.) {                     \/\/view\r\n                Image(\"fabulous.png\")\r\n\r\n                Label($\"Count is {model.Count}\")\r\n\r\n                Button(\"Increment\", Increment)\r\n                Button(\"Decrement\", Decrement)\r\n            }\r\n        )\r\n    )<\/code><\/pre>\n<p>The RT.Assistant is a <em>.NET MAUI<\/em> application and so the project structure is defined by .NET MAUI. Its a single project that targets multiple platforms. Components specific to each target platform are under the <em>Platforms<\/em> folder:<\/p>\n<pre><code class=\"language-text\">\/RT.Assistant\r\n  \/Platforms\r\n    \/Android\r\n    \/IOS\r\n    \/MacCatalyst\r\n    \/Windows<\/code><\/pre>\n<p>The platform specific folders contain the native-app required components (plists, app manifests, etc.). For example, here is the IOS <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Platforms\/iOS\/Info.plist\">plist<\/a>.<\/p>\n<p>RT.Assistant application code is 90% shared across platforms. However platform-specific libraries are required when interfacing with hardware that .NET MAUI does not cover. For WebRTC, RTOpenAI uses platform-native libraries with <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/native-library-interop-dotnet-maui\/\">Native Library Interop<\/a>. The <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Bindings\/FsWebRTC.Bindings.Maui.iOS\/FsWebRTC.Bindings.Maui.iOS.csproj\">iOS WebRTC binding library<\/a> wraps the <code>WebRTC.xcframework<\/code> written in C++. And <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Bindings\/FsWebRTC.Bindings.Maui.Android\/FsWebRTC.Bindings.Maui.Android.csproj\">for Android<\/a> the native <code>libwebrtc.aar<\/code> Android Archive is wrapped.<\/p>\n<p>Since most mobile apps have both IOS and Android versions, as such, .NET MAUI makes a lot of sense. Instead of maintaining multiple code bases and dev teams, with .NET MAUI one can maintain a single code base with 90% shared code across platforms. And unlike other mobile platforms (e.g. React Native), .NET MAUI apps are proper native apps. For example, it would be problematic to host a realtime multi-agent systems like RTFlow in a JavaScript-based system like React Native.<\/p>\n<h2>4. Prolog for RAG<\/h2>\n<p>To make the sample somewhat fun and interesting, I decided to use Prolog-based <a href=\"https:\/\/aws.amazon.com\/what-is\/retrieval-augmented-generation\/\">\u2018RAG\u2019<\/a>. Generative AI meets Symbolic AI.<\/p>\n<p>Prolog is a language for logic programming that was created almost 50 years ago. It has endured well even till today. The best known open source implementation is <a href=\"https:\/\/github.com\/SWI-Prolog\/swipl\">SWI- Prolog<\/a>. However here I am using the much lighter weight <a href=\"https:\/\/github.com\/tau-prolog\/tau-prolog\">Tau<\/a> Prolog engine that runs in the browser.<\/p>\n<p>Fortunately web content can be easily hosted in .NET MAUI apps via the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/maui\/user-interface\/controls\/hybridwebview?view=net-maui-10.0\">HybridWebViewControl<\/a>. In RT.Assistant there is a <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Views\/Chat.fs#L105\">hidden web view<\/a> that loads the Tau engine and the <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Resources\/Raw\/wwwroot\/plan_clauses.pl\"><em>plan facts<\/em><\/a>.<\/p>\n<h3>Prolog Representation<\/h3>\n<p>The typical phone plans from the major telecoms are \u2018rich\u2019 offerings. The interplay of base plans, number of lines, features and promotions suggest a rules engine based approach. This is precisely where Prolog excels. By representing valid combinations of plans, features, and pricing as logical facts, Prolog ensures consistency and removes ambiguity.<\/p>\n<p>Prolog is a declarative language for first-order logic programming.<br \/>\nA Prolog \u2018database\u2019 consists of facts (e.g. plans and their features) and rules (to derive new facts from existing ones). A Prolog implementation will find any and all possible solutions that satisfy a query, given a set of facts and rules.<\/p>\n<p>The \u2018schema\u2019 for the plan and its features is in <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Resources\/Raw\/plan_schema.pl\">plan_schema.pl<\/a>. The skeletal form is:<\/p>\n<pre><code class=\"language-plaintext\">plan(title,category,prices,features)\r\n% where each feature may have a different attribute set<\/code><\/pre>\n<p>A partial fact for the \u2018Connect\u2019 plan is given below:<\/p>\n<pre><code class=\"language-plaintext\">plan(\r\n    \"Connect\",\r\n    category(\"all\"),\r\n    prices([\r\n      line(1, monthly_price(20), original_price(25)),\r\n      line(2, monthly_price(40), original_price(26)),\r\n      ...\r\n    ]),\r\n    features([\r\n\r\n        feature(\r\n            netflix(\r\n                desc(\"Netflix Standard with Ads On Us\"),\r\n                included(yes)\r\n            ),\r\n            applies_to_lines(lines(2, 2))\r\n        ),\r\n\r\n        feature(\r\n            autopay_monthly_discount(\r\n                desc(\"$5 disc. per line up to 8 lines w\/AutoPay &amp; eligible payment method.\"),\r\n                discount_per_line(5),\r\n                lines_up_to(8),\r\n                included_in_monthly_price(yes)\r\n            ),\r\n            applies_to_lines(all)\r\n        ),\r\n        ...\r\n\r\n    ])\r\n).\r\n<\/code><\/pre>\n\n<div class=\"alert alert-primary\">\n<p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Note<\/strong><\/p>\n<p>The <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/Resources\/Raw\/plan_schema.pl\">full Prolog fact<\/a> may seem complex, however the same rules expressed in a relational database schema would be far more complex to understand and query. The <em>metadata<\/em> (columns, tables, relations) required to represent the rules and facts will be far greater than what is required under Prolog.<\/p><\/div>\n<h3>Query Processing<\/h3>\n<p>While we can obtain an answer by prompting the LLM with text descriptions of the plans along with the query, there is a sound reason for not doing so. LLMs are not perfect and can make mistakes. And here we desire a more precise answer. So, instead we transform the natural language user query into an equivalent Prolog query \u2013 with the help of an LLM. It is surmised that the reformulation of the question is easier for the LLM, i.e. the LLM is less likely to hallucinate compared to the case of generating the answer directly. For direct answer generation, the LLM will need to sift through a much larger context \u2013 the entire plan database as plain text. For query generation, the LLM need only look at the database \u2018schema\u2019 \u2013 which is much more compact, especially in the case of Prolog.<\/p>\n<p>If query transformation goes awry then the Prolog query may fail entirely or produce strange results. Either way the user will be alerted and will not rely on the results to make a decision. If on the other hand, the answer is generated directly, a hallucination may subtly alter or miss facts. The user is likely to accept it without questioning because the answer looks plausible. This is a more egregious error.<\/p>\n<h3>Example Queries<\/h3>\n<p>Below are some typical questions that can be asked:<\/p>\n<ul>\n<li>What categories of plans are available?<\/li>\n<li>What is the lowest cost plan for 4 lines?\n<ul>\n<li>Follow up: Does this plan include taxes and fees in the price?<\/li>\n<\/ul>\n<\/li>\n<li>Are there any special plans for military veterans?\n<ul>\n<li>Follow up: What is the amount of mobile hotspot data available for this plan?<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The RT.Assistant application shows the natural language query; the generated Prolog; and the Prolog query results on the UI in realtime.<\/p>\n<p>Example:<\/p>\n<p><strong>Natural language query generated by voice model from conversation:<\/strong><\/p>\n<pre><code class=\"language-text\">Find the plans in the category 'military_veteran' for 2 lines and list their costs.<\/code><\/pre>\n<p><strong>Prolog query:<\/strong><\/p>\n<pre><code class=\"language-plaintext\">plan(Title,\r\n     category(military_veteran),\r\n     prices(Lines),\r\n     _),\r\nmember(price(2, monthly_price(Price), _), Lines).<\/code><\/pre>\n\n<div class=\"alert alert-primary\">\n<p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Note<\/strong><\/p>\n<p>In Prolog, uppercase-starting names are \u2018free\u2019 variables that can be bound to values. For example, \u2018Title\u2019 above will bind to each of the plan titles for the found solutions. A solution satisfies all constraints. One obvious constraint is \u2018category=military_veteran\u2019 so only Military Veteran plans will be considered.\n<\/p><\/div>\n<p><strong>Results:<\/strong><\/p>\n<pre><code class=\"language-text\">Title = Connect Next Military, Lines =\r\n[line(1,monthly_price(85),original_price(90)),\r\n line(2,monthly_price(130),original_price(140)),\r\n line(3,monthly_price(165),original_price(180)),\r\n line(4,monthly_price(200),original_price(220)),\r\n line(5,monthly_price(235),original_price(260))],\r\nPrice = 130\r\n\r\nTitle = Core, Lines = ...<\/code><\/pre>\n<p>If a Prolog error occurs, the system regenerates the Prolog query but this time includes the Prolog error message along with the original query. This cycle may be repeated up to a limit.<\/p>\n<h3>Prolog Code Generation and Results<\/h3>\n<p>For code generation, the application allows for a choice between Claude Sonnet 4.5 and GPT 5.1 (via the app Settings). The GPT Codex model was also tested but there the latency is too high for realtime needs.<\/p>\n<p>For this particular task, GPT-5.1 has the clear edge, generating code that produces concise and relevant output. See <a href=\"https:\/\/github.com\/fwaris\/RTOpenAI\/blob\/master\/src\/Samples\/PlanSelection\/RT.Assistant\/docs\/prologgen.md\">this analysis<\/a> for more details.<\/p>\n<p>For what it\u2019s worth, both models generate syntactically correct Prolog 99% of the time. (A retry loop corrects generated errors, if any.)<\/p>\n<p>For question-answering, the OpenAI realtime model generates satisfactory answers to user queries from the generated Prolog output. Note that for any real production system there should be a well-crafted \u2018eval\u2019 suite to truly gauge the performance.<\/p>\n<p>The post <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/rt-assistant-a-realtime-multiagent-voice-bot-using-dotnet-and-open-ai-api\/\">RT.Assistant: A Multi-Agent Voice Bot Using .NET and OpenAI<\/a> appeared first on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\">.NET Blog<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>This is a guest post by Faisal Waris, an AI strategist in the telecom industry. Faisal built RT.Assistant to explore [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3649,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[7],"tags":[],"class_list":["post-3648","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/3648","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=3648"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/3648\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/3649"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=3648"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=3648"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=3648"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}