<?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: Mayank Laddha</title>
    <description>The latest articles on DEV Community by Mayank Laddha (@mayank_laddha_ml).</description>
    <link>https://dev.to/mayank_laddha_ml</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%2F2434275%2F4b1eaaae-7496-4fe1-87e5-b9241f3d732e.jpeg</url>
      <title>DEV Community: Mayank Laddha</title>
      <link>https://dev.to/mayank_laddha_ml</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mayank_laddha_ml"/>
    <language>en</language>
    <item>
      <title>GRPO to get structured data</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Wed, 14 May 2025 08:41:52 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/grpo-to-get-structured-data-bel</link>
      <guid>https://dev.to/mayank_laddha_ml/grpo-to-get-structured-data-bel</guid>
      <description>&lt;p&gt;I saw this line "GRPO gives developers a way to teach models how to reason better, faster, and without breaking the bank." and just loved it.&lt;br&gt;
I personally feel we will see more task specific models and GRPO is a good choice if the task is logical or format-sensitive..&lt;br&gt;
Took a 1.5B Qwen2.5-Coder model, fine-tuned with GRPO, asking to extract structured JSON from OCR text based on 'any user-defined schema'. Needs more work but it works! &lt;br&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%2Fpgdtx8xa46twzb8wv6nk.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%2Fpgdtx8xa46twzb8wv6nk.png" alt="Unstructured to structured" width="800" height="895"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the model, you can use it in combination with paddleocr:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://huggingface.co/MayankLad31/invoice_schema" rel="noopener noreferrer"&gt;https://huggingface.co/MayankLad31/invoice_schema&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My interest in the intersection of AI and developer advocacy has been growing significantly.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>My take on the Agentic Object Detection</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Sun, 16 Feb 2025 08:42:09 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/my-take-on-the-agentic-object-detection-4612</link>
      <guid>https://dev.to/mayank_laddha_ml/my-take-on-the-agentic-object-detection-4612</guid>
      <description>&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Segmenting Everything with SAM : We detect everything and worry about filtering later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filtering with CLIP: Once we have all the segmented objects, we don’t want all of them. We need to filter out the noise and keep only the relevant objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adding Reasoning with a model like GPT-4o: Okay, so we’ve segmented and filtered. But what about finalising, understanding? That’s where a strong LLM like GPT-4o comes in.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is what I did with SAM and clip, we now need to use a good LLM on top and add some reasoning..&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%2F9f0miiavlvjoxlz92ans.gif" 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%2F9f0miiavlvjoxlz92ans.gif" alt="Agentic Object Detection demo" width="320" height="240"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;code: &lt;a href="https://github.com/maylad31/agentic-object-detection" rel="noopener noreferrer"&gt;https://github.com/maylad31/agentic-object-detection&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>computervision</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Semantic search on top of object detection</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Tue, 11 Feb 2025 09:08:28 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/semantic-search-on-top-of-object-detection-1pf5</link>
      <guid>https://dev.to/mayank_laddha_ml/semantic-search-on-top-of-object-detection-1pf5</guid>
      <description>&lt;p&gt;Semantic search on top of object detection: Imagine this across multiple cameras with tracking,VQA and more.. A few years ago, when I worked on computer vision projects, it was hard to imagine how quickly things would evolve. &lt;/p&gt;

&lt;p&gt;This project combines YOLO for real-time object detection, semantic search using text-based queries, and live camera feeds to detect and search for objects. But, it also raises important privacy concerns. I think surveillance systems need to be used responsibly and ethically. &lt;/p&gt;

&lt;p&gt;If you have an interesting project, let's connect!&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/mayankladdha31/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/mayankladdha31/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>computervision</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Food Recognition and Nutrition Estimation using OpenAI</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Mon, 10 Feb 2025 14:55:47 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/food-recognition-and-nutrition-estimation-using-openai-4mdo</link>
      <guid>https://dev.to/mayank_laddha_ml/food-recognition-and-nutrition-estimation-using-openai-4mdo</guid>
      <description>&lt;p&gt;Here is how you can build a &lt;strong&gt;simple food recognition and nutrition estimation app using OpenAI in just 20 minutes&lt;/strong&gt;!&lt;/p&gt;

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

&lt;p&gt;Image Encoding: The image is converted into a base64 format to be processed by OpenAI’s API.&lt;/p&gt;

&lt;p&gt;Food Recognition Prompt: The app sends the image to OpenAI to identify food items and their respective quantities.&lt;/p&gt;

&lt;p&gt;Nutritional Estimation: Another prompt is used to estimate the nutritional values based on the identified food items and their quantities.&lt;/p&gt;

&lt;p&gt;Displaying the Results: The estimated calorie, protein, fat, and carbohydrate values are shown using Gradio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is a very simple code, which can be improved/better organized, but the idea was to show how easily it can be done to create a simple poc.&lt;/strong&gt;&lt;br&gt;
If you are working on interesting projects, connect with me on&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/mayankladdha31/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/mayankladdha31/&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openai import OpenAI
from pydantic import BaseModel
import base64
from typing import List
import gradio as gr

def encode_image(image_path):
  with open(image_path, "rb") as image_file:
    return base64.b64encode(image_file.read()).decode('utf-8')

openai_api_key = "key"
client = OpenAI(api_key=openai_api_key)

"""pydantic models to record food items and nutrient information, 
not necessary but helpful if you intend to create apis 
or use the data in other ways.
"""
class Food(BaseModel):
    name: str
    quantity: str

class Items(BaseModel):
    items: List[Food]

class Nutrient(BaseModel):
    steps: List[str]
    reasons: str
    kcal: str
    fat: str
    proteins: str
    carbohydrates: str


def recognize_items(image):
    """This function takes an image and returns a list of recognized food items along with their count and the nutrition. 
    """
    #first recognize items and quantities
    messages = [
        {
        "role": "user",
        "content": [
            {
            "type": "text",
            "text": f"You are an expert in recognising individual food items and their quantity. Give count(number) for countable items and an estimate for liquid/mixed or non countable items.  For example if you have one burger,two pastries, 2 pav, bhaji and dal in an image, you return burger,pastry,pav, bhaji and dal along with the count or estimates without any duplicates. For non countable items give an estimate in grams while explaining like 'looks 1 teaspoon of sauce, so around 5-8 grams' or 'looks 1 serving of bhaji, so around 150-200gms'. Given the image below, recognise food items with their quantity.",
            }
        ],
        }
    ]

    base64_image = encode_image(image)
    dic = {
                "type": "image_url",
                "image_url": {
                    "url":  f"data:image/jpeg;base64,{base64_image}",
                    "detail": "low"
                },
            }
    messages[0]["content"].append(dic)
    response = client.beta.chat.completions.parse(
    model="gpt-4o-mini",
    messages=messages,
    response_format=Items,
    max_tokens=300,
    temperature=0.1
    )
    foods = response.choices[0].message.parsed

    res = ""
    for food in foods.items:
        res=res+food.name+ " "+food.quantity+"\n"

    #now estimate nutrition, we can use a separate model for this task
    messages = [
        {
        "role": "user",
        "content": [
            {
            "type": "text",
            "text": f"You are an expert in estimating information regarding nutririon given the food items and thier quantities. Think step by step considering the given food items and their quantities, and give an estimated range(lowest - highest) of kcal, range(lowest - highest) of fat, range of proteins(lowest - highest) and carbohydrates(lowest - highest). Ignore contributions from minor items. Ensure your estimations are solely based on the provided quantities.  Return steps,reasons and estimations if this food was consumed. \n\nfood and quantity consumed by user: {res} \n\n.",
            }
        ],
        }
    ]
    dic = {
                "type": "image_url",
                "image_url": {
                    "url":  f"data:image/jpeg;base64,{base64_image}",
                    "detail": "low"
                },
            }
    messages[0]["content"].append(dic)
    response = client.beta.chat.completions.parse(
    model="gpt-4o-mini",
    messages=messages,
    response_format=Nutrient,
    max_tokens=500,
    temperature=0.1
    )
    nuts = response.choices[0].message.parsed
    steps = " ".join(nuts.steps)
    res=res+"\n"+steps+"\n\ncalories: "+nuts.kcal+" \nfats: "+nuts.fat+" \nproteins: "+nuts.proteins+" \ncarbohydrates: "+nuts.carbohydrates+"\n"+nuts.reasons+"\n"+"*These are estimations based on image. They might not be perfect or accurate. Please calculate based on the food you consume for a more precise estimate."
    return res


with gr.Blocks() as demo:
    foods=None
    with gr.Row():
        image_input = gr.Image(label="Upload Image",height=300,width=300,type="filepath")

    with gr.Row() as but_row:
        submit_btn = gr.Button("Detect food and quantity")

    with gr.Row() as text_responses_row: 
        text_response_1 = gr.Textbox(label="Detected food and quantity",scale=1)

    submit_btn.click(
        recognize_items,
        inputs=[image_input],
        outputs=[text_response_1]
    )

if __name__ == "__main__":
    demo.launch() 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Your Personal AI Cricket Coach</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Wed, 05 Feb 2025 13:52:03 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/your-personal-ai-cricket-coach-1jm9</link>
      <guid>https://dev.to/mayank_laddha_ml/your-personal-ai-cricket-coach-1jm9</guid>
      <description>&lt;p&gt;Your Personal AI Cricket Coach &lt;/p&gt;

&lt;p&gt;Get Feedback Anytime Anywhere: AI can make learning more accessible, especially for those without access to expert guidance—not just for cricket or sports, but for any area of education. It took me less than an hour to create this poc, it needs work, and I guess with more time and effort it could be better. Instead of using AI to replace people, why not use it to empower and uplift? &lt;/p&gt;

&lt;p&gt;Note: I downloaded a random video from YouTube for testing purposes. &lt;/p&gt;

&lt;p&gt;I am always open to interesting projects/opportunities and connecting with like minded people.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/mayankladdha31/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/mayankladdha31/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>startup</category>
    </item>
    <item>
      <title>A multi-head classifier using SetFit for query preprocessing</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Mon, 03 Feb 2025 08:25:48 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/a-multi-head-classifier-using-setfit-for-query-preprocessing-4c7k</link>
      <guid>https://dev.to/mayank_laddha_ml/a-multi-head-classifier-using-setfit-for-query-preprocessing-4c7k</guid>
      <description>&lt;p&gt;Query preprocessing is a crucial step in customer-centric applications, ensuring accurate routing and action decisions. Instead of training separate models, I useSetFit with a multi-head classifier— a single shared embedding space with independent classification heads.&lt;/p&gt;

&lt;p&gt;Each head specialises in one task, allowing task-specific learning while maintaining efficiency through shared representations. Positive and negative pairs are created using (intent, domain, hitl(human in the loop)) tuples for contrastive learning, ensuring the model effectively distinguishes related queries. This structured approach balances efficiency and flexibility, making it ideal for real-time applications where query classification must be both accurate and scalable. This is just an example. You may modify it as per your needs.&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%2Fbobpmcm719k8v6grll9u.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%2Fbobpmcm719k8v6grll9u.png" alt="Image description" width="800" height="1395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maylad31/setfit_query_preprocesing" rel="noopener noreferrer"&gt;Link to the Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>python</category>
    </item>
    <item>
      <title>Faiss with sqlite for RAG</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Wed, 11 Dec 2024 12:02:55 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/faiss-with-sqlite-for-rag-3e2b</link>
      <guid>https://dev.to/mayank_laddha_ml/faiss-with-sqlite-for-rag-3e2b</guid>
      <description>&lt;p&gt;Want to use faiss for Local RAG? Okay, but where to store my chunks(metadata). &lt;br&gt;
Solution: Connect faiss with sqlite(or any other sql). &lt;br&gt;
How: Keep vectors in faiss, data in sqlite.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use faiss for vector data(what it has been made for) and sqlite for normal data(what it has been made for).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Often, you already have a database. You might only need to create an additional table or columns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You get support for full text search in most of the database engines: be it sqlite or postgres. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, you can try pgvector, but using FAISS comes with its own advantages. Ultimately, it depends upon your use case.&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%2F1sg5amxy7smtsidbois8.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%2F1sg5amxy7smtsidbois8.png" alt="Image description" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maylad31/vector_sqlite" rel="noopener noreferrer"&gt;Link to the repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Detecting Hallucinations in LLMs with Discrete Semantic Entropy and Perplexity</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Thu, 05 Dec 2024 08:52:05 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/detecting-hallucinations-in-llms-with-discrete-semantic-entropy-and-perplexity-3jck</link>
      <guid>https://dev.to/mayank_laddha_ml/detecting-hallucinations-in-llms-with-discrete-semantic-entropy-and-perplexity-3jck</guid>
      <description>&lt;p&gt;When working with large language models (LLMs), spotting hallucinations can be tricky. Instead of relying solely on an LLM as the judge (which can still make mistakes, and many evaluation frameworks use only that for hallucination detection), we can use perplexity, entailment, and discrete semantic entropy to better identify potential hallucinations. Although I’m using an LLM here to detect entailment, that’s not necessary. That said, this method works best for questions with straightforward, factual answers—those that aren’t too vague or subjective. What do you think about using these combined metrics for better hallucination detection? I understand the code can be improved/optimized, but the goal was to quickly test how it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openai import OpenAI
import numpy as np
from pydantic import BaseModel
import time

client = OpenAI(api_key="key")

class CheckEntailment(BaseModel):
    label: str

def check_entailment(fragment1: str, fragment2: str) -&amp;gt; bool:
    """check entailment"""
    messages = [
        {
            "role": "user",
            "content": f"""You have two responses from a large language model. 
                 Check if the meaning of one repsonse is entailed by the other, or if there is a contradiction. 
                 Return '0' if entailment. Return '1' if contradiction. 
                 Return only the label, without any explanation. 
                 \n Response1: \n {fragment1}\n\n Response2: \n {fragment2}""",
        }
    ]
    completion = client.beta.chat.completions.parse(
        model="gpt-4o-mini",
        messages=messages,
        temperature=0.1,
        logprobs=True,
        top_logprobs=2,
        response_format=CheckEntailment,
    )
    entailment = False
    # print(completion.choices[0].logprobs.content[3].top_logprobs)
    for top_logprob in completion.choices[0].logprobs.content[3].top_logprobs:
        print(top_logprob.token, np.round(np.exp(top_logprob.logprob), 2))
        if "0" in top_logprob.token and np.exp(top_logprob.logprob) &amp;gt; 0.7:
            entailment = True
    return entailment


def calculate_entropy(probs):
    """
    Calculate the entropy
    """
    probs = np.array(probs)
    probs = probs / probs.sum()
    probs = probs[probs &amp;gt; 0]
    entropy = -np.sum(probs * np.log2(probs))
    return entropy


some_tricky_questions = [
    "Which state does Alabama have its longest border with? Is it Florida or Tennessee?",
    "Who hosted the British Gameshow Countdown in 2007: a) Nick Hewer b) Richard Whiteley c) Jeff Stelling?",
    "Trivia question: Which Black Eyed Peas band member was the only one to host Saturday Night Live?",
    "What year in the 1980s were the FIS Alpine World Ski Championships hosted in Argentina?",
    "How many Brazilian numbers are there between 1-6?",
    "Which Israeli mathematician founded an online sequences repository in the 1970s?",
    "Write the 7 english words that have three consecutive double letters. No need to provide explanations, just say the words.",
    # adding two questions where it should not hallucinate
    "What is the capital of India?",
    "what is the full form of CPU?",
]


for question in some_tricky_questions:
    print("question", question)
    messages = [{"role": "user", "content": f"{question}"}]
    gpt_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        temperature=0.1,
        logprobs=True,
        max_completion_tokens=60,
    )
    time.sleep(2)
    # get perplexity score using a low temperature response
    logprobs = [token.logprob for token in gpt_response.choices[0].logprobs.content]
    perplexity_score = np.round(np.exp(-np.mean(logprobs)), 2)
    # initialize clusters with the first response
    clusters = [[gpt_response.choices[0].message.content]]
    # generate some more responses using higher temperature and check entailment
    gpt_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        n=7,
        temperature=0.9,
        logprobs=True,
        max_completion_tokens=60,
    )
    time.sleep(2)
    # check entailment and form clusters
    responses = [choice.message.content for choice in gpt_response.choices]

    for response in responses[1:]:
        found_cluster = False
        for cluster in clusters:
            if check_entailment(cluster[0], response):
                cluster.append(response)
                found_cluster = True
                break
        if not found_cluster:
            clusters.append([response])
    cluster_probs = [len(cluster) / (len(responses) + 1) for cluster in clusters]
    discrete_entropy = calculate_entropy(cluster_probs)
    print("clusters", clusters)
    print("no of clusters", len(clusters))
    print("perplexity", perplexity_score)
    print("entropy", discrete_entropy)

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

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Simple Code to Understand Self-Reflection (Agentic Design Pattern)</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Sun, 01 Dec 2024 07:29:45 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/simple-code-to-understand-self-reflection-agentic-design-pattern-58o3</link>
      <guid>https://dev.to/mayank_laddha_ml/simple-code-to-understand-self-reflection-agentic-design-pattern-58o3</guid>
      <description>&lt;p&gt;Reflection/self-reflection is a bit underrated. If your application relies on prompting, I strongly recommend exploring this concept. It is not hard to implement and reflective techniques can help in iteratively refining llm responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from mirascope.core import BaseMessageParam, ResponseModelConfigDict, openai
from pydantic import BaseModel
import os

os.environ["OPENAI_API_KEY"] = ""


class Review(BaseModel):
    issues: list[str]
    is_good: bool

    model_config = ResponseModelConfigDict(strict=True)


class Story(BaseModel):
    story: str

    model_config = ResponseModelConfigDict(strict=True)


class StoryWriter(BaseModel):
    keywords: list[str]
    generator_history: list[openai.OpenAIMessageParam] = []

    @openai.call(
        "gpt-4o-mini",
        response_model=Story,
        json_mode=True,
        call_params={"temperature": 0.8},
    )
    def generator(self, query: str) -&amp;gt; list[openai.OpenAIMessageParam]:
        return [
            BaseMessageParam(
                role="system",
                content="You are an expert in writing short moral stories for kids below the age of 10.",
            ),
            *self.generator_history,
        ]

    @openai.call(
        "gpt-4o-mini",
        response_model=Review,
        json_mode=True,
        call_params={"temperature": 0.1},
    )
    def reviewer(self, story: str) -&amp;gt; list[openai.OpenAIMessageParam]:
        return [
            BaseMessageParam(
                role="system",
                content="You are an expert in reviewing short moral stories for kids below the age of 10, checking whether all the keywords were used effectively and identifying issues related to relevance and ease of understanding",
            ),
            BaseMessageParam(
                role="user",
                content=f""" Review the given moral story for kids. Check if the story uses all the given keywords. Also check if the story is reasonably realistic, engaging and uses basic vocabulary that is easy to understand for kids below the age of 10. Return the issues. Finally, return True if the moral story is good enough for kids and contains all the keywords. \n story: {story} \n keywords: {self.keywords}""",
            ),
        ]

    def run(self, steps=3) -&amp;gt; str:
        query = f"""Generate a moral story for kids, using all the given keywords. Return only the story. {self.keywords}"""
        self.generator_history += [
            BaseMessageParam(role="user", content=query),
        ]
        story = ""
        for _ in range(steps):
            generator_response = self.generator(query)
            story = generator_response.story
            reviewer_response = self.reviewer(story)
            if reviewer_response.is_good:
                break
            query = f"""Use the given feedback to improve the story. Return only the story."""
            self.generator_history += [
                BaseMessageParam(role="assistant", content=generator_response.story),
                BaseMessageParam(
                    role="user",
                    content=" ".join(reviewer_response.issues) + " " + query,
                ),
            ]
        print(self.generator_history)
        return story


story = StoryWriter(
    keywords=[
        "elephant",
        "boy",
        "strong",
        "funny",
        "good",
        "ride",
        "Nikolas",
        "road",
        "cap",
        "car",
    ]
).run()
print("==================")
print("result", story)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Using DSPy(COPRO) to refine prompt instructions</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Fri, 29 Nov 2024 11:41:26 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/using-dspycopro-to-refine-prompt-instructions-2ki0</link>
      <guid>https://dev.to/mayank_laddha_ml/using-dspycopro-to-refine-prompt-instructions-2ki0</guid>
      <description>&lt;p&gt;I recently tested DSPy with phi3, aiming to use COPRO to refine the prompt instructions and achieve the best score. I intentionally did not use assertions.&lt;/p&gt;

&lt;p&gt;Here’s what I liked:&lt;/p&gt;

&lt;p&gt;I used the CoLA dataset, which includes sentences from 23 linguistic publications annotated for grammaticality, I wanted outputs with "just the label" (no explanations or extra words). DSPy generated instructions that performed well, ensuring grammatical correctness:&lt;/p&gt;

&lt;p&gt;Original Instruction: Check if the sentence is correct(1) or not(0)&lt;/p&gt;

&lt;p&gt;Optimized Instruction: Determine if the given sentence adheres to standard English grammar rules by outputting a '1' for grammatically correct and '0' for incorrect.&lt;/p&gt;

&lt;p&gt;Both the BootstrapFewShot and Signature Optimizer are great starting points.&lt;/p&gt;

&lt;p&gt;What could be improved:&lt;/p&gt;

&lt;p&gt;DSPy states, "Once the training is done, you’ll have better instructions and prefixes to edit in the signature manually." However, I believe that post-compilation, the tool should be ready to predict. The instruction optimizer needs more flexibility for easier tweaking, such as better compatibility with other languages. Alternatively, creating a program for the signature optimizer to tweak prompts as needed would be beneficial.&lt;/p&gt;

&lt;p&gt;It is a cool concept but I guess as a framework it still needs improvement. Connect with me if you have an interesting project/common interests. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maylad31/dspy-phi3" rel="noopener noreferrer"&gt;Gihub repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>python</category>
      <category>rag</category>
    </item>
    <item>
      <title>An easy way to remove PII before sending to LLMs</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Tue, 19 Nov 2024 18:29:32 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/an-easy-way-to-remove-pii-before-sending-to-llms-2n6</link>
      <guid>https://dev.to/mayank_laddha_ml/an-easy-way-to-remove-pii-before-sending-to-llms-2n6</guid>
      <description>&lt;p&gt;Not all scenarios demand perfect anonymization. In less critical cases, a lightweight anonymization pipeline can suffice. Here, I share a Python-based approach leveraging GLiNER, Faker, and rapidfuzz to anonymize text by replacing sensitive entities with realistic placeholders.&lt;/p&gt;

&lt;p&gt;The code first identifies sensitive entities (like names, countries, and professions) using GLiNER. Then, it replaces these entities with fake counterparts generated by Faker. Approximate string matching (rapidfuzz) ensures even variations in the text are anonymized. After processing with the LLM, the original entities are restored.&lt;/p&gt;

&lt;p&gt;This method is designed for non-critical use cases where perfect anonymization isn't mandatory. For example, analyzing reviews or answering a query that comes to the chatbot on your website without saving data generally fall under less critical cases. The code is not perfect but good enough to get you started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from gliner import GLiNER
from faker import Faker
from faker.providers import job
import google.generativeai as genai
import re
import warnings
from rapidfuzz import process, utils
warnings.filterwarnings("ignore")

genai.configure(api_key="key")
model_llm = genai.GenerativeModel("gemini-1.5-flash-002")
fake = Faker()
fake.add_provider(job)
model_gliner = GLiNER.from_pretrained("urchade/gliner_small-v2.1")

# let's say we have this prompt along with context that we want to anonymize before sending to LLM
prompt= f"""Given the context, answer the question. \n context: Hi, I am Mayank Laddha.  I lives in India. I love my country. But I would like to go to Singapore once. I am a software developer.\n question: Where does Mayank Laddha want to go?"
"""
# Perform entity prediction
labels = ["Person", "Country", "Profession"]
entities = model_gliner.predict_entities(prompt, labels, threshold=0.4)
print(entities)

# create a replacement dictionary
replacement = {}
for entity in entities: 
    if "Person" in entity["label"] and entity["text"] not in replacement:
        fake_set = {fake.name() for _ in range(3)}
        fake_set.discard(entity["text"])
        new_name = fake_set.pop()
        replacement[entity["text"]] = new_name
    elif "Country" in entity["label"] and entity["text"] not in replacement:
        name_set = {fake.country() for _ in range(10)}
        print(name_set)
        name_set.discard(entity["text"])
        new_name = name_set.pop()
        replacement[entity["text"]] = new_name
    elif "Profession" in entity["label"] and entity["text"] not in replacement:
        name_set = {fake.job() for _ in range(20)}
        name_set = {k for k in name_set if len(k.split())==1}
        print(name_set)
        name_set.discard(entity["text"])
        new_name = name_set.pop()
        replacement[entity["text"]] = new_name

#also create a reverse dictionary
replacement_reversed = {v: k for k, v in replacement.items()}

#perform replacement
for k, v in replacement.items():
    # Split text into a list of words
    words = prompt.split()  
    n = len(k.split()) 
    # so the key appears fully in choices
    choices = [' '.join(words[i:i+n]) for i in range(len(words) - n + 1)] 
    matches = process.extract(k, choices, limit=1, processor=utils.default_process)
    for match in matches:
        if match[1]&amp;gt;80:
            prompt = re.sub(match[0], v, prompt, flags=re.IGNORECASE)

#prompt
response = model_llm.generate_content(prompt)
content = response.text
print("llm response",content)

#perform replacement again
for k, v in replacement_reversed.items():
    words = content.split()  
    n = len(k.split())
    choices = [' '.join(words[i:i+n]) for i in range(len(words) - n + 1)]
    matches = process.extract(k, choices, limit=1, processor=utils.default_process)
    for match in matches:
        if match[1]&amp;gt;80:
            content = re.sub(match[0], v, content, flags=re.IGNORECASE)

print("final result", content)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Graph + LLM or simply LLM for summarization?</title>
      <dc:creator>Mayank Laddha</dc:creator>
      <pubDate>Thu, 14 Nov 2024 19:24:34 +0000</pubDate>
      <link>https://dev.to/mayank_laddha_ml/graph-llm-or-simply-llm-for-summarization-c6a</link>
      <guid>https://dev.to/mayank_laddha_ml/graph-llm-or-simply-llm-for-summarization-c6a</guid>
      <description>&lt;p&gt;let's say I have some documents. I want to generate a summary (get an overall essence) may be based on certain criteria. What do you think is a better approach: creating a graph and then using LLM to generate summary or answer queries that involve most of the documents? Or create summaries of individual documents and then may be create a final summary. I came up with a code where i tried to get some news articles on a topic, create a graph and summarize. It is not perfect and I am trying to improve. But is it worth creating a graph? I have some opinions but I would love to yours..&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import networkx as nx
from duckduckgo_search import DDGS
import requests
from bs4 import BeautifulSoup
import google.generativeai as genai
import typing_extensions as typing
import ast
import matplotlib.pyplot as plt
import time
import pickle


genai.configure(api_key="key")
model = genai.GenerativeModel("gemini-1.5-flash-002")
topic = "climate change"

#get data 
docs = {}
headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:84.0) Gecko/20100101 Firefox/84.0",
}
results = DDGS().news(f'{topic}',timelimit='w', max_results=10)



for news in results:
    try:
        page = requests.get(news['url'], headers=headers,timeout=10).text
        soup = BeautifulSoup(page, "html.parser")
        body = soup.find('body')
        docs[news["url"]] = body.get_text(separator="\n",strip=True)
    except:
        print(f"unable to fetch {news['url']}")


#create graph
class Entity(typing.TypedDict):
    name: str
    type: str

class Relation(typing.TypedDict):
    source: str
    target: str
    relationship: str


G = nx.Graph()
possible_entities = ["Country", "Person", "Location", "Event", "Topic", "Policy", "Technology", "Other"]
for url in docs:
    try:
        response = model.generate_content(
        f"""news: {docs[url]} \n Based on the news article above, identify only the most relevant entities that capture the essence of the news.  Entity types must be strictly limited to the following: {possible_entities}. No other types are allowed. If no relevant entity is present, return an empty list. Return each entity along with its type.""",
        generation_config=genai.GenerationConfig(
            response_mime_type="application/json", response_schema=list[Entity]
        ),
        )
        entities = ast.literal_eval(response.text) 
        entity_dict = {}

        for entity in entities:
            if entity["name"] in possible_entities or entity["name"].lower().startswith("err"):
                continue
            entity_dict[entity["name"]] = entity["type"]
        if not entity_dict:
            continue
        print(entity_dict)
        response = model.generate_content(
        f"""news: {docs[url]} \n entities: {list(entity_dict.keys())} \n Based on the news article and the list of entities above, return the list of source and target entity pairs that have a clear relationship between them.(source name, target name,relationship). Choose entities only from the provided list. Relationship can include sentiment and opinions as well and should be 1 - 2 sentences, mentioning the entities and describing the relationship between them.""",
        generation_config=genai.GenerationConfig(
            response_mime_type="application/json", response_schema=list[Relation]
        ),
        )
        relationships = ast.literal_eval(response.text)
        print(relationships)
        for relation in relationships:
            source  = relation["source"].lower().strip()
            target = relation["target"].lower().strip()
            if source not in G:
                G.add_node(source)
            if target not in G:
                G.add_node(target)
            if G.has_edge(source,target):
                data = G[source][target]
                data["relationship"] = data["relationship"] + "\n" +relation["relationship"]+f"{url}"
            else:
                G.add_edge(source,target,relationship=relation["relationship"]+f"{url}")
        time.sleep(5)
    except Exception as e:
        print(e)

def shorten_edge_labels(edge_labels, max_characters=12):
    return {k: edge_labels[k][:max_characters] + '.. ' for k in edge_labels}

G.remove_nodes_from(list(nx.isolates(G)))

xml='\n'.join(nx.generate_graphml(G))
print(xml)
time.sleep(30)

response = model.generate_content(
        f"""graph: {xml} \n You are an expert in news storytelling. Based on the knowledge graph above, generate a compelling, professional and captivating story related to {topic} in 500-800 words. Be creative and effectively utilise relationship information between different news articles, but do not make up things. Exclude irrelevant information. Provide source URLs so the user can read more. Return only the story, without a title or additional explanation.""",)
print(response.text)

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

&lt;/div&gt;



</description>
      <category>llm</category>
      <category>ai</category>
      <category>python</category>
    </item>
  </channel>
</rss>
