<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Frank Noorloos</title>
    <description>The latest articles on DEV Community by Frank Noorloos (@frankiey).</description>
    <link>https://dev.to/frankiey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2058052%2F4abc3b79-0fd9-4348-9a73-8c9ea764d366.jpeg</url>
      <title>DEV Community: Frank Noorloos</title>
      <link>https://dev.to/frankiey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/frankiey"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Tue, 13 Jan 2026 14:25:01 +0000</pubDate>
      <link>https://dev.to/frankiey/-ojc</link>
      <guid>https://dev.to/frankiey/-ojc</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/frankiey" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2058052%2F4abc3b79-0fd9-4348-9a73-8c9ea764d366.jpeg" alt="frankiey"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/frankiey/rag-without-the-cloud-net-semantic-kernel-ollama-on-your-laptop-1hg7" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;RAG without the cloud: .NET + Semantic Kernel + Ollama on your laptop&lt;/h2&gt;
      &lt;h3&gt;Frank Noorloos ・ Jan 9&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dotnet&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#llm&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>ai</category>
      <category>programming</category>
      <category>dotnet</category>
      <category>llm</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Fri, 09 Jan 2026 14:07:54 +0000</pubDate>
      <link>https://dev.to/frankiey/-3249</link>
      <guid>https://dev.to/frankiey/-3249</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/frankiey" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2058052%2F4abc3b79-0fd9-4348-9a73-8c9ea764d366.jpeg" alt="frankiey"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/frankiey/rag-without-the-cloud-net-semantic-kernel-ollama-on-your-laptop-1hg7" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;RAG without the cloud: .NET + Semantic Kernel + Ollama on your laptop&lt;/h2&gt;
      &lt;h3&gt;Frank Noorloos ・ Jan 9&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dotnet&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#llm&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>ai</category>
      <category>programming</category>
      <category>dotnet</category>
      <category>llm</category>
    </item>
    <item>
      <title>RAG without the cloud: .NET + Semantic Kernel + Ollama on your laptop</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Fri, 09 Jan 2026 13:56:07 +0000</pubDate>
      <link>https://dev.to/frankiey/rag-without-the-cloud-net-semantic-kernel-ollama-on-your-laptop-1hg7</link>
      <guid>https://dev.to/frankiey/rag-without-the-cloud-net-semantic-kernel-ollama-on-your-laptop-1hg7</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Generative AI is now impossible to ignore in software development. Tools like ChatGPT and GitHub Copilot can answer technical questions in seconds. But there is a downside: you become dependent on external APIs, mainly from US providers, you pay per prompt, and you may have to hand over sensitive company data. An alternative is to run language models locally with Ollama. In this article I show how to make documents searchable locally with a console application that applies Retrieval Augmented Generation (RAG). We use Ollama together with the language model llama3.2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Retrieval Augmented Generation (RAG)?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
RAG is a pattern where you do not only give a Large Language Model (LLM) the question, but also provide extra context from your own sources. Via a semantic search query (&lt;em&gt;retrieval&lt;/em&gt;) you pull the most relevant text fragments from documents, databases, or API responses. You add those fragments to the prompt, so during answering (&lt;em&gt;generation&lt;/em&gt;) the LLM can use up to date and domain specific knowledge. The result is fewer hallucinations and direct reuse of your existing data assets.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Requirements and setup
&lt;/h2&gt;

&lt;p&gt;The AI world moves fast, and many components are still in preview or experimental. The configuration below works at the time of writing. Check the GitHub repository for the most up to date information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET 9 SDK&lt;/li&gt;
&lt;li&gt;Ollama v0.9.5 or newer&lt;/li&gt;
&lt;li&gt;4 GB (V)RAM for the language model&lt;/li&gt;
&lt;li&gt;Licenses: Llama 3.2 falls under the Meta Llama Community License v2. Semantic Kernel and the demo code are MIT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a standard console application, add Semantic Kernel, and install the prerelease package &lt;code&gt;Microsoft.SemanticKernel.Connectors.Ollama&lt;/code&gt;. Then start Ollama and download the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
ollama run llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Architecture at a glance
&lt;/h2&gt;

&lt;p&gt;The RAG demo consists of three core components that together form the chain from question to retrieval to answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Semantic Kernel (orchestrator)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;.NET SDK for calling large language models, embeddings, chat history, and optionally function calling.&lt;/li&gt;
&lt;li&gt;Keeps conversation state and makes it easy to enrich context with documents.&lt;/li&gt;
&lt;li&gt;Has connectors to, among others, OpenAI, Azure OpenAI, Ollama, and multiple vector databases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Ollama (local LLM runtime)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Starts and manages models locally (CPU/GPU), including model pulls.&lt;/li&gt;
&lt;li&gt;Supports dozens of open source models. We use llama3.2.&lt;/li&gt;
&lt;li&gt;Can also generate embeddings with the same language model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Document storage and retrieval
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In this demo: a simple &lt;strong&gt;in memory&lt;/strong&gt; store with &lt;strong&gt;cosine similarity&lt;/strong&gt; (&lt;em&gt;a calculation that measures similarity between two vectors&lt;/em&gt;) and &lt;strong&gt;top k&lt;/strong&gt; selection (&lt;em&gt;take the k best scoring matches&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Great for small document sets and ideal to understand the core of RAG.&lt;/li&gt;
&lt;li&gt;For production or larger datasets: replace it with &lt;strong&gt;Qdrant&lt;/strong&gt; or another &lt;strong&gt;vector database&lt;/strong&gt; (&lt;em&gt;a specialized datastore for vector embeddings with persistent storage, indexing, filtering, and horizontal scalability&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Tip: try Qdrant locally: &lt;code&gt;docker run -p 6333:6333 qdrant/qdrant&lt;/code&gt; (starts an instance on your machine in seconds).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a question is asked, it flows like this: user prompt -&amp;gt; embedding -&amp;gt; most relevant docs -&amp;gt; context in prompt -&amp;gt; language model answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Step by step implementation
&lt;/h2&gt;

&lt;p&gt;First install two NuGet packages: Microsoft.SemanticKernel and Microsoft.SemanticKernel.Connectors.Ollama. For the Ollama connector you need the prerelease version. Also add the attribute below to the class, otherwise the project will not compile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Experimental&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SKEXP0070"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SkOllama&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Main method we initialize the required components. The OllamaApiClient points to the default URL of the locally running Ollama API and we choose llama3.2 as the model. That same model also generates embeddings, so no separate embedding model is needed. This choice is a bit slower and sometimes less accurate than a specialized embedding model, but for a local proof of concept it is fine.&lt;/p&gt;

&lt;p&gt;Next we read the documents. The application scans the documents folder, reads all markdown files, and generates an embedding for each document. You do not want to repeat this process every time; the larger the set, the longer it takes.&lt;/p&gt;

&lt;p&gt;Finally we start the chat loop with the initialized services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OllamaApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uriString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:11434"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"llama3.2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// llama3.2 supports embeddings, so no separate model is required &lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsTextEmbeddingGenerationService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;documentsPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"documents"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;documentStore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ImportDocumentsFromDirectoryAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documentsPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ChatLoop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ollamaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentStore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Importing documents is straightforward. The application reads all files in the specified folder, generates an embedding per document, and stores both the text and the vector in the in memory document store. During a chat question we can directly retrieve both values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InMemoryDocumentStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ImportDocumentsFromDirectoryAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ITextEmbeddingGenerationService&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;documentStore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InMemoryDocumentStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Directory '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;' not found. No documents loaded."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;documentStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.md"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAllTextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateEmbeddingsAsync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;]))[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;documentStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Loaded &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.md"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; documents."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;documentStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The document store keeps each document together with its embedding in a list of records. During semantic search we convert your question into a vector and measure with &lt;em&gt;cosine similarity&lt;/em&gt; an angle based metric that shows how close two vectors are and how well each document matches. We then keep the two best scores.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InMemoryDocumentStore&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_documents&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetRelevantWithScores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_documents&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;CosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderByDescending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;CosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normA&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normB&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dot&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;normA&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;normB&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dot&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1e-5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxilhmuboklgqsuzwlra3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxilhmuboklgqsuzwlra3.png" alt="Chat Loop" width="636" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything comes together in the ChatLoop, the heart of the application. Here we connect the semantic search results to a live conversation with the language model and make sure the context stays up to date on every turn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ChatLoop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;OllamaApiClient&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ITextEmbeddingGenerationService&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;InMemoryDocumentStore&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The method starts by creating a ChatCompletionService based on the Ollama client. Semantic Kernel provides standard helpers, such as a ChatHistory object, role handling, and token streaming.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsChatCompletionService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Next we initialize a ChatHistory with a system prompt. That prompt defines the model behavior so we do not need to repeat rules in every prompt.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are an expert in comics and sci-fi, you always try to help the user and give random facts about the topics"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// In the do while loop the actual conversation happens:&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// 1. Read the user message.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// 2. Ask Ollama for an embedding. Each question becomes a vector of length 3072.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;queryEmbedding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateEmbeddingsAsync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;]))[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// 3. Use cosine similarity to find the two documents with the highest relevance.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;contextTuples&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRelevantWithScores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;bestSimilarityScore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contextTuples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// 4. Check the highest score. If it is above 0.10 (determine empirically via, for example, a manual evaluation) then we add the corresponding text fragments as context. This keeps the prompt short and relevant.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;addDocsToContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bestSimilarityScore&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addDocsToContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[gate] relevant docs added (bestSim=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bestSimilarityScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;F2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, hits=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;contextTuples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addDocsToContext&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n---\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contextTuples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// 5. Create a temporary copy of the ChatHistory (this works as prompt toxicity isolation and keeps your system prompt clean), add the context (if present) and the user message, and send it to the model.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;promptHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;promptHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Context:\n&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;promptHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 6. The model returns the full response.&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatMessageContentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;promptHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 7. Then we store it in the global ChatHistory and print it to the screen.&lt;/span&gt;
            &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lastMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;[^&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Then the loop starts again and the conversation continues until the user stops the program.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Result: a local RAG conversation
&lt;/h2&gt;

&lt;p&gt;The console session below shows at a glance what happens when you start the demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application reports that fourteen documents were loaded and vectorized. In the /documents directory there happen to be several Star Wars texts. See the GitHub repository for a full overview of the documents and setup.&lt;/li&gt;
&lt;li&gt;After the user question the gate line appears, showing that two documents were added as context and showing the best relevance score (0.17 in this example).&lt;/li&gt;
&lt;li&gt;The language model processes those Star Wars texts and returns an answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For readability the model output is truncated after a few lines, but in a real session the dialog continues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loaded 14 documents.
user: Who is the greatest user of the force?
[gate] relevant docs added (bestSim=0.17, hits=2)
assistant: A question that sparks debate among Star Wars fans! While opinions may vary, I'd argue that Yoda is one of the most powerful users of the Force.

As a wise and ancient Jedi Master, Yoda's mastery of the Force is unparalleled. His unique connection to the Living Force allows him to tap into its energy, using it to augment his physical abilities and....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Optimizations and extensions
&lt;/h2&gt;

&lt;p&gt;This demo is intentionally kept as simple as possible: one console app, one model, and an in memory vector store. If you scale up more seriously you can get results quickly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use a vector database: As the number of documents grows, replace the InMemoryDocumentStore with a vector database like Qdrant (started with Docker in two lines). This gives millisecond search performance, persistent storage, and advanced filters, and prevents recalculating embeddings on every startup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a specialized embedding model: Right now we use llama3.2 for both chat and embeddings. Instead, use a compact model trained specifically for embeddings. That reduces compute time and improves similarity accuracy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick a better language model: If you have extra (V)RAM, consider a stronger model such as Phi-4 or llama3.3 for richer answers. For niche domains a smaller tuned model can sometimes be better and faster. Always measure latency and quality with an objective evaluation framework so you pick the right model.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  7. Conclusion
&lt;/h2&gt;

&lt;p&gt;With the combination of Semantic Kernel, Ollama, and a compact in memory vector store, you have seen how easy it is to implement Retrieval Augmented Generation fully locally. No vendor lock in with a cloud provider, no compliance headaches due to data exfiltration, and with smart hardware choices, a response time that can compete with SaaS alternatives.&lt;/p&gt;

&lt;p&gt;In this article we built it step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Laying the foundation: Semantic Kernel plus the preview connector for Ollama is enough to run a proof of concept within minutes.&lt;/li&gt;
&lt;li&gt;Embeddings and storage: one llama3.2 model can produce both chat responses and embeddings. The simple InMemoryDocumentStore shows the core logic is only a few dozen lines of code.&lt;/li&gt;
&lt;li&gt;Context injection: with a minimal threshold you keep the prompt lean and relevant, which is crucial for smaller models.&lt;/li&gt;
&lt;li&gt;Expand when needed: replace the in memory store with a vector database for faster and persistent storage, use a specialized embedding model, and move to a stronger or tuned LLM when the use case and hardware allow it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a robust RAG chat app that runs on a developer laptop, but also fits just as well in an air gapped datacenter. That gives your .NET team full control over privacy, cost, and performance, while still using the power of generative AI.&lt;/p&gt;

&lt;p&gt;My advice: start small, measure a lot, and scale with intent. First run an internal pilot with a handful of documents, monitor accuracy, and then experiment with larger datasets and models. You will notice the learning curve is steep, but the time to value is even steeper.&lt;/p&gt;

&lt;p&gt;That way you can benefit from GenAI today without compromising on governance or budget. In short: RAG in .NET is not only production ready, it may be the most pragmatic path to safe and scalable AI assistants on your own turf.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub repo (demo): &lt;a href="https://github.com/Frankiey/semantic-kernel-ollama-demo" rel="noopener noreferrer"&gt;https://github.com/Frankiey/semantic-kernel-ollama-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ollama documentation: &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;https://github.com/ollama/ollama&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Semantic Kernel docs: &lt;a href="https://aka.ms/semantic-kernel" rel="noopener noreferrer"&gt;https://aka.ms/semantic-kernel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Qdrant Quickstart: &lt;a href="https://qdrant.tech/documentation/quick-start/" rel="noopener noreferrer"&gt;https://qdrant.tech/documentation/quick-start/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>dotnet</category>
      <category>llm</category>
    </item>
    <item>
      <title>Building an Azure VM Sizer for LLMs — with Codex Doing 90% of the Work</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Sat, 16 Aug 2025 10:25:46 +0000</pubDate>
      <link>https://dev.to/frankiey/building-an-azure-vm-sizer-for-llms-with-codex-doing-90-of-the-work-7kn</link>
      <guid>https://dev.to/frankiey/building-an-azure-vm-sizer-for-llms-with-codex-doing-90-of-the-work-7kn</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Lately I’ve been looking into hosting open‑source models on dedicated Azure virtual machines and thought: how hard can it be to pick the right VM and how much cost can you actually save by choosing a smaller model? Of course, Microsoft’s serverless options are cheaper and much easier to deploy. But I like to know how things work and to manage my own compute, partly out of technical curiosity, partly for compliance and privacy. Running on dedicated VMs gives you more control on both fronts.&lt;/p&gt;

&lt;p&gt;I couldn’t find a clear, practical guide for sizing a VM based on the model you choose, so I built one: a quick single page web app. &lt;strong&gt;Try it here → &lt;a href="https://frankiey.github.io/azure-llm-sizer/?model=Meta-Llama-3-70B&amp;amp;prec=fp16&amp;amp;ctx=8k" rel="noopener noreferrer"&gt;Live Azure LLM Sizer&lt;/a&gt;&lt;/strong&gt; (code: &lt;a href="https://github.com/Frankiey/azure-llm-sizer" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;).&lt;br&gt;
 To move fast (and avoid writing more code than necessary) I used Codex for about 90% of the work. In this post I’ll cover how I built the site with Codex, where it’s useful and where it hits its limits, and I’ll share what I learned along the way, plus what’s next.&lt;/p&gt;
&lt;h2&gt;
  
  
  How i build it with Codex
&lt;/h2&gt;

&lt;p&gt;I started with a simple problem definition, a rough goal for the app, and a few broad requirements. I deliberately kept things open ended to see what frameworks it would pick. I fed the description into ChatGPT with a “deep research” prompt, and out came a functional spec (trimmed here for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Azure-LLM-Sizer — Functional Spec (≤ 512 words)

Purpose
Browser-only SPA that tells users the smallest Azure VM (GPU SKU) capable of serving / fine-tuning a chosen open-source large-language model under given precision, context length, batch size, and optional multi-GPU constraints.

⸻

Core Workflow
    1.  Select inputs
    • Model (type-ahead over HF IDs)
    • Precision (FP32 / FP16 / BF16 / INT8 / INT4)
    • Context length slider (256 – 128 k)
    • Batch size slider (1 – 64)
    • Advanced: “Training mode” toggle adds optimizer-state multiplier; “World-size” sets required GPUs.
    2.  Estimator (runs client-side, ≤ 100 ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It even suggested the tech stack, some non-functional requirements, and the reasoning behind certain choices. I quickly reviewed it, dropped it into Codex, and off it went 🚀. The very first PR included a complete repo setup and a working version of the app. The frontend wasn’t pretty, but it did the job.&lt;/p&gt;

&lt;p&gt;From there, the process was a loop of iterating and adding features. If something didn’t work or I didn’t like it, I’d just describe the change to Codex. It would spin up its own environment, make the changes, and open a PR. My job was to review, test locally, and merge to main.&lt;/p&gt;

&lt;p&gt;My workflow looked like this:&lt;br&gt;
Define problem or feature → Create Codex task → Review PR → Test locally → Approve/merge or request tweaks → Repeat.&lt;/p&gt;

&lt;p&gt;When Codex hit a wall, either struggling with complex issues or producing subpar results, I’d switch tools. ChatGPT and Perplexity handled research heavy questions. For the frontend, I brought in Claude from Anthropic to rework the UI in Tailwind, which massively improved the look. Once Claude produced the HTML, I pasted it back into Codex to integrate with the existing codebase, and it slotted in seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Insights
&lt;/h2&gt;

&lt;p&gt;Where Codex shines&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turns a vague problem into a working repo fast. The first PR had scaffolding, routing, basic state, and CI/CD to GitHub Pages—without me touching YAML.&lt;/li&gt;
&lt;li&gt;Excellent at wiring and repetitive tasks: project setup, glue code, small refactors, and “add this option to the form + estimator.”&lt;/li&gt;
&lt;li&gt;PR-first flow works well: I describe the change, it opens a PR, I review/test, and merge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where it struggled&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual design: getting a polished UI took multiple passes. I handed the layout to Claude (Tailwind) and then had Codex integrate it.&lt;/li&gt;
&lt;li&gt;Cross cutting changes: when work spanned multiple files or data flow edges, it sometimes lost the thread and anchored on the existing approach.&lt;/li&gt;
&lt;li&gt;Code critique &amp;amp; alternatives: it tended to justify the current implementation instead of proposing new designs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What worked&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep tasks tight and atomic with explicit acceptance criteria.&lt;/li&gt;
&lt;li&gt;Minimize context: link to 1–2 files and quote exact function names or components.&lt;/li&gt;
&lt;li&gt;Use specialists: ChatGPT/Perplexity for research, Claude for UI drafts, Codex for integration and wiring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat codegen like a sharp junior engineer on rails: great at well-specified tasks; less great at architecture and product taste.&lt;/li&gt;
&lt;li&gt;Always keep PR review and a local run in the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results &amp;amp; Impact
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg203sscatvwdzv6rropz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg203sscatvwdzv6rropz.png" alt="Resulting live website" width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest wins was how quickly I could go from a blank page to a fully working, deployed application. With Codex handling most of the scaffolding and setup, the initial project came together in hours, not days. The automatic build and deployment pipeline to GitHub Pages was in place right after the first PR merge. Again, all generated by Codex without me touching a single YAML file.&lt;/p&gt;

&lt;p&gt;Iteration speed was another game changer. Adding new features or fixing bugs became a matter of describing the change, letting Codex implement it, reviewing the PR, and pushing it live. User feedback could be integrated almost instantly. If someone reported that something looked off or didn’t work as expected, I could have a fix deployed within minutes. The only caveat was the UI: while Codex could wire up the functionality quickly, the styling usually needed a few rounds of refinement (or a complete handoff to Claude) to make it fit the look and feel I wanted.&lt;/p&gt;

&lt;p&gt;Overall, the development loop felt incredibly fast and satisfying, giving me the ability to turn ideas into live, functional features almost as quickly as I could think of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This is just the first iteration of a wild idea. I want to expand it so I can use it for any open source model on any hardware. For example, I still need to add the latest gpt-oss models from OpenAI and many others. For frontend and functional changes, I will try to do as much as possible with Codex or other AI systems, while the data mining and sourcing will mostly be done by hand or with other approaches, since Codex does not excel in this part. Also, the way the calculation is done can be improved, as it makes some assumptions and is only relevant for loading the whole model with a certain KV cache. While it gives a decent estimate, it does not represent real world scenarios: you might have a multi cluster setup or want to know how fast a system is and how many requests it can handle. The website currently focuses solely on inference and is limited to Azure. Expanding into training capabilities and support for other cloud platforms would be a natural next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Codex really surprised me. I started with a broad, almost vague prompt, and it spun up a complete repository with everything I needed to get going. Adding new features was just as smooth, sometimes I’d have five changes in progress at once, each handled in parallel. As a full stack engineer, I’ve never been fond of wrangling CSS or Tailwind, so being able to delegate the design side and focus on the technical logic felt like a huge productivity win.&lt;/p&gt;

&lt;p&gt;That said, Codex isn’t perfect. It can be stubborn, especially when dealing with bigger patterns or more abstract feedback. Frontend polish often took a few extra passes, and longer conversations sometimes left it stuck in a loop. The way I’ve come to see it: Codex is like a sharp junior engineer. It’s fast, confident, and great with well-defined tasks, but it struggles when the problem gets fuzzy or spans multiple layers.&lt;/p&gt;

&lt;p&gt;Even with those limitations, the experience of working this way was exciting. It shifted my role from typing out every line to directing, reviewing, and refining. That’s a glimpse of where software development is headed: AI-assisted tools embedded in our everyday workflows, helping us move faster while we focus on the higher level decisions. I’m curious to see how quickly these tools mature and how they’ll change the way we build software in the enterprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://frankiey.github.io/azure-llm-sizer/?model=Meta-Llama-3-70B&amp;amp;prec=fp16&amp;amp;ctx=8k" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Frankiey/azure-llm-sizer" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>ai</category>
      <category>llm</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Codex CLI – Adding Azure OpenAI to Codex by using… Codex!</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Fri, 18 Apr 2025 18:24:42 +0000</pubDate>
      <link>https://dev.to/frankiey/codex-cli-adding-azure-openai-to-codex-by-using-codex-201l</link>
      <guid>https://dev.to/frankiey/codex-cli-adding-azure-openai-to-codex-by-using-codex-201l</guid>
      <description>&lt;p&gt;You might have missed it amid the launch of the new models &lt;strong&gt;o3&lt;/strong&gt; and &lt;strong&gt;o4‑mini&lt;/strong&gt;, but OpenAI also &lt;a href="https://openai.com/index/introducing-o3-and-o4-mini/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; &lt;strong&gt;Codex&lt;/strong&gt;, a lightweight, open‑source coding agent that runs entirely in your terminal.  &lt;/p&gt;

&lt;p&gt;Because Codex is open source, it immediately caught my attention. Three questions sprang to mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How well does it work with the latest frontier models?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How does it work under the hood—how complex is an agent like this?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How easy is it to adapt the code and make it enterprise‑ready by connecting it to an Azure OpenAI endpoint?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ll tackle each of these in the sections below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly &lt;em&gt;is&lt;/em&gt; Codex?
&lt;/h2&gt;

&lt;p&gt;Originally “Codex” referred to an early OpenAI code‑generation model, but the name has now been repurposed for an &lt;strong&gt;open‑source CLI companion for software engineers&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/openai/codex" rel="noopener noreferrer"&gt;https://github.com/openai/codex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In practice, Codex is a coding assistant you run from your terminal. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask questions about your code base
&lt;/li&gt;
&lt;li&gt;Request suggestions or refactors
&lt;/li&gt;
&lt;li&gt;Let it automatically edit, test, and even run parts of your project—safely isolated inside a sandbox to prevent unwanted commands or outbound connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve used tools like &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;Claude Code&lt;/strong&gt;, or &lt;strong&gt;GitHub Copilot&lt;/strong&gt;, you’ll feel right at home. What sets Codex apart is its fully open‑source nature and its simple CLI interface, which makes it easy to integrate into existing workflows.&lt;/p&gt;
&lt;h2&gt;
  
  
  Extending Codex with… Codex!
&lt;/h2&gt;

&lt;p&gt;During the launch stream the presenters hinted that you could modify Codex &lt;strong&gt;using Codex itself&lt;/strong&gt;. Challenge accepted! My goal: &lt;strong&gt;wire Codex to an enterprise‑ready Azure OpenAI deployment—using only Codex.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the repo and install the latest release.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Export your OpenAI API key as &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Run Codex in the directory you want to target.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By default, the CLI selects the &lt;code&gt;gpt‑4o&lt;/code&gt; model, which is brilliant for many tasks but can struggle with very large code bases. I switched to &lt;strong&gt;o4‑mini&lt;/strong&gt; immediately. (In the more recent versions it is defaulted to o4-mini) Add this to &lt;code&gt;~/.codex/config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;o4-mini&lt;/span&gt;   &lt;span class="c1"&gt;# default model&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;—or pass it ad‑hoc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;-m&lt;/span&gt; o4-mini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Finding the hook
&lt;/h3&gt;

&lt;p&gt;After a few exploratory questions I located the heart of the system: &lt;strong&gt;&lt;code&gt;agent-loop.ts&lt;/code&gt;&lt;/strong&gt;. That’s the place to add Azure OpenAI support. But manual edits were slow, so I relaunched Codex in &lt;em&gt;full‑auto&lt;/em&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;-m&lt;/span&gt; o4-mini &lt;span class="nt"&gt;--approval-mode&lt;/span&gt; full-auto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I asked it to update the code so that, when a specific environment variable is present, it calls my Azure OpenAI endpoint instead of &lt;code&gt;api.openai.com&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watching it work
&lt;/h3&gt;

&lt;p&gt;Codex began reading files, drafting patches, and applying them. Because I was still using my regular OpenAI account, the agent occasionally hit rate limits on &lt;strong&gt;o4‑mini&lt;/strong&gt; and crashed, losing context. Annoying, but expected in an early release; simply re‑running the last instruction resumes after it scans the project again.&lt;/p&gt;

&lt;p&gt;I was impressed by how economically it sliced the repo into small context windows. One reason I &lt;strong&gt;only&lt;/strong&gt; burned ~4.5 M tokens during the experiment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylihzhehee1kq975ylpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylihzhehee1kq975ylpz.png" alt="Codex progress during the run" width="746" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The results
&lt;/h3&gt;

&lt;p&gt;The generated code was surprisingly solid—minor syntax errors cropped up once or twice, usually after an interrupted run, but nothing serious. My only real blunder was pointing Codex to the wrong Azure endpoint (&lt;code&gt;/chat/completions&lt;/code&gt; instead of &lt;code&gt;/responses&lt;/code&gt;), but Codex even helped debug that.&lt;/p&gt;

&lt;p&gt;You can find the patched fork here on Github: &lt;a href="https://github.com/Frankiey/codex/tree/feature/add_openai_service" rel="noopener noreferrer"&gt;https://github.com/Frankiey/codex/tree/feature/add_openai_service&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Codex isn’t just another code‑completion toy; it’s a hackable, scriptable engineering agent small enough to grasp in an afternoon and powerful enough to refactor itself. With a few tweaks (and a lot of tokens) I had it talking to Azure OpenAI, proving that an enterprise‑ready workflow is within easy reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Here’s the personal checklist I’m working through to take Codex from “cool prototype” to production‑ready helper:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wire up Azure Entra ID&lt;/strong&gt; so Codex authenticates through SSO without API keys scattered around.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add robust retries and back‑off&lt;/strong&gt; - rate limits should become log lines, not crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop Codex into the CI pipeline&lt;/strong&gt; so every pull request gets an automated review (and, where safe, an auto‑patch).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spin up companion agents&lt;/strong&gt; for docs, security, and test generation, all sharing the same project context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate releases&lt;/strong&gt; once the workflow feels stable, keeping a final human approval gate in place.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ll report back on each milestone and any surprises in the next post!&lt;/p&gt;

</description>
      <category>openai</category>
      <category>azure</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Run LLMs Locally with Ollama &amp; Semantic Kernel in .NET: A Quick Start</title>
      <dc:creator>Frank Noorloos</dc:creator>
      <pubDate>Tue, 24 Dec 2024 15:09:55 +0000</pubDate>
      <link>https://dev.to/frankiey/run-llms-locally-with-ollama-semantic-kernel-in-net-a-quick-start-4go4</link>
      <guid>https://dev.to/frankiey/run-llms-locally-with-ollama-semantic-kernel-in-net-a-quick-start-4go4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As AI becomes increasingly central to modern applications, developers in the .NET ecosystem are exploring ways to incorporate powerful language models with minimal friction. &lt;strong&gt;Ollama&lt;/strong&gt; and &lt;strong&gt;Semantic Kernel&lt;/strong&gt; provide a compelling approach for running &lt;strong&gt;generative AI applications locally&lt;/strong&gt;, giving teams the flexibility to keep data on-premises, reduce network latency, and avoid recurring cloud costs.&lt;/p&gt;

&lt;p&gt;In this post, you’ll learn how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up Ollama on your machine.
&lt;/li&gt;
&lt;li&gt;Pull and serve a local model (like &lt;code&gt;llama3.2&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Integrate it with Semantic Kernel in a .NET 9 project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the end, you’ll have a simple yet powerful local AI application — no cloud dependency required.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Ollama?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ollama&lt;/strong&gt; is a self-hosted platform for running language models locally, eliminating the need for external cloud services. Key benefits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data Privacy&lt;/strong&gt;: Your data never leaves your environment.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower Costs&lt;/strong&gt;: Eliminate the “pay by API call” model of external services.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Setup&lt;/strong&gt;: A quick and straightforward way to get up and running with local AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Ollama, you pull the models you need (for example, &lt;code&gt;llama3.2&lt;/code&gt;), serve them locally, and integrate them just like you would with a remote API—except it all stays on your machine or server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing Semantic Kernel
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Semantic Kernel&lt;/strong&gt; is an open-source SDK from Microsoft that enables developers to seamlessly integrate AI capabilities into .NET applications. It allows you to combine AI models with APIs and existing application logic to build intelligent and context-aware solutions.&lt;/p&gt;

&lt;p&gt;Key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function Orchestration: Efficiently manage and compose multiple AI functions, such as generating text, summarization, and Q&amp;amp;A, for your applications.&lt;/li&gt;
&lt;li&gt;Extensibility: Support for a wide range of AI model providers, including OpenAI, Azure OpenAI, and local deployment options like Ollama.&lt;/li&gt;
&lt;li&gt;Context Management: Retain and utilize contextual information, such as conversation history and user preferences, to create personalized and coherent AI-driven experiences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By pairing Semantic Kernel with Ollama, you can explore powerful AI interactions entirely on your own hardware. This ensures you maintain full control over your data and resources, and eliminates the ongoing costs tied to cloud-based APIs. It’s a great way to experiment, prototype, or run offline—without sacrificing the advanced capabilities of AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;.NET 9 SDK&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Make sure you have the .NET 9 SDK installed on your system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ollama&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install or build Ollama from the &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;Ollama GitHub repository&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;Start Ollama locally:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Pull the desired model, for example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ollama pull llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step-by-Step Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create a New .NET 9 Project
&lt;/h3&gt;

&lt;p&gt;Open your terminal or command prompt and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; OllamaSemanticKernelDemo
&lt;span class="nb"&gt;cd &lt;/span&gt;OllamaSemanticKernelDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add the Required NuGet Packages
&lt;/h3&gt;

&lt;p&gt;Inside your project directory, add the Semantic Kernel package and the Ollama connector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Microsoft.SemanticKernel --version 1.32.0
dotnet add package Microsoft.SemanticKernel.Connectors.Ollama --version 1.32.0-alpha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Ensure Ollama is Running and Pull Your Model
&lt;/h3&gt;

&lt;p&gt;In another terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then pull the model you want to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama pull llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ollama will listen on &lt;a href="http://localhost:11434" rel="noopener noreferrer"&gt;http://localhost:11434&lt;/a&gt; by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Code
&lt;/h3&gt;

&lt;p&gt;In your Program.cs (or any .cs file you designate as the entry point), use the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics.CodeAnalysis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.SemanticKernel.ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.SemanticKernel.Connectors.Ollama&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OllamaSemanticKernelDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Experimental&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SKEXP0070"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Initialize the Ollama client with the local endpoint and model&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OllamaClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:11434"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"llama3.2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Create a chat service from the Ollama client&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollamaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsChatCompletionService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Define the AI's role/behavior via a system message&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are an expert about comic books"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. User initiates the conversation&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi, I'm looking for comic suggestions"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;OutputLastMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 5. Get the AI's reply&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatMessageContentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;OutputLastMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 6. User follows up with more info&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I love sci-fi, I'd like to learn something new about the galatic empire, any suggestion"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;OutputLastMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 7. AI responds with more tailored suggestions&lt;/span&gt;
        &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatMessageContentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;OutputLastMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OutputLastMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatHistory&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lastMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user: Hi, I'm looking for comic suggestions

assistant: I'd be happy to help you find some great comics.

Before we get started, can you please tell me a bit more about what you're in the mood for? Here are a few questions to help me narrow down some recommendations:

1. What genre are you interested in? (e.g., superhero, fantasy, horror, romance, etc.)
2. Are there any specific characters or franchises that you enjoy?
3. Do you prefer classic comics from the past (e.g., 50s-90s), or more modern releases?
4. Is there a particular tone you're looking for? (e.g., light-hearted, dark and gritty, adventurous, etc.)
5. Are you looking for something new to read, or are you open to exploring older comics?

Let me know your answers to these questions, and I'll do my best to suggest some fantastic comics that fit your tastes!

user: I love sci-fi, I'd like to learn something new about the galatic empire, any suggestion

assistant: Science fiction is an amazing genre.

If you're interested in learning more about the Galactic Empire from a comic book perspective, here are some suggestions:

**Must-read comics:**

1. **Darth Vader** (Marvel Comics, 2015-2019) - A solo series that explores Darth Vader's backstory and his fall to the dark side.
2. **Star Wars: Tarkin** (Dynamite Entertainment, 2016) - A graphic novel that delves into Grand Moff Tarkin's personality and motivations.
3. **Star Wars: Lando** (Marvel Comics, 2018-2020) - A series that explores the life of Lando Calrissian, a key character in the Galactic Empire.

**Recommended comics for Imperial insights:**

1. **Star Wars: Rebel Run** (Dark Horse Comics, 2009) - A miniseries that shows the inner workings of the Empire's security forces and their efforts to track down Rebel Alliance members.
2. **Star Wars: The Old Republic - Revan** (Dark Horse Comics, 2014-2015) - A limited series based on the popular video game, which explores the complexities of the Mandalorian Wars and the Imperial forces involved.

**Classic comics with Imperial connections:**

1. **Tales of the Jedi** (Dark Horse Comics, 1993-1996) - A comic book series that explores various events in the Star Wars universe, including some involving the Galactic Empire.
2. **Star Wars: X-Wing** (Dark Horse Comics, 1998-2000) - A comic book series based on the popular video game, which focuses on a group of Rebel Alliance pilots fighting against Imperial forces.

**Recent comics with Imperial themes:**

1. **Star Wars: The High Republic** (Marvel Comics, 2020-present) - An ongoing series that explores a new era in the Star Wars universe, including events and characters connected to the Galactic Empire.
2. **Star Wars: Resistance** (IDW Publishing, 2018-2020) - A comic book series set during the First Order's rise to power, which includes connections to the original trilogy.

These comics offer a mix of Imperial perspectives, character studies, and historical context that can help deepen your understanding of the Galactic Empire. May the Force be with you!


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ollama is listening on localhost:11434. All requests go to your local server.&lt;/li&gt;
&lt;li&gt;By specifying "llama3.2", you tell Ollama which model to use for inference.&lt;/li&gt;
&lt;li&gt;The ChatHistory class from Semantic Kernel logs each turn in the conversation; the AsChatCompletionService() sends those messages to Ollama, which generates the next reply.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Running the Application
&lt;/h3&gt;

&lt;p&gt;To run your newly created console application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interact with your console and observe how the local AI model responds to your prompts—no cloud calls required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Resource Usage: Larger models demand more CPU/RAM resources. Llama3.2 is very small and only requires 2.0GB.&lt;/li&gt;
&lt;li&gt;Latency: Local inference is often faster than cloud-based solutions, but ensure your hardware can handle the load.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Prototyping: Experiment quickly without incurring cloud costs or dealing with rate limits.&lt;/li&gt;
&lt;li&gt; Internal Knowledge Bases: Keep information in-house for compliance and data privacy.&lt;/li&gt;
&lt;li&gt; Edge or Offline Applications: Perfect for scenarios with limited internet access or strict data governance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By running AI models locally with Ollama and coordinating them through Semantic Kernel, you can build private, cost-effective, and high-performance .NET applications. After installing Ollama, serving your model, and adding the Semantic Kernel + Ollama NuGet packages, you can host generative AI experiences entirely on your own infrastructure. This setup is especially well-suited for local development, letting you experiment and prototype without relying on cloud services or external APIs. In doing so, you maintain complete control over your environment while reducing both complexity and recurring costs.&lt;/p&gt;

&lt;p&gt;As local AI continues to evolve, the combination of Ollama and Semantic Kernel lays the groundwork for building robust, self-contained solutions that scale from simple prototypes to production-ready applications—without the hidden costs of cloud dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Experiment with different models in Ollama to find the best fit for your application.
&lt;/li&gt;
&lt;li&gt;Tweak your system and user prompts to see how they change the model’s responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview of What’s Next:&lt;/strong&gt; In an upcoming post, we’ll dive into &lt;em&gt;Retrieval Augmented Generation (RAG)&lt;/em&gt; to give your LLM context-aware responses sourced from your own data—still running entirely on local infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 🎄&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt;: &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;Ollama GitHub Repository&lt;/a&gt; – Install instructions, docs, and source code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Kernel&lt;/strong&gt;: &lt;a href="https://github.com/microsoft/semantic-kernel" rel="noopener noreferrer"&gt;Semantic Kernel GitHub Repository&lt;/a&gt; – Documentation, samples, and community contributions&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>ai</category>
      <category>semantickernel</category>
      <category>ollama</category>
    </item>
  </channel>
</rss>
