https://www.gravatar.com/avatar/485df9434f4908b5f6fab0750c113972?s=240&d=mp

Han

Agentic AI with Pydantic-AI Part 1.

Introduction

AI has already changed how we interact with technology. The real shift is happening now with agents: AI systems that can reason, make decisions, and take action.

Unlike a chatbot that passively replies, an agent can break down complex tasks, call APIs or databases, use tools, and deliver structured results. This is what makes the idea of Agentic AI so powerful — it’s not just about conversation, it’s about problem-solving with initiative.

Clean Validation with Pydantic v2

📝 Update (2025-08): This post was originally published in April 2024 and has been updated to reflect changes in Pydantic v2, including the new field validator, model validator, and Annotated-based validation patterns. Also, this post now includes a new section on using Pydantic with MongoDB.

Python’s dynamic typing system is indeed convenient, allowing you to create variables without explicitly declaring their types. While this flexibility can streamline development, it can also introduce unexpected behavior, particularly when handling data from external sources like APIs or user input.

A Lesson from a Naive Binary Search

I’ve been grinding hard every day, solving coding problems to get better at algorithms. Recently, I came across something interesting—a naive implementation of binary search can actually cause a bug. It’s a small detail, but it matters.

def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

It works fine in Python—but I recently learned that the way I calculate mid can cause problems in some cases.

Managing Password using Pass

A Minimalist’s Guide to pass— the Unix Password Manager

Safely wrangle your secrets from the command-line using GPG encryption and a few intuitive commands.


1. Why pass?

  • Single-purpose & transparent – every secret is just a GPG-encrypted file in ~/.password-store/.
  • Leverages tools you already trust – GnuPG for encryption and standard Unix commands for everything else (grep, git, find, etc.).
  • Portable & scriptable – works the same on any POSIX shell and is easy to automate.

Prerequisites

Introduction to logging in Python

A gentle, practical introduction to logging in Python


Why bother with a dedicated logging library?

  • Prints don’t scale. print() is fine during quick experiments, but real programs need a record that can be filtered, rotated, or shipped elsewhere.
  • Separation of concerns. You decide what to log in your code; logging decides where and how to write it (console, file, etc.).
  • Built-in, no extra dependency. The standard library’s logging module is powerful enough for most applications.

Core concepts

ConceptRole in the ecosystemTypical examples
LoggerThe entry point your code calls (logger.info(...)). You can have many, one per module."__main__", "my_package.worker"
HandlerDecides where the record goes.StreamHandler (stdout), FileHandler, TimedRotatingFileHandler, SMTPHandler
FormatterDecides how the record looks.'%(asctime)s - %(levelname)s - %(name)s - %(message)s'

A minimal logger

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s | %(message)s"
)

logging.info("Hello, world!")
  • basicConfig is a one-liner good for small scripts.
  • In bigger projects, mixing multiple modules / log files, you’ll want finer control.

Rotating files at midnight

Rotating a log file means creating a new log file after a certain time or size limit is reached. In this case, a new file is created every night at midnight. Only the most recent two log files are kept—yesterday’s and today’s—while older ones are deleted automatically.

Rediscovering Python's Pathlib

From Type Hint to Power Tool: Python’s Pathlib

For a long time, I used Path from Python’s pathlib module purely as a type hint - a way to make function signatures look more modern and semantically clear. Like this:

from pathlib import Path

def process_file(file_path: Path):
    ...

It changed when I started building an application that handled user-uploaded documents. I had to create temporary folders, write intermediate files, manage output paths, and ensure directories existed before saving results. That’s when Path went from just a type hint to a core part of my file management logic.