Date Created: October 26, 2025 Last Updated: October 26, 2025
The agent-patterns library provides reusable AI agent workflow patterns implemented using LangGraph and LangChain. All patterns are synchronous (no async/await) and follow a consistent architecture based on abstract base classes.
Decision: No async code anywhere in the codebase Rationale: Simplifies implementation, debugging, and integration. Avoids event loop complexity. Impact: All LLM calls, tool executions, and state transitions are synchronous.
Decision: Use LangGraph's StateGraph for all pattern implementations
Rationale: Provides clear DAG structure, explicit state transitions, and debuggability.
Implementation: Each pattern builds a StateGraph in build_graph() method.
Decision: Store all prompts as markdown files in prompts/ directory
Rationale: Enables easy modification without code changes, version control of prompts.
Structure: prompts/{PatternClassName}/{StepName}/system.md and user.md
Decision: Support multiple LLM roles (thinking, reflection, documentation, etc.)
Rationale: Different tasks benefit from different models (e.g., expensive for planning, cheap for execution).
Implementation: _get_llm(role) method in BaseAgent.
Decision: Two base classes - BaseAgent and MultiAgentBase
Rationale: Enforces consistency, provides reusable infrastructure, clear contracts.
Key Methods: build_graph(), run(), _get_llm(), _load_prompt()
For each new pattern:
-
Design Phase
- Identify the pattern's workflow stages
- Define state dictionary structure
- Map nodes and edges in StateGraph
- Identify LLM roles needed
-
Implementation Phase
- Subclass BaseAgent (or MultiAgentBase)
- Implement
build_graph()with all nodes and edges - Implement
run()method - Implement all node handler methods (e.g.,
_generate_plan()) - Add type hints to all methods
- Add comprehensive docstrings
-
Prompt Phase
- Create directory:
prompts/{PatternName}/ - For each LLM step, create system.md and user.md
- Include placeholder documentation in prompts
- Create directory:
-
Testing Phase
- Create test file:
tests/test_{pattern_name}.py - Test with mocked LLMs
- Test state transitions
- Test edge cases (empty state, max iterations, etc.)
- Create test file:
-
Documentation Phase
- Create example:
examples/{pattern_name}_example.py - Document in README.md
- Add usage notes
- Create example:
All patterns should use consistent naming:
input_taskorinput_data: Initial user query/requestfinal_answerorfinal_result: Final outputintermediate_steps: List of (action, result) tuplesiteration_countortrial_count: Loop countermax_iterationsormax_trials: Loop limit
Document pattern-specific keys in the pattern's docstring:
class MyAgent(BaseAgent):
"""
My Agent Pattern.
State Keys:
- input_task (str): User's query
- custom_state (dict): Pattern-specific data
- final_answer (str): Final output
"""Target providers (configured via .env):
- OpenAI (GPT-3.5, GPT-4)
- Anthropic (Claude)
- Other LangChain-supported providers
Standard roles across patterns:
- thinking: Primary reasoning model (e.g., GPT-4)
- reflection: Self-critique model (can be same or different)
- documentation: Output generation (can be cheaper model)
- planning: Planning and decomposition (typically expensive model)
- execution: Task execution (can be cheaper model)
Each prompt directory contains:
system.md: System prompt defining role and behavioruser.md: User prompt template with {placeholders}
Example:
prompts/
└── ReActAgent/
├── ThoughtStep/
│ ├── system.md
│ └── user.md
└── ActionStep/
├── system.md
└── user.md
- Mock LLM Calls: Use
unittest.mock.patchto mock_get_llm() - Test State Transitions: Verify state dict changes through graph
- Test Edge Cases: Empty inputs, max iterations, error conditions
- Test Tool Registry: Mock tool calls for patterns that use tools
def test_pattern_basic():
agent = MyAgent(llm_configs={}, prompt_dir="prompts")
with patch.object(agent, "_get_llm", return_value=mock_llm):
result = agent.run("test input")
assert result is not None- No Streaming: Current implementation doesn't support streaming responses
- No Async: Deliberately excluded for simplicity
- Limited Tool Registry: Basic tool integration, extensible by users
- No Persistence: State not persisted between runs (by design)
- Tool Registry Module: Comprehensive tool management system
- State Persistence: Optional state saving/loading
- Observability Hooks: Structured logging and tracing
- Parallel Execution: Where patterns support it (without async)
- Prompt Versioning: Version control for prompt templates
- Pattern Composition: Combining patterns hierarchically
- Python 3.10+
- Virtual environment (venv)
- pip for dependency management
- pytest for testing
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -e .
pytest tests/Required in .env:
OPENAI_API_KEY=your-key-here
THINKING_MODEL_PROVIDER=openai
THINKING_MODEL_NAME=gpt-4-turbo
REFLECTION_MODEL_PROVIDER=openai
REFLECTION_MODEL_NAME=gpt-4-turbo
DOCUMENTATION_MODEL_PROVIDER=openai
DOCUMENTATION_MODEL_NAME=gpt-3.5-turbo
- Iterative thought → action → observation loop
- Requires tool registry for action execution
- Stop condition: explicit "FINAL ANSWER" marker or max steps
- Two-phase: planning then execution
- Plan is a list of structured steps
- Execution can be sequential or parallel (future enhancement)
- Generate → Critique → Refine cycle
- Single refinement pass (can be extended to multiple)
- Uses separate LLM roles for generation and critique
- Multiple trial attempts with memory
- Each trial: plan → execute → evaluate → reflect → store
- Memory accumulates across trials within a session
- Plan execution as DAG
- Topological execution order
- Supports parallel tool execution (conceptually)
- Separates planning (Worker) from execution (Solver)
- Uses placeholders in plan template
- Cost-efficient: expensive model only for planning and integration
- Tree search over reasoning paths
- Monte Carlo-inspired selection and backpropagation
- Computationally intensive, best for complex problems
- Dynamic module selection from library
- Adaptation phase customizes modules to task
- Useful when task type varies significantly
- Multi-perspective research and synthesis
- Outline → Perspectives → Questions → Retrieval → Synthesis
- Best for long-form content generation
Problem: Node functions must return the state dict
Solution: Always return state at end of node functions
Problem: Accidentally importing async LangChain components Solution: Review imports, use synchronous variants
Problem: Prompts embedded in code
Solution: Always use _load_prompt(step_name)
Problem: Unclear parameter and return types Solution: Add type hints to all public and private methods
Problem: Patterns with loops don't have stop conditions Solution: Always include max_iterations counter and check
Before merging pattern implementations:
- No async/await keywords anywhere
- All methods have type hints
- All public methods have docstrings
- Prompts externalized to prompts/ directory
- No hard-coded API keys or model names
- State keys documented in class docstring
- Max iterations or stop condition implemented
- Unit tests exist and pass
- Example script exists and runs
- No breaking changes to base classes
- Minimize number of LLM calls in critical paths
- Use cheaper models for simple tasks
- Cache prompt templates (loaded once)
- Keep state dictionaries lean
- Avoid storing large objects in state
- Clean up intermediate results when possible
- Mock LLM calls in tests (never call real APIs in tests)
- Keep test execution time under 30 seconds total
- Use semantic versioning: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes to base classes or pattern interfaces
- MINOR: New patterns or significant features
- PATCH: Bug fixes, documentation updates
- All tests pass
- Documentation updated
- CHANGELOG.md updated
- Version bumped in pyproject.toml
- Git tag created
Decision: No, synchronous only Date: 2025-10-26 Rationale: Simplicity, easier debugging, design document requirement
Decision: Defer to pattern implementations, provide example in ReAct Date: 2025-10-26 Rationale: Different patterns need different tool capabilities
Decision: Basic validation (file exists), not content validation Date: 2025-10-26 Rationale: Allow maximum flexibility for prompt experimentation
For questions or suggestions:
- Review Design.md for architectural guidance
- Check task_list.md for current development status
- Follow coding conventions outlined in this document