<?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: Mahmoud Mabrouk</title>
    <description>The latest articles on DEV Community by Mahmoud Mabrouk (@mmabrouk).</description>
    <link>https://dev.to/mmabrouk</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%2F1011914%2F279e6598-de85-4f6c-9783-4a2caab668a6.jpeg</url>
      <title>DEV Community: Mahmoud Mabrouk</title>
      <link>https://dev.to/mmabrouk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mmabrouk"/>
    <language>en</language>
    <item>
      <title>How to Actually Start Contributing to Open Source AI Projects (A Step-by-Step Guide)</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Wed, 17 Dec 2025 23:00:00 +0000</pubDate>
      <link>https://dev.to/mmabrouk/how-to-actually-start-contributing-to-open-source-ai-projects-a-step-by-step-guide-357d</link>
      <guid>https://dev.to/mmabrouk/how-to-actually-start-contributing-to-open-source-ai-projects-a-step-by-step-guide-357d</guid>
      <description>&lt;p&gt;So you've decided to contribute to open source. Great choice. But where do you actually start?&lt;/p&gt;

&lt;p&gt;This guide will walk you through the exact process I've seen work for dozens of contributors to &lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; and other AI projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Actually Start Contributing (The Step-by-Step Guide)
&lt;/h2&gt;

&lt;p&gt;You've picked a project. Now what? Here's exactly how to make your first contribution:&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Build Trust (Week 1)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Don't jump straight into code.&lt;/strong&gt; Start small and build credibility:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Join the community&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join Slack/Discord&lt;/li&gt;
&lt;li&gt;Introduce yourself (mention you're looking to contribute)&lt;/li&gt;
&lt;li&gt;Read the last 50+ messages to understand the vibe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Fix documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Found a typo? Fix it.&lt;/li&gt;
&lt;li&gt;Unclear setup instructions? Clarify them.&lt;/li&gt;
&lt;li&gt;Missing examples? Add one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These PRs get merged fast and show you can follow the contribution process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Help others&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer questions in issues/discussions&lt;/li&gt;
&lt;li&gt;Reproduce bugs for others&lt;/li&gt;
&lt;li&gt;Share your setup experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; Maintainers notice helpful community members. When you submit a code PR later, they'll remember you positively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Learn the Codebase (Week 2-3)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Don't just read the code. Use it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Build something with it&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow tutorials&lt;/li&gt;
&lt;li&gt;Build a small project&lt;/li&gt;
&lt;li&gt;Try to break it (in a good way)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Run the tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up the dev environment&lt;/li&gt;
&lt;li&gt;Run the test suite&lt;/li&gt;
&lt;li&gt;Understand what's being tested&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Read issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look at closed issues to understand common problems&lt;/li&gt;
&lt;li&gt;Read open issues to see what's needed&lt;/li&gt;
&lt;li&gt;Pay attention to "good first issue" labels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Study merged PRs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See what maintainers approve&lt;/li&gt;
&lt;li&gt;Notice code style patterns&lt;/li&gt;
&lt;li&gt;Understand the review process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Take notes! Document your setup process, confusing parts, and potential improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Make Your First Code Contribution (Week 3-4)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start with the smallest possible change:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Pick a good first issue&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look for labels: "good first issue," "help wanted," "beginner-friendly"&lt;/li&gt;
&lt;li&gt;Comment saying you'd like to work on it&lt;/li&gt;
&lt;li&gt;Ask questions if anything is unclear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Before writing code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discuss your approach in the issue&lt;/li&gt;
&lt;li&gt;Get feedback from maintainers&lt;/li&gt;
&lt;li&gt;Avoid surprises in the PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Write the code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow the project's style guide&lt;/li&gt;
&lt;li&gt;Write tests (this is crucial!)&lt;/li&gt;
&lt;li&gt;Update documentation&lt;/li&gt;
&lt;li&gt;Keep it small (&amp;lt; 200 lines changed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Submit a great PR:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;   ## What
   [Brief description of what you changed]

   ## Why
   [Why this change is needed; reference the issue]

   ## How
   [Brief explanation of your approach]

   ## Testing
   [How you tested this; include steps to reproduce]

   Fixes #123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Be responsive:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to review feedback quickly&lt;/li&gt;
&lt;li&gt;Don't take criticism personally&lt;/li&gt;
&lt;li&gt;Ask questions when you don't understand&lt;/li&gt;
&lt;li&gt;Say "thank you" when the PR is merged&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 4: Become a Regular Contributor (Ongoing)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Now you're part of the community:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Keep contributing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tackle slightly harder issues&lt;/li&gt;
&lt;li&gt;Help review other PRs (learn from them)&lt;/li&gt;
&lt;li&gt;Suggest improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Specialize:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Become the go-to person for a specific area&lt;/li&gt;
&lt;li&gt;"The evaluation metrics person"&lt;/li&gt;
&lt;li&gt;"The docs person"&lt;/li&gt;
&lt;li&gt;"The TypeScript integration person"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Build relationships:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thank maintainers for reviews&lt;/li&gt;
&lt;li&gt;Help onboard new contributors&lt;/li&gt;
&lt;li&gt;Participate in community calls/discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Think long-term:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This isn't about one PR. It's about becoming a trusted contributor.&lt;/li&gt;
&lt;li&gt;Some contributors become maintainers&lt;/li&gt;
&lt;li&gt;Some get hired by the company&lt;/li&gt;
&lt;li&gt;Some build their network and reputation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Don't Do This:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Submitting huge PRs without discussion&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I rewrote the entire evaluation system!"&lt;/li&gt;
&lt;li&gt;Maintainers will reject these almost instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Ignoring contribution guidelines&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not running tests&lt;/li&gt;
&lt;li&gt;Not following code style&lt;/li&gt;
&lt;li&gt;Not writing documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Getting discouraged by rejection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your first PR might get rejected&lt;/li&gt;
&lt;li&gt;This is normal and part of learning&lt;/li&gt;
&lt;li&gt;Ask for feedback and try again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Working on issues already assigned&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if someone is already working on it&lt;/li&gt;
&lt;li&gt;Don't duplicate effort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Being defensive about feedback&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review is not personal criticism&lt;/li&gt;
&lt;li&gt;Learn from every comment&lt;/li&gt;
&lt;li&gt;Thank reviewers for their time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Do This Instead:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Communicate early and often&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I'd like to work on this, here's my approach"&lt;/li&gt;
&lt;li&gt;"I'm stuck on X, can someone help?"&lt;/li&gt;
&lt;li&gt;"Thanks for the review, let me fix that"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Start small, think big&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First PR: Fix a typo&lt;/li&gt;
&lt;li&gt;Second PR: Add a test&lt;/li&gt;
&lt;li&gt;Third PR: Add a feature&lt;/li&gt;
&lt;li&gt;Tenth PR: Architect a major improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Be patient and persistent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good contributions take time&lt;/li&gt;
&lt;li&gt;Some PRs need multiple rounds of review&lt;/li&gt;
&lt;li&gt;That's how you learn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Help others&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer questions in issues&lt;/li&gt;
&lt;li&gt;Review other PRs (even as a beginner)&lt;/li&gt;
&lt;li&gt;Share what you learned&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Career Impact: Real Talk
&lt;/h2&gt;

&lt;p&gt;Let me share what I've seen happen to developers who contribute to open source:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Portfolio Effect
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Traditional portfolio:&lt;/strong&gt; "Todo app, weather app, portfolio website"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open source portfolio:&lt;/strong&gt; "Contributed evaluation metrics to DeepEval, added RAG features to LlamaIndex, fixed critical bugs in Instructor"&lt;/p&gt;

&lt;p&gt;Which would you hire?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Network Effect
&lt;/h3&gt;

&lt;p&gt;One contributor I know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Started fixing docs in an AI project&lt;/li&gt;
&lt;li&gt;Became a regular contributor&lt;/li&gt;
&lt;li&gt;Got noticed by the maintainers&lt;/li&gt;
&lt;li&gt;Was referred for a job at their company&lt;/li&gt;
&lt;li&gt;Got hired as a junior ML engineer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Timeline: 4 months from first PR to job offer.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Learning Effect
&lt;/h3&gt;

&lt;p&gt;You'll learn more in 3 months of open source contributions than 6 months of tutorials because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're reading production code&lt;/li&gt;
&lt;li&gt;You're getting feedback from senior engineers&lt;/li&gt;
&lt;li&gt;You're solving real problems&lt;/li&gt;
&lt;li&gt;You're learning tools and workflows used in industry&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Confidence Effect
&lt;/h3&gt;

&lt;p&gt;There's a massive difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I learned LangChain from a tutorial"&lt;/li&gt;
&lt;li&gt;"I contributed features to LlamaIndex that are used by thousands of developers"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One shows you can follow instructions. The other shows you can create value.&lt;/p&gt;




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

&lt;p&gt;Here's your action plan for the next 30 days:&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 1: Explore
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Pick 2-3 projects from AI open source projects&lt;/li&gt;
&lt;li&gt;[ ] Star them on GitHub&lt;/li&gt;
&lt;li&gt;[ ] Read their README and documentation&lt;/li&gt;
&lt;li&gt;[ ] Join their community channels&lt;/li&gt;
&lt;li&gt;[ ] Try building something small with each&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 2: Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Pick one project to focus on&lt;/li&gt;
&lt;li&gt;[ ] Set up the development environment&lt;/li&gt;
&lt;li&gt;[ ] Run the tests&lt;/li&gt;
&lt;li&gt;[ ] Build something with it&lt;/li&gt;
&lt;li&gt;[ ] Note any issues/improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 3: Contribute
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Find 3 "good first issues"&lt;/li&gt;
&lt;li&gt;[ ] Comment on one saying you'll work on it&lt;/li&gt;
&lt;li&gt;[ ] Submit your first PR (even if it's just docs)&lt;/li&gt;
&lt;li&gt;[ ] Respond to feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 4: Grow
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Submit a code contribution&lt;/li&gt;
&lt;li&gt;[ ] Help answer a question in issues&lt;/li&gt;
&lt;li&gt;[ ] Review another contributor's PR&lt;/li&gt;
&lt;li&gt;[ ] Plan your next contribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After 30 days, you'll have:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ At least 1 merged PR&lt;/li&gt;
&lt;li&gt;✅ Understanding of how professional development works&lt;/li&gt;
&lt;li&gt;✅ Connections in the AI/ML community&lt;/li&gt;
&lt;li&gt;✅ Something real to put on your resume&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Contributing to open source isn't just about giving back to the community (though that's great too).&lt;/p&gt;

&lt;p&gt;It's about &lt;strong&gt;investing in yourself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every PR you submit is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A line on your resume&lt;/li&gt;
&lt;li&gt;A conversation with a potential mentor&lt;/li&gt;
&lt;li&gt;A demonstration of your skills&lt;/li&gt;
&lt;li&gt;A step toward your dream job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best time to start was yesterday. The second-best time is today.&lt;/p&gt;

&lt;p&gt;Pick a project. Join their community. Submit your first PR this week.&lt;/p&gt;

&lt;p&gt;Your future self will thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ready to Make Your First Contribution? Start with Agenta
&lt;/h2&gt;

&lt;p&gt;If you're looking for the perfect project to start your open source journey, I'd love to invite you to contribute to &lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Agenta is perfect for your first contribution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beginner-friendly community:&lt;/strong&gt; We have 70+ contributors, many of whom started as complete beginners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active mentorship:&lt;/strong&gt; Our maintainers actively review PRs and mentor new contributors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-world impact:&lt;/strong&gt; Work on production features used by companies building LLM applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-stack learning:&lt;/strong&gt; Contribute to Python/FastAPI backend or React/TypeScript frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good first issues:&lt;/strong&gt; We maintain a list of beginner-friendly issues to get you started&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Career opportunities:&lt;/strong&gt; Many of our contributors have landed ML engineer roles at top companies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you'll work on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLM prompt management and evaluation&lt;/li&gt;
&lt;li&gt;Observability and tracing features&lt;/li&gt;
&lt;li&gt;UI/UX improvements&lt;/li&gt;
&lt;li&gt;New LLM provider integrations&lt;/li&gt;
&lt;li&gt;Documentation and examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Getting started is easy:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check out the &lt;a href="https://github.com/Agenta-AI/agenta/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contributing Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Browse &lt;a href="https://github.com/Agenta-AI/agenta/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;good first issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join our &lt;a href="https://join.slack.com/t/agenta-hq/shared_invite/zt-37pnbp5s6-mbBrPL863d_oLB61GSNFjw" rel="noopener noreferrer"&gt;Slack community&lt;/a&gt; and introduce yourself&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm a maintainer of Agenta, and I personally commit to helping you make your first contribution. Drop a message in our Slack, and let's get you started!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;github.com/Agenta-AI/agenta&lt;/a&gt;&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.firsttimersonly.com/" rel="noopener noreferrer"&gt;First Timers Only&lt;/a&gt;; Find beginner-friendly issues&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensource.guide/how-to-contribute/" rel="noopener noreferrer"&gt;How to Contribute to Open Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://skills.github.com/" rel="noopener noreferrer"&gt;GitHub Skills&lt;/a&gt;; Learn Git and GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Found this helpful? Drop a ❤️ and share it with other developers!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or success stories? Drop them in the comments below! 👇&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>career</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
    <item>
      <title>8 Beginner-Friendly AI Open Source Projects to Kickstart Your Career in 2026</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Wed, 17 Dec 2025 14:04:17 +0000</pubDate>
      <link>https://dev.to/mmabrouk/8-beginner-friendly-ai-open-source-projects-to-kickstart-your-career-in-2026-1d45</link>
      <guid>https://dev.to/mmabrouk/8-beginner-friendly-ai-open-source-projects-to-kickstart-your-career-in-2026-1d45</guid>
      <description>&lt;h2&gt;
  
  
  Why Contributing to AI Open Source is Your Secret Weapon
&lt;/h2&gt;

&lt;p&gt;If you're a junior developer trying to break into tech in 2025, I'm going to share something that changed my career: &lt;strong&gt;contributing to open source AI projects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not taking more courses. Not building another todo app. &lt;strong&gt;Open source contributions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the truth that nobody tells you:&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 AI is Eating the World (And You Can Be Part of It)
&lt;/h3&gt;

&lt;p&gt;Every company (from startups to Fortune 500s) is racing to integrate AI into their products. But here's the problem: there aren't enough developers who actually understand how to build with LLMs in production.&lt;/p&gt;

&lt;p&gt;When you contribute to open source AI projects, you're not just learning theory. You're working with production-grade code that powers real applications. You're understanding how AI systems actually work under the hood. You're building expertise that companies desperately need right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  💼 Your GitHub Profile Beats Your Resume
&lt;/h3&gt;

&lt;p&gt;I've reviewed hundreds of applications for junior roles. You know what makes candidates stand out?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not the degree. Not the bootcamp certificate. It's the GitHub profile.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I see a candidate with merged pull requests to popular AI projects, I know they can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read and understand complex codebases&lt;/li&gt;
&lt;li&gt;Collaborate with teams asynchronously&lt;/li&gt;
&lt;li&gt;Write code that passes real-world code reviews&lt;/li&gt;
&lt;li&gt;Take feedback and iterate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's exactly what we hire for.&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 The Best Education is Free
&lt;/h3&gt;

&lt;p&gt;Forget paying $10,000 for bootcamps. The best AI/ML education is happening in public, for free, right now on GitHub.&lt;/p&gt;

&lt;p&gt;When you contribute to open source, you get mentorship from senior engineers reviewing your code. You get real-world experience with CI/CD and testing and documentation. You get industry best practices baked into every PR. You get portfolio projects that actually matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Network Effects
&lt;/h3&gt;

&lt;p&gt;Open source is how you meet the people who will change your career.&lt;/p&gt;

&lt;p&gt;Maintainers become mentors. Contributors become colleagues. GitHub issues become job referrals.&lt;/p&gt;

&lt;p&gt;I've seen it happen countless times. A junior contributor becomes a regular, gets noticed by the maintainers, and gets offered a job (or at least a strong referral).&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Choose Where to Contribute
&lt;/h2&gt;

&lt;p&gt;Before we dive into the projects, let's talk strategy. Not all open source projects are created equal, especially for beginners.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ What to Look For:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Active Maintenance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recent commits (within the last week)&lt;/li&gt;
&lt;li&gt;Maintainers who respond to issues and PRs&lt;/li&gt;
&lt;li&gt;Regular releases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Welcoming Community&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Good first issue" labels&lt;/li&gt;
&lt;li&gt;Contribution guidelines (&lt;code&gt;CONTRIBUTING.md&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Code of conduct&lt;/li&gt;
&lt;li&gt;Responsive on Slack/Discord&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Clear Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup instructions that actually work&lt;/li&gt;
&lt;li&gt;Architecture documentation&lt;/li&gt;
&lt;li&gt;API references&lt;/li&gt;
&lt;li&gt;Example code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Your Interests + Skills&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you like frontend? Pick projects with TypeScript/React&lt;/li&gt;
&lt;li&gt;Prefer backend? Look for Python/FastAPI projects&lt;/li&gt;
&lt;li&gt;Interested in a specific AI domain? (RAG, agents, evaluation, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Size Sweet Spot&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt; 100k+ star projects (too big, too complex)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt; &amp;lt;500 star projects (might not be maintained)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sweet spot:&lt;/strong&gt; 3k-30k stars (active, manageable, impactful)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ What to Avoid:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Projects by big tech (Google, Microsoft); often too complex, enterprise-focused&lt;/li&gt;
&lt;li&gt;Projects with no recent activity&lt;/li&gt;
&lt;li&gt;Projects with toxic communities (check how maintainers respond to issues)&lt;/li&gt;
&lt;li&gt;Projects with unclear contribution guidelines&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The 8 Best AI Open Source Projects for Beginners
&lt;/h2&gt;

&lt;p&gt;I've personally vetted these projects based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beginner-friendliness:&lt;/strong&gt; Clear codebase, good docs, welcoming community&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning value:&lt;/strong&gt; You'll gain practical AI/ML skills&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Career impact:&lt;/strong&gt; These technologies are in demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diversity:&lt;/strong&gt; Different categories so you can follow your interests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. 🎯 Agenta (The Open-Source LLMOps Platform)
&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%2Fimagedelivery.net%2FUNvjPBCIZFONpkVPQTxVuA%2F6fa19a9d-9785-4acf-5d08-e81b1e38b100%2Flarge" 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%2Fimagedelivery.net%2FUNvjPBCIZFONpkVPQTxVuA%2F6fa19a9d-9785-4acf-5d08-e81b1e38b100%2Flarge" alt="Agenta Platform" width="2839" height="1596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Agenta is a complete platform for building production-grade LLM applications. It provides prompt management, LLM evaluation, and observability. Everything you need to take an LLM app from prototype to production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python, FastAPI, PostgreSQL, MongoDB, Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; TypeScript, React, Next.js, Ant Design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevOps:&lt;/strong&gt; Docker, Docker Compose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 3,529&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 112&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Contributors:&lt;/strong&gt; 52+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can contribute to both backend (Python/FastAPI) and frontend (React/TypeScript), depending on your interests. This is full-stack learning.&lt;/p&gt;

&lt;p&gt;You'll work on features that companies actually use. Prompt versioning. A/B testing. Evaluation pipelines. Observability dashboards. These are real production features.&lt;/p&gt;

&lt;p&gt;The codebase is well-organized with clear separation between services. Great for learning microservices patterns. The team is super responsive to PRs and actively mentors contributors. The community already has 70+ contributors from around the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building full-stack AI applications&lt;/li&gt;
&lt;li&gt;LLM prompt management and versioning&lt;/li&gt;
&lt;li&gt;Evaluation pipelines and metrics&lt;/li&gt;
&lt;li&gt;Observability and tracing patterns&lt;/li&gt;
&lt;li&gt;Working with vector databases&lt;/li&gt;
&lt;li&gt;CI/CD for AI applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎨 &lt;strong&gt;UI/UX improvements:&lt;/strong&gt; Make the playground more intuitive&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Add new evaluators:&lt;/strong&gt; Implement evaluation metrics (toxicity, bias, etc.)&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Improve setup guides, add tutorials&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;LLM integrations:&lt;/strong&gt; Add support for new providers (Anthropic, Mistral, etc.)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Testing:&lt;/strong&gt; Improve test coverage&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Observability features:&lt;/strong&gt; Enhance tracing and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Getting Started:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the &lt;a href="https://github.com/Agenta-AI/agenta/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contributing Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check out &lt;a href="https://github.com/Agenta-AI/agenta/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;good first issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://join.slack.com/t/agenta-hq/shared_invite/zt-37pnbp5s6-mbBrPL863d_oLB61GSNFjw" rel="noopener noreferrer"&gt;Slack community&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;github.com/Agenta-AI/agenta&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;👋 Full disclosure:&lt;/strong&gt; I'm one of the maintainers of Agenta, and I'm actively looking for contributors! We have a welcoming community and love mentoring junior developers. If you're interested in LLMOps, this is a great place to start.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. 🌐 LiteLLM (Universal LLM API Gateway)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; LiteLLM is like a universal adapter for LLMs. It lets you call 100+ LLM APIs (OpenAI, Anthropic, Gemini, Mistral, etc.) using the same OpenAI-compatible format. Plus, it includes cost tracking, load balancing, and fallbacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Async Python, Pydantic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy:&lt;/strong&gt; FastAPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 32,548&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 1,346&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Active community&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The library solves one problem really well: LLM API abstraction. This makes the codebase easy to understand. Adding support for a new LLM provider follows a clear, repeatable pattern.&lt;/p&gt;

&lt;p&gt;You'll understand how different LLM APIs work and how to build resilient API clients. The documentation has clear examples for every use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working with async Python&lt;/li&gt;
&lt;li&gt;API design and abstraction patterns&lt;/li&gt;
&lt;li&gt;Error handling and retry logic&lt;/li&gt;
&lt;li&gt;Cost tracking and analytics&lt;/li&gt;
&lt;li&gt;Load balancing strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➕ &lt;strong&gt;Add new LLM providers:&lt;/strong&gt; Support for new models/APIs&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Improve error handling:&lt;/strong&gt; Better error messages and retry logic&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Enhance cost tracking:&lt;/strong&gt; More accurate token counting&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Examples for specific use cases&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Coverage for edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;github.com/BerriAI/litellm&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 🤖 Pydantic AI (Agent Framework, The Pydantic Way)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Pydantic AI is an agent framework built by the creators of Pydantic (the most popular Python validation library). It brings type safety and validation to LLM agents, making them more reliable and easier to debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python 3.10+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Pydantic V2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations:&lt;/strong&gt; OpenAI, Anthropic, Gemini, Ollama, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 13,816&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 398&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Built by the Pydantic team&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Pydantic team is known for excellent code quality and documentation. If you know Python type hints and Pydantic, you can contribute. You'll learn how to build reliable AI agents with proper validation. It's a new project with lots of opportunities to contribute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building AI agents with proper validation&lt;/li&gt;
&lt;li&gt;Pydantic advanced patterns&lt;/li&gt;
&lt;li&gt;Type-safe LLM interactions&lt;/li&gt;
&lt;li&gt;Streaming structured outputs&lt;/li&gt;
&lt;li&gt;Tool calling and function execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 &lt;strong&gt;Examples and tutorials:&lt;/strong&gt; Show different agent patterns&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;LLM provider integrations:&lt;/strong&gt; Add new model support&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Validation patterns:&lt;/strong&gt; Improve retry/validation logic&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Expand guides and API docs&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Edge case coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/pydantic/pydantic-ai" rel="noopener noreferrer"&gt;github.com/pydantic/pydantic-ai&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. 📊 RAGAS (RAG Evaluation Framework)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; RAGAS (Retrieval Augmented Generation Assessment) is a framework specifically designed for evaluating RAG pipelines. It provides metrics like Faithfulness, Context Relevancy, Answer Relevancy, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations:&lt;/strong&gt; LangChain, LlamaIndex, OpenAI, Anthropic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; NumPy, Pandas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 11,772&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 284&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Growing community&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RAGAS specializes in RAG evaluation, making it easier to understand than general-purpose frameworks. Each evaluation metric is well-defined and isolated. This makes contributions straightforward.&lt;/p&gt;

&lt;p&gt;RAG is everywhere, and evaluating it is critical. You're solving a real problem. The documentation clearly explains what each metric measures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAG architecture and patterns&lt;/li&gt;
&lt;li&gt;Evaluation metrics and methodologies&lt;/li&gt;
&lt;li&gt;Working with embeddings and vector similarity&lt;/li&gt;
&lt;li&gt;LLM-as-a-judge patterns&lt;/li&gt;
&lt;li&gt;Statistical analysis for AI systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📏 &lt;strong&gt;New evaluation metrics:&lt;/strong&gt; Implement new ways to measure RAG quality&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Improve existing metrics:&lt;/strong&gt; Make them more accurate/efficient&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Visualization:&lt;/strong&gt; Better reporting and dashboards&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; More examples and guides&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Validate metrics work correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/explodinggradients/ragas" rel="noopener noreferrer"&gt;github.com/explodinggradients/ragas&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 🎯 Instructor (Structured Outputs from LLMs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Instructor is the most popular Python library for getting structured outputs from LLMs. It uses Pydantic to validate and parse LLM responses, with automatic retries when validation fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python, TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Pydantic, OpenAI SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports:&lt;/strong&gt; 15+ LLM providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 11,995&lt;/li&gt;
&lt;li&gt;📥 &lt;strong&gt;Downloads:&lt;/strong&gt; 1M+ monthly&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 85&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instructor does one thing exceptionally well: structured LLM outputs. If you know Pydantic, you can contribute. The documentation has tons of examples for every use case.&lt;/p&gt;

&lt;p&gt;Millions of developers use this library. Your contributions matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pydantic validation patterns&lt;/li&gt;
&lt;li&gt;LLM function calling and tool use&lt;/li&gt;
&lt;li&gt;Retry logic and error handling&lt;/li&gt;
&lt;li&gt;Working with multiple LLM providers&lt;/li&gt;
&lt;li&gt;Type-safe API design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 &lt;strong&gt;Examples and recipes:&lt;/strong&gt; Show how to solve specific problems&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;Provider support:&lt;/strong&gt; Add/improve LLM provider integrations&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Better error messages:&lt;/strong&gt; Help users debug validation issues&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Tutorials and guides&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Coverage for different providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/instructor-ai/instructor" rel="noopener noreferrer"&gt;github.com/instructor-ai/instructor&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. 🧪 DSPy (Programming, Not Prompting, LLMs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; DSPy (from Stanford NLP) shifts the paradigm from manual prompt engineering to programming with structured modules. It includes algorithms to automatically optimize prompts based on your data and metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; PyTorch-style modular design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research:&lt;/strong&gt; Stanford NLP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 30,825&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 404&lt;/li&gt;
&lt;li&gt;🎓 &lt;strong&gt;Stanford research project&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You'll learn a completely different way of working with LLMs. You'll stand out. The code is clean and well-documented (research quality from Stanford researchers). You'll understand how to systematically improve prompts instead of guessing.&lt;/p&gt;

&lt;p&gt;This is an academic project with community governance (not enterprise-controlled).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt optimization algorithms&lt;/li&gt;
&lt;li&gt;Modular LLM programming&lt;/li&gt;
&lt;li&gt;Evaluation-driven development&lt;/li&gt;
&lt;li&gt;Bayesian optimization&lt;/li&gt;
&lt;li&gt;Meta-learning concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧮 &lt;strong&gt;New optimizers:&lt;/strong&gt; Implement optimization algorithms&lt;/li&gt;
&lt;li&gt;📝 &lt;strong&gt;Examples:&lt;/strong&gt; Show DSPy patterns for different use cases&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Metrics:&lt;/strong&gt; Add new evaluation metrics&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Tutorials and guides&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Bug fixes:&lt;/strong&gt; Improve stability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/stanfordnlp/dspy" rel="noopener noreferrer"&gt;github.com/stanfordnlp/dspy&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. ✅ DeepEval ("Pytest for LLMs")
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; DeepEval is an evaluation framework that works like pytest. Write tests for your LLM applications and run them in CI/CD. It includes 14+ metrics for RAG, fine-tuning, hallucination detection, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python 3.9+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Pydantic, pytest patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations:&lt;/strong&gt; OpenAI, Anthropic, Cohere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 12,632&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 226&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Growing community&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you know pytest, you'll understand DeepEval immediately. Each metric is isolated and well-defined. Every LLM app needs evaluation (you're solving a universal problem).&lt;/p&gt;

&lt;p&gt;The metrics tell you &lt;em&gt;why&lt;/em&gt; the score is what it is. This makes debugging easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLM evaluation methodologies&lt;/li&gt;
&lt;li&gt;Writing test frameworks&lt;/li&gt;
&lt;li&gt;Metrics design and implementation&lt;/li&gt;
&lt;li&gt;CI/CD for AI applications&lt;/li&gt;
&lt;li&gt;LLM-as-a-judge patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📏 &lt;strong&gt;New evaluation metrics:&lt;/strong&gt; Implement metrics for specific use cases&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Improve existing metrics:&lt;/strong&gt; Make them more accurate&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Better reporting:&lt;/strong&gt; Improve test output and visualization&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Guides and examples&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Meta-tests for the testing framework!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/confident-ai/deepeval" rel="noopener noreferrer"&gt;github.com/confident-ai/deepeval&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8. 🛡️ Guardrails AI (Reliable AI Applications)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Guardrails AI helps you build reliable AI applications by validating inputs and outputs. It includes a hub of pre-built validators (toxicity, PII detection, factual consistency, etc.) that you can combine into "guards."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Python, JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Pydantic validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hub:&lt;/strong&gt; 50+ pre-built validators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Stars:&lt;/strong&gt; 6,155&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Open Issues:&lt;/strong&gt; 18&lt;/li&gt;
&lt;li&gt;🔐 &lt;strong&gt;Focus:&lt;/strong&gt; AI safety and reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it's perfect for beginners:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI safety is critical and getting more attention. Each validator is isolated, making it easy to contribute new ones. The use cases are practical: PII detection, toxicity checking, hallucination detection. You can contribute to Python or JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI safety and validation patterns&lt;/li&gt;
&lt;li&gt;Input/output filtering&lt;/li&gt;
&lt;li&gt;PII detection algorithms&lt;/li&gt;
&lt;li&gt;Toxicity classification&lt;/li&gt;
&lt;li&gt;Building validation pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contribution Opportunities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛡️ &lt;strong&gt;New validators:&lt;/strong&gt; Add validators for specific risks&lt;/li&gt;
&lt;li&gt;🔧 &lt;strong&gt;Improve existing validators:&lt;/strong&gt; Better accuracy/performance&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;Integrations:&lt;/strong&gt; Support for new LLM providers&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; Security best practices&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Tests:&lt;/strong&gt; Validate the validators!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/guardrails-ai/guardrails" rel="noopener noreferrer"&gt;github.com/guardrails-ai/guardrails&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started Today
&lt;/h2&gt;

&lt;p&gt;Pick one project from this list. Star it on GitHub. Read the README. Join their community.&lt;/p&gt;

&lt;p&gt;Then make your first contribution this week.&lt;/p&gt;

&lt;p&gt;The best time to start was yesterday. The second-best time is now.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm a maintainer of &lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt;, an open-source LLMOps platform with 70+ contributors from around the world. I've seen firsthand how open source contributions can transform careers. Many of our contributors have gone on to land ML engineer roles at top companies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're interested in contributing to Agenta or have questions about getting started with open source, feel free to reach out on our &lt;a href="https://join.slack.com/t/agenta-hq/shared_invite/zt-37pnbp5s6-mbBrPL863d_oLB61GSNFjw" rel="noopener noreferrer"&gt;Slack community&lt;/a&gt;. We love mentoring junior developers!&lt;/strong&gt;&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.firsttimersonly.com/" rel="noopener noreferrer"&gt;First Timers Only&lt;/a&gt;; Find beginner-friendly issues&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensource.guide/how-to-contribute/" rel="noopener noreferrer"&gt;How to Contribute to Open Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://skills.github.com/" rel="noopener noreferrer"&gt;GitHub Skills&lt;/a&gt;; Learn Git and GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Found this helpful? Drop a ❤️ and share it with other developers looking to break into AI!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have questions or success stories? Drop them in the comments below! 👇&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.instaclustr.com/education/open-source-ai/top-10-open-source-llms-for-2025/" rel="noopener noreferrer"&gt;Top 10 open source LLMs for 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.projectpro.io/article/llm-project-ideas/881" rel="noopener noreferrer"&gt;40 LLM Projects to Upgrade Your AI Skillset in 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://greennode.ai/blog/best-open-source-ai-platforms" rel="noopener noreferrer"&gt;Best Open-Source AI Platforms for 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/codeelevation/top-10-open-source-machine-learning-projects-every-developer-should-know-2025-4b056ba32543" rel="noopener noreferrer"&gt;Top Open Source Machine Learning Projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Agenta-AI/agenta" rel="noopener noreferrer"&gt;GitHub - Agenta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;GitHub - LiteLLM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pydantic/pydantic-ai" rel="noopener noreferrer"&gt;GitHub - Pydantic AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/explodinggradients/ragas" rel="noopener noreferrer"&gt;GitHub - RAGAS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/instructor-ai/instructor" rel="noopener noreferrer"&gt;GitHub - Instructor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stanfordnlp/dspy" rel="noopener noreferrer"&gt;GitHub - DSPy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/confident-ai/deepeval" rel="noopener noreferrer"&gt;GitHub - DeepEval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/guardrails-ai/guardrails" rel="noopener noreferrer"&gt;GitHub - Guardrails AI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>Create a Custom Playground to your LLM application</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Wed, 16 Apr 2025 15:59:43 +0000</pubDate>
      <link>https://dev.to/agenta/create-a-custom-playground-to-your-llm-application-21j0</link>
      <guid>https://dev.to/agenta/create-a-custom-playground-to-your-llm-application-21j0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article is part of Agenta Launch Week (April 14-18, 2025), where we're announcing new features daily.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; is an open-source platform built for AI engineers and teams working with large language models. Our playground, evaluation tools, and deployment solutions help streamline the entire LLM application development process. As an open-source tool, Agenta gives developers complete control over their AI infrastructure while providing enterprise-grade features for teams of all sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Custom Workflows
&lt;/h2&gt;

&lt;p&gt;LLM applications aren't just single prompts. They include complicated workflow logic with retrieved documents, multiple LLM calls, and various parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAG applications have embedding models, top-K values, and reranking settings&lt;/li&gt;
&lt;li&gt;Agent workflows include reasoning steps and tool calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When Subject Matter Experts (SMEs) like lawyers or doctors test prompts in isolation, the results often differ from what happens in the complete application or in production. The experts can't access the full application context or modify critical parameters. As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SMEs working on prompts without full application context get unexpected results when those prompts run in the actual application&lt;/li&gt;
&lt;li&gt;SMEs cannot change important parameters beyond the prompt (tool descriptions, embedding models, top-k values in RAG)&lt;/li&gt;
&lt;li&gt;Time is wasted between developers and SMEs handing over prompt configurations only to find different results&lt;/li&gt;
&lt;li&gt;Engineering bottlenecks form when only developers can run evaluations while SMEs can only work on single prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates unnecessary back-and-forth between developers and domain experts, slowing down development cycles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Custom Workflows does
&lt;/h2&gt;

&lt;p&gt;Custom Workflows allows you to connect your application to Agenta with minimal changes. Once connected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your team will have access to a playground where they can modify any parameter you expose - prompts, embedding models, top-K values, etc.&lt;/li&gt;
&lt;li&gt;You can version, compare, and deploy the entire configuration schema you specify to your application&lt;/li&gt;
&lt;li&gt;SMEs can not only run the app with different configs, they can also run evaluations and annotations end-to-end for the app&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to implement it
&lt;/h2&gt;

&lt;p&gt;To connect your application to Agenta, you need to expose an endpoint to your application using our Python SDK. Agenta uses the exposed endpoint to read your configuration schema and generate the appropriate playground to interact with your app, letting your team modify parameters without touching code.&lt;/p&gt;

&lt;p&gt;Here's an example of our SDK in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;agenta&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agenta.sdk.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;

&lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;prompt1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the following blog post: {blog_post}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;prompt2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a tweet based on this: {output_1}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# We define the configuration of the app using a Pydantic Class
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoPConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;prompt2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Agenta will interact with your code using this route
&lt;/span&gt;&lt;span class="nd"&gt;@ag.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CoPConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog_post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# config contains the context fetched from the request
&lt;/span&gt;    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_from_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CoPConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;formatted_prompt1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog_post&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;blog_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;formatted_prompt1&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
    &lt;span class="n"&gt;formatted_prompt2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;output_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;formatted_prompt2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You can read the documentation for more details, get started, and watch the announcement for a short demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Solution
&lt;/h2&gt;

&lt;p&gt;Behind the scenes, our SDK creates an endpoint authenticated with your Agenta credentials that Agenta uses to communicate with your app. The SDK uses FastAPI to create an &lt;code&gt;openapi.json&lt;/code&gt; that exposes the Pydantic class you configure for your configuration. Agenta uses the &lt;code&gt;openapi.json&lt;/code&gt;to discover the config schema and generate the UI playground. It then uses that endpoint to communicate with the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get started
&lt;/h2&gt;

&lt;p&gt;Check out &lt;a href="https://docs.agenta.ai/custom-workflows" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt; for step-by-step instructions and more examples.&lt;/p&gt;

&lt;p&gt;Custom Workflows is the second of five major features we're releasing this week. Stay tuned for more tomorrow.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⭐ Star Agenta
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consider giving us a star!&lt;/strong&gt; It helps us grow our community and gets Agenta in front of more developers.&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;

  &lt;img alt="Star us" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F2c8e580a-c930-4312-bf1b-08f631b41c62" width="864" height="364"&gt;
    &lt;/a&gt;&lt;a href="https://cloud.agenta.ai?utm_source=github&amp;amp;utm_medium=referral&amp;amp;utm_campaign=readme" rel="noopener noreferrer"&gt;

&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llmops</category>
      <category>llm</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>Compare All LLMs in one Playground: Introducing Agenta's AI Model Hub</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Tue, 15 Apr 2025 16:13:17 +0000</pubDate>
      <link>https://dev.to/agenta/compare-all-llms-in-one-playground-introducing-agentas-ai-model-hub-3o66</link>
      <guid>https://dev.to/agenta/compare-all-llms-in-one-playground-introducing-agentas-ai-model-hub-3o66</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article is part of Agenta Launch Week (April 14-18, 2025), where we're announcing new features daily.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; is an open-source platform built for AI engineers and teams working with large language models. Our playground, evaluation tools, and deployment solutions help streamline the entire LLM application development process. As an open-source tool, Agenta gives developers complete control over their AI infrastructure while providing enterprise-grade features for teams of all sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing AI Model Hub
&lt;/h2&gt;

&lt;p&gt;When working with LLMs, comparing different models is essential. Our users face several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have fine-tuned models for your specific use cases but couldn't use them in Agenta's playground or evaluations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Many of you run self-hosted models for privacy, security, or cost reasons – through Ollama or dedicated servers – but couldn't connect them to Agenta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enterprise users who rely on Azure OpenAI and AWS Bedrock had to switch between platforms when using Agenta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And teams had no way to share model access without sharing raw API keys, creating security risks and complicating collaboration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Introducing Model Hub: Your central connection point
&lt;/h3&gt;

&lt;p&gt;Model Hub gives you a central place in Agenta to manage all your model connections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connect any model&lt;/strong&gt;: Azure OpenAI, AWS Bedrock, self-hosted models, fine-tuned models – any model with an OpenAI-compatible API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure once, use everywhere&lt;/strong&gt;: Set up your models once and use them in playground experiments, evaluations, and deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secure team sharing&lt;/strong&gt;: Give your team access to models without sharing API keys. Control permissions per model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent experience&lt;/strong&gt;: Use the same Agenta interface for all your models.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/BOKkpOk-3oM"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Model Hub is under Configuration in your Agenta interface:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select your provider (OpenAI, Azure, AWS, Ollama, etc.)&lt;/li&gt;
&lt;li&gt;Enter your API keys&lt;/li&gt;
&lt;li&gt;Connect to your self-hosted models&lt;/li&gt;
&lt;li&gt;Share access with teammates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once configured, your models appear throughout Agenta. Run comparisons in the playground, test performance in evaluations, and deploy with confidence – using models from any provider.&lt;/p&gt;
&lt;h3&gt;
  
  
  Security is built-in
&lt;/h3&gt;

&lt;p&gt;When building Model Hub, we prioritized the security of your API keys and credentials:&lt;/p&gt;

&lt;p&gt;Your model keys and credentials are encrypted at rest in our database and protected in transit with TLS encryption. We never log these sensitive details, and they're only held in memory for the minimum time needed to process requests.&lt;/p&gt;

&lt;p&gt;Currently, access to models is managed at the project level, with all team members working on a project able to use the configured models. Every access to these credentials requires proper authentication with tokens tied to specific users and projects.&lt;/p&gt;

&lt;p&gt;For Business and Enterprise customers, Role-Based Access Control (RBAC) provides additional security by letting you control exactly who on your team can view or modify model configurations.&lt;/p&gt;

&lt;p&gt;We've designed this system from the ground up with security best practices, ensuring your valuable API keys and model access credentials remain protected.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;The AI Model Hub is just the first feature in our Launch Week. Four more announcements are coming in the next few days to improve your LLM development workflow.&lt;/p&gt;

&lt;p&gt;Ready to try it? Check out &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;Agenta on GitHub&lt;/a&gt; or &lt;a href="https://app.agenta.ai" rel="noopener noreferrer"&gt;log in to your Agenta account&lt;/a&gt; to set up your Model Hub.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Stay tuned for tomorrow's announcement!&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  ⭐ Star Agenta
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consider giving us a star!&lt;/strong&gt; It helps us grow our community and gets Agenta in front of more developers.&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;

  &lt;img alt="Star us" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F2c8e580a-c930-4312-bf1b-08f631b41c62" width="864" height="364"&gt;
    &lt;/a&gt;&lt;a href="https://cloud.agenta.ai?utm_source=github&amp;amp;utm_medium=referral&amp;amp;utm_campaign=readme" rel="noopener noreferrer"&gt;

&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>Build an AI code review assistant with v0.dev, litellm and Agenta</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Mon, 13 Jan 2025 19:49:47 +0000</pubDate>
      <link>https://dev.to/agenta/build-an-ai-code-review-assistant-with-v0dev-litellm-and-agenta-1ojc</link>
      <guid>https://dev.to/agenta/build-an-ai-code-review-assistant-with-v0dev-litellm-and-agenta-1ojc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The code for this tutorial is available &lt;a href="https://github.com/Agenta-AI/agenta/tree/main/examples/custom_workflows/ai-code-reviewer" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ever wanted your own AI assistant to review pull requests? In this tutorial, we'll build one from scratch and take it to production. We'll create an AI assistant that can analyze PR diffs and provide meaningful code reviews—all while following LLMOps best practices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://7acthbzemz2hgvi8l7us455b6tcigd8s.vercel.app/" rel="noopener noreferrer"&gt;You can try out the final product here&lt;/a&gt;. Just provide the URL to a public PR and receive a review from our AI assistant.&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%2F4qmyinnc9p6g2tj5uhkx.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%2F4qmyinnc9p6g2tj5uhkx.gif" alt="Code review demo" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'll build
&lt;/h2&gt;

&lt;p&gt;This tutorial walks through creating a production-ready AI assistant. Here's what we'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writing the Code:&lt;/strong&gt; Fetching the PR diff from GitHub and calling an LLM using &lt;a href="https://www.litellm.ai/" rel="noopener noreferrer"&gt;LiteLLM&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adding Observability:&lt;/strong&gt; Instrumenting the code with &lt;a href="https://agenta.ai" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; to debug and monitor our app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Engineering:&lt;/strong&gt; Refining prompts and comparing different models using &lt;a href="https://agenta.ai" rel="noopener noreferrer"&gt;Agenta's&lt;/a&gt; playground.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM Evaluation:&lt;/strong&gt; Using LLM-as-a-judge to evaluate prompts and select the best model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Deploying the app as an API and building a simple UI with &lt;a href="https://v0.dev" rel="noopener noreferrer"&gt;v0.dev&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the core logic
&lt;/h2&gt;

&lt;p&gt;Our AI assistant's workflow is straightforward: When given a PR URL, it fetches the diff from GitHub and passes it to an LLM for review. Let's break this down step by step.&lt;/p&gt;

&lt;p&gt;First, we'll fetch the PR diff. GitHub provides this in an easily accessible format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://patch-diff.githubusercontent.com/raw/{owner}/{repo}/pull/{pr_number}.diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a Python function to retrieve the diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_pr_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Fetch the diff for a GitHub Pull Request given its URL.

    Args:
        pr_url (str): Full GitHub PR URL (e.g., https://github.com/owner/repo/pull/123)

    Returns:
        str: The PR diff text
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github\.com/([^/]+)/([^/]+)/pull/(\d+)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid GitHub PR URL format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://patch-diff.githubusercontent.com/raw/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/pull/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pr_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.diff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/vnd.github.v3.diff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PR-Diff-Fetcher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll use LiteLLM to handle our interactions with language models. &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;LiteLLM&lt;/a&gt; provides a unified interface for working with various LLM providers—making it easy to experiment with different models later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;prompt_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are an expert Python developer performing a file-by-file review of a pull request. You have access to the full diff of the file to understand the overall context and structure. However, focus on reviewing only the specific hunk provided.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;prompt_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Here is the diff for the file:
{diff}

Please provide a critique of the changes made in this file.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_critique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_pr_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding observability
&lt;/h2&gt;

&lt;p&gt;Observability is crucial for understanding and improving LLM applications. It helps you track inputs, outputs, and the information flow, making debugging easier. We'll use Agenta for this purpose.&lt;/p&gt;

&lt;p&gt;Agenta is an open-source LLMOps platform that provides you with all the tools needed to build production-ready LLM-powered applications. It offers a centralized environment to manage prompts, instrument applications for tracking inputs and outputs, and run evaluations to assess result quality. You can signup for free for the &lt;a href="https://cloud.agenta.ai" rel="noopener noreferrer"&gt;cloud platform&lt;/a&gt; or &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;self-host our platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&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%2Fk6fa0ywll57xln0vnlkx.png" alt="Star our repository" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we initialize Agenta and set up LiteLLM callbacks. The callback automatically instruments all the LiteLLM calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;agenta&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;

&lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;litellm_handler&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we add instrumentation decorators to both functions (&lt;code&gt;generate_critique&lt;/code&gt; and &lt;code&gt;get_pr_diff&lt;/code&gt;) to capture their inputs and outputs. Here's how it looks for the &lt;code&gt;generate_critique&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ag.instrument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_critique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_pr_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_from_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To set up Agenta, we need to set the environment variable &lt;code&gt;AGENTA_API_KEY&lt;/code&gt; (which you can find &lt;a href="https://cloud.agenta.ai/settings?tab=apiKeys" rel="noopener noreferrer"&gt;here&lt;/a&gt;) and optionally &lt;code&gt;AGENTA_HOST&lt;/code&gt; if we're self-hosting.&lt;/p&gt;

&lt;p&gt;We can now run the app and see the traces in Agenta.&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%2Frz4wzfkz840nrg4u46ro.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%2Frz4wzfkz840nrg4u46ro.gif" alt="Observability in Agenta" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an LLM playground
&lt;/h2&gt;

&lt;p&gt;Now that we have our POC, we need to iterate on it and make it production-ready. This means experimenting with different prompts and models, setting up evaluations, and versioning our configuration.&lt;/p&gt;

&lt;p&gt;Agenta custom workflow feature lets us create an IDE-like playground for our AI-assistant workflow.&lt;/p&gt;

&lt;p&gt;We'll add a few lines of code to create an LLM playground for our application. This will enable us to version the configuration, run end-to-end evaluations, and deploy the last versions in one click.&lt;/p&gt;

&lt;p&gt;Here's the modified code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;agenta&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;litellm&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agenta.sdk.assets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;supported_llm_models&lt;/span&gt;

&lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;litellm_handler&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

&lt;span class="n"&gt;prompt_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are an expert Python developer performing a file-by-file review of a pull request. You have access to the full diff of the file to understand the overall context and structure. However, focus on reviewing only the specific hunk provided.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;prompt_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Here is the diff for the file:
{diff}

Please provide a critique of the changes made in this file.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# highlight-start
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt_system&lt;/span&gt;
    &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt_user&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MultipleChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;supported_llm_models&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# highlight-end
&lt;/span&gt;
&lt;span class="c1"&gt;# highlight-next-line
&lt;/span&gt;&lt;span class="nd"&gt;@ag.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ag.instrument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_critique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_pr_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# highlight-next-line
&lt;/span&gt;    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_from_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Let's break it down:&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the configuration and the layout of the playground
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agenta.sdk.assets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;supported_llm_models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt_system&lt;/span&gt;
    &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt_user&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MultipleChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;supported_llm_models&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To integrate our code in Agenta, we first define the configuration schema. This helps Agenta understand the inputs and outputs of our function and create a playground for it. The configuration defines the playground's layout: the system and user prompts (str) appear as text areas, and the model appears as a multi-select dropdown (note that supported_llm_models is variable in the Agenta SDK that contains a dictionary of providers and their supported models).&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the entrypoint
&lt;/h3&gt;

&lt;p&gt;We'll adjust our function to use the configuration. &lt;code&gt;@ag.route&lt;/code&gt; creates an API endpoint for our function with the configuration schema we defined. This endpoint will be used by Agenta's playground, evaluation, and the deployed API to interact with our application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ag.ConfigManager.get_from_route(schema=Config)&lt;/code&gt; fetches the configuration from the request sent to that API endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# highlight-next-line
&lt;/span&gt;&lt;span class="nd"&gt;@ag.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ag.instrument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_critique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_pr_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# highlight-next-line
&lt;/span&gt;    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_from_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;litellm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Serving the application with Agenta
&lt;/h2&gt;

&lt;p&gt;We can now add the application to Agenta. Here's what we need to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;agenta init&lt;/code&gt; and specify our app name and API key&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;agenta variant serve app.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The last command builds and serves your application, making it accessible through Agenta's playground. There, you can run the application end-to-end by giving it a PR URL and getting the review generated by our LLM.&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%2F38vb4mxx7i9czixba4ob.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%2F38vb4mxx7i9czixba4ob.png" alt="Prompt engineering playground in Agenta" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluating using LLM-as-a-judge
&lt;/h2&gt;

&lt;p&gt;To evaluate the quality of our AI assistant's reviews and compare prompts and models, we need to set up evaluation.&lt;/p&gt;

&lt;p&gt;First, we'll create a small test set with publicly available PRs.&lt;/p&gt;

&lt;p&gt;Next, we'll set up an LLM-as-a-judge to evaluate the quality of the reviews.&lt;/p&gt;

&lt;p&gt;To do this, navigate to the evaluation view, click on "Configure evaluators", then "Create new evaluator" and select "LLM-as-a-judge".&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%2Ffnqu2d8aki7ka4xgiotk.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%2Ffnqu2d8aki7ka4xgiotk.png" alt="LLM-as-a-judge in Agenta" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll get a playground where we can test different prompts and models for our human evaluator. We use the following system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an evaluator grading the quality of a PR review.
CRITERIA:
Technical Accuracy

The reviewer identifies and addresses technical issues, ensuring the PR meets the project's requirements and coding standards.
Code Quality

The review ensures the code is clean, readable, and adheres to established style guides and best practices.
Functionality and Performance

The reviewer provides clear, actionable, and constructive feedback, avoiding vague or unhelpful comments.
Timeliness and Thoroughness

The review is completed within a reasonable timeframe and demonstrates a thorough understanding of the code changes.

SCORE:
-The score should be between 0 and 10
-A score of 10 means that the answer is perfect. This is the highest (best) score.
A score of 0 means that the answer does not any of of the criteria. This is the lowest possible score you can give.

ANSWER ONLY THE SCORE. DO NOT USE MARKDOWN. DO NOT PROVIDE ANYTHING OTHER THAN THE NUMBER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the user prompt, we'll use the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM APP OUTPUT: {prediction}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the evaluator accesses the LLM app's output through the &lt;code&gt;{prediction}&lt;/code&gt; variable. We can iterate on the prompt and test different models in the evaluator test view.&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%2F10f9xeehdjiam5ebm3oi.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%2F10f9xeehdjiam5ebm3oi.png" alt="human eval in agenta" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With our evaluator set up, we can run experiments and compare different prompts and models. In the playground, we can create multiple variants and run batch evaluations using the &lt;code&gt;pr-review-quality&lt;/code&gt; LLM-as-a-judge.&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%2Fvl873v6vrri0slq6x1u0.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%2Fvl873v6vrri0slq6x1u0.png" alt="Running human evaluation in Agenta" width="800" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After comparing models, we found similar performance across the board. Given this, we chose GPT-3.5-turbo for its optimal balance of speed and cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to production
&lt;/h2&gt;

&lt;p&gt;Deployment is straightforward with Agenta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the overview page&lt;/li&gt;
&lt;li&gt;Click the three dots next to your chosen variant&lt;/li&gt;
&lt;li&gt;Select "Deploy to Production"&lt;/li&gt;
&lt;/ol&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%2Fv2c3lqbo690fvszhbtpa.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%2Fv2c3lqbo690fvszhbtpa.png" alt="Deploying to production" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives you an API endpoint ready to use in your application.&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%2Foy1rrckoz3b659ohj6f1.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%2Foy1rrckoz3b659ohj6f1.png" alt="API endpoint for assistant" width="800" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agenta works in both proxy mode and prompt management mode. You can either use Agenta's endpoint or deploy your own app and use the Agenta SDK to fetch the production configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Building the frontend
&lt;/h2&gt;

&lt;p&gt;For the frontend, we used &lt;a href="https://v0.dev" rel="noopener noreferrer"&gt;v0.dev&lt;/a&gt; to quickly generate a UI. After providing our API endpoint and authentication requirements, we had a working UI in minutes. You can try it yourself: &lt;a href="https://7acthbzemz2hgvi8l7us455b6tcigd8s.vercel.app/" rel="noopener noreferrer"&gt;PR Review Assistant&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;With our AI assistant in production, Agenta continues to provide observability tools. We can continue enhancing it by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refine the Prompt: Improve the language to get more precise critiques.&lt;/li&gt;
&lt;li&gt;Add More Context: Include the full code of changed files, not just the diffs.&lt;/li&gt;
&lt;li&gt;Handle Large Diffs: Break down extensive changes and process them in parts.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this tutorial, we've:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built an AI assistant that reviews pull requests.&lt;/li&gt;
&lt;li&gt;Implemented observability and prompt engineering using Agenta.&lt;/li&gt;
&lt;li&gt;Evaluated our assistant with LLM-as-a-judge.&lt;/li&gt;
&lt;li&gt;Deployed the assistant and connected it to a frontend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One last thing
&lt;/h2&gt;

&lt;p&gt;If you liked this tutorial, and you to learn more about AI engineering and how to build production-ready AI applications. Follow our page, and &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;start our repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&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%2F2xkmpvqu4v3dd6cra39o.png" alt="Star Agenta" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>python</category>
      <category>llm</category>
    </item>
    <item>
      <title>🔥🤖 Build an AI-Powered Discord Bot to Recommend HackerNews Posts using OpenAI, Novu and Agenta 🚀</title>
      <dc:creator>Mahmoud Mabrouk</dc:creator>
      <pubDate>Wed, 06 Sep 2023 21:34:58 +0000</pubDate>
      <link>https://dev.to/agenta/build-an-ai-powered-discord-bot-to-recommend-hackernews-posts-using-openai-novu-and-agenta-3ffi</link>
      <guid>https://dev.to/agenta/build-an-ai-powered-discord-bot-to-recommend-hackernews-posts-using-openai-novu-and-agenta-3ffi</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you'll learn how to create an AI agent that alerts you about relevant Hacker News posts tailored to your interests. The agent sends Discord notifications whenever a post matches your criteria. &lt;/p&gt;

&lt;p&gt;We'll write the code using Python, use Beautifulsoup for web scraping, use OpenAI with Agenta for building the AI, and Novu for Slack notifications.&lt;/p&gt;

&lt;p&gt;Goal? No more endless scrolling on Hacker News. Let the AI bring worthy posts to you!&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%2Fujiie4r5z35zsvtp827j.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%2Fujiie4r5z35zsvtp827j.gif" alt="No more Procrastination" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Agenta: The Open-source LLM app builder  &lt;strong&gt;🤖&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A bit about us: &lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; is an end-to-end open-source LLM app builder. It enables you to quickly build, experiment, evaluate, and deploy LLM apps as APIs. You can use it by writing code in Langchain or any other framework, or directly from the UI. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Agenta-AI/agenta/" rel="noopener noreferrer"&gt;&lt;br&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%2Fcllefy5nhyh3a5nhr5oz.png" alt="Star us on Github" width="800" height="336"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Here's the plan 📝
&lt;/h2&gt;

&lt;p&gt;We will write a script that does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrape the first five Hacker News pages for post titles using Python and Beautifulsoup.&lt;/li&gt;
&lt;li&gt;Use Agenta and GPT3.5 to categorize posts based on your interests.&lt;/li&gt;
&lt;li&gt;Send compelling posts to your Slack channel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting things up:
&lt;/h2&gt;

&lt;p&gt;To get started, let's create a project folder and run Poetry. If you aren't familiar with Poetry, you should check it out as it provides an alternative to virtual environments that is much easier to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;hnbot&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;hnbot/
poetry init

This &lt;span class="nb"&gt;command &lt;/span&gt;will guide you through creating your pyproject.toml config.


Would you like to define your main dependencies interactively? &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;/no&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nb"&gt;yes

&lt;/span&gt;Package to add or search &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;leave blank to skip&lt;span class="o"&gt;)&lt;/span&gt;: novu
Add a package &lt;span class="o"&gt;(&lt;/span&gt;leave blank to skip&lt;span class="o"&gt;)&lt;/span&gt;: beautifulsoup4


Do you confirm generation? &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;/no&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts to set up your project. Don't forget to install novu and beautifulsoup4.&lt;/p&gt;

&lt;p&gt;Now let's create the folder for our package and initialize the poetry environement&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;mkdir&lt;/span&gt; &lt;span class="n"&gt;hn_bot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;hn_bot&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;hn_bot&lt;/span&gt; 
&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;poetry&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;poetry&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have a local environment where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All our requirements are installed.&lt;/li&gt;
&lt;li&gt;We have a Python package called hn_bot that is in our Python lib.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means if we have multiple files in our library, we can import them using &lt;code&gt;import hn_bot.module_name&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping Hacker News Posts
&lt;/h2&gt;

&lt;p&gt;Scraping the Hacker News page is straightforward since it does not use any complicated JavaScript. The pages are located at &lt;code&gt;https://news.ycombinator.com/?p=pagenumber&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To find the titles and links on the page, we just need to open the web browser and access the dev console. Once there, we can check if there are any elements we can use to locate the titles and links. Luckily, it seems that every post is a span with the class "titleline."&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%2Fq48tqg8xtzr61f0bi282.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%2Fq48tqg8xtzr61f0bi282.png" alt="Firefox Dev Console" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can use this to extract information from a single page. Let's write a function that extracts titles and links from Hacker News.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# hn_scraper.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scrape_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://news.ycombinator.com/news?p=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;page_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;yc_web_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yc_web_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;article_tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;span&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titleline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;article_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;article_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can test it by adding a &lt;code&gt;print(scrape_page(1))&lt;/code&gt; at the end of the script and running it on shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;hn_scraper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Linux Network Performance Parameters Explained (github.com/leandromoreira)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Double Commander – Changes in version 1.1.0 (github.com/doublecmd)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;If You’ve Got a New Car, It’s a Data Privacy Nightmare (gizmodo.com)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ask HN: I’m an FCC Commissioner proposing regulation of IoT security updates&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gcsfuse: A user-space file system for interacting with Google Cloud S

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

&lt;/div&gt;



&lt;p&gt;Congratulations!🎉 Now we have a script that scrapes post titles from HackerNews&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating the AI Agent 🤖
&lt;/h2&gt;

&lt;p&gt;Now that we have a list of posts, we need to use OpenAI gpt models to classify whether they are relevant based on the user's interests. For this, we are going to use Agenta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/agenta-ai/agenta" rel="noopener noreferrer"&gt;Agenta&lt;/a&gt; allows you to create LLM apps from code or from the UI. Since our LLM app today is quite simple, we will create it from the UI.&lt;/p&gt;

&lt;p&gt;Agenta can be &lt;a href="https://docs.agenta.ai/installation/local-installation/local-installation" rel="noopener noreferrer"&gt;self-hosted&lt;/a&gt;, however to get started quickly we'll use &lt;a href="http://demo.agenta.ai" rel="noopener noreferrer"&gt;demo.agenta.ai&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since our LLM app today is quite simple, we will just go ahead and create it from the UI.&lt;/p&gt;

&lt;p&gt;You can self host agenta (Check out docs for that here (&lt;a href="https://docs.agenta.ai/installation/local-installation/local-installation" rel="noopener noreferrer"&gt;https://docs.agenta.ai/installation/local-installation/local-installation&lt;/a&gt;) or use the cloud-hosted demo. To get started quickly we'll do the later.&lt;/p&gt;

&lt;p&gt;Let's go to &lt;a href="http://demo.agenta.ai" rel="noopener noreferrer"&gt;demo.agenta.ai&lt;/a&gt; and login.&lt;/p&gt;

&lt;p&gt;First, let's create a new app by clicking on "Create New App".&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%2Fmxf7j6nt2p5eu2v2yb5n.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%2Fmxf7j6nt2p5eu2v2yb5n.png" alt="Create New App" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we select start from template&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%2Fn0wg0rvc7a2wjgzyntv5.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%2Fn0wg0rvc7a2wjgzyntv5.png" alt="Start From Template" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And use a single prompt template&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%2Fp907dyujwuf54hw5q661.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%2Fp907dyujwuf54hw5q661.png" alt="Single Prompt Template" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing some Prompt Engineering In Agenta 🪄 ✨
&lt;/h2&gt;

&lt;p&gt;Now we have a playground for creating the app.&lt;/p&gt;

&lt;p&gt;First, let's add the inputs for our application. In this case, we will be using "title" for the Hacker News title and "interests" for the user's interests.&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%2Fcr9dukpu2wwjid77uss0.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%2Fcr9dukpu2wwjid77uss0.png" alt="Adding Inputs" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to do a little prompt engineering. Since we are using gpt3.5 (the cheapest variant in OpenAI). It takes two messages: the system message and the user message. We can use the system message to guide the language model to answer in a certain way, while the prompt prompts the human to give the parameters of the task.&lt;/p&gt;

&lt;p&gt;In this case, I tried a simple prompt for the system that ensures the answer is either "True" or "False." For the human prompt, I just asked the system to classify. Note that we used the fstring usual format to inject the inputs that we have added into the prompt.&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%2Fpf7rqf26zd9bdujl86l5.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%2Fpf7rqf26zd9bdujl86l5.png" alt="The Prompts" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can then test the application with some examples of Hacker News titles:&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%2Fgot5fqujxcdqkvlxesvs.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%2Fgot5fqujxcdqkvlxesvs.png" alt="Testing the Prompt" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agenta provides tools to systematically evaluate applications and optimize prompts, parameters, and workflows (in case we are using something more complex with embeddings and retrieval augmented generations). However, in this case, such evaluation is unnecessary. The app itself is very simple, and gpt3.5 is able to solve the classification problem with minimal effort.&lt;/p&gt;

&lt;p&gt;Let's save our changes&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%2F0qr4u5tlelso4hpuoxyg.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%2F0qr4u5tlelso4hpuoxyg.png" alt="Saving Changes" width="800" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then deploy the application as an API.&lt;/p&gt;

&lt;p&gt;For this we jump to the endpoints menu and copy paste the code snippet to our code.&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%2Fyj5xizartrdzuuhv2b5o.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%2Fyj5xizartrdzuuhv2b5o.png" alt="Endpoint Menu" width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping it up 🌯
&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%2F4dwlmgyq27argsl7j1ej.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%2F4dwlmgyq27argsl7j1ej.gif" alt="Wrapping it up" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can create a function based on this code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# llm_classifier.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://demo.agenta.ai/64f1d1aefeebd024bbdb1ea4/hn_bot/v1/generate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;interests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;interests&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt_system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are an expert in classification. You answer only with True or False.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt_human&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify whether this hackernews post is interesting for someone with the following interests:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Hacker news post title: {title}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Interests: {interests}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop_sequence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frequence_penalty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;presence_penalty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Sending a Discord message 🎮
&lt;/h2&gt;

&lt;p&gt;First we need to create a new channel in Discord&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%2F2tl092e04g4v1v8125a0.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%2F2tl092e04g4v1v8125a0.png" alt="Creating a new channel in Discord" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we need to create a webhook and copy the url&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%2Fsi0zrs7gwqdxtsqxy1gc.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%2Fsi0zrs7gwqdxtsqxy1gc.png" alt="Getting the Webhook of the channel" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to setup the integration in Novu. For this we have to go to the Integration Store, click on “Add a provider”, select Discord, and don't forget to activate it!&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%2Firwwv1wfohthf03pdfdy.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%2Firwwv1wfohthf03pdfdy.png" alt="Add a provider in Novu" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last, we need to create a workflow that triggers the message to be sent to our Discord. We will add the &lt;code&gt;{{content}}&lt;/code&gt; variable to the message which we will later inject using the code.&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%2Fqnx4kr02zjor6argqqwz.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%2Fqnx4kr02zjor6argqqwz.png" alt="Create a Workflow in Novu" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Write the messaging function
&lt;/h2&gt;

&lt;p&gt;Now it's time to write the message that will trigger the workflow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# novu_bot
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;novu.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NovuConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;novu.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EventApi&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;novu.api.subscriber&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SubscriberApi&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;novu.dto.subscriber&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SubscriberDto&lt;/span&gt;

&lt;span class="nc"&gt;NovuConfig&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.novu.co&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# the webhook url we got from Discord
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;your_subscriber_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Replace this with a unique user ID.
&lt;/span&gt;
    &lt;span class="c1"&gt;# Define a subscriber instance
&lt;/span&gt;    &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SubscriberDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;subscriber_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;your_subscriber_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Doe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nc"&gt;SubscriberApi&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;SubscriberApi&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;your_subscriber_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;provider_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discord&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="nc"&gt;EventApi&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slackbot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# The trigger ID of the workflow. It can be found on the workflow page.
&lt;/span&gt;        &lt;span class="n"&gt;recipients&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;your_subscriber_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;  &lt;span class="c1"&gt;# Your Novu payload goes here
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Putting everything together
&lt;/h2&gt;

&lt;p&gt;Now we're ready to assemble all the elements to get our AI assistant running.&lt;/p&gt;

&lt;p&gt;Let's create an &lt;code&gt;app.py&lt;/code&gt; file in which we first call the scraper, then the LLM classifier, and finally send a message with the interesting posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hn_bot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hn_scraper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_classifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;novu_bot&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;interests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLMs, LLMOps, Python, Infrastructure, Tennis, MLOps, Data science, AI, startups, Computational Biology&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;novu_bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Interesting posts at HackerNews:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hn_scraper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrape_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;llm_classifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;True&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;novu_bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Et voila, we have all the interesting posts coming up in our Discord.
&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%2F1d02h6s47giku5nvjk0q.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%2F1d02h6s47giku5nvjk0q.png" alt="The result" width="800" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally let's schedule this to run each hour ⏰
&lt;/h2&gt;

&lt;p&gt;We would like to run the script to check new posts each hour. For this we need to add the python library schedule&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hn_bot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hn_scraper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_classifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;novu_bot&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;
&lt;span class="n"&gt;interests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLM, LLMOps, MLOps, Data science, AI, startups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;done_post_titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;novu_bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Interesting posts at HackerNews:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;hn_scraper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrape_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;llm_classifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done_post_titles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;done_post_titles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;novu_bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations on making it thus far!🎉 You've now got an automated AI assistant keeping an eye on Hacker News for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary 📜
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we've built an AI-powered assistant to keep you in the loop with relevant Hacker News posts. You should have learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to use Beautifulsoup for scraping hackernews&lt;/li&gt;
&lt;li&gt;How to create an LLM app based on one prompt using Agenta and OpenAI gpt3.5 &lt;/li&gt;
&lt;li&gt;How to send notifications on Discord using Novu&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check the code at this &lt;a href="https://github.com/Agenta-AI/blog/tree/main/hackernews-bot" rel="noopener noreferrer"&gt;https://github.com/Agenta-AI/blog/tree/main/hackernews-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Agenta-AI/agenta/" rel="noopener noreferrer"&gt;&lt;br&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%2Fcllefy5nhyh3a5nhr5oz.png" alt="Star us on Github" width="800" height="336"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>chatgpt</category>
    </item>
  </channel>
</rss>
