<?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: Mateusz Kowalewski</title>
    <description>The latest articles on DEV Community by Mateusz Kowalewski (@mateuszpoland).</description>
    <link>https://dev.to/mateuszpoland</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%2F1039079%2Fb09411b1-c6f9-4d92-8772-1ef64492ac17.png</url>
      <title>DEV Community: Mateusz Kowalewski</title>
      <link>https://dev.to/mateuszpoland</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mateuszpoland"/>
    <language>en</language>
    <item>
      <title>Harnessing OpenAI Assistant 2.0 for Named Entity Recognition in PHP/Symfony 7</title>
      <dc:creator>Mateusz Kowalewski</dc:creator>
      <pubDate>Thu, 19 Dec 2024 22:51:57 +0000</pubDate>
      <link>https://dev.to/mateuszpoland/harnessing-openai-assistant-20-for-named-entity-recognition-in-phpsymfony-7-boe</link>
      <guid>https://dev.to/mateuszpoland/harnessing-openai-assistant-20-for-named-entity-recognition-in-phpsymfony-7-boe</guid>
      <description>&lt;p&gt;Integrating large language models into real-world product backends is the latest battleground for innovation. But as with all tech trends, the real winners aren’t those who rush to know it all. Instead, it's about pausing, reflecting, and making smart decisions.&lt;/p&gt;

&lt;p&gt;Even though AI is more accessible than ever, thinking it's a trivial task is a big mistake. The field is still in its infancy, and almost everyone in tech and business is trying to figure out how to make sense of it. The internet is overflowing with both reliable information and misleading hype.&lt;/p&gt;

&lt;p&gt;Believe it or not, you don’t need to jump on every piece of AI gossip you hear. Take a step back and approach it thoughtfully.&lt;/p&gt;

&lt;p&gt;If you’re a PHP developer or work with a PHP codebase, AI might feel like an alien concept. Classes, interfaces, message queues, and frameworks seem worlds apart from NLP, fine-tuning, stochastic gradient descents, LoRA, RAG, and all that jargon. I get it. To systematically learn these concepts, like anything in software engineering, we need time and good practice.&lt;/p&gt;

&lt;p&gt;But isn’t AI, machine learning, and data science the domain of Python or R programmers? You’re partially right. Most foundational machine learning is done in Python. And honestly, Python is a joy to learn—it’s versatile and can be applied to countless interesting projects. From building and training a language model to deploying it via an API, Python has you covered.&lt;/p&gt;

&lt;p&gt;But let’s get back to PHP.&lt;/p&gt;

&lt;p&gt;Don’t worry—you’re not about to become obsolete as a regular software engineer. Quite the opposite. As we’re increasingly expected to understand related fields like containerization, CI/CD, systems engineering, and cloud infrastructure, AI is quickly becoming another essential skill in our toolkit. There’s no better time than now to start learning.&lt;/p&gt;

&lt;p&gt;That said, I don’t recommend diving headfirst into building neural networks from scratch (unless you really want to). It’s easy to get overwhelmed. Instead, let me show you a practical starting point for your AI experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can You Expect?
&lt;/h2&gt;

&lt;p&gt;Here’s what we’ll cover in this guide:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asynchronous AI Workflows&lt;/strong&gt;&lt;br&gt;
I’ll show you how to implement an AI workflow using a message queue—whether it’s RabbitMQ, Amazon SQS, or your preferred broker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production-Ready Solution&lt;/strong&gt;&lt;br&gt;
You’ll see a real example of the solution deployed in a production system that solves basic business requirement with AI&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symfony Integration&lt;/strong&gt;&lt;br&gt;
The solution is fully implemented within the Symfony PHP framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI Tools&lt;/strong&gt;&lt;br&gt;
We’ll use the OpenAI PHP library and the latest OpenAI Assistants 2.0.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Topics Will We Cover?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dataset Structure&lt;/strong&gt;&lt;br&gt;
How to build a dataset for training and evaluating your AI model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fine-Tuning OpenAI Models&lt;/strong&gt;&lt;br&gt;
Learn to prepare a proper &lt;code&gt;.jsonl&lt;/code&gt; file and fine-tune the &lt;code&gt;GPT-4o-mini&lt;/code&gt; model or another one from GPT family for your specific business use case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating and Testing an OpenAI Assistant 2.0&lt;/strong&gt;&lt;br&gt;
Understand how to set up an OpenAI Assistant and test it in the OpenAI Playground.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Knowledge Bases&lt;/strong&gt;&lt;br&gt;
Dive into the concept of knowledge bases: why GPT doesn’t know everything and how to supply it with the right context to significantly boost accuracy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PHP&amp;amp;Symfony Integration&lt;/strong&gt;&lt;br&gt;
Learn how to seamlessly connect your AI agent with your Symfony application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interested? Let's roll.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites for OpenAI + PHP Projects
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI Account&lt;/strong&gt;
You’ll need an account on openai.com.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional: Weights &amp;amp; Biases Account&lt;/strong&gt;
Setting up an account on wandb.ai is optional but highly recommended. It’s an excellent tool for tracking AI experiments and visualizing results.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Defining the Problem
&lt;/h2&gt;

&lt;p&gt;Let’s dive into the problem we’re solving.&lt;br&gt;
At its core, we’re dealing with a block of text that represents something—an address, in our case. The goal is to classify its components into predefined groups.&lt;/p&gt;

&lt;p&gt;So, when a user sends us an address, we aim to return a JSON structure that segments the address into its parts. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "street": "Strada Grui",
  "block": "Bloc 7",
  "staircase": "Scara A",
  "apartment": "Apartament 6",
  "city": "Zărnești"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Does This Matter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, these addresses are typed by people—our customers—and they’re often inconsistent. Here’s why we need structured, parsed data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Formatting&lt;/strong&gt;
Ensures addresses follow a standard format for seamless processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address Validation (Optional)&lt;/strong&gt;
Allows us to verify if the address is valid or exists.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve this, we dissect the address into predefined groups like "street," "house," "postcode," etc., and then reassemble it into the desired sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Not Use Regular Expressions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, it sounds straightforward. If we enforce formatting for new customers or know they typically write addresses in a specific way, regular expressions might seem like a reasonable solution.&lt;/p&gt;

&lt;p&gt;However, let’s consider examples of Romanian addresses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRADA GRUI, BLOC 7, SCARA A, APARTAMENT 6, ZĂRNEŞTI

BD. 21 DECEMBRIE 1989 nr. 93 ap. 50, CLUJ, 400124
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Romanian addresses are often complex, written in no particular order, and frequently omit elements like the postcode. Even the most sophisticated regular expressions struggle to handle such variability reliably.&lt;/p&gt;

&lt;p&gt;This is where AI models like &lt;code&gt;GPT-3.5 Turbo&lt;/code&gt; or &lt;code&gt;GPT-4o-mini&lt;/code&gt; shine—they can manage inconsistencies and complexities far beyond what static rules like regex can handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Smarter AI Workflow
&lt;/h2&gt;

&lt;p&gt;Yes, we’re developing an AI workflow that significantly improves on the traditional approach.&lt;/p&gt;

&lt;p&gt;Every machine learning project boils down to two essentials: data and model. And while models are important, data is far more critical.&lt;/p&gt;

&lt;p&gt;Models come pre-packaged, tested, and can be swapped out to see which one performs better. But the true game-changer is the quality of the data we provide to the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Role of Data in Machine Learning&lt;/strong&gt;&lt;br&gt;
Typically, we split our dataset into two or three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training Data:&lt;/strong&gt; Used to teach the model what it should do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Data:&lt;/strong&gt; Used to evaluate how well the model performs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this project, we want to split an address into its components—making this a classification task. To get good results with a large language model (LLM) like GPT, we need at least 100 examples in our training dataset.&lt;/p&gt;
&lt;h2&gt;
  
  
  Structuring the Dataset
&lt;/h2&gt;

&lt;p&gt;The raw format of our dataset doesn’t matter much, but I’ve chosen a structure that’s intuitive and easy 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;INPUT: &amp;lt;what goes into the LLM&amp;gt;
OUTPUT: &amp;lt;what the LLM should produce&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input: STRADA EREMIA GRIGORESCU, NR.11 BL.45B,SC.B NR 11/38, 107065 PLOIESTI
output: {{"street": "Eremia Grigorescu", "house_number": "11", "flat_number": "38", "block": "45B", "staircase": "45B", floor: "", "apartment": "", "landmark": "", "postcode": "107065", "county": "Prahova", 'commune': '', 'village': '', "city" "Ploiesti"}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the goal is to produce a structured &lt;code&gt;JSON&lt;/code&gt; response based on the input address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your OpenAI API Account
&lt;/h2&gt;

&lt;p&gt;First, you’ll need an OpenAI API account. It’s a straightforward process, and I recommend adding some initial funds—$10 or $20 is plenty to get started. OpenAI uses a convenient pre-paid subscription model, so you’ll have full control over your spending.&lt;/p&gt;

&lt;p&gt;You can manage your account billing here:&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%2Fjyrw72fvcghrf7j1xkn3.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%2Fjyrw72fvcghrf7j1xkn3.png" alt="Image description" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploring the OpenAI Playground&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once your account is set up, head over to the OpenAI Playground.&lt;/p&gt;

&lt;p&gt;Take a moment to familiarize yourself with the interface. When you’re ready, look for the "Assistants" section in the menu on the left.&lt;/p&gt;

&lt;p&gt;This is where we’ll create our custom GPT instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizing Your GPT Assistant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ll tailor our GPT assistant to meet our specific needs in two stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detailed System Instructions with Examples&lt;/strong&gt;&lt;br&gt;
This step helps us quickly test and validate whether the solution works. It’s the fastest way to see results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fine-Tuning with Knowledge Base (simple RAG)&lt;/strong&gt;&lt;br&gt;
Once we’re satisfied with the initial results, we’ll fine-tune the model. This process reduces the need to provide extensive examples in every prompt, which, in turn, decreases inference time (how long it takes for the model to respond via the API).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both steps combined give us the most accurate and efficient results.&lt;/p&gt;

&lt;p&gt;So, let's begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing a System Prompt for Named Entity Recognition
&lt;/h2&gt;

&lt;p&gt;For our Named Entity Recognition system, we need the model to output structured JSON responses consistently. Crafting a thoughtful system prompt is critical to achieving this.&lt;/p&gt;

&lt;p&gt;Here’s what we’ll focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structuring the initial "dirty" system prompt to produce clear results.&lt;/li&gt;
&lt;li&gt;Refining the prompt later to improve efficiency and minimize costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting with the Few-Shot Technique
&lt;/h2&gt;

&lt;p&gt;The "few-shot" technique is a powerful approach for this task. It works by providing the model with a few examples of the desired input-output relationship. From these examples, the model can generalize and handle inputs it hasn’t seen before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Elements of a Few-Shot Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The prompt consists of several parts, which should be thoughtfully structured for best results. While the exact order can vary, the following sections are essential to include:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Clear Objective
&lt;/h2&gt;

&lt;p&gt;The first part of the prompt provides a general overview of what we expect the model to accomplish.&lt;/p&gt;

&lt;p&gt;For example, in this use case, the input is a Romanian address, which may include spelling mistakes and incorrect formatting. This context is essential as it sets the stage for the model, explaining the kind of data it will process.&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%2Fx2d9y1nltisrzbf79ag8.jpg" 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%2Fx2d9y1nltisrzbf79ag8.jpg" alt="Image description" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Formatting instruction
&lt;/h2&gt;

&lt;p&gt;After defining the task, we guide the AI on how to format its output.&lt;/p&gt;

&lt;p&gt;Here, the JSON structure is explained in detail, including how the model should derive each key-value pair from the input. Examples are included to clarify expectations. Additionally, any special characters are properly escaped to ensure valid JSON.&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%2F5vra0z0kaixjmcbq740x.jpg" 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%2F5vra0z0kaixjmcbq740x.jpg" alt="Image description" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Examples
&lt;/h2&gt;

&lt;p&gt;Examples are the backbone of the "few-shot" technique. The more relevant examples we provide, the better the model’s performance.&lt;/p&gt;

&lt;p&gt;Thanks to GPT’s extensive context window (up to 16K tokens), we can include a large number of examples.&lt;/p&gt;

&lt;p&gt;To build the example set:&lt;/p&gt;

&lt;p&gt;Start with a basic prompt and evaluate the model’s output manually.&lt;br&gt;
If the output contains errors, correct them and add the fixed version to the example set.&lt;br&gt;
This iterative process improves the assistant’s performance over time.&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%2F1akmr5g7fvqe8p5r02p5.jpg" 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%2F1akmr5g7fvqe8p5r02p5.jpg" alt="Image description" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Corrective information
&lt;/h2&gt;

&lt;p&gt;It’s inevitable—your assistant will make mistakes. Sometimes, it might repeatedly make the same error.&lt;/p&gt;

&lt;p&gt;To address this, include clear and polite corrective information in your prompt. Clearly explain:&lt;/p&gt;

&lt;p&gt;What mistakes the assistant is making.&lt;br&gt;
What you expect from the output instead.&lt;br&gt;
This feedback helps the model adjust its behavior and generate better results.&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%2Fb92jdy7wqbyberwfun6i.jpg" 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%2Fb92jdy7wqbyberwfun6i.jpg" alt="Image description" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring and Testing Your Assistant
&lt;/h2&gt;

&lt;p&gt;Now that we’ve crafted the initial prompt for our proof-of-concept few-shot solution, it’s time to configure and test the Assistant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Head over to the &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;OpenAI dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;From the menu on the left, select "Assistants"&lt;/li&gt;
&lt;li&gt;You’ll be redirected to the Assistant creation page.
Go back to &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;OpenAI dashboard&lt;/a&gt; and from menu on the left, choose "Assistants".
You will be redirected to the Assistant creator. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configure Your Assistant&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give Your Assistant a Name: Choose a descriptive name for your Assistant that reflects its purpose (e.g., "Address Parser").&lt;/li&gt;
&lt;li&gt;Paste the System Instructions: Copy your entire crafted prompt and paste it into the System Instructions input box. This defines your Assistant’s behavior and guides its outputs.&lt;/li&gt;
&lt;li&gt;Save Your Assistant: Once you’ve pasted the prompt, click Save to store the configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Choose the Right Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this project, I chose GPT-4o-mini because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s cheaper than GPT-3.5 Turbo&lt;/li&gt;
&lt;li&gt;It's more accurate :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, you should always select your model based on a balance between accuracy and cost. Run or search for benchmarks to determine what works best for your specific task.&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%2Fn5kw0infr9t0bwdkefg7.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%2Fn5kw0infr9t0bwdkefg7.png" alt="Assistant v2 System Instruction" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Defining the Output Schema
&lt;/h2&gt;

&lt;p&gt;With the initial configuration in place, we can now specify the output schema directly in the Assistant creator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Using the "Generate" Option&lt;/strong&gt;&lt;br&gt;
Instead of creating the schema manually, I used the "Generate" option provided by the Assistant creator. Here’s how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take the JSON output structure from your prompt (from one of the examples)&lt;/li&gt;
&lt;li&gt;Paste it into the "Generate" field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool does a great job of automatically generating a schema that matches your structure.&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%2Fb5ia84nfk6koyidxlz82.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%2Fb5ia84nfk6koyidxlz82.png" alt="Output schema" width="800" height="846"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
To ensure consistent and predictable responses, set the temperature as low as possible.&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%2Frwmenjpgetd9wny1t3ud.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%2Frwmenjpgetd9wny1t3ud.png" alt="Image description" width="800" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Temperature?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Temperature controls the randomness of the model’s responses. A low temperature value means the model produces more predictable and deterministic outputs.&lt;/p&gt;

&lt;p&gt;For our use case, this is exactly what we want. When given the same address as input, the model should always return the same, correct response. Consistency is key for reliable results.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the Assistant
&lt;/h2&gt;

&lt;p&gt;With all the parameters in place, head to the Playground to test your solution.&lt;/p&gt;

&lt;p&gt;The Playground provides a console where you can put your Assistant to work. This is where the fun begins—you can rigorously test your bare-bones solution to uncover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where the model is hallucinating (generating irrelevant or incorrect information).&lt;/li&gt;
&lt;li&gt;Potential shortcomings in your prompt or schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These findings will help you refine the &lt;strong&gt;Corrective Information&lt;/strong&gt; section of your prompt, making your Assistant more robust.&lt;/p&gt;

&lt;p&gt;Below is an example of a result from one of my manual tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INPUT: BD. 21 DECEMBRIE 1989 nr. 93 ap. 50, CLUJ, 400124
OUTPUT:
{
  "street": "Bdul 21 Decembrie 1989",
  "house": "93",
  "flat": "50",
  "block": "",
  "staircase": "",
  "floor": "",
  "apartment": "",
  "landmark": "",
  "intercom": "",
  "postcode": "400124",
  "county": "Cluj",
  "commune": "",
  "village": "",
  "city": "Cluj"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Manual Testing Matters ?&lt;/strong&gt;&lt;br&gt;
Old-school, hands-on testing is the foundation of building a reliable solution. By evaluating the model’s outputs manually, you’ll quickly spot issues and understand how to fix them. While automation will come later, manual testing is an invaluable step in creating a solid proof-of-concept.&lt;/p&gt;
&lt;h2&gt;
  
  
  Plugging the Assistant into Your PHP Symfony Application
&lt;/h2&gt;

&lt;p&gt;Now it’s time to integrate everything into your PHP Symfony application. The setup is straightforward and follows a classic asynchronous architecture.&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%2F579672il6qe6tijg1ek7.jpg" 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%2F579672il6qe6tijg1ek7.jpg" alt="Overview of the Architecture" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of the flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.Frontend Interaction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In general, what we have here is a classic asynchronous setup with message queue. We have 2 instances of Symfony application running inside Docker container. The first one is interacting with the frontend client.&lt;/p&gt;

&lt;p&gt;For example, when a customer types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STR. IORGA NICOLAE nr. 20 et 1, CONSTANŢA, 900622
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application processes the input address and packages it into a Message object.&lt;br&gt;
The Message object is then wrapped in a Symfony Messenger &lt;code&gt;Envelope&lt;/code&gt; instance. The message is serialized into &lt;code&gt;JSON&lt;/code&gt; format, with additional metadata for processing.&lt;/p&gt;

&lt;p&gt;Symfony Messenger is perfect for handling asynchronous tasks. It allows us to offload time-consuming operations, like calling the OpenAI API, to background processes.&lt;br&gt;
This approach ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Responsiveness:&lt;/strong&gt; The frontend remains fast and responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Tasks can be distributed across multiple workers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is the message class used for our system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

declare(strict_types=1);

namespace App\Messenger\Message\Customer;

use Symfony\Component\Validator\Constraints as Assert;

final class CustomerAddressAddedMessage
{
    public function __construct(
        #[Assert\NotBlank]
        #[Assert\Type('int')]
        public readonly int $customerId,
        #[Assert\NotBlank]
        #[Assert\Type('string')]
        public readonly string $inputAddress,
    ) {
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application connects to a RabbitMQ instance using the amqp PHP extension. To set this up, you need to define the transport and message binding in your &lt;code&gt;messenger.yaml&lt;/code&gt; configuration file.&lt;/p&gt;

&lt;p&gt;Refer to the official Symfony Messenger documentation for detailed guidance:&lt;br&gt;
Symfony Messenger Transport Configuration&lt;/p&gt;

&lt;p&gt;Documentation: &lt;a href="https://symfony.com/doc/current/messenger.html#transport-configuration" rel="noopener noreferrer"&gt;https://symfony.com/doc/current/messenger.html#transport-configuration&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the message is pushed to the broker (e.g., RabbitMQ, AmazonMQ, or AWS SQS), it’s picked up by the second instance of the application. This instance runs the messenger daemon to consume messages, as marked &lt;strong&gt;3&lt;/strong&gt; in the architecture schema.&lt;/p&gt;

&lt;p&gt;The consuming process is handled by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bin/console messenger:consume&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;is working as it's process.&lt;/p&gt;

&lt;p&gt;The daemon picks up the message from the configured queue, deserializes it back into the corresponding Message class, and forwards it to the Message Handler for processing.&lt;/p&gt;

&lt;p&gt;Here’s the &lt;strong&gt;Message Handler&lt;/strong&gt; where the interaction with the OpenAI Assistant occurs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

declare(strict_types=1);

namespace App\Messenger\MessageHandler\Customer;

use App\Entity\CustomerAddressNormalized;
use App\Messenger\Message\Customer\CustomerAddressAddedMessage;
use App\Service\Address\Normalization\OpenAI\OpenAIAddressNormalizationService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

final class CustomerAddressAddedHandler implements MessageHandlerInterface
{
    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly EntityManagerInterface $entityManager,
        private readonly OpenAIAddressNormalizationService $addressNormalizationService,
    ) {
    }

    public function __invoke(CustomerAddressAddedMessage $customerAddressAddedMessage)
    {
        $this-&amp;gt;logger-&amp;gt;info(
            'Start processing "{message}", customerId:{customerId}.',
            [
                'message'    =&amp;gt; \get_class($customerAddressAddedMessage),
                'customerId' =&amp;gt; $customerAddressAddedMessage-&amp;gt;customerId,
            ],
        );

        $normalizationString = $this-&amp;gt;addressNormalizationService-&amp;gt;createAddressNormalization($customerAddressAddedMessage-&amp;gt;inputAddress);
        $addressNormalized   = new CustomerAddressNormalized(
            $customerAddressAddedMessage-&amp;gt;customerId,
            $customerAddressAddedMessage-&amp;gt;inputAddress,
            $normalizationString,
            false,
        );

        $this-&amp;gt;entityManager-&amp;gt;persist($addressNormalized);
        $this-&amp;gt;entityManager-&amp;gt;flush();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key Points of the Handler&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logging&lt;/strong&gt;&lt;br&gt;
Logs the start of processing for traceability and debugging.&lt;br&gt;
Normalization Service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;OpenAIAddressNormalizationService&lt;/code&gt;&lt;/strong&gt; is called to process the input address via the Assistant.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistence&lt;/strong&gt;&lt;br&gt;
The normalized address is saved in the database using Doctrine's &lt;code&gt;EntityManager&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The message handler may seem like standard Symfony code, but the core is located in this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private readonly OpenAIAddressNormalizationService $addressNormalizationService,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service is responsible for interacting with OpenAI via the designated PHP library. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/openai-php/client" rel="noopener noreferrer"&gt;OpenAI PHP Client&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s dive into the implementation of the service with the usage of PHP client.&lt;/p&gt;

&lt;p&gt;Here’s the full implementation of the OpenAIAddressNormalizationService:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

declare(strict_types=1);

namespace App\Service\Address\Normalization\OpenAI;

use OpenAI;
use OpenAI\Client;
use OpenAI\Responses\Threads\Runs\ThreadRunResponse;
use Psr\Log\LoggerInterface;
use RuntimeException;

class OpenAIAddressNormalizationService
{
    private Client $openAIClient;

    public function __construct(
        private readonly string $openAIApiKey,
        private readonly string $openAIOrganizationID,
        private readonly LoggerInterface $logger,
    ) {
        $this-&amp;gt;openAIClient = OpenAI::client($this-&amp;gt;openAIApiKey, $this-&amp;gt;openAIOrganizationID);
    }

    public function createAddressNormalization(string $inputAddress): string
    {
        $assistant = $this-&amp;gt;openAIClient-&amp;gt;assistants()-&amp;gt;retrieve('asst_VKQLz1xaIY5cFP8dlk2SPZkg');
        $threadRun = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;createAndRun(
            [
                'assistant_id' =&amp;gt; $assistant-&amp;gt;id,
                'thread'       =&amp;gt; [
                    'messages' =&amp;gt; [
                        [
                            'role'    =&amp;gt; 'user',
                            'content' =&amp;gt; $inputAddress,
                        ],
                    ],
                ],
            ],
        );

        $normalizationResponse = $this-&amp;gt;loadAnswer($threadRun);
        $this-&amp;gt;logger-&amp;gt;warning($normalizationResponse);

        return json_encode($normalizationResponse, JSON_THROW_ON_ERROR);
    }

    private function loadAnswer(ThreadRunResponse $threadRun): string
    {
        while (in_array($threadRun-&amp;gt;status, ['queued', 'in_progress'])) {
            $threadRun = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;runs()-&amp;gt;retrieve(
                threadId: $threadRun-&amp;gt;threadId,
                runId: $threadRun-&amp;gt;id,
            );
        }

        if ('completed' !== $threadRun-&amp;gt;status) {
            throw new RuntimeException('OpenAI request failed, please try again.');
        }

        $messageList = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;messages()-&amp;gt;list(threadId: $threadRun-&amp;gt;threadId);

        return $messageList-&amp;gt;data[0]-&amp;gt;content[0]-&amp;gt;text-&amp;gt;value;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow for getting an inference (or completion)—essentially the response from GPT—using the &lt;code&gt;openai-php/client&lt;/code&gt; library follows three main stages:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Initialization of the OpenAI Client and Assistant retrieval
&lt;/h2&gt;

&lt;p&gt;The first step is setting up the OpenAI client and retrieving the configured Assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$this-&amp;gt;openAIClient = OpenAI::client($this-&amp;gt;openAIApiKey, $this-&amp;gt;openAIOrganizationID);

$assistant = $this-&amp;gt;openAIClient-&amp;gt;assistants()-&amp;gt;retrieve('asst_VKQLz1xaIY5cFP8dlk2SPZkg');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client Initialization:&lt;/strong&gt; The &lt;code&gt;OpenAI::client()&lt;/code&gt; method creates a new client using your &lt;code&gt;API key&lt;/code&gt; and &lt;code&gt;organization ID&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assistant Retrieval:&lt;/strong&gt; The &lt;code&gt;retrieve()&lt;/code&gt; method connects to the specific Assistant instance configured for your task, identified by its unique ID.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Creating and Running a Thread
&lt;/h2&gt;

&lt;p&gt;Once the client and Assistant are initialized, you can create and run a thread to begin the interaction. A thread acts as the communication pipeline, handling the exchange of messages between the user and the Assistant.&lt;/p&gt;

&lt;p&gt;Here’s how the thread is initiated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$threadRun = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;createAndRun([
    'assistant_id' =&amp;gt; $assistant-&amp;gt;id,
    'thread'       =&amp;gt; [
        'messages' =&amp;gt; [
            [
                'role'    =&amp;gt; 'user',
                'content' =&amp;gt; $inputAddress,
            ],
        ],
    ],
]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assistant ID&lt;/strong&gt;: The ID of the Assistant determines which model instance will process the input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messages:&lt;/strong&gt; Each thread includes a sequence of messages. In this case, the message has &lt;code&gt;Role&lt;/code&gt; which indicates whether the message is from the user (input) or system (response). And the second is &lt;strong&gt;Content&lt;/strong&gt; - contains the actual input text (e.g., an address).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Handling the Thread Response
&lt;/h2&gt;

&lt;p&gt;After initiating the thread, the application handles the response. Since OpenAI processes may not complete instantly, you need to poll the thread’s status until it’s marked as 'completed':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while (in_array($threadRun-&amp;gt;status, ['queued', 'in_progress'])) {
    $threadRun = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;runs()-&amp;gt;retrieve(
        threadId: $threadRun-&amp;gt;threadId,
        runId: $threadRun-&amp;gt;id,
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Polling:&lt;/strong&gt; The application repeatedly checks the thread’s status (queued or in_progress). Once the status changes to 'completed', the thread contains the final output. &lt;/p&gt;

&lt;p&gt;Once the thread is complete, the application retrieves the response messages to extract the normalized address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$messageList = $this-&amp;gt;openAIClient-&amp;gt;threads()-&amp;gt;messages()-&amp;gt;list(threadId: $threadRun-&amp;gt;threadId);
return $messageList-&amp;gt;data[0]-&amp;gt;content[0]-&amp;gt;text-&amp;gt;value;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The normalized address can now be returned to the frontend client for immediate use or stored in the database as a structured entity, like &lt;code&gt;CustomerAddressNormalized&lt;/code&gt;, for future processing.&lt;/p&gt;

&lt;p&gt;When you run this setup, you should be able to extract and save structured output from OpenAI for Named Entity Recognition and other classification or generation tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make Assistant Blazing Accurate with Fine Tuning and Knowledge Base (Retrieval Augmented Generation)
&lt;/h2&gt;

&lt;p&gt;In some cases, basic AI solutions aren’t enough. When regulatory compliance and business requirements demand high accuracy, we need to go the extra mile to ensure the output is factual and reliable.&lt;/p&gt;

&lt;p&gt;For example, when generating a JSON structure, we need to guarantee that its contents align with reality. A common risk is the model hallucinating information—like inventing a place based on a provided postal code. This can cause serious issues, particularly in high-stakes environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ground Truth with Knowledge Base&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To eliminate hallucinations and ensure factual accuracy, we need to supply the Assistant with a ground truth knowledge base. This acts as the definitive reference for the model, ensuring it uses accurate information during inference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Approach: A Postal Code Knowledge Base&lt;/strong&gt;&lt;br&gt;
I created a simple (but quite large—around 12 MB) &lt;code&gt;JSON&lt;/code&gt; file containing the full demarcation of all postal codes in Romania. This dictionary-like structure provides:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Postal Code:&lt;/strong&gt; The input value.&lt;br&gt;
&lt;strong&gt;Validated Information:&lt;/strong&gt; Facts like the corresponding city, county, and overall place name.&lt;br&gt;
This knowledge base serves as a reference point for the Assistant when performing Named Entity Recognition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure of the Knowledge Base&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s an example snippet of the knowledge base &lt;code&gt;JSON&lt;/code&gt; structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "ro_places": [
    {
        "place_mark": "Râmnicu Vâlcea, Vâlcea, 240454, RO",
        "country_code": "RO",
        "place_name": "Râmnicu Vâlcea",
        "county": "Vâlcea",
        "postal_code": "240454",
        "admin_code": "39",
        "coordinates": {
            "lat": 45.1,
            "lon": 24.3667
        }
    },
    {
        "place_mark": "Râmnicu Vâlcea, Vâlcea, 240493, RO",
        "country_code": "RO",
        "place_name": "Râmnicu Vâlcea",
        "county": "Vâlcea",
        "postal_code": "240493",
        "admin_code": "39",
        "coordinates": {
            "lat": 45.1,
            "lon": 24.3667
        }
    }, 
..... #around 40 000 post codes
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the knowledge base ready, it’s time to instruct the Assistant to use it effectively. While you could embed this directly into the prompt, this approach increases token usage and costs. This makes it an excellent moment to introduce &lt;strong&gt;fine-tuning&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Fine-Tuning?
&lt;/h2&gt;

&lt;p&gt;Fine-tuning involves modifying the outermost layers (specifically the weight matrices) of a pre-trained model, adapting it for a specific task. In our case, Named Entity Recognition (NER) is a perfect candidate.&lt;/p&gt;

&lt;p&gt;A fine-tuned model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Requires Smaller Prompts:&lt;/strong&gt; Reducing the need for lengthy instructions or embedded examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cuts Costs:&lt;/strong&gt; Shorter prompts and fewer examples lower token consumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improves Inference Time:&lt;/strong&gt; Faster responses mean better real-time performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main goal is to align the model more closely with the nuances of the real-world data it will process. Training on domain-specific data makes the model better at understanding and generating appropriate responses. It also reduces the need for post-processing or error correction by the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the Fine-Tuning Dataset
&lt;/h2&gt;

&lt;p&gt;To fine-tune the model, we need a properly formatted dataset in &lt;code&gt;.jsonl&lt;/code&gt; (JSON Lines) format, as required by OpenAI. Each entry in the dataset includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System Prompt:&lt;/strong&gt; The initial instruction for the Assistant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Input:&lt;/strong&gt; The raw input data (e.g., a messy address).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected Output:&lt;/strong&gt; The desired structured response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This dataset provides the Assistant with domain-specific examples, enabling it to learn how to respond to similar prompts in the future.&lt;/p&gt;

&lt;p&gt;Here’s an example of how to structure a fine-tuning entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "messages":[
      {
         "role":"system",
         "content":"You are a system that &amp;lt;does something&amp;gt;. Use the knowledge base provided for accurate results."
      },
      {
         "role":"user",
         "content":"TOOLS \n-------\n\n\n\nRESPONSE FORMAT INSTRUCTIONS\n--------------------\n\n\n--------------------\nUSER'S INPUT\n--------------------\n"
      },
      {
         "role":"assistant",
         "content":"{\"key\": \"value\"}"
      }
   ]
}

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

&lt;/div&gt;



&lt;p&gt;Let’s break down the structure of a fine-tuning entry in the &lt;code&gt;.jsonl&lt;/code&gt; file, using our Romanian address processing case as an example:&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%2Fkkri30u7mg4fbfrzdm8d.jpg" 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%2Fkkri30u7mg4fbfrzdm8d.jpg" alt="Image description" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each entry is designed to simulate a real interaction between the user and the Assistant, encapsulating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Assistant’s purpose and scope.&lt;/li&gt;
&lt;li&gt;The user’s input scenario.&lt;/li&gt;
&lt;li&gt;The expected structured output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach helps the model learn the desired behavior in real-world contexts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. System Role Message&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Describes the assistant’s capabilities and the scope of its functionality, setting expectations for the types of entities it should recognize and extract, such as street names, house numbers, and postal codes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Example: The system explains that the assistant is designed to function as an Named Entity Recognition model for Romanian addresses, detailing the components it should extract and classify.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. User Role Message&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a detailed scenario or query in which the user gives a specific address input. This part of the data entry is crucial as it directly influences how the model will learn to respond to similar inputs in operational settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Assistant Role Message&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains the expected response from the assistant, formatted in JSON. This response is crucial as it trains the model on the desired output format and precision.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Validation File for Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;Once you’ve created the training file, the next step is to prepare a validation file. This file evaluates the accuracy of the fine-tuned Assistant on real data. The structure is similar to the training file, which makes automating the creation of both files more straightforward.&lt;/p&gt;

&lt;p&gt;The validation file is designed to test the model’s generalization capabilities. It ensures that the fine-tuned Assistant can handle new inputs and perform consistently, even when faced with unfamiliar or challenging examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure of the Validation File
&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%2Fthmimvpn44sz3t7ik4rt.jpg" 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%2Fthmimvpn44sz3t7ik4rt.jpg" alt="Image description" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;System Message:&lt;/strong&gt;&lt;br&gt;
To maintain consistency with the training process, the system message should be &lt;strong&gt;identical&lt;/strong&gt; to the one used in the training file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Message:&lt;/strong&gt;&lt;br&gt;
Introduces a &lt;strong&gt;new input&lt;/strong&gt; (e.g., an address) that was not included in the training file. The new example should be realistic and domain-specific, as well as challenge the model by including minor errors or complexities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assistant Message:&lt;/strong&gt;&lt;br&gt;
Provides the expected structured response for the new user input.&lt;br&gt;
This serves as the gold standard for validating the model’s accuracy.&lt;/p&gt;

&lt;p&gt;To streamline the creation of both training and validation files, automation scripts can be used. These scripts generate properly formatted &lt;code&gt;.jsonl&lt;/code&gt; files based on input datasets.&lt;/p&gt;

&lt;p&gt;Visit the repository containing scripts to generate training and validation files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mateuszpoland/openai-gpt-finetuning/blob/master/fine_tuning.py" rel="noopener noreferrer"&gt;Automated .jsonl generation scripts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a Python version of fine-tuning automation. PHP version is coming soon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Validation Matters?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy Testing:&lt;/strong&gt; Measures how well the fine-tuned model performs on unseen data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generalization:&lt;/strong&gt; Validates the model’s ability to handle new, complex, or error-prone inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback Loop:&lt;/strong&gt; Helps identify areas where additional fine-tuning or data is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fine-Tuning Your Assistant Manually
&lt;/h2&gt;

&lt;p&gt;If you prefer, you can fine-tune the Assistant manually using OpenAI's graphical user interface (GUI). Once you’ve prepared both the training and validation files, follow these steps to get started:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Access the Fine-Tuning Wizard&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Dashboard and click on "Fine-Tuning" in the menu on the left-hand side.&lt;/li&gt;
&lt;li&gt;Click the green "Create" button to open the fine-tuning menu.&lt;/li&gt;
&lt;/ul&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%2F49fjl0upetttwpy2w3ke.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%2F49fjl0upetttwpy2w3ke.png" alt="Image description" width="454" height="853"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Configure Fine-Tuning&lt;/p&gt;

&lt;p&gt;In the fine-tuning menu, update the following settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base model: At the time of writing, the most cost-effective and high-performing base model is &lt;code&gt;gpt-4o-mini-2024-07-18&lt;/code&gt;. Choose the base model that best aligns with your performance and budget requirements.&lt;/li&gt;
&lt;li&gt;Training and Validation Data - Upload the training data and validation data files you created earlier.&lt;/li&gt;
&lt;li&gt;Number of epochs - Set the number of epochs (iterations of training process over the entire dataset). I recommend starting with 3 epochs, but you can experiment with more epochs if your budget allows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Monitor the Fine-Tuning Process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the fine-tuning process starts, you can track its progress in the Fine-Tuning Dashboard:&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%2Fv6sym0vzmvlo3ycbceii.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%2Fv6sym0vzmvlo3ycbceii.png" alt="Image description" width="800" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dashboard provides real-time updates and displays several metrics to help you monitor and evaluate the training process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Metrics to Understand&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Training loss&lt;/strong&gt; Measures how well the model is fitting the training data. A lower training loss indicates the model is effectively learning patterns within the dataset.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Full Validation Loss&lt;/strong&gt; Indicates performance on unseen data from the validation dataset. A lower validation loss suggests the model will generalize well to new inputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps and Time&lt;/strong&gt; Training steps are number of iterations where model weights are updated based on batches of data.&lt;br&gt;
The time stamp indicates when each step was processed, which helps to monitor the duration of training and intervals between evaluations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Interpreting the Metrics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Monitoring below metrics helps you determine whether the fine-tuning process is converging correctly or requires adjustments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decreasing Loss: Both training and validation losses should ideally decrease over time, eventually stabilizing.
&lt;strong&gt;Overfitting:&lt;/strong&gt; Occurs when training loss keeps decreasing while validation loss starts to increase or fluctuate. This indicates the model is over-specialized to the training data and performs poorly on unseen data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Underfitting:&lt;/strong&gt; Happens when both losses remain high, showing the model isn’t learning effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug in the Model &amp;amp; Evaluate
&lt;/h2&gt;

&lt;p&gt;With your Assistant trained and fine-tuned, it’s time to integrate it into your application and start evaluating its performance.&lt;/p&gt;

&lt;p&gt;Start Simple.&lt;/p&gt;

&lt;p&gt;Begin with manual tests, either in the Assistants Playground or directly within your application. Avoid overcomplicating your evaluation at this stage; focus on ensuring the basics work as expected.&lt;/p&gt;

&lt;p&gt;For example, you can use a simple tool like Google Sheets to manually compare the input and output, as shown here:&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%2F9khdi4w7vqmza5liwfam.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%2F9khdi4w7vqmza5liwfam.png" alt="Image description" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the first step.&lt;br&gt;
AI and ML solutions require ongoing evaluation to ensure performance remains consistent. For tasks like Named Entity Recognition, automated tools such as the PromptFlow extension for Visual Studio Code can help streamline testing and validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://platform.openai.com/docs/guides/fine-tuning" rel="noopener noreferrer"&gt;OpenAI Fine-Tuning Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://symfony.com/doc/current/messenger.html" rel="noopener noreferrer"&gt;Symfony Messenger Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/openai-php/client" rel="noopener noreferrer"&gt;OpenAI PHP Client Library&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/microsoft/promptflow" rel="noopener noreferrer"&gt;PromptFlow Extension for VS Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank You!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to explore this guide! I hope it provides you with a solid foundation to build and fine-tune your AI-powered solutions. If you have any questions or suggestions, feel free to reach out or leave a comment.&lt;/p&gt;

&lt;p&gt;Happy coding and best of luck with your AI projects! 🚀&lt;/p&gt;

</description>
      <category>llm</category>
      <category>php</category>
      <category>gpt3</category>
    </item>
    <item>
      <title>PART 1: Microservice Prospect Generator System (Tools Involved: Symfony, Python, RabbitMQ, MySQL, Docker, ChatGPT)</title>
      <dc:creator>Mateusz Kowalewski</dc:creator>
      <pubDate>Fri, 29 Sep 2023 17:50:44 +0000</pubDate>
      <link>https://dev.to/mateuszpoland/part-1-microservice-prospect-generator-system-tools-involved-symfony-python-rabbitmq-mysql-docker-chatgpt-36io</link>
      <guid>https://dev.to/mateuszpoland/part-1-microservice-prospect-generator-system-tools-involved-symfony-python-rabbitmq-mysql-docker-chatgpt-36io</guid>
      <description>&lt;h2&gt;
  
  
  The Challenge of Sourcing Prospects
&lt;/h2&gt;

&lt;p&gt;Every business grapples with the challenge of sourcing prospects. While there are tons of solutions on the market currently, each comes with its limitations. Learning curves, tool-specific constraints, and sometimes unmet expectations are common pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost vs. Quality of Leads
&lt;/h2&gt;

&lt;p&gt;Investing money in a business is obvious and all the costs can quickly add up, especially at the beginning. But it's the quality of leads you get in return that really counts. Outsourcing your customer sources raises questions: Is the quality of your leads a priority for them? Is the data fresh and relevant ?&lt;/p&gt;

&lt;p&gt;From my perspective, the prospecting process should be transparent and hands-on. It's essential for entrepreneurs to trace back every customer's origin.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of the Described System
&lt;/h2&gt;

&lt;p&gt;Despite the challenges, the system I've elaborated below is able to yield over 10,000 quality contacts within an hour,but  specifically tailored for the gym niche across several US States. And the good part is that it can be done to many niches that can be found on Google (as for the moment).&lt;/p&gt;

&lt;h2&gt;
  
  
  BEWARE
&lt;/h2&gt;

&lt;p&gt;This tool can generate very high amount of prospects very fast. But the API I described uses Google API under the hood. &lt;br&gt;
YOU NEED TO fire it up in portions, and set up a budget in your Google Cloud account, where you use your API. This is just a reminder for a future reference, in case you build this project yourself from the ground up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Can Benefit?
&lt;/h2&gt;

&lt;p&gt;Software developers making the shift to entrepreneurship or business-savvy individuals with a developer ally will find this guide invaluable. Not only will you acquire a leads-filled excel/csv file, but also a step-by-step guide on utilizing ChatGPT for efficient lead sourcing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeking Ready-Made Prospects for Your Venture?
&lt;/h2&gt;

&lt;p&gt;Exploring a new niche or aiming to expand your current one? I've put together some ready to use files for the gym and plumbing/foundation repair sectors on my website. In case you seek outside of these niches, fill out a straightforward form detailing your needs, and I'll promptly generate new prospects for you – completely free. Your growth is important to me, let's navigate it together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Currently:&lt;/strong&gt; The system gleans leads from Google, but I'm on the path to incorporating LinkedIn, Instagram, and more.&lt;/p&gt;

&lt;p&gt;Essential Skills for Developers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proficiency In:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker&lt;br&gt;
RabbitMQ&lt;br&gt;
Microservice patterns: Understand their operations, interactions via message queues, and HTTP calls.&lt;br&gt;
APIs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Familiarity With:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PHP &amp;amp; Symfony Framework&lt;br&gt;
Python&lt;br&gt;
Bash scripting&lt;br&gt;
ChatGPT prompting&lt;/p&gt;

&lt;h2&gt;
  
  
  A Glimpse into the System's Architecture
&lt;/h2&gt;

&lt;p&gt;The system functions through three primary stages.&lt;/p&gt;

&lt;p&gt;Here is just a miniature, so we are on the same page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJZ4zX8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3ughoty2g7knt6mzi38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJZ4zX8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3ughoty2g7knt6mzi38.png" alt="Image description" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For an intricate look, here's the full-resolution Miro board:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miro.com/app/board/uXjVMiSun6Y=/?share_link_id=994343205773"&gt;https://miro.com/app/board/uXjVMiSun6Y=/?share_link_id=994343205773&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll methodically dissect each segment, beginning with the ChatGPT stage. Here, you'll master the art of prompting ChatGPT for diverse formatted outputs, extending to website content scraping. For the ensuing stages, I harness an API that is in the very core of the system and is responsible for getting business details from Google Maps.&lt;/p&gt;

&lt;p&gt;Location Nuances: Many places often share identical names, a phenomenon evident in countries like the US.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Endpoint Breakdown:
&lt;/h2&gt;

&lt;p&gt;For clarity, let's decode the following API endpoint:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:8080/leads/England/Aley+Green(Bedfordshire)?keyword=gym&amp;amp;keywords=fitness%20center&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Where we have 3 moving parts (parameters) we need to provide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Country:&lt;/strong&gt; England&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Place name &amp;amp; Administrative Area:&lt;/strong&gt; Aley Green, Bedfordshire&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search Criteria:&lt;/strong&gt; Gyms and fitness centers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result? A JSON array of venues fitting our specified criteria.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCza5eMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7spuv47mfz3r45y1b18e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCza5eMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7spuv47mfz3r45y1b18e.png" alt="Image description" width="800" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the Process: How Far Have We Come?
&lt;/h2&gt;

&lt;p&gt;Though we're yet to achieve full automation with this solution, the current system exemplifies rapid, formatted data extraction via ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tapping into ChatGPT for Swift Data Formatting
&lt;/h2&gt;

&lt;p&gt;I targeted the potent markets of the UK and US due to their immense opportunities for digital services.&lt;/p&gt;

&lt;p&gt;So, the first prompt to chatGPT is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--co5DGrvB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3htg68gnskkoibnwym92.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--co5DGrvB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3htg68gnskkoibnwym92.png" alt="Image description" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;as I didn't care about googling it, when I can have everything in one tool. As I was sure that counties is what England is build of, I asked:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TVFTESQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwjy7fzy5tghht0p8zmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TVFTESQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwjy7fzy5tghht0p8zmq.png" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this gave me a good list of 48 counties.&lt;/p&gt;

&lt;p&gt;Now. We obviously could ask now one by one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GNbY3yFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r99ssnaty4pdibkk9p2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GNbY3yFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r99ssnaty4pdibkk9p2q.png" alt="Image description" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But ChatGPT is not that good with extracting that long lists in structured format from it's "internals". &lt;br&gt;
The task we gave - to generate a list of all cities will be tedious and will require a lot of asking to "generate more", it often freezes and crashes.&lt;/p&gt;

&lt;p&gt;I tried to give this task to BARD, to see if it can generate such a comprehensive dataset from the get-go.&lt;br&gt;
For this prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---O2KjBt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/42th9rb0yy91saeilzp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---O2KjBt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/42th9rb0yy91saeilzp5.png" alt="Image description" width="627" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BARD generated only a handful of places. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnEfAZO6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yffltdt68jua4m4qa7c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnEfAZO6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yffltdt68jua4m4qa7c9.png" alt="Image description" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When nurtured, it again generated just a few examples, just take a look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ld2KyMgN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v72amns51hpme96ezm72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ld2KyMgN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v72amns51hpme96ezm72.png" alt="Image description" width="754" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe these responses could be made longer with a bit of prompt-engineering and generating more "few-shot" examples, but we won't eventually know whether or not AI did not skip some important places, which are filled with juicy prospects. &lt;/p&gt;

&lt;p&gt;So, we need to move on.&lt;br&gt;
Let me show you what approach worked out well in this situation and can be a good foundation for your experiments.&lt;br&gt;
We will make ChatGPT just a part of data processing engine. &lt;/p&gt;

&lt;p&gt;At this point, I would recommend ChatGPT Plus, the paid version. It's the only way ChatGPT can access the internet through plugins. While Google's BARD is an alternative, I won't delve into it in this piece. If you're not already using ChatGPT Plus, give it a whirl. For just $20/month, with some acclimatization, it can drastically reduce your workload. &lt;br&gt;
Here I have my ChatGPT instance switched on with Plugins enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oyaPIIjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qwor22ss3c5byoel1ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oyaPIIjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qwor22ss3c5byoel1ly.png" alt="Image description" width="367" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The plugin we actually need to use is &lt;strong&gt;Webpilot&lt;/strong&gt;. It allows ChatGPT to process data from websites(html), but as well text files, such as .pdf's.&lt;/p&gt;

&lt;p&gt;In case you need a guide on how to set this up, pause reading and head off to: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://devopscube.com/chatgpt-plugins/"&gt;https://devopscube.com/chatgpt-plugins/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I did is I found a source of reputable information to give ChatGPT as a reference. &lt;br&gt;
I used google, and for England, I found out that Wikipedia has a website for each of the counties, listing all the places worth interest in that particular county. As we know, Wikipedia is tried and tested tool, so I have no problem in trusting there are all the places. No need to worry that ChatGPT can screw us over. Nevermind,&lt;/p&gt;

&lt;p&gt;For the first on our list, it's :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/List_of_places_in_Bedfordshire"&gt;https://en.wikipedia.org/wiki/List_of_places_in_Bedfordshire&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we see, the template for these urls is: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://en.wikipedia.org/wiki/List_of_places_in_&amp;lt;county&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you actually visit this url, you'll see that the page is neatly structured. It lists every place in Bedfordshire county in alphabetical order.&lt;/p&gt;

&lt;p&gt;There we go back to the ChatGPT and put this prompt there:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NaT7FOC8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wl8si3uv07tf0obknmm3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NaT7FOC8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wl8si3uv07tf0obknmm3.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any advanced, fancy prompt engineering is not needed here.&lt;br&gt;
However it always pays off to describe the requirements in detail, even writing them down from 2 different angles and put at least one example of the output we want. &lt;/p&gt;

&lt;p&gt;When AI understands us clearly enough, it will generate actual data we need in desired format at blazing speed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fwm2-hQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3fsewgwj5z1fdmt8iy3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fwm2-hQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3fsewgwj5z1fdmt8iy3.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, when ChatGPT machinery is properly set up for a job, we can easily provide the links to next counties and generate .csv files within minutes. &lt;br&gt;
I provided a second link, for Berkshire this time and shortly after, I have a list od 369 places to scrape the prospects from. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hv9VUHBo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pg77fbvbyp2rm7cfiwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hv9VUHBo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pg77fbvbyp2rm7cfiwt.png" alt="Image description" width="326" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve done the task once, you can easily scale it, saving time and accelerating results.&lt;/p&gt;

&lt;h2&gt;
  
  
  INTERESTED IN MORE DETAILS ?
&lt;/h2&gt;

&lt;p&gt;If you want the series to be continued, put the hand up and I will prepare the next parts. &lt;br&gt;
Specifically, how to tie together this architecture between components written in two different programming languages - Python &amp;amp; PHP. I can dive into details and give you ready reciepes on how to connect 2 different systems via message queue. &lt;/p&gt;

&lt;p&gt;Let me know in the comments. &lt;br&gt;
Thank you.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
