Python Agent Dev Environment Setup

8 min read Module 1 of 10 Topic 3 of 30

What you'll learn

  • Set up a reproducible Python environment using uv and pyproject.toml
  • Manage LLM API keys securely with python-dotenv and secret managers
  • Install and verify core agent libraries (LangGraph, Pydantic AI, OpenAI, Anthropic)
  • Run a first multi-step agent from scratch to verify the setup works end-to-end
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

Why Environment Setup Matters for Agent Work

Agent development has higher setup stakes than typical Python work because:

  • Multiple expensive API keys need to stay out of source control
  • Async code is the norm, sync-only setups break at scale
  • Reproducibility is critical, model behavior can shift with library version changes
  • Local observability from day one saves hours of debugging later

This lesson gets you from zero to a working, professional setup in under 20 minutes.


Package Manager: Why uv

The Python ecosystem has settled on uv as the standard for new projects. Install it once:

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# Verify
uv --version  # uv 0.5.x

Create and activate a new project:

uv init agent-project
cd agent-project

# uv creates .venv automatically on first use: no separate python -m venv step needed
uv venv --python 3.12
source .venv/bin/activate  # macOS/Linux
# .venv\Scripts\activate    # Windows

Project Structure

Follow this layout for every agent project. It scales from a weekend hack to a production service:

agent-project/
├── pyproject.toml          # Dependencies and project metadata
├── uv.lock                 # Locked dependency tree (commit this)
├── .env                    # API keys, NEVER commit
├── .env.example            # Template with key names, no values (commit this)
├── .gitignore              # Include .env and .venv
├── src/
│   └── agents/
│       ├── __init__.py
│       ├── tools.py        # Tool definitions
│       ├── graph.py        # LangGraph agent graph
│       └── config.py       # Settings via pydantic-settings
├── tests/
│   ├── test_tools.py
│   └── test_graph.py
└── scripts/
    └── run_agent.py        # Entry point for local runs

Installing Core Dependencies

The pyproject.toml is the single source of truth for your project’s dependencies. Pin minimum versions to avoid breaking changes from upstream libraries.

# pyproject.toml
[project]
name = "agent-project"
version = "0.1.0"
requires-python = ">=3.12"  # 3.12+ required for modern typing features used by Pydantic AI and LangGraph
dependencies = [
    # LLM providers
    "openai>=1.50.0",            # 1.50+ includes the Assistants v2 API and structured output mode
    "anthropic>=0.40.0",         # 0.40+ includes streaming tool use and the Messages API
    "google-generativeai>=0.8.0",
    
    # Agent frameworks
    "langgraph>=0.2.0",          # 0.2+ is the stable graph API, earlier versions had breaking changes
    "langchain-core>=0.3.0",     # core abstractions shared by LangGraph and LangChain tools
    "pydantic-ai>=0.0.14",
    
    # Memory & retrieval
    "langchain-openai>=0.2.0",   # OpenAI embeddings for vector search
    "qdrant-client>=1.11.0",     # vector database client for semantic memory
    
    # Configuration & validation
    "pydantic>=2.9.0",           # Pydantic v2 is required, v1 is incompatible with these frameworks
    "pydantic-settings>=2.6.0",  # reads .env files and validates types at startup
    "python-dotenv>=1.0.0",
    
    # Observability
    "langsmith>=0.1.130",        # LangSmith tracing, auto-instruments LangGraph runs with zero config
    
    # HTTP & async
    "httpx>=0.27.0",             # async HTTP client, prefer over requests for agent tool implementations
    "aiohttp>=3.10.0",
    "tenacity>=9.0.0",           # retries, agents hitting flaky APIs need exponential backoff
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0.0",
    "pytest-asyncio>=0.24.0",  # required to run async def test_ functions
    "pytest-mock>=3.14.0",
    "ruff>=0.7.0",             # replaces black + flake8 + isort in a single fast tool
]

Install everything:

uv sync                  # installs all dependencies from pyproject.toml and uv.lock
uv sync --extra dev      # also installs the [dev] optional group (pytest, ruff, etc.)

API Key Management

Step 1: Create your .env file (never commit this):

# .env: local secrets, excluded from git
OPENAI_API_KEY=sk-proj-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AIza...
LANGSMITH_API_KEY=lsv2_...
LANGSMITH_TRACING=true               # set to false to disable tracing and avoid quota usage
LANGSMITH_PROJECT=my-agent-project   # groups runs in the LangSmith UI

Step 2: Create .env.example (commit this as documentation):

# .env.example: copy to .env and fill in your keys
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GOOGLE_API_KEY=
LANGSMITH_API_KEY=
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=

Step 3: Add to .gitignore:

echo ".env" >> .gitignore     # prevents accidental key commits
echo ".venv/" >> .gitignore   # keeps the virtual environment out of the repo

Step 4: Load in Python via pydantic-settings (preferred over raw dotenv):

# src/agents/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    # SettingsConfigDict tells pydantic-settings where to find the .env file
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
    
    # Required fields: startup raises ValidationError if these are missing from .env
    openai_api_key: str
    anthropic_api_key: str
    langsmith_api_key: str = ""       # optional: empty string disables tracing
    langsmith_tracing: bool = False
    langsmith_project: str = "default"
    
    # Agent defaults: centralizing these here makes it easy to tune without touching agent code
    default_model: str = "gpt-5.6-luna"
    max_iterations: int = 20   # hard cap on ReAct loop iterations, prevents runaway agents
    temperature: float = 0.0   # 0.0 = deterministic outputs, important for tool calling reliability

# Instantiated once at import time: all modules import this singleton
settings = Settings()

pydantic-settings validates types at startup, you’ll get a clear error if a required key is missing, instead of a cryptic None crash three layers deep.


Verify Your Setup: First Agent Run

Test that everything works end to end with this minimal script:

# scripts/run_agent.py
import asyncio
from openai import AsyncOpenAI      # async client: use this inside async def functions
from anthropic import AsyncAnthropic
from src.agents.config import settings

async def test_openai():
    client = AsyncOpenAI(api_key=settings.openai_api_key)
    response = await client.chat.completions.create(
        model="gpt-5.6-luna",
        messages=[{"role": "user", "content": "Reply with just: OpenAI OK"}],
        max_tokens=20,  # small limit keeps this verification call cheap
    )
    print(response.choices[0].message.content)

async def test_anthropic():
    client = AsyncAnthropic(api_key=settings.anthropic_api_key)
    response = await client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=20,
        messages=[{"role": "user", "content": "Reply with just: Anthropic OK"}],
    )
    # Anthropic returns a list of content blocks; [0].text extracts the text from the first block
    print(response.content[0].text)

async def main():
    print("Testing OpenAI...")
    await test_openai()
    print("Testing Anthropic...")
    await test_anthropic()
    print("\nSetup complete!")

if __name__ == "__main__":
    asyncio.run(main())  # asyncio.run() is the standard entry point for async scripts

Run it:

uv run python scripts/run_agent.py
# OpenAI OK
# Anthropic OK
# Setup complete!

Setting Up LangSmith Tracing

LangSmith is Anthropic-agnostic, it traces any LangChain or LangGraph execution. Enabling it is two lines:

# At the top of any entry point script: must be set before importing LangGraph
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = settings.langsmith_api_key

# Now any LangGraph or LangChain run automatically sends traces to
# https://smith.langchain.com/projects/my-agent-project
# No other code changes needed: LangGraph instruments itself via the env vars

With tracing on, every agent run appears in the LangSmith UI with a complete breakdown of each LLM call: input, output, latency, tokens, and cost. This becomes indispensable the moment your agent breaks in a non-obvious way.


Quick Dev Workflow Tips

Use ruff for fast linting:

uv run ruff check src/   # lint, catches unused imports, undefined names, style issues
uv run ruff format src/  # format (replaces black)

Run async tests with pytest-asyncio:

# tests/test_tools.py
import pytest

# @pytest.mark.asyncio tells pytest-asyncio to run this coroutine in an event loop
@pytest.mark.asyncio
async def test_my_tool():
    result = await my_tool("test input")
    assert result is not None
uv run pytest tests/ -v

Hot-reload during development: For FastAPI agent servers, use uvicorn --reload. For LangGraph scripts, just re-run the script, state is managed explicitly, so reruns are clean.

You now have a professional Python agent workspace. The next lessons build the actual agent logic on top of this foundation.

Knowledge Check

3 questions to test your understanding

1 Why should you never hard-code API keys directly in your Python source files?

2 What is the advantage of using `uv` over `pip` and `venv` for managing Python agent projects?

3 When should you use `AsyncOpenAI` instead of `OpenAI` in your agent code?

Discussion

Questions and notes from learners on this topic

Loading discussion…

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan