Observed since 20 Oct, 23:17 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agents-builder is a small Python framework for building retrieval-aware agents on top of LangGraph, LangChain, and MCP tools.
The package gives you a few focused primitives:
Agent that owns graph construction, LLM factories, and MCP tool loading.Prompt, LLMNode, ToolBasedNode, and RouterNode building blocks.The design is intentionally narrow: compose agents from small graph nodes, keep configuration explicit, and avoid framework-heavy abstractions that hide behavior.
Core package layout:
src/agents_builder/
├── __init__.py # Base Agent
├── constants.py # Shared typing aliases and config path
├── exceptions.py # Domain-specific exceptions
├── mixins.py # Config loading helpers
├── settings.py # Pydantic settings models
├── utils.py # Dynamic loading, MCP caching, message helpers
├── eval/
│ ├── __init__.py # AgentEvalRunner, TraceAnnotator, assertions, scoring
│ ├── annotator.py # Phoenix trace annotation integration
│ ├── assertion.py # Reusable trajectory assertions
│ └── trajectory.py # EvalScenario and AgentTrajectory models
├── eval/
│ ├── __init__.py # AgentEvalRunner, TraceAnnotator, assertions, scoring
│ ├── annotator.py # Phoenix trace annotation integration
│ ├── assertion.py # Reusable trajectory assertions
│ └── trajectory.py # EvalScenario and AgentTrajectory models
├── llm/
│ ├── __init__.py
│ ├── factory.py # LLMFactory and role-based model lookup
│ └── llm.py # OllamaLLM / OpenRouterLLM implementations
└── langgraph/
├── __init__.py # Prompt, Node, LLMNode, DeletionStrategy
├── nodes.py # Ready-made retrieval/answering node types
└── states.py # Typed graph state
Tests are split by intent:
tests/unit: isolated behavior for factories, nodes, mixins, utils, and errors.tests/integration: config loading and caching behavior.tests/e2e: a full retrieval-answer graph wired together end to end.This project uses uv and Python 3.12.
uv sync --group dev --all-extras
Common local commands:
make lint
make type-check
make test
make test-cov
make ci
Subclass agents_builder.Agent and implement build_graph().
The base class already handles:
push()Most runtime classes follow the same pattern:
FromConfigMixin[...]This keeps runtime behavior strongly tied to validated configuration instead of ad hoc dictionaries.
LLMFactory maps roles like query, grader, or answer to specific model configs.
That makes it easy to:
The package exposes low-level but reusable graph parts:
Prompt: render a system prompt from stateSchemaBasedPrompt: prompt plus structured output schemaLLMNode: invoke an LLM against rendered messagesToolBasedNode: base for MCP-backed tool nodesRouterNode: return route keys for conditional edgesDeletionStrategy: trim message history in a controlled wayUse agents_builder.eval to run repeatable evaluation scenarios against a compiled agent graph.
The core flow is:
EvalScenario with the user query and metadataAgentEvalRunnerAgentTrajectorySuccessAssertion and EfficiencyAssertion checksTraceAnnotatorfrom agents_builder.eval import (
AgentTrajectory,
EfficiencyAssertion,
AgentEvalRunner,
EvalScenario,
SuccessAssertion,
TraceAnnotator,
TrajectoryScorer,
)
class RequiresAnswer(SuccessAssertion):
def check(self, trajectory: AgentTrajectory) -> bool:
passed = bool(trajectory.steps) and bool(trajectory.steps[-1].action.content)
self.logger.annotate(
annotation_name="answered",
annotator_kind="CODE",
label="pass" if passed else "fail",
score=1.0 if passed else 0.0,
explanation=trajectory.serialize(),
)
if not passed:
raise AssertionError("agent did not produce a final answer")
return passed
class LimitsStepCount(EfficiencyAssertion):
def __init__(self, logger: TraceAnnotator, max_steps: int) -> None:
super().__init__(logger)
self.max_steps = max_steps
def check(self, trajectory: AgentTrajectory) -> bool:
passed = len(trajectory.steps) <= self.max_steps
self.logger.annotate(
annotation_name="step_count",
annotator_kind="CODE",
label="pass" if passed else "fail",
score=1.0 if passed else 0.0,
metadata={"max_steps": self.max_steps, "actual_steps": len(trajectory.steps)},
)
return passed
scenario = EvalScenario(
name="contract lookup",
query="Find the renewal terms",
category="retrieval",
tier="smoke",
metadata={"dataset": "contracts"},
)
logger = ...
runner = AgentEvalRunner(graph=agent.graph, logger=logger)
scorer = TrajectoryScorer().success(RequiresAnswer(logger)).expect(LimitsStepCount(logger, max_steps=3))
trajectory = await runner.run(scenario, scorer)
For LLM-as-judge checks, use LLMJudgeAssertion from agents_builder.eval.assertion. Its prompt must contain exactly {query} and {trajectory} placeholders, and each criterion is emitted as an LLM trace annotation.
Phoenix-backed annotation is available through PhoenixTraceAnnotator with PhoenixTraceAnnotatorSettings:
from agents_builder.eval.annotator import PhoenixTraceAnnotator
from agents_builder.settings import PhoenixTraceAnnotatorSettings
logger = PhoenixTraceAnnotator(
PhoenixTraceAnnotatorSettings(
module_path="agents_builder.eval.annotator.PhoenixTraceAnnotator",
base_url="http://localhost:6006",
project_name="agents-builder",
)
)
Configuration is built around Pydantic settings models in src/agents_builder/settings.py.
Important settings types:
AgentSettingsLLMFactorySettingsOllamaLLMSettingsOpenRouterLLMSettingsMCPServerSettingsTraceAnnotatorSettingsPhoenixTraceAnnotatorSettingsTraceAnnotatorSettingsPhoenixTraceAnnotatorSettingsThe package-level default YAML path is:
/config/config.yaml
You can also construct objects directly from Python with .from_config(...) or load from YAML with .from_yaml(...).
Quality checks already configured in the project:
ruff for linting and formattingty for type checkingpytest for unit, integration, and e2e testsbandit for security checksRecommended local loop:
make lint
make type-check
make test
Before merging broader changes:
make ci
This repository works best when changes stay aligned with a few constraints:
If you are contributing code or using AI coding agents in this repository, read AGENTS.md next.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 4.0.0 | 28 May 2026 | 9 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 23:17 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:7946d9e6626668e418f696c69ab6b04446f047f2c0344a00c376a93b7d13ba3f