A small, simple, and structured AI agent.
Project description
StructAgent
A small, simple, and structured AI agent with reasoning capabilities and tool integration. Built with modern Python patterns and designed for extensibility.
Features
- ReAct Agent: Think-Act-Observe loop with structured reasoning steps
- Tool Integration: Modular tool system with pluggable capabilities
- Multiple LLM Providers: Support for OpenRouter and other providers via Instructor
- Mathematical Operations: Arithmetic and expression evaluation tools
- Web Search: SearXNG integration for meta-search capabilities
- Vector Indexing: LEANN integration for semantic search and RAG
- Type Safety: Full Pydantic validation and structured outputs
Installation
Prerequisites
- Python 3.12+
- Poetry (for dependency management)
- OpenRouter API key
Setup
-
Install dependencies
poetry install -
Configure environment
cp .env.example .env # Edit .env with your API keys
-
Set required environment variables
OPENROUTER_API_KEY="your-api-key" OPENROUTER_MODEL_ID="qwen/qwen3-next-80b-a3b-thinking" OPENROUTER_BASE_URL="https://openrouter.ai/api/v1" ...
Configuration
Required Environment Variables
# OpenRouter (primary LLM provider)
OPENROUTER_API_KEY=your-openrouter-api-key
OPENROUTER_MODEL_ID=qwen/qwen3-next-80b-a3b-thinking
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
# Optional: LM Studio (local models)
USE_LMSTUDIO=false
LMSTUDIO_MODEL_ID=qwen/qwen3-next-80b-a3b-thinking
LMSTUDIO_BASE_URL=http://localhost:1234/v1
LMSTUDIO_API_KEY=your-lmstudio-key
# Tools
SEARXNG_BASE_URL=http://localhost:8080
LEANN_INDEX_PATH=/path/to/your/leann_index
LEANN_CHAT_MODEL=qwen/qwen3-next-80b-a3b-thinking
LEANN_CHAT_BASE_URL=https://openrouter.ai/api/v1
LEANN_CHAT_API_KEY=your-openrouter-api-key
Supported Models
The agent supports various models through OpenRouter:
qwen/qwen3-next-80b-a3b-thinking(recommended for reasoning)anthropic/claude-3.5-sonnetopenai/gpt-4o- And many more
It also supports whatever models you host locally with LM Studio.
- 'ibm/ibm/granite-4-h-tiny'
Quick Start
Run With a Query
from struct_agent import build_client, run_react_loop
# Initialize client
# Client parameters are taken from .env
client = build_client()
# Run the agent with a query
query = "What is the weather in the capital of France, and what is that city known for?"
response = run_react_loop(query, client)
print(f"Answer: {response}")
Use a Custom Configuration
Possible parameters:
config = {
"max_steps": int, # The maximum ReAct loop steps allowed
"tools": list[ToolSpec], # The tools available to the agent (By default, Searxng, Leann, and Maths tools are available)
"verbose": bool, # Print out ReAct steps if True
}
Usage example:
from struct_agent import build_client, run_react_loop, make_searxng_search_tool
client = build_client()
query = "Research recent advances in quantum computing"
# Custom configuration
config = {
"max_steps": 15,
"tools": [make_searxng_search_tool()],
"verbose": True
}
response = run_react_loop(query, client, config)
Available Tools
MathsToolkit
- Arithmetic operations
- Expression evaluation
- Mathematical calculations
MetaSearchToolkit
- Web search via SearXNG
- Meta-search capabilities
- Information retrieval
VectorIndexToolkit
- Semantic search via LEANN
- Vector indexing
- RAG capabilities
Adding a Custom Tool
First, you'll need to define the response model for your tool (to force the llm to reply with a correct structure).
from pydantic import BaseModel, Field
class CustomTool(BaseModel):
"""Inputs for the custom tool."""
message: str = Field(default="", description="Some input message") # The input can be anything (int, list, dict, .etc).
# list_of_things: list = Field(default=[], description="A list of things") # It can also have as many fields as you need
# ...
Second, you'll need to define the factory function, that will return the ToolSpec with the argument info for the llm and the handler (actual functionality).
from struct_agent import ToolSpec
def make_custom_tool() -> ToolSpec:
"""Create a custom tool"""
def handler(args: Dict[str, Any]) -> Dict[str, Any]:
"""Handle the custom tool call."""
parsed_args = AddTextArgs(**args)
message = parsed_args.message
# Some functionality...
return {"result": "function result"}
return ToolSpec(
name="custom_tool",
description="A custom tool that does something.",
args_model=CustomTool,
handler=handler,
parameters={
"message": "Some input message"
}
)
Then, you'll need to simply add it to the config, which you pass to the agent.
Note that when you override the config['tools'], the default tools are removed, so you'll have to add them manually.
from struct_agent import build_client, run_react_loop, create_toolspecs_from_toolkits, MathsToolkit, MetaSearchToolkit, VectorIndexToolkit
client = build_client()
query = "What is 2 + 2?"
# Add default tools
toolkits = [MathsToolkit, MetaSearchToolkit, VectorIndexToolkit]
all_tools = create_toolspecs_from_toolkits(toolkits) + [make_custom_tool()] # Add custom tool
# Add tools to the agent
config = {
"tools": all_tools
}
# Run
response = run_react_loop(query, client, config)
Project Structure
src/struct_agent/
├── __init__.py # Main package exports
├── instructor_based/ # LLM client and agent logic
│ ├── __init__.py
│ ├── new_agent.py # Main ReAct agent implementation
│ ├── client_manager.py # LLM client management
│ ├── prompt_manager.py # System prompts
│ ├── tool_manager.py # Tool registry and execution
│ ├── reasoning_modules.py # Reasoning data models
│ └── utils.py # Utility functions
├── tools/ # Modular tool system
│ ├── __init__.py
│ ├── maths_tools.py # Mathematical operations
│ ├── searxng_tools.py # Web search integration
│ ├── leann_tools.py # Vector indexing
│ ├── blank_tool.py # No-op tool
│ └── toolkits.py # Pre-configured tool bundles
└── README.md # Package documentation
Core Components
ReAct Agent (new_agent.py)
Implements the Reasoning + Acting loop:
- Think: Generate reasoning steps
- Act: Select and execute tools
- Observe: Process tool results
- Repeat: Until final answer is reached
Client Manager (client_manager.py)
Handles LLM provider setup and model resolution through Instructor for structured outputs.
Tool System
- Modular toolkits with standardized interfaces
- Pydantic models for type-safe tool arguments
- Easy extensibility for custom tools
Dependencies
instructor>=1.3: Structured outputs for LLMopenai>=1.30: OpenAI API clientpydantic>=2.7: Data validation and serializationleann>=0.3.4,<0.4.0: Vector indexing and search
Troubleshooting
Common Issues
-
ModuleNotFoundError: Ensure PYTHONPATH includes the src directory
export PYTHONPATH=~/projects/struct_agent/src
-
Missing API Keys: Verify .env file exists and contains required keys
cp .env.example .env # Edit .env with your actual API keys
-
SearXNG Connection: Ensure SearXNG is running on configured port
SEARXNG_BASE_URL=http://localhost:8080 # Update SEARXNG_BASE_URL if using different instance
-
LEANN Index: Set correct absolute path to your LEANN index
#.env LEANN_INDEX_PATH="/absolute/path/to/your/leann_index"
License
This project is open source and available under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file struct_agent-1.0.1.tar.gz.
File metadata
- Download URL: struct_agent-1.0.1.tar.gz
- Upload date:
- Size: 18.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.0 CPython/3.11.13 Darwin/25.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7601426cb11664a921cc659e87b7c8f0d3f3b28f0c72cfd2b1a401cb66aa729
|
|
| MD5 |
e4a0dc23ca9d51ccc5482aca364ed37c
|
|
| BLAKE2b-256 |
32cecfebabacf22bfc74e9c5eaf2aef4f7bbcb29edc4a58d3202bd87b0cc2ba2
|
File details
Details for the file struct_agent-1.0.1-py3-none-any.whl.
File metadata
- Download URL: struct_agent-1.0.1-py3-none-any.whl
- Upload date:
- Size: 24.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.0 CPython/3.11.13 Darwin/25.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4461886f3b4f33ec894778129a597e665108e87aa660409cc562e7c7996793d4
|
|
| MD5 |
ea4d6e40faa03a964f713fa94b397c28
|
|
| BLAKE2b-256 |
98692aa719f5452b1edc114f7cdbd92c4572ba63dfb7f7beac5266a431e9f48a
|