Manage Dependencies with Poetry
Manage Dependencies with Poetry — Applied AI engineering tutorial. Hands-on steps, troubleshooting, and what to study next.
Focus: manage dependencies with poetry
You've nailed the model, crafted the perfect prompt, and your AI app works flawlessly on your laptop. Then you push it to the cloud, the dependency resolver screams 'conflict', or a teammate's environment explodes because they installed numpy 1.x while you built against 2.x. Dependency management with pip freeze > requirements.txt is a ticking time bomb for any serious AI project. In this lesson, you'll master Poetry, the modern Python dependency and packaging tool that brings lock files, virtual environments, and pyproject.toml together — giving you reproducible builds and a sane workflow for your LLM Apps, RAG pipelines, and evaluation harnesses.
The problem this lesson solves
Every AI project has dependencies — openai, pydantic, langchain, llama-index, numpy, pandas, and a dozen more. The moment you share code with a teammate, a CI runner, or your production server, you discover that pip install -r requirements.txt is fragile:
- Order matters — the order in a
requirements.txtfile can change resolution outcomes. - No dependency tree —
pipdoesn't tell you whynumpyis installed or which package pulled it in. - Version drift — two developers install slightly different versions, and the "works on my machine" curse strikes your AI system.
- No automatic virtual environment — manual venv activation is easy to forget, polluting your global site-packages.
With the rapid churn of AI libraries (new releases weekly), a single unconstrained pip install can break your prompt parsing, model inference, or vector search. You need a tool that locks the entire dependency graph into a single deterministic file, isolates environments, and keeps your project structure clean.
Core concept / mental model
Think of Poetry as a dependency manager with a receipt. The pyproject.toml file is your shopping list — it declares top-level dependencies (what you directly import: openai, requests). The poetry.lock file is the receipt — it captures the exact version of every package, including transitive dependencies (urllib3, httpx, certifi), so that every machine recreates the same environment byte-for-byte.
Poetry combines four essential responsibilities:
- PyProject.toml — the modern standard for Python project metadata and dependencies (PEP 621).
- Poetry.lock — the lock file that pins the exact versions of all packages and their hashes.
- Virtual environment — Poetry creates a dedicated
.venvper project automatically, isolating your AI code from the global interpreter. - CLI fu — add, remove, update, export, run — everything happens through one consistent command set.
The workflow analogy
Imagine you're preparing a dataset pipeline. pyproject.toml lists the raw ingredients (csv, pandas); poetry.lock is the precisely weighed mixture that reproduces identical results every batch; the virtual environment is your clean, contamination-free workbench. When you update a package, Poetry recalculates the entire graph — like a chef re-balancing the recipe when one supplier changes the cocoa fat content.
poetry add numpy— adds topyproject.tomland resolves the full graph.poetry install— reads the lock file, creates/updates the venv, and installs exact versions.poetry update numpy— re-resolves and updates the lock file fornumpyand its dependents.
Pro tip: Never edit
poetry.lockmanually — it's Poetry's contract with determinism. Let the CLI manage it.
How it works step by step
Poetry follows a clear, methodical workflow. Here's the logical progression from zero to a reproducible AI project:
1. Install Poetry
Use the official installer (always the recommended path — not pip install poetry which can pollute your base environment):
curl -sSL https://install.python-poetry.org | python3 -
On macOS/Linux this adds Poetry to your home directory. Windows users can install via the PowerShell installer or pipx. Verify with poetry --version. Take note: the installer respects your PYTHON_BIN if you need to target a specific Python version.
2. Create a new project
poetry new llm-evaluator
cd llm-evaluator
Poetry scaffolds a full project structure:
llm-evaluator/
├── llm_evaluator/
│ └── __init__.py
├── tests/
│ └── __init__.py
├── pyproject.toml
├── poetry.lock
└── README.md
If you're adopting Poetry on an existing repo, simply create a pyproject.toml by running poetry init and answering the prompts.
3. Declare dependencies with add
poetry add openai pydantic
poetry add --group dev pytest
The first command installs openai and pydantic into the project; the second installs pytest as a dev-only dependency. Poetry analyzes the entire dependency tree, resolves version conflicts, builds the lock file, and installs exactly matching versions into the environment.
4. Install from an existing lock
When a teammate pulls your repo, all they need is:
poetry install
Poetry checks the lock file — if it exists, it downloads and installs the exact pinned versions. No surprise upgrades ever. For production deployments where you want zero dev dependencies:
poetry install --without dev
5. Run scripts inside the environment
poetry run python your_ai_script.py
poetry shell # activates the venv (optional)
Hands-on walkthrough
Let's build a small AI evaluation script end-to-end with Poetry.
Project setup
# create and navigate
poetry new sentiment-checker
cd sentiment-checker
# add runtime deps for an AI API call
poetry add openai pydantic
# add a dev test framework
poetry add --group dev pytest
Write the AI code
Create sentiment_checker/main.py with a function that calls an LLM and returns a structured sentiment label:
from openai import OpenAI
from pydantic import BaseModel, Field
class SentimentResult(BaseModel):
label: str = Field(description="either positive, negative, or neutral")
client = OpenAI()
def analyze_sentiment(text: str) -> SentimentResult:
"""Return a structured sentiment analysis."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Classify sentiment: {text}"}],
response_format={"type": "json_object"},
)
return SentimentResult.model_validate_json(response.choices[0].message.content)
if __name__ == "__main__":
print(analyze_sentiment("Poetry makes dependency management painless!"))
Run it inside the poetry environment
export OPENAI_API_KEY="your-api-key"
poetry run python -m sentiment_checker.main
Expected output (approximate — the label text varies with the model):
label='positive'
Verify reproducibility
Now simulate the teammate experience. Commit pyproject.toml and poetry.lock, then on a clean machine run:
poetry install
poetry run pytest
Key output: no version conflicts, environment ready, tests pass. Add a test file tests/test_sentiment.py that imports the function — it will be resolved exactly as in your dev environment.
Pro tip: Run
poetry show --treeto visualize the full dependency graph. You'll see whyslowapiortyping_extensionsgot installed — invaluable for debugging weird transitive conflicts.
Lock file deep dive
Open poetry.lock — notice it contains [[package]] entries with name, version, description, and content-hash for every package, including transitive ones like httpx and certifi. This isn't just a list — it's a Merkle-style hash of the resolved graph, allowing Poetry to detect if pyproject.toml was modified after the lock was created (it warns: "The lock file is not up to date").
Compare options / when to choose what
| Tool | Lock file | Virtual env | Monorepo support | Best for |
|---|---|---|---|---|
| Poetry | poetry.lock |
Built-in .venv |
Moderate | AI apps, CLI tools, libraries with rich dependency graphs |
| pip + venv + requirements.txt | No (manual freeze) | Manual | Good | Quick scripts or legacy projects |
| pipenv | Pipfile.lock |
Built-in | Weak | Simpler projects still on Pipfile |
| uv | uv.lock |
Built-in | Excellent | High-performance CI, monorepos, new Python projects |
| conda | No (used with pip) | Built-in | Good | Data science with non-Python deps (CUDA, MKL) |
When to choose Poetry:
- You need a readable
pyproject.tomlwith grouped dependencies. - You want deterministic builds across CI and production.
- You build Python libraries that need correct metadata for publishing.
When to consider alternatives:
- uv — dramatically faster resolver and installer; worth learning next if your project grows into a monorepo.
- conda — when you must manage C/C++/CUDA libraries alongside Python (e.g., GPU-specific ML stacks).
Variations and bonus features
- Scripts and plugins — Poetry supports project-level commands in
[tool.poetry.scripts]. - Multi-group management — use
[tool.poetry.group.dev.dependencies]to separate dev tools from runtime deps. - Export requirements — some CI systems still need
requirements.txt:poetry export -f requirements.txt --output requirements.txt. - Package publishing —
poetry publishtargets PyPI with the same metadata inpyproject.toml.
Troubleshooting & edge cases
Common errors and how to fix them
1. Command not found: poetry
Ensure your shell looks in Poetry's directory: ~/.local/bin (Linux/macOS). Add it to $PATH in your .bashrc/.zshrc.
2. Same library but different versions cause a lock conflict
When you change pyproject.toml manually, Poetry warns. Always use poetry add/remove so the lock stays in sync. If you manually edited, run poetry lock then poetry install to repair.
3. Package not found after adding
If you get ModuleNotFoundError, you probably ran plain python instead of poetry run python. The venv isn't activated in your current shell. Use poetry shell or prefix every command with poetry run.
4. Slow resolver on big AI projects
Poetry's resolver is slower than uv. For huge graph changes, try poetry lock in isolation (not during add) and expect 30–60s. Alternatively, switch to uv in CI.
5. Migrating from requirements.txt
Import existing packages first:
cat requirements.txt | xargs poetry add
Then delete requirements.txt and commit the lock. But beware: requirements.txt often contains unpinned or 📁 proven constraints — run poetry update afterwards to surface conflicts.
6. Python version mismatches
Poetry uses the current system Python by default. Pin a specific interpreter in pyproject.toml under [tool.poetry.dependencies] with python = "^3.10", and manage versions with pyenv if needed.
Edge cases specific to AI
- OpenAI/Anthropic SDK updates — frequent releases; lock can feel stale. Update deliberately:
poetry add openai@latestthen run your eval suite before deploying. - GPU dependencies —
torchortensorfloware often too large for Poetry's resolver; you may want to usepipin a separate step or a conda environment. Poetry can still manage the rest controllingdependency-groupsto skip optional GPU libs.
What you learned & what's next
You now know how to manage dependencies with Poetry — from installing the tool, creating a project, adding packages, and using lock files to ensure deterministic builds. You applied a hands-on AI evaluation script with OpenAI and Pydantic, recognized the difference between Poetry and alternatives, and can troubleshoot common dependency pitfalls.
Core takeaways:
pyproject.tomldeclares what you use;poetry.lockpins exactly what you get.- Virtual environments are automatic — no more manual
venv+ activation. - The lock file is your reproducibility contract for CI and teammates.
- Dev versus runtime dependencies separate cleanly with
--group dev. - Use
poetry runto stay inside the project's environment.
Your next step in the Applied AI engineering path is to explore environment variables and secrets management — because now that your lock file is deterministic, your environment config is the next critical piece to test, deploy, and scale your AI applications safely.
Practice recap
Mini exercise: Take an existing AI script or quick experiment and migrate it to Poetry. Run poetry init, add the runtime dependencies you import, then commit pyproject.toml and poetry.lock. Finally, simulate a teammate by cloning your repo into a fresh directory and running poetry install — confirm it works with zero manual steps. This builds the muscle memory for reproducible AI projects.
Common mistakes
- Forgetting to use
poetry runand running plainpython, causing ModuleNotFoundError because the venv isn't active. - Manually editing
poetry.lockorpyproject.tomlinstead of usingpoetry add/remove, leading to out-of-sync lock warnings and broken installs. - Treating
poetry.lockas optional and not committing it to git — you lose reproducibility for teammates and CI. - Using
pip install poetryinstead of the official installer, which pollutes the global Python environment.
Variations
- uv — a faster Rust-based tool that uses the same
pyproject.tomlbut with its own lock file; great for CI and monorepos. - Conda — needed when you manage non-Python dependencies like CUDA, MKL, or system libraries for AI/ML stacks.
- Pipenv — a simpler alternative for small projects that still supports Pipfile and a lock file, but with less active development.
Real-world use cases
- Pin exact versions of openai, pydantic, and langchain for a production LLM API service to ensure every deployment behaves identically.
- Manage dev-only tools like pytest, ruff, and mypy in a dedicated group so production installs skip them entirely.
- Keep an evaluation harness's dependency graph isolated per project, preventing transitive version conflicts between different AI experiments.
Key takeaways
- Poetry unifies pyproject.toml, poetry.lock, and automatic virtual environments for deterministic builds.
- The lock file is a contract — commit it, and every machine installs identical versions.
- Declare dependencies with
poetry add; use--group devfor test/lint tools only. - Run scripts via
poetry runto stay inside the project environment. - Troubleshoot conflicts by using
poetry updateandpoetry show --treeto inspect the graph. - Assess alternatives like uv or conda when speed or non-Python deps are a priority.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.