<?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: Dmitrii</title>
    <description>The latest articles on DEV Community by Dmitrii (@dbolotov).</description>
    <link>https://dev.to/dbolotov</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%2F794983%2F9da15b51-51c3-499d-b2c0-2a1e78f35f4f.jpg</url>
      <title>DEV Community: Dmitrii</title>
      <link>https://dev.to/dbolotov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dbolotov"/>
    <language>en</language>
    <item>
      <title>Anthropic Skills. The Landscape for New Models and Architecture</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Mon, 15 Dec 2025 08:50:11 +0000</pubDate>
      <link>https://dev.to/dbolotov/anthropic-skills-the-landscape-for-new-models-and-architectures-2ld3</link>
      <guid>https://dev.to/dbolotov/anthropic-skills-the-landscape-for-new-models-and-architectures-2ld3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Skills are modular, on-demand data that transform general-purpose LLMs into specialized agents. It's not about MCP or fancy protocols - it's about &lt;strong&gt;context engineering&lt;/strong&gt;: loading the right information at the right time.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;A &lt;strong&gt;skill&lt;/strong&gt; is a memory, instruction, fact, or code snippet loaded on-demand into your LLM's context window.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;skill&lt;/strong&gt; is also RAG + System Instructions + Domain Expertise.&lt;/p&gt;

&lt;p&gt;Everything you can achieve with skills, you can technically achieve without them. Just load 50k tokens of tools, instructions, and examples into context, use the biggest reasoning model with enough thinking time, and you'll get decent results.&lt;/p&gt;

&lt;p&gt;But the new skills-based approach is: simpler, faster, cheaper, and so scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  It's Not About MCP
&lt;/h2&gt;

&lt;p&gt;I've been writing code for 14 years. I'm a tech geek following most AI updates, and a vibe-coder. Almost a year ago I shared my &lt;a href="https://dev.to/dbolotov/anthropic-mcp-developers-thoughts-3dkk"&gt;thoughts about MCP from a developer's perspective&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My core argument&lt;/strong&gt;: MCP as an approach and programming pattern may not be the best solution.&lt;/p&gt;

&lt;p&gt;Here's my personal experience so far.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;I don't use MCP servers&lt;/strong&gt; - because I don't need to, because they don't work well for my use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coding agents already have enough&lt;/strong&gt; - terminal commands, file system, web search handle most scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When agents must call external tools&lt;/strong&gt; (e.g., creating a Google Calendar event), I need more robust custom code, not an MCP wrapper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My debugging workflow&lt;/strong&gt; - manually add 3-4 files + 1-2 documentation links. Works better than any automated context retrieval&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those 3-4 files and 1-2 links could be found automatically. That's what skills promise to deliver: to make agents smarter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context Engineering
&lt;/h2&gt;

&lt;p&gt;I feel like architectural shift is coming:&lt;/p&gt;

&lt;p&gt;Before:  Big generalized pretrained model OR fine-tuned model&lt;br&gt;
After:   Small reasoning model with new architecture to learn + Skills for task-specific problems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Context engineering is the delicate art and science of filling the context window with just the right information for the next step." — Andrej Karpathy&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key insight: it's not about having the biggest model - it's about having the &lt;strong&gt;right context at the right moment&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Path Forward
&lt;/h2&gt;

&lt;p&gt;It's still R&amp;amp;D - a branch from mainstream LLM development, not a replacement. Anthropic has been working on this for 6+ months, and we're now just early adopters - discovering a better direction before the mainstream. My prediction, is that in 6 months, everyone will rush into skills-based agent architectures.&lt;/p&gt;

&lt;p&gt;What we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic skill discovery&lt;/li&gt;
&lt;li&gt;Composable skill libraries - combine skills for complex multi-step workflows like n8n&lt;/li&gt;
&lt;li&gt;Domain-specific skill packs - pre-built expertise for common developer tasks, e.g. Angular skills, or Github Runner skills&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Skills represent this philosophy: modular, reusable, on-demand expertise that transforms any LLM into a specialized agent for your specific workflow. I believe this is the right path, and I'm planning closely follow its progress and start coding some cool new things for skills or using skills.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building Reliable Pricing for AI Chatbots</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Thu, 23 Oct 2025 09:15:47 +0000</pubDate>
      <link>https://dev.to/dbolotov/building-reliable-pricing-for-ai-chatbots-48d9</link>
      <guid>https://dev.to/dbolotov/building-reliable-pricing-for-ai-chatbots-48d9</guid>
      <description>&lt;p&gt;&lt;strong&gt;🚀 New Open-Source Project&lt;/strong&gt;: We're building QuotyAI from the ground up with our backend engine and API open-sourced at &lt;a href="https://github.com/QuotyAI/QuotyAI-Engine" rel="noopener noreferrer"&gt;QuotyAI/QuotyAI-Engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;QuotyAI helps businesses create reliable pricing systems for chatbots and apps. We use AI to turn natural language business rules into working code that always gives consistent results.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 The Big Problems We Solve
&lt;/h2&gt;

&lt;p&gt;Building reliable pricing systems for chatbots and apps shouldn't be this hard. Yet most businesses struggle with the same frustrating issues.&lt;/p&gt;

&lt;p&gt;Customers often get different quotes for identical requests, eroding trust and creating confusion. Manual coding of complex pricing rules takes weeks of developer time, and even then, subtle bugs can slip through. Testing becomes an endless cycle of trying to catch every possible scenario, while standard AI solutions deliver inconsistent results that are too slow for real-time customer interactions.&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%2Fsmon7lc9fhi5qqyq8cs8.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%2Fsmon7lc9fhi5qqyq8cs8.png" alt="Unreliable AI vs QuotyAI" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QuotyAI changes all of this by combining the best of AI automation with rock-solid reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ What Makes QuotyAI Special
&lt;/h2&gt;

&lt;p&gt;Instead of wrestling with code for months, you simply describe your pricing rules in plain English. Tell us "General cleaning costs $100 for 3 hours, deep cleaning is $1.50 per square meter," and our AI generates production-ready code automatically.&lt;/p&gt;

&lt;p&gt;Every calculation follows your exact business rules with perfect consistency - no more "why did they get a different price?" questions. We automatically test millions of scenarios to catch problems before they affect customers, and our system delivers instant results that work reliably at scale.&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%2Fltysx8gsdfkeg6ppvix6.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%2Fltysx8gsdfkeg6ppvix6.png" alt="Unreliable AI vs QuotyAI" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plus, QuotyAI integrates seamlessly with your existing tools - whether it's chatbots, CRMs, booking systems, or automation platforms like Zapier and n8n.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Key Features
&lt;/h2&gt;

&lt;p&gt;QuotyAI combines AI-powered code generation with rock-solid reliability. You describe your pricing rules in plain English, and our system automatically creates production-ready code that handles complex calculations instantly.&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%2Ffzhbdafkrwcpjuu6azmz.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%2Ffzhbdafkrwcpjuu6azmz.png" alt="Unreliable AI vs QuotyAI" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The platform includes comprehensive testing that validates thousands of pricing scenarios automatically, ensuring accuracy before anything goes live. For businesses with multiple locations or brands, we provide secure multi-tenant support that keeps data completely isolated between companies.&lt;/p&gt;

&lt;p&gt;Integration is seamless with RESTful APIs, full audit trails for compliance, and built-in testing environments. You can even upload pricing tables from images, and connect with popular automation tools like Zapier and n8n.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Perfect For
&lt;/h2&gt;

&lt;p&gt;Whether you're running a service business with complex pricing tiers, building an e-commerce platform that needs dynamic pricing, or developing chatbots that must provide reliable quotes - QuotyAI adapts to your needs.&lt;/p&gt;

&lt;p&gt;Enterprises get the audit trails and compliance features they require, while startups benefit from fast setup without extensive coding. Anyone who needs consistent, automated pricing will find QuotyAI makes their life much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 Future Integrations
&lt;/h2&gt;

&lt;p&gt;We're building connections to make QuotyAI work everywhere:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🤖 Chatbots&lt;/strong&gt;: Dialogflow, Microsoft Bot Framework, Amazon Lex&lt;br&gt;
&lt;strong&gt;🌐 Customer Service&lt;/strong&gt;: Chatwoot, Intercom, Zendesk&lt;br&gt;
&lt;strong&gt;⚡ Automation&lt;/strong&gt;: Zapier, n8n, Microsoft Power Automate&lt;br&gt;
&lt;strong&gt;📱 Apps&lt;/strong&gt;: Mobile SDKs and API libraries&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Our backend engine and API are open-sourced at &lt;a href="https://github.com/QuotyAI/QuotyAI-Engine" rel="noopener noreferrer"&gt;QuotyAI/QuotyAI-Engine&lt;/a&gt;. Check out the full project at &lt;a href="https://github.com/WitcherD/QuotyAI" rel="noopener noreferrer"&gt;WitcherD/QuotyAI&lt;/a&gt; and our &lt;a href="https://quotyai.com/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to get started.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>startup</category>
      <category>ai</category>
    </item>
    <item>
      <title>Choosing the Right AI Model for Stock Prediction</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Sun, 05 Oct 2025 04:28:26 +0000</pubDate>
      <link>https://dev.to/dbolotov/choosing-the-right-ai-model-for-stock-prediction-13pi</link>
      <guid>https://dev.to/dbolotov/choosing-the-right-ai-model-for-stock-prediction-13pi</guid>
      <description>&lt;p&gt;Hey everyone! Following up on my &lt;a href="https://dev.to/dbolotov/im-building-an-ai-to-predict-stocks-dk8"&gt;previous post&lt;/a&gt; about building StocketAI, I wanted to dive deeper into how I'm picking AI models for stock prediction.&lt;/p&gt;

&lt;p&gt;This research journey began with &lt;a href="https://gemini.google.com/share/b4d03200c737" rel="noopener noreferrer"&gt;Google Deep Research&lt;/a&gt;, which provided the initial analysis and comparison of different AI models for stock prediction. This comprehensive AI-powered research served as my starting point for understanding the landscape of available models and their capabilities.&lt;/p&gt;

&lt;p&gt;I'm not a finance expert or a machine learning expert. I'm a solution architect who's learning as I go, relying heavily on AI tools and research to figure this out. So let me break down what I've learned in simple terms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important Disclaimer&lt;/strong&gt; ⚠️&lt;/p&gt;

&lt;p&gt;This analysis represents my current understanding based on the research and experimentation I've done so far. I might be wrong, and I fully expect to change my approach as I learn more, experiment with real data, and discover new techniques. Consider this a snapshot of an ongoing journey rather than definitive conclusions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Big Challenge: Markets Keep Changing
&lt;/h2&gt;

&lt;p&gt;Predicting stock prices 6 months ahead is really hard because &lt;strong&gt;markets are constantly changing&lt;/strong&gt;. What worked last year might not work this year. The fancy term for this is "concept drift" - basically, the rules of the game keep changing.&lt;/p&gt;

&lt;p&gt;Most AI models assume the future will look like the past, but that's not how stock markets work. Economic conditions change, new trends emerge, and what drove stock prices before might not matter anymore.&lt;/p&gt;

&lt;p&gt;After researching different AI models in &lt;a href="https://github.com/microsoft/qlib" rel="noopener noreferrer"&gt;Qlib&lt;/a&gt; (a quantitative finance platform), here's what I learned:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Winner: A Hybrid Approach
&lt;/h3&gt;

&lt;p&gt;The best approach seems to be combining two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://arxiv.org/abs/2201.04038" rel="noopener noreferrer"&gt;DDG-DA (Data Distribution Generation for Predictable Concept Drift Adaptation)&lt;/a&gt;&lt;/strong&gt; - A meta-learning technique that helps models adapt to changing market conditions by predicting future data distribution changes&lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/google-research/google-research/tree/master/tft" rel="noopener noreferrer"&gt;TFT (Temporal Fusion Transformer)&lt;/a&gt;&lt;/strong&gt; - A state-of-the-art attention-based model for multi-horizon forecasting&lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Think of DDG-DA as a "market change detector" and TFT as a "pattern finder." Together, they create a system that can handle the messy, ever-changing world of stock markets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Combo Works (In Simple Terms)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DDG-DA: The Market Change Detector
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DDG-DA&lt;/strong&gt; helps by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Predicting how market conditions might change in the future using meta-learning&lt;sup id="fnref3"&gt;3&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;Adjusting the training data so the model learns from "future-like" scenarios through data distribution generation&lt;/li&gt;
&lt;li&gt;Basically preparing the model for surprises before they happen by proactively adapting to concept drift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's like having a weather forecaster who not only tells you today's weather but also predicts how the climate might be changing over the next few months.&lt;/p&gt;

&lt;h3&gt;
  
  
  TFT: The Pattern Finder
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TFT&lt;/strong&gt; is great because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can look at long time periods (like 6 months of data) and find meaningful patterns using attention mechanisms&lt;sup id="fnref4"&gt;4&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;It considers different types of information (like company basics, market trends, and economic indicators) through multi-modal input processing&lt;/li&gt;
&lt;li&gt;It doesn't just look at stock prices - it tries to understand the bigger picture using temporal fusion of different data sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine trying to predict someone's behavior not just by looking at their recent actions, but by understanding their personality, their environment, and the broader context of their life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Models I Considered
&lt;/h2&gt;

&lt;p&gt;I also looked at other options available in Qlib like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/qlib/blob/main/qlib/contrib/model/pytorch_hist.py" rel="noopener noreferrer"&gt;HIST (Heterogeneous Information Stock Transformer)&lt;/a&gt;&lt;/strong&gt; - Uses concept stocks and relationship mining to find connections between different stocks and market sectors&lt;sup id="fnref5"&gt;5&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/qlib/blob/main/qlib/contrib/model/pytorch_adarnn.py" rel="noopener noreferrer"&gt;ADARNN (Adaptive RNN)&lt;/a&gt;&lt;/strong&gt; - Another model that adapts to changing conditions using transfer learning, but more reactive than proactive&lt;sup id="fnref6"&gt;6&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/qlib/blob/main/qlib/contrib/model/pytorch_sandwich.py" rel="noopener noreferrer"&gt;Sandwich&lt;/a&gt;&lt;/strong&gt; - A CNN-KRNN architecture designed for stock prediction&lt;sup id="fnref7"&gt;7&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DDG-DA + TFT combo is the most reliable for long-term predictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for StocketAI
&lt;/h2&gt;

&lt;p&gt;For my VN30 stock prediction project, this means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;I'll use the hybrid approach&lt;/strong&gt; - DDG-DA to handle market changes + TFT for the actual predictions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on 6-month predictions&lt;/strong&gt; - This approach works best for longer time horizons&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it practical&lt;/strong&gt; - I want models that work well in the real world, not just in theory&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building Confidence Through Multiple Models
&lt;/h2&gt;

&lt;p&gt;What makes this even better is the idea of training multiple models and using meta-analysis to validate predictions. Instead of relying on just one model, we can train several different approaches and compare their results. The real confidence comes when multiple models - whether it's DDG-DA combined with TFT, or other approaches like HIST and ADARNN - all point to similar predictions. Only when we see that level of agreement across different modeling techniques do we really trust the forecast. This approach helps filter out the noise and gives us more reliable insights for making investment decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps in My Journey
&lt;/h2&gt;

&lt;p&gt;I'm currently setting up experiments to test this hybrid approach with real VN30 data. I'll be using AI tools to help me configure everything properly and understand what the results mean.&lt;/p&gt;

&lt;p&gt;The goal is still the same: help regular people like me make better investment decisions without needing a finance degree or years of trading experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Come Join the Fun! 🎉
&lt;/h2&gt;

&lt;p&gt;What do you think? Have you tried predicting stock prices with AI? What's been your experience? I'd love to hear from other non-experts who are figuring this out as they go!&lt;/p&gt;

&lt;p&gt;Check out the StocketAI project on &lt;a href="https://github.com/WitcherD/StocketAI" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; if you want to follow along with my experiments.&lt;/p&gt;




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

&lt;p&gt;&lt;em&gt;Built with ❤️ using AI assistance and some of coffee ☕&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Original DDG-DA paper: "DDG-DA: Data Distribution Generation for Predictable Concept Drift Adaptation" (&lt;a href="https://arxiv.org/abs/2201.04038" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2201.04038&lt;/a&gt;) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Original TFT paper: "Temporal Fusion Transformers for Interpretable Multi-horizon Time Series Forecasting" (&lt;a href="https://arxiv.org/abs/1912.09363" rel="noopener noreferrer"&gt;https://arxiv.org/abs/1912.09363&lt;/a&gt;) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Implemented in Qlib as a meta-model that works in four steps: (1) Calculate meta-information, (2) Train DDG-DA, (3) Inference to get guide information, (4) Apply guidance to forecasting models&lt;sup id="fnref8"&gt;8&lt;/sup&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Implemented in Qlib as a benchmark model with full TensorFlow implementation supporting multi-horizon forecasting and quantile regression&lt;sup id="fnref9"&gt;9&lt;/sup&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;HIST model in Qlib uses concept stocks to capture market sector relationships and improve prediction accuracy ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;ADARNN model in Qlib uses domain adaptation techniques to handle changing market conditions reactively ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Sandwich model in Qlib combines CNN and KRNN (Kernel Recurrent Neural Network) for spatiotemporal feature extraction ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Qlib Meta Controller Documentation: &lt;a href="https://github.com/microsoft/qlib/blob/main/docs/component/meta.rst" rel="noopener noreferrer"&gt;https://github.com/microsoft/qlib/blob/main/docs/component/meta.rst&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;Qlib TFT Benchmark Documentation: &lt;a href="https://github.com/microsoft/qlib/tree/main/examples/benchmarks/TFT" rel="noopener noreferrer"&gt;https://github.com/microsoft/qlib/tree/main/examples/benchmarks/TFT&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I'm Building an AI to Predict Stocks!</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Fri, 03 Oct 2025 06:03:01 +0000</pubDate>
      <link>https://dev.to/dbolotov/im-building-an-ai-to-predict-stocks-dk8</link>
      <guid>https://dev.to/dbolotov/im-building-an-ai-to-predict-stocks-dk8</guid>
      <description>&lt;h2&gt;
  
  
  Just Starting Out 💡
&lt;/h2&gt;

&lt;p&gt;Hey everyone! I'm a solution architect with no finance background, but I had this idea: what if I could build an AI that helps answer &lt;strong&gt;Should I buy this stock or sell it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's called &lt;strong&gt;StocketAI&lt;/strong&gt;. I have little experience with Python and finance, and rely on AI and coding agents while building models for &lt;em&gt;VN30 companies&lt;/em&gt; (Vietnam's top 30). The goal is to predict stock price movements in &lt;em&gt;1, 3, or 6 months&lt;/em&gt;, with low risk.&lt;/p&gt;

&lt;p&gt;Check out the project on &lt;a href="https://github.com/WitcherD/StocketAI" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to help regular people make better investment choices without being finance experts. Built with Python 3.12+ and qlib for quantitative finance modeling.&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%2Fewfh1r4gj4ejkaybd3jx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewfh1r4gj4ejkaybd3jx.jpg" alt="Chill and enjoy" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes It Different ⚡
&lt;/h2&gt;

&lt;p&gt;Most stock prediction tools are complicated. StocketAI is different - I'm not inventing new algorithms, but building a complete product by integrating existing powerful tools for data sources, technical analysis, and AI models.&lt;/p&gt;

&lt;p&gt;Technically speaking, StocketAI uses a modular pipeline architecture with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-provider data acquisition&lt;/strong&gt; from VCI, TCBS, MSN, and FMARKET with automatic fallback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible provider interface&lt;/strong&gt; for adding new data sources beyond vnstock&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qlib format conversion&lt;/strong&gt; for processing and feature engineering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced AI models&lt;/strong&gt; including LightGBM, XGBoost, LSTM, GRU, and Transformer-based architectures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's designed for regular developers like me, focusing on &lt;em&gt;human-AI collaboration&lt;/em&gt;. It analyzes &lt;em&gt;longer time periods&lt;/em&gt; (months, not days) and builds individual models for each company rather than relying on generic market approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Actually Get
&lt;/h2&gt;

&lt;p&gt;We'll be utilizing a lot of data to build good models: financial reports, trading data, news, company information, and market analysis from multiple sources to create comprehensive predictions.&lt;/p&gt;

&lt;p&gt;You get to spot investment opportunities that show up over months of data instead of just reacting to today's market noise, plus you learn when to trust the predictions versus when market conditions make it hard to forecast accurately.&lt;/p&gt;

&lt;p&gt;It also helps you learn faster by letting you try different AI models and see how they perform in various market situations. And it helps you make smarter choices by showing you how uncertain predictions are, so you don't get overconfident when markets are crazy.&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%2Fo8035cyikv49hzip1awc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8035cyikv49hzip1awc.jpg" alt="Be rich" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming Next
&lt;/h2&gt;

&lt;p&gt;StocketAI isn't just a tool - it's a growing project that will keep getting better! Here's what I'm excited to add:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smarter AI Models&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even better prediction accuracy with advanced machine learning techniques&lt;/li&gt;
&lt;li&gt;Models that adapt and learn from new market conditions automatically&lt;/li&gt;
&lt;li&gt;Easy-to-understand explanations of why the AI makes certain predictions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Research Tools&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track and compare different model experiments to see what works best&lt;/li&gt;
&lt;li&gt;Tools to understand which data points matter most for each company&lt;/li&gt;
&lt;li&gt;Statistical testing to validate prediction quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to make stock investing more accessible, understandable, and successful for regular people like us!&lt;/p&gt;

&lt;h2&gt;
  
  
  Come Join the Fun! 🎉
&lt;/h2&gt;

&lt;p&gt;Check out the project on GitHub: &lt;a href="https://github.com/WitcherD/StocketAI" rel="noopener noreferrer"&gt;github.com/WitcherD/StocketAI&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Vibe code your next CV, for free</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Wed, 11 Jun 2025 16:03:28 +0000</pubDate>
      <link>https://dev.to/dbolotov/vibe-code-your-next-cv-for-free-54f4</link>
      <guid>https://dev.to/dbolotov/vibe-code-your-next-cv-for-free-54f4</guid>
      <description>&lt;p&gt;Every once in a while, you need a CV - clean, well-formatted, easy to read, and professional-looking. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn’s AI suggestions are still pretty weak.&lt;/li&gt;
&lt;li&gt;LinkedIn's PDF export... looks a bit outdated.&lt;/li&gt;
&lt;li&gt;Most online resume builders are behind a paywall and you don’t want to pay for a one-time thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a free, fast workaround:&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Step 1: For Everyone
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Export your LinkedIn profile to PDF.&lt;/strong&gt; (Go to your profile → More → Save to PDF)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Upload that PDF to Gemini (or ChatGPT).&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Optional:&lt;/strong&gt; &lt;strong&gt;Attach an image of a CV you liked from the internet.&lt;/strong&gt; This gives the AI a visual reference for layout and style.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prompt it like this:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Convert this PDF into a modern, professional CV.
- Generate clean HTML + CSS, suitable for printing as PDF
- Use a modern, clean and responsive layout and good typography and good visual hierarchy
- Highlight skills, roles, and achievements
- Proofread and improve the language
- Make it ATS-friendly and human-readable
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save the resulting HTML file&lt;/strong&gt;, open it in your browser, and &lt;strong&gt;Print to PDF&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it. You get a polished resume, fast — no signups, no watermarks, no recurring fees.&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%2Fww7dg3vzd4wat98oh643.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%2Fww7dg3vzd4wat98oh643.png" alt="Optimized CV" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🖨 Step 2: Export as a PDF (Advanced, Browser-Agnostic)
&lt;/h3&gt;

&lt;p&gt;If your browser's "Print to PDF" doesn’t render the layout properly, use &lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; - a Node.js library that gives you full control over headless Chrome.&lt;/p&gt;

&lt;h4&gt;
  
  
  Installation:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;puppeteer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Script: &lt;code&gt;export-cv.js&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&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;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cv.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`file://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;htmlFilePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cv.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;printBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8mm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8mm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8mm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8mm&lt;/span&gt;&lt;span class="dl"&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;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cv.pdf generated successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node export-cv.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>cv</category>
      <category>frontend</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Distributed C# AI Framework for Enterprise: Orleans (Part 1)</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Sun, 11 May 2025 10:32:33 +0000</pubDate>
      <link>https://dev.to/dbolotov/distributed-c-ai-framework-for-enterprise-orleans-part-1-273m</link>
      <guid>https://dev.to/dbolotov/distributed-c-ai-framework-for-enterprise-orleans-part-1-273m</guid>
      <description>&lt;p&gt;I'm a .NET solution architect, AI enthusiast, and... yes, a vibe coder.&lt;/p&gt;

&lt;p&gt;It feels like just 10 years ago the big topic was breaking monoliths into microservices. Now, it's all about multi-agent frameworks, and .NET is definitely far behind Python and TS here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff52n8arkuwcxl6k8afhk.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%2Ff52n8arkuwcxl6k8afhk.png" alt="Multi-agent AI frameworks discussion" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Good
&lt;/h3&gt;

&lt;p&gt;.NET is a strong choice for enterprise development, enabling the creation of big, &lt;strong&gt;scalable&lt;/strong&gt;, &lt;strong&gt;fault-tolerant&lt;/strong&gt;, and &lt;strong&gt;distributed&lt;/strong&gt; real-time applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bad
&lt;/h3&gt;

&lt;p&gt;The most popular in .NET &lt;strong&gt;Semantic Kernel&lt;/strong&gt; focuses on enabling AI capabilities in new applications, specifically designed for building &lt;strong&gt;AI-first&lt;/strong&gt; experiences rather than serving as the foundation for traditional enterprise systems like ERP or broker platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ugly
&lt;/h3&gt;

&lt;p&gt;I want something similar to &lt;a href="https://www.langchain.com/langgraph" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;: a combination of traditional bytecode infrastructure and AI integrations.&lt;/p&gt;

&lt;p&gt;By "traditional bytecode," I mean the foundational elements like routing, messaging, recovery, observability, concurrency, isolation, ACID, timers and scheduling, state management, and all the workflow logic required around an application.&lt;/p&gt;

&lt;p&gt;In our case, an Actor is an AI agent encapsulated within this bytecode layer. It's distributed and runs across multiple processes on a network.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;.NET 9, C#&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai" rel="noopener noreferrer"&gt;Microsoft.Extensions.Ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/orleans" rel="noopener noreferrer"&gt;Microsoft Orleans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/languages/dotnet/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Microsoft Orleans
&lt;/h3&gt;

&lt;p&gt;Microsoft Orleans is a cloud-native framework based on the virtual &lt;strong&gt;actor model&lt;/strong&gt;. In Orleans, each actor (called a grain) is identified by a stable key and is always "virtually" available. Grains are activated on-demand and automatically garbage-collected when idle. This means developers write code as if all actors are in-memory, while the Orleans runtime transparently handles activation, placement, and recovery.&lt;/p&gt;

&lt;p&gt;Grains encapsulate their own state and behavior, enabling intuitive modeling of business entities (customers, accounts, orders, etc.) as long-lived objects.&lt;/p&gt;

&lt;p&gt;Orleans was designed for massive scale. By default grains automatically partition application state and logic, letting the system scale out simply by adding silos (server hosts).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simply saying "refund agent for user 12345" in support chat is our grain, and we can have millions of them, with no engineering overhead.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RefundGrain: Grain, IRefundGrain
{
    public async Task Refund(decimal amount, string currency)
    {
        // The state is loaded; all you need to do is call an LLM.
    }
}

// no db calls, no api calls, no routes, simply like that.
var refundGrain = client.GetGrain&amp;lt;IRefundGrain&amp;gt;(12345);
await refundGrain.Refund(100, "USD");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Microsoft.Extensions.Ai
&lt;/h3&gt;

&lt;p&gt;Microsoft.Extensions.AI libraries provide with a unified and consistent way to integrate and interact with various generative AI services, offering core abstractions like IChatClient and IEmbeddingGenerator to simplify the process, promote portability, and enable the easy addition of features such as telemetry and caching through dependency injection and middleware patterns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Similar to &lt;a href="https://github.com/langchain-ai/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;, abstracts away OpenAI or Ollama from implementation details.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var response = await _chatClient.GetResponseAsync&amp;lt;ResponseModelType&amp;gt;(prompt);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Show me the code!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/WitcherD/IcpOrleansDemo" rel="noopener noreferrer"&gt;Full source code on Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The primary goal of this application is to empower sales development representatives (SDRs) to reach out to prospects at the most opportune moment with highly relevant and personalized messaging, increasing the chances of engagement and conversion. Instead of cold outreach, it enables "warm" outreach based on real-time triggers.&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%2F2tpj5u3x3c9wy2ynqvmv.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%2F2tpj5u3x3c9wy2ynqvmv.png" alt="Mermaid diagram" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Running the cluster is super simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.UseOrleans(siloBuilder =&amp;gt;
{
    siloBuilder
        .UseLocalhostClustering()
        .AddMemoryGrainStorageAsDefault()
        .AddActivityPropagation();
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, moving to k8s is also quite &lt;a href="https://github.com/OrleansContrib/Orleans.Clustering.Kubernetes" rel="noopener noreferrer"&gt;straightforward&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I love about .NET is that it's perfect for day-two operations. The debugging, troubleshooting, and observability tools are seamlessly integrated for large enterprise products. Traces, metrics, logs to different sinks, everything is just couple of lines of configuration!&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%2F34keji0cmjko3jln3o7a.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%2F34keji0cmjko3jln3o7a.png" alt="Traces" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/WitcherD/IcpOrleansDemo" rel="noopener noreferrer"&gt;Github repo to see the code&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;p&gt;I hope you now have an idea of what Orleans is and how it can be useful for building distributed applications.&lt;/p&gt;

&lt;p&gt;Next time, we'll dive deep into AI implementation and explore using Orleans to build a chat application where each grain manages its own memory.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>csharp</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Hiring Best AI Talents: Interview Questions in 2025</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Fri, 27 Dec 2024 11:41:08 +0000</pubDate>
      <link>https://dev.to/dbolotov/hiring-best-ai-talents-interview-questions-in-2025-208f</link>
      <guid>https://dev.to/dbolotov/hiring-best-ai-talents-interview-questions-in-2025-208f</guid>
      <description>&lt;p&gt;The first part of the article focuses on the characteristics and personality traits of developers (soft skills). &lt;br&gt;
The second part covers topics to discuss during an interview.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Disclaimer
&lt;/h2&gt;

&lt;p&gt;This article is focused on providing &lt;code&gt;practical questions&lt;/code&gt; for companies aiming to &lt;code&gt;integrate AI into their traditional products and businesses with minimal effort and high-quality outcomes&lt;/code&gt;. Examples include using &lt;strong&gt;AI chatbots&lt;/strong&gt; for &lt;strong&gt;retail&lt;/strong&gt;, analyzing patient data in &lt;strong&gt;healthcare&lt;/strong&gt;, and delivering personalized experiences in &lt;strong&gt;educational platforms&lt;/strong&gt;. It is not intended for research-oriented firms (e.g., Mistral, Anthropic, ElevenLabs) or enterprises (e.g., Google, Amazon, Microsoft).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Value of Soft Skills
&lt;/h3&gt;

&lt;p&gt;A trustworthy developer who is eager to learn and experiment can adapt more effectively to the rapidly evolving AI landscape. &lt;code&gt;The ability to learn and pivot quickly is more valuable than proficiency in a specific framework or programming language.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you know such a developer, you're in luck — they can solve problems without relying on guides like this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Changing AI Landscape: From NLP to LLMs and Multimodal models.
&lt;/h3&gt;

&lt;p&gt;Previously, solving specific NLP problems required specialized tools and deep expertise in areas such as: &lt;code&gt;Sentiment Analysis, Spam Detection, Topic Classification, Named Entity Recognition, Text Summarization, Translation, Duplicate Detection, Recommendation Systems, Intent Detection, Grammar Correction, Audio/Image Recognition&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Today, with the advent of large language models (LLMs) and multimodal models, many of these tasks can be addressed more efficiently and comprehensively. &lt;code&gt;The focus has shifted from building custom models/pipelines to applying pre-trained models to real-world use cases.&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid Overengineering
&lt;/h3&gt;

&lt;p&gt;When building your first product or MVP, simplicity should be the priority. If you're unsure where to start, begin with straightforward solutions.&lt;/p&gt;

&lt;p&gt;For instance, if you're building an MVP with limited usage—say, 10 requests per day for customer support — and someone suggests &lt;a href="https://www.philschmid.de/fine-tune-modern-bert-in-2025" rel="noopener noreferrer"&gt;training&lt;/a&gt; a BERT or ModernBERT model, hosting it locally, and managing the entire setup, that's likely overengineering. You'll invest significantly more time, but without the scale to handle 1,000 RPS or a dedicated tech team to maintain the system, it’s not a practical approach.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Getting started with conversational chatbots no longer requires an in-depth understanding of NLP concepts&lt;/code&gt; like encoder-only vs. decoder models. An analogy: You don't need to write assembly code to develop most business applications. Instead, &lt;code&gt;nowadays you can rely on high-level languages, frameworks&lt;/code&gt;, or even no-code/low-code solutions to efficiently solve business problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Topics to Discuss During an Interview
&lt;/h2&gt;

&lt;p&gt;From the business problem to implementation.&lt;br&gt;
Each topic also includes links to specialized resources for deeper exploration&lt;/p&gt;

&lt;h3&gt;
  
  
  Dataset Management
&lt;/h3&gt;

&lt;p&gt;Working with data is often the most challenging aspect of AI development. It's critical for &lt;strong&gt;fine-tuning and evaluations&lt;/strong&gt;. Key topics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preprocessing: Cleaning and preparing raw data for training. This includes transformations, combining data from multiple sources, and reducing dimensionality while preserving relevant information. Developers should also understand concepts like training, validation, and testing sets:

&lt;ul&gt;
&lt;li&gt;Training set: Your textbook and practice problems (you actively learn from these).&lt;/li&gt;
&lt;li&gt;Validation set: Practice tests that are different from the practice problems (you use these to gauge your understanding and identify areas needing improvement, adjusting your study methods accordingly).&lt;/li&gt;
&lt;li&gt;Testing set: The actual exam (this is the final, unseen evaluation of your knowledge).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Labeling and Annotation: Labeling entities (e.g., person, organization, location) in text, annotating sentiment (positive, negative, neutral) in reviews, and other critical tasks for fine-tuning models.&lt;/li&gt;

&lt;li&gt;Tooling: Tools for exploration, visualization, and annotation.&lt;/li&gt;

&lt;li&gt;Data Pipelines and Workflows: Automation, data sources, responsibilities in the team.&lt;/li&gt;

&lt;li&gt;Security and Privacy: Encryption, anonymization, pseudonymization, and access control to ensure data security and compliance with regulations.&lt;/li&gt;

&lt;li&gt;Versioning&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Read more &lt;a href="https://docs.smith.langchain.com/evaluation/concepts#datasets" rel="noopener noreferrer"&gt;Datasets&lt;/a&gt; and &lt;a href="https://github.com/HumanSignal/label-studio" rel="noopener noreferrer"&gt;Tagging&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  AI Agents Architecture
&lt;/h3&gt;

&lt;p&gt;Consider the &lt;strong&gt;level of control&lt;/strong&gt; and custom logic required for your application: Do you want the LLM to make decisions autonomously, or will it work in tandem with traditional bytecode programming logic? Then choose a framework or approach based on your specific requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low-code agent builders (e.g., n8n, Langflow)&lt;/li&gt;
&lt;li&gt;Multi-agent systems (e.g., CrewAI, Autogen).&lt;/li&gt;
&lt;li&gt;Multi-actor systems (e.g., LangGraph).&lt;/li&gt;
&lt;li&gt;Custom architectures tailored to specific use cases (e.g. Semantic Kernel).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Streaming&lt;/strong&gt; provides a more dynamic and responsive user experience but requires careful implementation to ensure that all system components support streaming capabilities. &lt;strong&gt;Messaging&lt;/strong&gt;, on the other hand, is easier to debug and troubleshoot but may lead to a less seamless user experience compared to streaming.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/dbolotov/ai-agents-architecture-actors-and-microservices-lets-try-langgraph-command-4ah7"&gt;Read more&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Memory and State Management
&lt;/h3&gt;

&lt;p&gt;Effective chatbot memory management involves balancing precision and recall with considerations of accuracy, latency, and cost. The principle is simple: &lt;code&gt;"Garbage In, Garbage Out."&lt;/code&gt; The ultimate goal is to equip the agent with &lt;code&gt;exactly what is needed—no more, no less&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Short-Term Memory (Conversation Thread)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Message Buffering: Retain the last N messages or a specific time window to maintain conversational context.&lt;/li&gt;
&lt;li&gt;Summarization: Condense previous interactions to preserve relevance without overwhelming the system.&lt;/li&gt;
&lt;li&gt;Session Timeout: Define when a session should expire (e.g., after 30 minutes of inactivity).&lt;/li&gt;
&lt;li&gt;Tools History: Determine whether interactions with external tools should be included in the conversation history.&lt;/li&gt;
&lt;li&gt;State Passing: Ensure seamless state transfer between agents or modules.&lt;/li&gt;
&lt;li&gt;Entity Storage: Capture and update entities, facts, and IDs relevant to the conversation.&lt;/li&gt;
&lt;li&gt;Update Timing: Decide when and how memory updates should occur.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Long-Term Memory
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Diverse Storage: Leverage various storage solutions to retrieve relevant information efficiently.&lt;/li&gt;
&lt;li&gt;Update Mechanisms: Implement robust processes for updating long-term memory with new data.&lt;/li&gt;
&lt;li&gt;Few-Shot Prompting: Use stored conversations as context for dynamic prompting.&lt;/li&gt;
&lt;li&gt;Data Masking: Ensure sensitive information is appropriately masked or anonymized.&lt;/li&gt;
&lt;li&gt;Context-Dependent Instructions: Tailor memory behavior to the specific use case or scenario.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Storage Solutions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;SQL Databases: Best suited for structured data and simple, predefined queries.&lt;/li&gt;
&lt;li&gt;Vector Databases: Optimal for storing embeddings and performing similarity searches.&lt;/li&gt;
&lt;li&gt;Document Databases: Ideal for unstructured data, such as conversation history, and flexible schemas.&lt;/li&gt;
&lt;li&gt;Graph Databases: Perfect for representing and querying intricate relationships within data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://langchain-ai.github.io/langgraph/concepts/memory" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  RAG, Embeddings, and Vector Stores
&lt;/h3&gt;

&lt;p&gt;Deep technical topic to discuss.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedding Dimensions: Balance between detail capture and computational efficiency.
&lt;/li&gt;
&lt;li&gt;Sparse vs. Dense: Sparse embeddings for discrete features; dense embeddings for semantic relationships.
&lt;/li&gt;
&lt;li&gt;Model Selection: Pre-trained models (e.g., BERT, GPT) for general tasks; fine-tuned models for specialized domains.
&lt;/li&gt;
&lt;li&gt;Language Support: Coverage of all target languages; additional training data for less common ones.
&lt;/li&gt;
&lt;li&gt;Input Type: Embedding of text, non-text data, or both based on chatbot needs.
&lt;/li&gt;
&lt;li&gt;Vector Stores: Scalable, efficient databases with metadata integration for enhanced retrieval.
&lt;/li&gt;
&lt;li&gt;Data Retrieval: Use of varied agents, approaches, and workflows for effective data access.
&lt;/li&gt;
&lt;li&gt;Reranking and Filtering: Reranking, filtering, and scoping techniques to refine results and improve relevance.
&lt;/li&gt;
&lt;li&gt;Ingest Workflows: Seamless data ingestion and transformation for embedding and storage preparation.
&lt;/li&gt;
&lt;li&gt;Quality Assurance: Regular fine-tuning of retrieval processes to maintain accuracy.
&lt;/li&gt;
&lt;li&gt;Large Datasets: Document chunking and relevance ranking for extensive data handling.
&lt;/li&gt;
&lt;li&gt;Embedding Updates: Periodic refreshing of embeddings to ensure relevance.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://qdrant.tech/documentation/beginner-tutorials/" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Integrations
&lt;/h3&gt;

&lt;p&gt;Yes, we aim to replace bytecode with tokens in most cases. However, integration with external systems remains one of the most time-consuming tasks, so it needs to be discussed as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs: REST, gRPC, GraphQL for standardized input/output interactions with AI models.
&lt;/li&gt;
&lt;li&gt;Webhooks: Real-time, event-driven communication between systems.
&lt;/li&gt;
&lt;li&gt;Two-Way Integrations: AI sending and retrieving relevant data in real time (e.g., chatbots accessing CRM data).
&lt;/li&gt;
&lt;li&gt;Data Synchronization: Consistent, up-to-date data through queues or pipelines.
&lt;/li&gt;
&lt;li&gt;Retries and Fallbacks: Failure management with retry mechanisms and default responses.
&lt;/li&gt;
&lt;li&gt;Error Handling: Input validation, error logging, and debugging alerts.
&lt;/li&gt;
&lt;li&gt;Performance Optimization: Batched API calls and caching for reduced latency.
&lt;/li&gt;
&lt;li&gt;Low-Code Platforms: like Zapier, Make for workflow automation.
&lt;/li&gt;
&lt;li&gt;Data Integration Tools: Airbyte, Apache Kafka for streaming and event processing.
&lt;/li&gt;
&lt;li&gt;Authentication: Api keys, OAuth2, 2-factor.&lt;/li&gt;
&lt;li&gt;Encryption: Vaults, protocols, keys.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;Before diving into prompt engineering, it's crucial to choose the right model for your needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Source: Is the model proprietary or open source?
&lt;/li&gt;
&lt;li&gt;Modality: Supported input/output types (e.g., text, images, audio).
&lt;/li&gt;
&lt;li&gt;Batching: Cost-efficient processing in the background (e.g., hours). &lt;/li&gt;
&lt;li&gt;Caching: Support for response reuse and optimization.
&lt;/li&gt;
&lt;li&gt;Fine-Tuning: Support and ease.
&lt;/li&gt;
&lt;li&gt;Cost: Pricing structure and affordability.&lt;/li&gt;
&lt;li&gt;Context Length: Maximum tokens supported per input/output.
&lt;/li&gt;
&lt;li&gt;SDK Frameworks: Availability of developer tools and APIs.
&lt;/li&gt;
&lt;li&gt;Ecosystem: Compatibility with libraries, plugins, or platforms.
&lt;/li&gt;
&lt;li&gt;Scaling &amp;amp; Throughput: Limits and quota.
&lt;/li&gt;
&lt;li&gt;Latency: Average response time (ms, sec, or min: o1 vs gemini flash).
&lt;/li&gt;
&lt;li&gt;Built-in Tools: Features like reasoning, code interpretation, or search.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Every week, a new model emerges that surpasses all previous ones. So, just open Twitter (x.com) and follow OpenAI, Gemini, Anthropic, Mistral, Hugging Face, LLaMA, DeepSeek, Qwen, Gemma, and Phi.&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Prompts
&lt;/h3&gt;

&lt;p&gt;It all comes down to personal experience and applying tips. LLMs are non-deterministic, meaning that similar prompts can produce different results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prioritize Longform Data: Place detailed context at the start, instructions and examples at the bottom. &lt;/li&gt;
&lt;li&gt;Prompt Chaining: Break tasks into steps (e.g., Extract → Transform → Analyze → Visualize).

&lt;ul&gt;
&lt;li&gt;Accuracy: Each step gets full attention.
&lt;/li&gt;
&lt;li&gt;Clarity: Simple tasks = clear outputs.
&lt;/li&gt;
&lt;li&gt;Traceability: Spot and fix issues easily.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Chain of Thought: Encourage step-by-step reasoning.
&lt;/li&gt;

&lt;li&gt;Multishot Prompting: Provide multiple examples for better learning.
&lt;/li&gt;

&lt;li&gt;Adopt a Persona: Specify the model’s role for focused responses.
&lt;/li&gt;

&lt;li&gt;Use Delimiters: Separate distinct input parts clearly.
&lt;/li&gt;

&lt;li&gt;Prompt Caching: Reuse prompts for efficiency.
&lt;/li&gt;

&lt;li&gt;Structured Outputs: Request organized formats like JSON or tables.
&lt;/li&gt;

&lt;li&gt;Directional Cues: Add hints or keywords or formatting like JSON to focus LLM on required problem.
&lt;/li&gt;

&lt;li&gt;ReAct Approach: Combine reasoning and action in problem-solving.
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Read more &lt;a href="https://platform.openai.com/docs/guides/prompt-engineering" rel="noopener noreferrer"&gt;OpenAI Guide&lt;/a&gt; and &lt;a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview" rel="noopener noreferrer"&gt;Anthropic Guide&lt;/a&gt; and &lt;a href="https://www.promptingguide.ai/" rel="noopener noreferrer"&gt;Prompt Engineering Guide&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Security / PII
&lt;/h3&gt;

&lt;p&gt;Basic hygiene to protect customer's data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimizing PII by removing or masking sensitive data.&lt;/li&gt;
&lt;li&gt;Trying pseudonymization techniques.&lt;/li&gt;
&lt;li&gt;Monitoring access and usage logs for unauthorized activity.&lt;/li&gt;
&lt;li&gt;Implementing real-time security breach alerts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://microsoft.github.io/presidio/" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  LLM Evaluations
&lt;/h3&gt;

&lt;p&gt;Huge topic to discuss.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performing regression testing and testing across different models.&lt;/li&gt;
&lt;li&gt;AI-Judge.&lt;/li&gt;
&lt;li&gt;Evaluating model performance in live environments (e.g., helpfulness) and offline using established gold-standard datasets.&lt;/li&gt;
&lt;li&gt;What specific metrics would you use to measure response accuracy in different contexts (e.g., &lt;code&gt;question answering&lt;/code&gt;, &lt;code&gt;summarization&lt;/code&gt;, &lt;code&gt;dialogue&lt;/code&gt;)? &lt;/li&gt;
&lt;li&gt;How do you balance competing evaluation objectives (e.g., &lt;code&gt;accuracy&lt;/code&gt; vs. &lt;code&gt;fluency&lt;/code&gt;, &lt;code&gt;helpfulness&lt;/code&gt; vs. &lt;code&gt;harmlessness&lt;/code&gt;)?&lt;/li&gt;
&lt;li&gt;What are the advantages/disadvantages of different evaluation methods (human evaluation, automated metrics, adversarial testing)?&lt;/li&gt;
&lt;li&gt;How would you detect context loss or contradictory statements across turns?&lt;/li&gt;
&lt;li&gt;Efficiency and Performance: What specific metrics would you use to measure LLM efficiency, and how would you optimize for them in production? Consider latency, throughput, and memory usage.&lt;/li&gt;
&lt;li&gt;Hallucination Detection: What specific techniques/tools would you use to detect hallucinations in LLMs? How would you distinguish between factual errors, creative interpretations, and genuine hallucinations?&lt;/li&gt;
&lt;li&gt;Human Evaluation: What specific criteria would guide human evaluators assessing LLM output quality? How would you ensure inter-rater reliability and minimize subjective bias?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.smith.langchain.com/evaluation/how_to_guides" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;p&gt;The first step is recognizing that an issue or hallucination exists. Then, you need to find the root cause, troubleshoot, and ensure it's resolved.&lt;/p&gt;

&lt;p&gt;Your application/framework should send all necessary information to an observability platform. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Metrics: Performance and cost data.&lt;/li&gt;
&lt;li&gt;Alerting: Automated alerts for performance issues or downtime.&lt;/li&gt;
&lt;li&gt;Logs and Traces: To help identify hallucinations and analyze prompt and response variance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare observability platforms, their SDKs, integrations, additional features, and cost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/dbolotov/open-source-llmops-langsmith-alternatives-langfuse-vs-lunaryai-2cl6"&gt;Read more&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;Chatbot development and AI Agent development is not just about math and NLP anymore. Today's best AI developers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versatile Problem-Solvers:&lt;/strong&gt; They combine business sense with technical skills and learn quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good Communicators:&lt;/strong&gt; They work well with others and think critically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practical Technologists:&lt;/strong&gt; They know how to use existing models and tools efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the provided topics to determine whether your candidate is a good fit. Good luck!&lt;/p&gt;

</description>
      <category>interview</category>
      <category>ai</category>
      <category>hiring</category>
      <category>startup</category>
    </item>
    <item>
      <title>AI Agents Architecture, Actors and Microservices: Let's Try LangGraph Command</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Mon, 23 Dec 2024 05:02:29 +0000</pubDate>
      <link>https://dev.to/dbolotov/ai-agents-architecture-actors-and-microservices-lets-try-langgraph-command-4ah7</link>
      <guid>https://dev.to/dbolotov/ai-agents-architecture-actors-and-microservices-lets-try-langgraph-command-4ah7</guid>
      <description>&lt;p&gt;In enterprise software development, distributed systems have been essential for the last 15 years. We've embraced SOA, microservices, actor models like Akka (Akka.NET), Microsoft Orleans, Erlang process, and countless message brokers, frameworks, and architectures.&lt;/p&gt;

&lt;p&gt;But two years ago, we started fresh.&lt;/p&gt;

&lt;p&gt;With AI/LLM models, there were no established frameworks, no observability tools, and no automated testing solutions. We were starting from zero.&lt;/p&gt;




&lt;h3&gt;
  
  
  Brief Recup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI Agent&lt;/strong&gt; is a software entity powered by artificial intelligence designed to perform tasks autonomously or semi-autonomously in a specific environment to achieve particular goals. It processes inputs, makes decisions, and takes actions based on predefined rules, learned patterns, or dynamic interactions.&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%2Fhxv3hpk9eyhjyrmntvrs.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%2Fhxv3hpk9eyhjyrmntvrs.png" alt="Image description" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actor&lt;/strong&gt; is finer-grained and lightweight, isolated entity that encapsulates state and behavior, communicates via message passing, and processes one message at a time. Thousands or millions of actors can exist within a single system, often within the same process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservice&lt;/strong&gt; is independently deployable, highly maintainable, and typically communicates over a network using protocols like HTTP/REST or gRPC. Coarser-grained and heavier compare to actors. Each microservice is typically a standalone application or process.&lt;/p&gt;

&lt;p&gt;Actors can be used within microservices to manage internal state and concurrency, combining the strengths of both paradigms. For example, a microservice can implement the actor model for event processing while exposing an API to other microservices.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Evolving Role of AI Agents
&lt;/h3&gt;

&lt;p&gt;The naming of &lt;code&gt;AI agents&lt;/code&gt; depends on context, marketing, and sometimes misunderstandings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the product level (e.g. chatbot in your company) &lt;strong&gt;AI agent&lt;/strong&gt; is an actor.&lt;/li&gt;
&lt;li&gt;At the company level  (e.g. &lt;a href="https://deepmind.google/technologies/project-mariner/" rel="noopener noreferrer"&gt;Google Mariner&lt;/a&gt;) &lt;strong&gt;AI agent&lt;/strong&gt; is a service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, the community may establish more precise terminology, such as &lt;code&gt;micro-agent&lt;/code&gt;, &lt;code&gt;AI actor&lt;/code&gt;, or &lt;code&gt;AI service&lt;/code&gt;, to distinguish these concepts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Actor&lt;/th&gt;
&lt;th&gt;Service / Microservice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Granularity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fine-grained&lt;/td&gt;
&lt;td&gt;Coarse-grained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal, encapsulated&lt;/td&gt;
&lt;td&gt;External, often stateless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Communication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Messages&lt;/td&gt;
&lt;td&gt;APIs over network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in, per actor&lt;/td&gt;
&lt;td&gt;Depends on service design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Within system or distributed&lt;/td&gt;
&lt;td&gt;Horizontal, per service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fault Tolerance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supervision hierarchies&lt;/td&gt;
&lt;td&gt;External patterns/mechanisms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time, event-driven&lt;/td&gt;
&lt;td&gt;Enterprise, modular&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you compare tools like &lt;code&gt;CrewAI Agent&lt;/code&gt;, &lt;code&gt;Autogen Agent&lt;/code&gt;, or &lt;code&gt;LangChain Agent&lt;/code&gt; to this table, you’ll see they function more like &lt;strong&gt;actors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As for an &lt;code&gt;AI service&lt;/code&gt; or &lt;code&gt;AI microservice&lt;/code&gt; I haven’t fully defined this for myself yet. It might be something we don’t need, or it’s a concept still &lt;em&gt;waiting to be built&lt;/em&gt;. I had hopes for &lt;strong&gt;Anthropic MCP&lt;/strong&gt; to fill this role, but it’s not quite there yet. (I wrote more about this &lt;a href="https://dev.to/dbolotov/anthropic-mcp-developers-thoughts-3dkk"&gt;here&lt;/a&gt;.)&lt;/p&gt;




&lt;h3&gt;
  
  
  LangGraph: Moving Toward Multi-Actor Applications
&lt;/h3&gt;

&lt;p&gt;Recently, &lt;strong&gt;LangGraph&lt;/strong&gt; &lt;a href="https://langchain-ai.github.io/langgraphjs/concepts/low_level/#command" rel="noopener noreferrer"&gt;introduced&lt;/a&gt; &lt;strong&gt;commands&lt;/strong&gt; and redefined itself from a "multi-agent" to a "multi-actor" framework. It now focuses on &lt;code&gt;stateful, multi-actor applications with LLMs for building workflows&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;I believe this is absolutely correct. Let's take a look closer and build some example.&lt;/p&gt;

&lt;p&gt;Source code on &lt;a href="https://github.com/WitcherD/langgraph-command-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Annotation, START } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { Command } from "@langchain/langgraph";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { StateGraph } from "@langchain/langgraph";
import dotenv from 'dotenv';

dotenv.config();

const StateAnnotation = Annotation.Root({
    customerInquiry: Annotation&amp;lt;string&amp;gt;({
        value: (_prev, newValue) =&amp;gt; newValue,
        default: () =&amp;gt; "",
    }),
    route: Annotation&amp;lt;string&amp;gt;({
        value: (_prev, newValue) =&amp;gt; newValue,
        default: () =&amp;gt; "",
    })
});

const model = new ChatOpenAI({
    modelName: "gpt-4o-mini"
});

const routeUserRequest = async (state: typeof StateAnnotation.State) =&amp;gt; {
    const response = await model.withStructuredOutput&amp;lt;{ route: "quotation" | "refund" }&amp;gt;({
        schema: {
            type: "object",
            properties: {
                route: { type: "string", enum: ["quotation", "refund"] }
            },
            required: ["route"]
        }
    }).invoke([
        new SystemMessage('Please categorize the user request'),        
        new HumanMessage(state.customerInquiry)
    ]);

    const routeToFunctionName = {
        "quotation": "quotationAgent",
        "refund": "refundAgent"
    };

    return new Command({
        update: {
            route: response.route
        },
        goto: routeToFunctionName[response.route],
    });
};

const quotationAgent = (state) =&amp;gt; {
    return {};
};

const refundAgent = (state) =&amp;gt; {
    return {};
};

const graph = new StateGraph(StateAnnotation)
    .addNode("routeUserRequest", routeUserRequest, { ends: ["quotationAgent", "refundAgent"] })
    .addNode("quotationAgent", quotationAgent)
    .addNode("refundAgent", refundAgent)
    .addEdge(START, "routeUserRequest")
    .compile();


async function main() {
  try {
    await graph.invoke({ customerInquiry: 'Hi, I need refund' });
    console.log("Done");
  } catch (error) {
    console.error("Error in main function:", error);
  }
}

main();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3d1ag3v5zda0wamybr2.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%2Fv3d1ag3v5zda0wamybr2.png" alt="Image description" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach removes &lt;code&gt;explicit edge&lt;/code&gt; declarations, leaving only nodes (actors).&lt;/p&gt;

&lt;p&gt;In the future, LangGraph might go beyond its graph-based structure. By adding a &lt;code&gt;message broker&lt;/code&gt;, &lt;code&gt;actor addresses&lt;/code&gt;, and &lt;code&gt;autodiscovery&lt;/code&gt;, it could evolve into something like Microsoft Orleans.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Future of AI Service Communication
&lt;/h3&gt;

&lt;p&gt;Tools like LangChain/LangGraph are still evolving. Right now, they focus on &lt;code&gt;in-service&lt;/code&gt; design and lack of &lt;code&gt;inter-service&lt;/code&gt; communication features, but they’re starting to add features for broader integration. For example, LangChain recently added &lt;a href="https://blog.langchain.dev/opentelemetry-langsmith/" rel="noopener noreferrer"&gt;OpenTelemetry support&lt;/a&gt;, which is critical for distributed systems.&lt;/p&gt;

&lt;p&gt;The next big step for community will be enabling seamless &lt;code&gt;AI-to-AI service communication&lt;/code&gt;. Whether it’s through Anthropic MCP, LangChain, or other innovations, this will define the future of AI in distributed systems.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>langchain</category>
      <category>architecture</category>
    </item>
    <item>
      <title>A Practical Guide to Reducing LLM Hallucinations with Sandboxed Code Interpreter</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Sat, 21 Dec 2024 02:06:30 +0000</pubDate>
      <link>https://dev.to/dbolotov/a-practical-guide-to-reducing-llm-hallucinations-with-sandboxed-code-interpreter-nbb</link>
      <guid>https://dev.to/dbolotov/a-practical-guide-to-reducing-llm-hallucinations-with-sandboxed-code-interpreter-nbb</guid>
      <description>&lt;p&gt;Most LLMs and SMLs are not designed for calulations (not talking about OpenAI o1 or o3 models). Just imagine the following dialogue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Company:&lt;/strong&gt; Today is Wednesday; you can return the delivery parcel within 24 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client:&lt;/strong&gt; Okay, let's do it on Tuesday.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are you sure the next AI response will be correct? As a human, you can understand that next Tuesday is six days ahead, while 24 hours is just one day. However, most LLMs cannot reliably handle such logic. Their responses are non-deterministic.&lt;/p&gt;

&lt;p&gt;This issue worsens as the context grows. If you have 30 rules and a conversation history of 30 messages, the AI loses focus and makes mistakes easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Use-Case
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're developing an AI scheduling chatbot or AI agent for your company.&lt;/li&gt;
&lt;li&gt;The company has scheduling rules that are frequently updated.&lt;/li&gt;
&lt;li&gt;Before scheduling, the chatbot must validate customer input parameters.&lt;/li&gt;
&lt;li&gt;If validation fails, the chatbot must inform the customer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Can We Do?
&lt;/h3&gt;

&lt;p&gt;Combine traditional code execution with LLMs. This idea is not new but remains underutilized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI integrates this feature into its Assistant API, but not in Complitions API.&lt;/li&gt;
&lt;li&gt;Google recently introduced &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/code-execution" rel="noopener noreferrer"&gt;code interpreter&lt;/a&gt; capabilities in Gemini 2.0 Flash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hugu2d772eye0obsxu6.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%2F7hugu2d772eye0obsxu6.png" alt="Image description" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Our Solution Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker (Podman)&lt;/li&gt;
&lt;li&gt;LangGraph.js&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/engineer-man/piston" rel="noopener noreferrer"&gt;Piston&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code Interpreter Sandbox
&lt;/h3&gt;

&lt;p&gt;To securely run generated code, the most popular cloud code interpreters are &lt;a href="https://e2b.dev/" rel="noopener noreferrer"&gt;e2b&lt;/a&gt;, Google, and OpenAI as I mentioned before. &lt;/p&gt;

&lt;p&gt;However, I was looking for an open-source, self-hosted solution for flexibility and cost-effectiveness. So, 2 good options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Piston&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jupyter&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose Piston for its ease of deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Piston Installation
&lt;/h3&gt;

&lt;p&gt;It took me a while to understand how to add python execution environment to Piston.&lt;/p&gt;

&lt;h4&gt;
  
  
  0. Enable cgroup v2
&lt;/h4&gt;

&lt;p&gt;For Windows WSL, &lt;a href="https://stackoverflow.com/questions/73021599/how-to-enable-cgroup-v2-in-wsl2" rel="noopener noreferrer"&gt;this article&lt;/a&gt; was helpful.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Run a Container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 2000:2000 &lt;span class="nt"&gt;-v&lt;/span&gt; d:&lt;span class="se"&gt;\p&lt;/span&gt;iston:&lt;span class="s1"&gt;'/piston'&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; piston_api ghcr.io/engineer-man/piston
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Checkout the Piston Repository
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/engineer-man/piston
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Add Python Support
&lt;/h4&gt;

&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node cli/index.js ppman &lt;span class="nb"&gt;install &lt;/span&gt;python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, this command uses your container API running on &lt;code&gt;localhost:2000&lt;/code&gt; to install Python.&lt;/p&gt;

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

&lt;p&gt;Using the &lt;a href="https://github.com/dthree/node-piston" rel="noopener noreferrer"&gt;Piston Node.js Client&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;piston&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;piston-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeInterpreter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;piston&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:2000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;codeInterpreter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;python&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;print("Hello World!")&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  AI Agents Implementation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/WitcherD/QuotationAI" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're going to use some advanced techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graph and subgraph architecture&lt;/li&gt;
&lt;li&gt;Parallel node execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qdrant&lt;/strong&gt; for storage&lt;/li&gt;
&lt;li&gt;Observability via &lt;strong&gt;LangSmith&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-4o-mini&lt;/strong&gt;, a cost-efficient LLM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to the LangSmith trace for a detailed overview of the flow:&lt;br&gt;
&lt;a href="https://smith.langchain.com/public/b3a64491-b4e1-423d-9802-06fcf79339d2/r" rel="noopener noreferrer"&gt;https://smith.langchain.com/public/b3a64491-b4e1-423d-9802-06fcf79339d2/r&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: Extract datetime-related scheduling parameters from user input
&lt;/h4&gt;

&lt;p&gt;Example: "Tomorrow, last Friday, in 2 hours, at noon time."&lt;br&gt;
We use code interpreter to ensure reliable extraction, as LLMs can fail even with current date-time contextual information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Prompt for Python Code Generation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your task is to transform natural language text into Python code that extracts datetime-related scheduling parameters from user input.  

## Instructions:  
- You are allowed to use only the "datetime" and "calendar" libraries.  
- You can define additional private helper methods to improve code readability and modularize validation logic.  
- Do not include any import statements in the output.  
- Assume all input timestamps are provided in the GMT+8 timezone. Adjust calculations accordingly.  
- The output should be a single method definition with the following characteristics:  
  - Method name: \`getCustomerSchedulingParameters\`  
  - Arguments: None  
  - Return: A JSON object with the keys:  
    - \`appointment_date\`: The day of the month (integer or \`None\`).  
    - \`appointment_month\`: The month of the year (integer or \`None\`).  
    - \`appointment_year\`: The year (integer or \`None\`).  
    - \`appointment_time_hour\`: The hour of the day in 24-hour format (integer or \`None\`).  
    - \`appointment_time_minute\`: The minute of the hour (integer or \`None\`).  
    - \`duration_hours\`: The duration of the appointment in hours (float or \`None\`).  
    - \`frequency\`: The recurrence of the appointment. Can be \`"Adhoc"\`, \`"Daily"\`, \`"Weekly"\`, or \`"Monthly"\` (string or \`None\`).  

- If a specific value is not found in the text, return \`None\` for that field.  
- Focus only on extracting values explicitly mentioned in the input text; do not make assumptions.  
- Do not include print statements or logging in the output.  

## Example:  

### Input:  
"I want to book an appointment for next Monday at 2pm for 2.5 hours."  

### Output:  
def getCustomerSchedulingParameters():  
    """Extracts and returns scheduling parameters from user input in GMT+8 timezone.  

    Returns:  
        A JSON object with the required scheduling parameters.  
    """  
    def _get_next_monday():  
        """Helper function to calculate the date of the next Monday."""  
        current_time = datetime.utcnow() + timedelta(hours=8)  # Adjust to GMT+8  
        today = current_time.date()  
        days_until_monday = (7 - today.weekday() + 0) % 7  # Monday is 0  
        return today + timedelta(days=days_until_monday)  

    next_monday = _get_next_monday()  
    return {  
        "appointment_date": next_monday.day,  
        "appointment_month": next_monday.month,  
        "appointment_year": next_monday.year,  
        "appointment_time_hour": 14,  
        "appointment_time_minute": 0,  
        "duration_hours": 2.5,  
        "frequency": "Adhoc"  
    }

### Notes:
Ensure the output is plain Python code without any formatting or additional explanations.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Fetch Rules from Storage
&lt;/h4&gt;

&lt;p&gt;And then transform them into Python code for validation.&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%2Fv854qj5r69k2wzy659cx.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%2Fv854qj5r69k2wzy659cx.png" alt="Image description" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Run Generated Code in Sandbox:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pythonCodeToInvoke = `
import sys
import datetime
import calendar
import json

${state.pythonValidationMethod}

${state.pythonParametersExtractionMethod}

parameters = getCustomerSchedulingParameters()

valiation_errors = validateCustomerSchedulingParameters(parameters["appointment_year"], parameters["appointment_month"], parameters["appointment_date"], parameters["appointment_time_hour"], parameters["appointment_time_minute"], parameters["duration_hours"], parameters["frequency"])

print(json.dumps({"validation_errors": valiation_errors}))`;

    const traceableCodeInterpreterFunction = await traceable((pythonCodeToInvoke: string) =&amp;gt; codeInterpreter.execute('python', pythonCodeToInvoke, { args: [] }));
    const result = await traceableCodeInterpreterFunction(pythonCodeToInvoke);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7vcweppkd5vc5rtnbto.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%2Fv7vcweppkd5vc5rtnbto.png" alt="Image description" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/WitcherD/QuotationAI" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Potential Improvements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implement an iterative loop for LLMs to debug and refine Python code execution dynamically.&lt;/li&gt;
&lt;li&gt;Human in the loop for validation method code generation.&lt;/li&gt;
&lt;li&gt;Caching generated code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Bytecode execution and token-based LLMs are highly complementary technologies, unlocking a new level of flexibility. This synergistic approach has a bright future, for example AWS's recent "Bedrock Automated Reasoning", which appears to offer a similar solution within their enterprise ecosystem. Google and Microsoft also will show us something similar very soon.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>sandbox</category>
      <category>openai</category>
    </item>
    <item>
      <title>Anthropic MCP: Developer's Thoughts</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Fri, 13 Dec 2024 15:08:35 +0000</pubDate>
      <link>https://dev.to/dbolotov/anthropic-mcp-developers-thoughts-3dkk</link>
      <guid>https://dev.to/dbolotov/anthropic-mcp-developers-thoughts-3dkk</guid>
      <description>&lt;p&gt;Anthropic wants access to your computer and data. The more data they can use, the more useful their service becomes. Recently, Anthropic launched computer use and subsequently introduced Model Context Protocol (MCP), which is an effort to create a standardized framework for AI interactions with local and remote resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of MCP Components&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo6a5htdy0eud6lvqiq5.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%2Fdo6a5htdy0eud6lvqiq5.png" alt="Anthropic MCP Model Context Protocol" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app can retrieve information from MCP servers (e.g., files or text, called resources) or invoke server functions (tools).&lt;/p&gt;

&lt;p&gt;It's very simple. To encourage developers to create servers, Anthropic provided TypeScript and Python SDKs and made everything open-source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP Server&lt;/strong&gt;: An HTTP Listener that uses JSON-RPC for requests and Server-Sent Events (SSE) for asynchronous communication (it's  WebSockets, allows servers to send events back to the app)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Schema&lt;/strong&gt;: A &lt;a href="https://github.com/modelcontextprotocol/specification/blob/main/schema/schema.json" rel="noopener noreferrer"&gt;JSON schema&lt;/a&gt; defining the structure of resources and interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Client&lt;/strong&gt;: A TypeScript or Python library within the host app that connects to the server, manages server connections, processes queries, and handles responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While technically not groundbreaking, MCP marks the &lt;strong&gt;beginning of a standardization process&lt;/strong&gt;. It addresses common technical needs, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔀 Connection lifecycle management.&lt;/li&gt;
&lt;li&gt;⚡ Error handling.&lt;/li&gt;
&lt;li&gt;🔬 Logging and monitoring capabilities.&lt;/li&gt;
&lt;li&gt;✅ Schema/input validation.&lt;/li&gt;
&lt;li&gt;⏳ Timeouts.&lt;/li&gt;
&lt;li&gt;📃 Unified message formats.&lt;/li&gt;
&lt;li&gt;📚 Testing.&lt;/li&gt;
&lt;li&gt;and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  That's nice, but what's next?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔍 &lt;strong&gt;Google&lt;/strong&gt; has introduced &lt;a href="https://deepmind.google/technologies/project-mariner/" rel="noopener noreferrer"&gt;Project Mariner&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Microsoft&lt;/strong&gt; has Copilot.
&lt;/li&gt;
&lt;li&gt;🤖 &lt;strong&gt;OpenAI&lt;/strong&gt; is incorporating tools into ChatGPT, with rumors swirling about their own browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the catch: none of these companies are discussing MCP. If you believe you can simply add MCP to your AI agent and expect seamless integration with all AI products, you’re likely mistaken 😥.&lt;/p&gt;

&lt;h3&gt;
  
  
  Need or force?
&lt;/h3&gt;

&lt;p&gt;Standards like OpenAPI and OpenTelemetry gained popularity because they addressed pain points shared by millions of developers working with distributed systems and complex infrastructures.&lt;/p&gt;

&lt;p&gt;The OpenAI API succeeded because they offered something so big that everyone wanted to use, and not because they open-sourced their schema and SDK.&lt;/p&gt;

&lt;p&gt;Another example. Take cloud providers like AWS, GCP, Azure, Alibaba, and Yandex. Even though they offer similar products, their APIs remain non-standardized. If you’re building a product like Pinecone, you must integrate with each cloud individually. Maybe MCP falls into this category—useful only for niche scenarios?&lt;/p&gt;

&lt;h3&gt;
  
  
  Need something bigger?
&lt;/h3&gt;

&lt;p&gt;As I mentioned earlier, Anthropic is solving their own commercial problem: they want access to your data. But why should I, as an AI developer building agents, care to adopt MCP?&lt;/p&gt;

&lt;p&gt;What we really need is an &lt;strong&gt;AI-to-AI&lt;/strong&gt; or &lt;strong&gt;AI-Agent-to-AI-Agent&lt;/strong&gt; communication protocol, not just another wrapper around HTTP using opinionated protocols like JSON-RPC and SSE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP flow&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6yx1iwr81c9tnsd0bte.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%2Fc6yx1iwr81c9tnsd0bte.png" alt="Current flow" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dream flow&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1m9bh5329qnvu2p5ix5.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%2Fe1m9bh5329qnvu2p5ix5.png" alt="Image description" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm thinking about a future where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤝 Claude Desktop uses a built-in code interpreter capable of interacting seamlessly with the external world and asking other agents for help using an internal language of LLMs. In this case, SDKs like MCP become unnecessary.&lt;/li&gt;
&lt;li&gt;🔗 AI systems communicate directly with each other without relying on us as intermediaries.&lt;/li&gt;
&lt;li&gt;🖼️ Protocols support &lt;strong&gt;realtime multimodality&lt;/strong&gt; (speech, vision, text).&lt;/li&gt;
&lt;li&gt;🧠 Context sharing is effortless, including system prompts, conversation history, and active prompts.&lt;/li&gt;
&lt;li&gt;🏗️ Hierarchical problem-solving allows tasks to be delegated to knowledge subgraphs.&lt;/li&gt;
&lt;li&gt;📊 Features like conversation summarization, compositional function calls, and conditional execution of functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's kind of &lt;strong&gt;Globally distributed CrewAI or Autogen&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;I believe the engineers at OpenAI and Anthropic are some of the smartest minds in AI, and they surely understand these challenges. &lt;/p&gt;

&lt;p&gt;That said, MCP feels overhyped and not a true "game changer"—at least not yet. You might be better off waiting for further breakthroughs in local AI assistants before investing significant effort in adopting and maintaining this solution.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>anthropic</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Open Source LLMOps LangSmith Alternatives: LangFuse vs. Lunary.ai</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Wed, 11 Dec 2024 03:55:54 +0000</pubDate>
      <link>https://dev.to/dbolotov/open-source-llmops-langsmith-alternatives-langfuse-vs-lunaryai-2cl6</link>
      <guid>https://dev.to/dbolotov/open-source-llmops-langsmith-alternatives-langfuse-vs-lunaryai-2cl6</guid>
      <description>&lt;p&gt;LangSmith is a powerful LLMOps platform, but its cost and cloud reliance can be drawbacks. Open-source options like LangFuse and Lunary.ai offer open source self-hostable alternatives. This guide compares their features to help you choose the best fit for your needs.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; This information is accurate as of December 2024. The landscape of LLMOps evolves rapidly, so updates within the next three months are likely. Also I'm focusing on TypeScript and NodeJs integrations and tooling, not Python.&lt;/p&gt;

&lt;p&gt;For testing, I’ve integrated these tools into my &lt;strong&gt;&lt;a href="https://github.com/WitcherD/QuotationAI" rel="noopener noreferrer"&gt;LangGraph.js demo project&lt;/a&gt;&lt;/strong&gt;, which mirrors common production tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nested execution flow (subgraphs).
&lt;/li&gt;
&lt;li&gt;Gemini and OpenAI LLM calls.
&lt;/li&gt;
&lt;li&gt;Input parameter handling.
&lt;/li&gt;
&lt;li&gt;Data retrievals from Qdrant.
&lt;/li&gt;
&lt;li&gt;Tagging for trace organization.
&lt;/li&gt;
&lt;li&gt;Conditional map-reduce branching.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore how these platforms stack up against LangSmith.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR for the Busy Reader&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observability Only:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free and self-hosted:&lt;/strong&gt; Choose &lt;strong&gt;LangFuse&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-based:&lt;/strong&gt; Opt for &lt;strong&gt;Lunary&lt;/strong&gt;, which is more cost-effective.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Full-Feature LLMOps Suite:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Using LangChain/LangGraph? Stick with &lt;strong&gt;LangSmith&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Exploring other frameworks? Go with &lt;strong&gt;LangFuse&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Special Conditions:&lt;/strong&gt; Non-profits, educational institutions, and open-source projects can negotiate favorable terms with &lt;strong&gt;LangFuse&lt;/strong&gt; and &lt;strong&gt;LangSmith&lt;/strong&gt;.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Traces&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Traces are vital for effective LLM observability. All three platforms excel in:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tree structure for trace visualization.
&lt;/li&gt;
&lt;li&gt;Metadata tagging for better trace organization.
&lt;/li&gt;
&lt;li&gt;Role-based conversation history (e.g., assistant, user, system).
&lt;/li&gt;
&lt;li&gt;Visual clarity with a polished UI.
&lt;/li&gt;
&lt;li&gt;Duration tracking for operations.
&lt;/li&gt;
&lt;li&gt;Pricing details for calls.
&lt;/li&gt;
&lt;li&gt;Error tracking for debugging.
&lt;/li&gt;
&lt;li&gt;Session-based data organization.
&lt;/li&gt;
&lt;li&gt;Support for Base64 images.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Platform-Specific Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image/Attachment Display:&lt;/strong&gt; Neither LangFuse nor Lunary supports displaying images or attachments via URLs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lunary:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Automatic PII masking requires an Enterprise license, with no manual masking options.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;LangFuse:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Automatic masking is unavailable, but basic manual masking is supported.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Overall, all three products are quite similar in a positive way when it comes to observability features&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;LangSmith UI&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fotpkr0owus585qyzc9ka.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%2Fotpkr0owus585qyzc9ka.png" alt="LangSmith" width="800" height="454"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lunary UI&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxduqp4v1dsbclsrr48xu.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%2Fxduqp4v1dsbclsrr48xu.png" alt="Lunary" width="800" height="502"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LangFuse UI&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkyxse4p3nynh015o6p4.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%2Fkkyxse4p3nynh015o6p4.png" alt="LangFuse" width="800" height="503"&gt;&lt;/a&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Metadata and Search&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All platforms offer robust search capabilities by date, metadata, IDs, status, and duration. No notable differences here.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Monitoring&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Monitoring features are strong across all three platforms:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangSmith:&lt;/strong&gt; Offers custom charts for deeper insights.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse &amp;amp; Lunary:&lt;/strong&gt; Provide built-in dashboards with filtering options.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lunary:&lt;/strong&gt; Advanced monitoring available in the Team Tier.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;LangFuse Monitoring Example&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx09dmjnfng08319y7y89.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%2Fx09dmjnfng08319y7y89.png" alt="LangFuse Monitoring" width="800" height="488"&gt;&lt;/a&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Datasets&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;LangSmith is fully featured.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Differences
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lunary:&lt;/strong&gt; Doesn’t support adding traces to datasets directly.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exporting:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;LangFuse lacks export options, such as those needed for OpenAI fine-tuning.
&lt;/li&gt;
&lt;li&gt;Lunary requires a Team license for exports.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Playground&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the must-have feature for debugging and improving agent prompts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lunary:&lt;/strong&gt; Offers limited usage in the basic tier.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse:&lt;/strong&gt; Requires a $100/user Pro license for self-hosted deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;LangFuse Playground Configuration&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd68bskpj8h442sr20bkk.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%2Fd68bskpj8h442sr20bkk.png" alt="LangFuse Playground" width="800" height="553"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lunary Playground Configuration&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp424rcekbr37eudzefo.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%2Fjp424rcekbr37eudzefo.png" alt="Lunary Playground" width="800" height="603"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Hard to compete with Langsmith here.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Deployment&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lunary:&lt;/strong&gt; Docker/Kubernetes deployment requires an Enterprise license. You have to install and maintain the product on your own. Don't remember when the last time run anything not in containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse:&lt;/strong&gt; The free self-hosted docker version lacks a playground and LLM-as-judge evaluators.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Prompt Experiments&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All platforms perform well in this area, offering robust tools for testing and refining prompts.  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Integrations&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse &amp;amp; Lunary:&lt;/strong&gt; Compatible with LangChain, LangGraph, LlamaIndex, DSPy, and more.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangSmith:&lt;/strong&gt; Limited to LangChain and LangGraph.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Evaluators&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Evaluators are essential for scoring traces and running tests on datasets.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangSmith&lt;/strong&gt;: Full evaluator suite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse&lt;/strong&gt;: Full evaluator suite. Requires a paid license for LLM-as-judge. Not limited with langchain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lunary&lt;/strong&gt;: Lacks built-in evaluators.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;LangFuse Evaluation&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstfwv8k2d04j2dcjbmcw.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%2Fstfwv8k2d04j2dcjbmcw.png" alt="Image description" width="800" height="1007"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both LangSmith and LangFuse have advanced scoring and evaluation features, extremely useful toolset for any LLM application.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Documentation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All 3 platforms provide perfect complete documentation with tons of examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Pricing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For a small team of 3 users:  &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Self-Hosted&lt;/th&gt;
&lt;th&gt;Cloud&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lunary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$20/user/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangSmith&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;$39/user/month (50% for startups)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangFuse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (Observability), $100/user/month (LLMOps)&lt;/td&gt;
&lt;td&gt;$60/user/month (50% for startups)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ Note: This information is accurate as of December 2024&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;More platforms&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/traceloop/openllmetry" rel="noopener noreferrer"&gt;OpenLLMetry sdk&lt;/a&gt; + &lt;a href="https://www.traceloop.com/" rel="noopener noreferrer"&gt;Traceloop&lt;/a&gt;. It's a great platform, but I was seaching for self-hosted product.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Arize-ai/phoenix" rel="noopener noreferrer"&gt;Phoenix - Arize AI&lt;/a&gt; one of the most powerful LLMOps tools with observability and evaluation features. But has poor langchain.js and other TypeScript framework integrations and focused on ML in general, not just LLM, so it's making UI less intuitive when develop chatbots or LLM agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary: Choosing an LLMOps Platform&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Best for Observability:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse&lt;/strong&gt;: Free self-hosted option.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lunary&lt;/strong&gt;: Affordable cloud-based solution.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Best for Full Features:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangSmith&lt;/strong&gt;: Comprehensive LLMOps suite, ideal for LangChain/LangGraph users.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangFuse&lt;/strong&gt;: Good for other frameworks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Observability with Grafana Cloud and OpenTelemetry in .net microservices</title>
      <dc:creator>Dmitrii</dc:creator>
      <pubDate>Thu, 06 Oct 2022 09:07:41 +0000</pubDate>
      <link>https://dev.to/dbolotov/observability-with-grafana-cloud-and-opentelemetry-in-net-microservices-448c</link>
      <guid>https://dev.to/dbolotov/observability-with-grafana-cloud-and-opentelemetry-in-net-microservices-448c</guid>
      <description>&lt;p&gt;People say, that application development lifecycle consists of 3 steps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make it work &lt;/li&gt;
&lt;li&gt;make it right  ← we’re here&lt;/li&gt;
&lt;li&gt;make it fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose you’re developing a microservice. It can be over rest/grpc/kafka or whatever, You’ve completed all functional and non-functional requirements incl. authentication/authorization, validation, your application is secure, scalable and solving a business problem.&lt;/p&gt;

&lt;p&gt;In this article, you’ll find out how to make your app production-ready whether it is cloud native and hosted in Kubernetes or more traditional without using containers.&lt;/p&gt;

&lt;p&gt;We will cover popular tools and frameworks aiming to solve common needs for every application without reinventing the wheel: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grafana Cloud (Prometheus, Grafana, Loki, Tempo), &lt;/li&gt;
&lt;li&gt;OpenTelemetry, &lt;/li&gt;
&lt;li&gt;Serilog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t heard about them, no worries, the article applies to any experience level. You can find a fully working demo project on &lt;a href="https://github.com/WitcherD/demo-services-dogs" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability/Monitoring
&lt;/h2&gt;

&lt;p&gt;Observability means some collected data explaining the state of your application. For production environments it is critical to know how your application behaves. &lt;a href="https://www.nginx.com/blog/opentelemetry-is-changing-how-we-trace-design-apps/" rel="noopener noreferrer"&gt;Nginx post&lt;/a&gt; simplified it into 3 simple questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/concepts/signals/metrics/" rel="noopener noreferrer"&gt;Metrics&lt;/a&gt;&lt;/strong&gt; – “Is there a problem?”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/" rel="noopener noreferrer"&gt;Traces&lt;/a&gt;&lt;/strong&gt; – “Where is the problem?”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/concepts/signals/logs/" rel="noopener noreferrer"&gt;Logs&lt;/a&gt;&lt;/strong&gt; – “What is the problem?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plenty of cool tools capable of doing full monitoring of your system can answer all the questions above. The problem we face nowadays isn’t knowing the answer but having too many answers. &lt;/p&gt;

&lt;p&gt;This article can help you make the right decision and save tons of time and money for your business. Described approach works best for start-ups and small companies.&lt;/p&gt;

&lt;p&gt;From a high-level perspective, there are 2 popular groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS: &lt;a href="https://www.dynatrace.com/" rel="noopener noreferrer"&gt;Dynatrace&lt;/a&gt;, &lt;a href="https://logz.io/" rel="noopener noreferrer"&gt;Logz.io&lt;/a&gt;, &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt;, &lt;a href="https://www.honeycomb.io/" rel="noopener noreferrer"&gt;Honeycomb&lt;/a&gt;, &lt;a href="https://newrelic.com/" rel="noopener noreferrer"&gt;New Relic&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open source, self-hosted: &lt;a href="https://github.com/prometheus/prometheus" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; + &lt;a href="https://github.com/grafana/grafana" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; and &lt;a href="https://github.com/elastic/elasticsearch" rel="noopener noreferrer"&gt;ElasticSearch&lt;/a&gt; + &lt;a href="https://github.com/elastic/kibana" rel="noopener noreferrer"&gt;Kibana&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today we’re talking about &lt;a href="https://grafana.com/products/cloud/" rel="noopener noreferrer"&gt;Grafana Cloud&lt;/a&gt; (Prometheus for metrics, Loki for logs, Tempo for traces), a SaaS product. Its free plan includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10,000 series for Prometheus or Graphite metrics&lt;/li&gt;
&lt;li&gt;50 GB of logs&lt;/li&gt;
&lt;li&gt;50 GB of traces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's very generous compared to competitors! &lt;/p&gt;

&lt;p&gt;When exceeding these limits, you can freely choose to continue on SaaS or switch to open-source self-hosted distribution.&lt;/p&gt;

&lt;p&gt;If you decide to give it a try, &lt;a href="https://grafana.com/auth/sign-up/create-user" rel="noopener noreferrer"&gt;Sign Up&lt;/a&gt; here before moving to the next step.&lt;/p&gt;

&lt;p&gt;Before we start, these 2 portals shortcut will be helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://grafana.com/orgs/witcherd92" rel="noopener noreferrer"&gt;https://grafana.com/orgs/&lt;/a&gt;{YouOrganizationName} - Account Management&lt;/li&gt;
&lt;li&gt;https://{YouOrganizationName}.grafana.net - Grafana UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Grafana Agent
&lt;/h3&gt;

&lt;p&gt;Grafana Agent is responsible for delivering your metrics/traces from your application to the cloud. We use grafana agent for **metrics **and **traces **only. The approach for **logging **will be different.&lt;/p&gt;

&lt;p&gt;The data flow is illustrated below: &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%2Flt4jgckudqohlcvdxttx.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%2Flt4jgckudqohlcvdxttx.png" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;
Grafana Agent



&lt;p&gt;Open https://{YouOrganizationName}.grafana.net &lt;/p&gt;

&lt;p&gt;=&amp;gt; go to the integrations tab,&lt;br&gt;
=&amp;gt; choose &lt;code&gt;grafana agent&lt;/code&gt; &lt;br&gt;
=&amp;gt; and then follow the instructions. &lt;/p&gt;

&lt;p&gt;Grafana Agent can work on Windows/MacOS/Debian/RedHat&lt;/p&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%2F9cltsz90tw969d5bozzb.png" width="800" height="404"&gt;Grafana Agent Integration Tab



&lt;p&gt;After installation, we need to configure the agent:&lt;/p&gt;

&lt;p&gt;If you’re using Windows with the default installation, go to C:\Program Files\Grafana Agent and edit agent-config.yaml For other cases check the &lt;a href="https://grafana.com/docs/agent/latest/configuration/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integrations&lt;/span&gt;
    &lt;span class="na"&gt;remote_write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;basic_auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dogs-service&lt;/span&gt;
        &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
        &lt;span class="na"&gt;metrics_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/metrics/prometheus&lt;/span&gt;
        &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost:5000'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dogs-service-healthchecks&lt;/span&gt;
        &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
        &lt;span class="na"&gt;metrics_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health/prometheus&lt;/span&gt;
        &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost:5000'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;
  &lt;span class="na"&gt;wal_directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/grafana-agent-wal&lt;/span&gt;

&lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;remote_write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;basic_auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;replaceit&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the username, password and URLs, go to &lt;a href="https://grafana.com/orgs/witcherd92" rel="noopener noreferrer"&gt;https://grafana.com/orgs/&lt;/a&gt;{YouOrganizationName} and hit ‘Details’ in Tempo and Prometheus.&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%2Fikqq7qr45ih316ck54ah.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%2Fikqq7qr45ih316ck54ah.png" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;
Organization settings



&lt;p&gt;Save the file and restart Grafana Agent service.&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%2F6dq9ykn82umf7qgysmb0.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%2F6dq9ykn82umf7qgysmb0.png" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;
Restarting Grafana Agent



&lt;p&gt;With this, Grafana Agent configuration is complete. Now, let’s send some data to Grafana Cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traces
&lt;/h3&gt;

&lt;p&gt;To understand the traces it’s easier to take a look at the picture.&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%2Fd7fm68086tvzlyfk3yhj.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%2Fd7fm68086tvzlyfk3yhj.png" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;
Grafana Tempo



&lt;p&gt;In short, you can trace an activity through your services in a distributed system.&lt;/p&gt;

&lt;p&gt;To demonstrate it, in our simple &lt;a href="https://github.com/WitcherD/demo-services-dogs" rel="noopener noreferrer"&gt;demo project&lt;/a&gt; we’ll be using 3 components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.net 6 web api&lt;/li&gt;
&lt;li&gt;sqlite database&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dog.ceo/dog-api/" rel="noopener noreferrer"&gt;external dogs api&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To send traces to the monitoring system, we need to use some framework. &lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is a standardized and recommended approach to implementing tracing in the application nowadays. It gets support from all popular tools so the integration will be seamless. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenTelemetry is a collection of tools, APIs, and SDKs. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software’s performance and behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We're going to use &lt;a href="https://github.com/open-telemetry/opentelemetry-dotnet" rel="noopener noreferrer"&gt;OpenTelemetry .NET SDK&lt;/a&gt;. Add following nuget dependencies to the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Exporter.OpenTelemetryProtocol"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.4.0-alpha.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Exporter.Prometheus.AspNetCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.4.0-alpha.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Extensions.Hosting"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0-rc9.6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Instrumentation.AspNetCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0-rc9.6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Instrumentation.EntityFrameworkCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0-beta.3"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Instrumentation.EventCounters"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"0.1.0-alpha.1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Instrumentation.Http"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0-rc9.6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry.Instrumentation.SqlClient"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0-rc9.6"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in Program.cs configure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenTelemetryTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resourceBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resourceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplicationName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnvironmentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry:ApplicationVersion"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MachineName&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="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instrumentationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;instrumentationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordException&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instrumentationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;instrumentationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordException&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSqlClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instrumentationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;instrumentationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordException&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;instrumentationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDbStatementForText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEntityFrameworkCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instrumentationOptions&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;instrumentationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDbStatementForText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OtlpExportProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grpc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry:Exporter:Otlp:Endpoint"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;‘OpenTelemetry:Exporter:Otlp:Endpoint’ comes from appsettings.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OpenTelemetry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ApplicationVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"Exporter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Otlp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4317"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;a href="http://localhost:4317" rel="noopener noreferrer"&gt;http://localhost:4317&lt;/a&gt; is an endpoint of the Grafana Agent we installed in the previous step.&lt;/p&gt;

&lt;p&gt;By using OTLP protocol, our application will send traces to grafana agent which will take care of the rest. In our case, the agent will resend it to grafana cloud. If required, you can always easily switch from grafana cloud to your self-hosted tempo just by configuring the agent. There's no need to modify the source code, &lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;Let’s run the app and hit the test endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET {{host}}/api/v1/dogs/new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l3up94f8cxvp8e4l3p5.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%2F0l3up94f8cxvp8e4l3p5.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;
Testing traces



&lt;p&gt;The parent span belongs to our API request, and 2 child spans for calling external API and saving data to the database. There’s a correlation between the duration, operation results, and metadata. That is basically everything we need to trace and debug activity from top to bottom.&lt;/p&gt;

&lt;p&gt;Let’s hit another endpoint to see what we get in case of error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET {{host}}/api/v1/fail500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going back to search panel, and searching for our request:&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%2Fvgposkxas9ea4t63uz8m.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%2Fvgposkxas9ea4t63uz8m.png" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;
Traces Search Panel



&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%2Ftufl46mc1kfyeroxv9sr.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%2Ftufl46mc1kfyeroxv9sr.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;
Trace Tags



&lt;p&gt;It looks perfect, we have all the needed information here to backtrace the issue: External API returned HTTP 404, Refit threw an exception, and our API returned HTTP 500 to the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics
&lt;/h3&gt;

&lt;p&gt;Metrics - aggregated real-time data to measure your application performance.&lt;/p&gt;

&lt;p&gt;For example, it can be the latency of your API endpoints, number of http 5XX errors, and the free space on the hard drive.&lt;/p&gt;

&lt;p&gt;There are many frameworks to collect metrics in .net core service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.app-metrics.io/" rel="noopener noreferrer"&gt;app-metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/prometheus-net/prometheus-net" rel="noopener noreferrer"&gt;prometheus-net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnetos.org/blog/2021-11-22-dotnet-monitor-grafana/" rel="noopener noreferrer"&gt;dotent-counters and dotnet-monitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of them are working just fine, but we’re going to use OpenTelemetry sdk again, and then expose prometheus endpoint. Grafana Agent will fetch it and send to Grafana Cloud, similarly to traces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenTelemetryMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resourceBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resourceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplicationName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnvironmentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"OpenTelemetry:ApplicationVersion"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MachineName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;resourceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTelemetrySdk&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="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEventCounterMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPrometheusExporter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHealthChecksPrometheusExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health/prometheus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResultStatusCodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it, very simple. Go to Grafana Cloud, change data source to prometheus and try to visualize some metrics of your choice. E.g. queries per second:&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%2Ft5xta26rizmj4g7x0yuz.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%2Ft5xta26rizmj4g7x0yuz.png" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;
Setting up metrics visualization



&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%2F9gvtexyouz4g2r2rc0kh.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%2F9gvtexyouz4g2r2rc0kh.png" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;
Queries per second



&lt;p&gt;You might already be familiar with Prometheus and Grafana metrics. It’s the most loved tool among DevOps experts all around the globe to monitor VMs, networks, databases, and whatever metrics you can imagine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Healthchecks
&lt;/h3&gt;

&lt;p&gt;Healthchecks are the edge case of app metrics to automate operations (e.g. when your app should automatically restart when it's running out of memory, route traffic, a new instance in your cluster becomes available, and so on).&lt;/p&gt;

&lt;p&gt;Microsoft provided extensive &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for us, explaining everything about health checks in depth. &lt;/p&gt;

&lt;p&gt;To keep the article short and simple, I won't add any more detail in this subject. So we just simply try it out. &lt;/p&gt;

&lt;p&gt;First step is Installing required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"AspNetCore.HealthChecks.Network"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.4"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"AspNetCore.HealthChecks.Prometheus.Metrics"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"AspNetCore.HealthChecks.Publisher.Prometheus"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"AspNetCore.HealthChecks.System"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.5"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.Diagnostics.NETCore.Client"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"0.2.328102"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure program.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDiskStorageHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPingHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPrivateMemoryHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDnsResolveHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContextCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;DogsDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthChecksLogWriters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponseAsync&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"health/ready"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthChecksLogWriters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponseAsync&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"health/live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthChecksLogWriters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponseAsync&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHealthChecksPrometheusExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health/prometheus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResultStatusCodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we collect some crucial health check metrics and expose them in 3 different ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For kubernetes probes&lt;/li&gt;
&lt;li&gt;For prometheus collector&lt;/li&gt;
&lt;li&gt;For people&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Kubernetes Probes
&lt;/h4&gt;

&lt;p&gt;You can skip it if you’re not using Kubernetes.&lt;/p&gt;

&lt;p&gt;Kubernetes pings our application. Depending on the result, it can decide to be up and running, restart ,or maybe wait a bit longer until the app loads all required dependencies. Only then it will be ready to handle incoming traffic. &lt;/p&gt;

&lt;p&gt;Detailed documentation you can find &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’re exposing 2 endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;/health/live - for **&lt;code&gt;startupProbe and livenessProbe&lt;/code&gt; if the app signalized that is not live (some of healthchecks fauld) Kubernetes has to restart the service.&lt;/li&gt;
&lt;li&gt;/health/ready - &lt;strong&gt;&lt;code&gt;readinessProbe &lt;/code&gt;&lt;/strong&gt;When the app is ready Kubernetes will route the traffic to this instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Prometheus healthcheck
&lt;/h4&gt;

&lt;p&gt;Additional endpoint for exporting health checks in prometheus format. And similar to app metrics we can use them in grafana:&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%2Fhclm9vjxfqbizjbpe7hu.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%2Fhclm9vjxfqbizjbpe7hu.png" width="800" height="777"&gt;&lt;/a&gt;&lt;/p&gt;
Prometheus exporter endpoint



&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%2F80kv5o66g8u3pc5mxj3v.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%2F80kv5o66g8u3pc5mxj3v.png" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;
Memory consumption



&lt;h4&gt;
  
  
  Human readable format
&lt;/h4&gt;

&lt;p&gt;And the last endpoint just to simplify testing and operations. Just open it in your browser:&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%2Fx9lhvzb2osj1lkkvsh7k.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%2Fx9lhvzb2osj1lkkvsh7k.png" width="800" height="921"&gt;&lt;/a&gt;&lt;/p&gt;
Human-readable health checks



&lt;h3&gt;
  
  
  Logs
&lt;/h3&gt;

&lt;p&gt;Logging for development and production environments will be different.&lt;/p&gt;

&lt;p&gt;On production best practice is to write logs to stdout, then use log collector (e.g. &lt;a href="https://grafana.com/docs/loki/latest/clients/promtail" rel="noopener noreferrer"&gt;promtail&lt;/a&gt;) to deliver it to the storage. And for development purposes, it is much easier to have a sink and write logs directly to Grafana Cloud.&lt;/p&gt;

&lt;p&gt;I believe that in 2022, &lt;a href="https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/logs/getting-started/README.md" rel="noopener noreferrer"&gt;OpenTelemetry logs&lt;/a&gt; are not ready for production use. It doesn't give you any advantages, and tooling is quite poor compared to well-known tools.&lt;/p&gt;

&lt;p&gt;So I still recommend using &lt;a href="https://github.com/serilog/serilog" rel="noopener noreferrer"&gt;Serilog&lt;/a&gt; for logging. It has an OpenTelemetry sink, so again, you can switch to OTLP anytime when needed without modifying your source code.&lt;/p&gt;

&lt;p&gt;As usual we start with installing packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.AspNetCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Enrichers.Demystifier"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Enrichers.Span"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"2.3.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Exceptions"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"8.4.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Exceptions.EntityFrameworkCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"8.4.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Exceptions.Refit"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"8.4.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Formatting.Compact"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Serilog.Sinks.Grafana.Loki"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"8.0.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Program.cs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSerilog&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFrom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSpan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithExceptionDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DestructuringOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultDestructurers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDestructurers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IExceptionDestructurer&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DbUpdateExceptionDestructurer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ApiExceptionDestructurer&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="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDemystifiedStackTraces&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoggingFields&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpLoggingFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;appsettings.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Serilog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Using"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Serilog.Sinks.Grafana.Loki"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MinimumLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Debug"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"WriteTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Console"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"formatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GrafanaLoki"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://logs-prod3.grafana.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"credentials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"labels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo-services-dogs"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"propertiesAsLabels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"app"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to update your grafana credentials in your &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&amp;amp;tabs=windows" rel="noopener noreferrer"&gt;secrets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our demo project we’re going to log http requests/responses. Mirosoft provides 2 middleware to log http messages (body, headers etc).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;http-logging&lt;/a&gt; &amp;lt;- we’re using this one&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/w3c-logger/?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;w3c-logger&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Serilog is the most popular logging framework and has tons of extensions, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WithSpan - adding information from OpenTelemetry traces.&lt;/li&gt;
&lt;li&gt;WithExceptionDetails - log the exceptions in convenient and human-readable format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay, after running our service go to Grafana Cloud -&amp;gt; Explore -&amp;gt; Loki Logs Datasource&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%2F5dztr0p56wcrryjpu35y.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%2F5dztr0p56wcrryjpu35y.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;
Loki Logs Datasource



&lt;p&gt;Here are our logs. Thanks to deep integration between Loki and Tempo, Grafana allows us to quickly jump from logs to according to traces.&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%2Fzrbt56ahzmmoafncsiv3.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%2Fzrbt56ahzmmoafncsiv3.png" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;
Loki-tempo integration



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

&lt;p&gt;&lt;a href="https://github.com/WitcherD/demo-services-dogs" rel="noopener noreferrer"&gt;Source code is on Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the article we explored Grafana Cloud and tried 3 observability tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus for metrics&lt;/li&gt;
&lt;li&gt;Tempo for traces&lt;/li&gt;
&lt;li&gt;Loki for logs &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every data store we use Grafana UI only, which is super convenient for analytics and troubleshooting.&lt;/p&gt;

&lt;p&gt;We also got our hands dirty with OpenTelemetry, which aims to standardize observability tools and protocols to make distributed applications maintenance much easier.&lt;/p&gt;

&lt;p&gt;In the next article we’ll cover more topics need to be done for production ready apps, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Errors handling;&lt;/li&gt;
&lt;li&gt;Retries, Jitter, Circuit Breaker patterns.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>saas</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
