Compare commits

...
5 Commits
Author SHA1 Message Date
Mervin Praison 1a71aac6b8 Release v3.4.0 2026-01-08 13:29:57 +00:00
Mervin Praison 287f42dfee Remove unused test and verification files 2026-01-08 12:50:16 +00:00
Mervin Praison 57b637948f Release v3.3.1 2026-01-08 12:28:06 +00:00
Mervin Praison 757d402cae Release v3.3.0 2026-01-08 10:33:27 +00:00
Mervin Praison 412783c5ed Release v3.2.1 2026-01-07 21:57:34 +00:00
165 changed files with 14865 additions and 2653 deletions

No files matched your search

+2 -2
View File
@@ -1930,11 +1930,11 @@ agent = Agent(
auto_save="my-project"
)
# Load history from last 5 sessions
# Load history from past sessions via context management
agent = Agent(
name="Assistant",
memory=True,
history_in_context=5
context=True, # Enable context management for history
)
```
-71
View File
@@ -1,71 +0,0 @@
"""
Auto-generated API server for PraisonAI agents.
"""
from flask import Flask, request, jsonify
from flask_cors import CORS
from praisonai import PraisonAI
import os
app = Flask(__name__)
# CORS configuration
CORS(app)
# Authentication
AUTH_ENABLED = False
AUTH_TOKEN = None
def check_auth():
"""Check authentication if enabled."""
if not AUTH_ENABLED:
return True
token = request.headers.get('Authorization', '').replace('Bearer ', '')
return token == AUTH_TOKEN
@app.route('/health', methods=['GET'])
def health():
"""Health check endpoint."""
return jsonify({"status": "ok", "service": "praisonai-api"})
@app.route('/chat', methods=['POST'])
def chat():
"""Chat endpoint for agent interaction."""
if not check_auth():
return jsonify({"error": "Unauthorized"}), 401
data = request.get_json()
if not data or 'message' not in data:
return jsonify({"error": "Message required"}), 400
try:
praisonai = PraisonAI(agent_file="agents.yaml")
result = praisonai.run()
return jsonify({
"response": result,
"status": "success"
})
except Exception as e:
return jsonify({
"error": str(e),
"status": "error"
}), 500
@app.route('/agents', methods=['GET'])
def list_agents():
"""List available agents."""
if not check_auth():
return jsonify({"error": "Unauthorized"}), 401
return jsonify({
"agents": ["default"],
"agent_file": "agents.yaml"
})
if __name__ == '__main__':
app.run(
host='0.0.0.0',
port=8080,
debug=False
)
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.2.0" \
"praisonai>=3.4.0" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.2.0" \
"praisonai>=3.4.0" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.2.0" \
"praisonai>=3.4.0" \
"praisonai[ui]" \
"praisonai[crewai]"
-203
View File
@@ -1,203 +0,0 @@
# Profiling CLI
PraisonAI provides unified profiling capabilities for performance analysis and debugging, with **full visibility from ENTER to first response**.
## Quick Start
```bash
# Basic profiling
praisonai "What is 2+2?" --profile
# Deep profiling with call graph, decision trace, and module breakdown
praisonai "What is 2+2?" --profile --profile-deep
# JSON output format
praisonai "What is 2+2?" --profile --profile-format json
```
## Time to First Response (TTFR)
**TTFR** is the key user-perceived latency metric. It measures the time from when you press ENTER until the first visible output appears.
### What contributes to TTFR?
1. **CLI Entry** - Time to reach the CLI entry point
2. **CLI Parse** - Argparse/Typer routing time
3. **Routing** - Command routing to handler
4. **Imports** - Loading required modules
5. **Agent Init** - Initializing the agent
6. **Network Request** - Sending request to LLM API
7. **First Token** - Receiving first token (streaming)
8. **First Output** - Rendering first visible output
### How to reduce TTFR
1. **Warm runs** - Imports are cached after first run
2. **Streaming mode** - See output as tokens arrive
3. **Lighter models** - Faster response times
4. **Local models** - Eliminate network latency
## CLI Flags
| Flag | Description | Default |
|------|-------------|---------|
| `--profile` | Enable profiling with timing breakdown | Disabled |
| `--profile-deep` | Enable deep profiling with call graph (higher overhead) | Disabled |
| `--profile-format` | Output format: `text` or `json` | `text` |
## Profile Command
For more detailed profiling options, use the `profile` subcommand:
```bash
# Profile a query
praisonai profile query "What is 2+2?"
# With deep call tracing
praisonai profile query "Hello" --deep
# Limit functions shown
praisonai profile query "Test" --limit 20
# Save artifacts to directory
praisonai profile query "Test" --save ./profile_results
# JSON output
praisonai profile query "Test" --format json
```
### Profile Query Options
| Option | Description | Default |
|--------|-------------|---------|
| `--model, -m` | Model to use | Default model |
| `--stream/--no-stream` | Use streaming mode | No stream |
| `--deep` | Enable deep call tracing | Disabled |
| `--limit, -n` | Top N functions to show | 30 |
| `--sort, -s` | Sort by: `cumulative` or `tottime` | `cumulative` |
| `--show-callers` | Show caller functions | Disabled |
| `--show-callees` | Show callee functions | Disabled |
| `--save` | Save artifacts to path | None |
| `--format, -f` | Output format: `text` or `json` | `text` |
## Other Profile Commands
```bash
# Profile module import times
praisonai profile imports
# Profile CLI startup time
praisonai profile startup
# Run comprehensive profiling suite
praisonai profile suite
# Create performance snapshots for comparison
praisonai profile snapshot --baseline
praisonai profile snapshot current --compare
# Configure performance optimizations
praisonai profile optimize --show
```
## Output Format
### Text Report
```
======================================================================
PraisonAI Profile Report
======================================================================
Run ID: abc12345
Timestamp: 2024-01-02T12:00:00.000000Z
Method: cli_direct
Version: 3.0.1
## Execution Timeline
---------------------------------------------
Imports : 500.00 ms
Agent Init : 0.50 ms
Execution : 2000.00 ms
───────────────────────────────────────────
⏱ Time to First Response : 2500.50 ms
TOTAL : 2500.50 ms
## Timing Breakdown
----------------------------------------
Imports: 500.00 ms
Agent Init: 0.50 ms
Execution: 2000.00 ms
─────────────────────────────────────
TOTAL: 2500.50 ms
## Decision Trace (deep profile only)
----------------------------------------
Agent Config: default
Model: gpt-4
Streaming: disabled
Profile Layer: 2
## Top Functions by Cumulative Time
----------------------------------------------------------------------
Function Calls Cumul (ms)
----------------------------------------------------------------------
request 1 1800.00
send 1 1750.00
...
```
### JSON Report
```json
{
"schema_version": "1.0",
"run_id": "abc12345",
"timestamp": "2024-01-02T12:00:00.000000Z",
"invocation": {
"method": "cli_direct",
"flags": {"layer": 1, "stream": false, "model": null},
"praisonai_version": "3.0.1",
"python_version": "3.12.0"
},
"timing": {
"total_ms": 2500.5,
"imports_ms": 500.0,
"agent_init_ms": 0.5,
"execution_ms": 2000.0
},
"functions": [...],
"response_preview": "four"
}
```
## Profiling Layers
| Layer | Overhead | Features |
|-------|----------|----------|
| 0 | < 1ms | Wall-clock phases only |
| 1 | ~5% | + cProfile function stats |
| 2 | ~15% | + Call graph (callers/callees) |
- `--profile` uses Layer 1
- `--profile --profile-deep` uses Layer 2
## Performance Tips
1. **Use Layer 0 for production monitoring** - minimal overhead
2. **Use Layer 1 for development profiling** - good balance
3. **Use Layer 2 only for deep debugging** - higher overhead
## Observational Equivalence
The following commands produce equivalent profile reports:
```bash
# CLI direct
praisonai "prompt" --profile
# Profile command
praisonai profile query "prompt"
```
Both use the same unified profiling architecture and produce identical schema output.
-257
View File
@@ -1,257 +0,0 @@
# Profiling Module
PraisonAI provides a unified profiling module for programmatic performance analysis.
## Quick Start
```python
from praisonai.cli.execution import (
ExecutionRequest,
Profiler,
ProfilerConfig,
)
# Create execution request
request = ExecutionRequest(prompt="What is 2+2?")
# Configure profiler
config = ProfilerConfig(layer=1)
# Run with profiling
profiler = Profiler(config)
result, report = profiler.profile_sync(request)
# Access results
print(f"Output: {result.output}")
print(f"Total time: {report.timing.total_ms}ms")
```
## ExecutionRequest
Immutable request descriptor for execution.
```python
from praisonai.cli.execution import ExecutionRequest
# Basic request
request = ExecutionRequest(prompt="Hello")
# With model
request = ExecutionRequest(
prompt="Hello",
model="gpt-4",
agent_name="MyAgent",
)
# With streaming
request = ExecutionRequest(
prompt="Hello",
stream=True,
)
```
### Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `prompt` | str | Required | The prompt to execute |
| `agent_name` | str | "Assistant" | Name of the agent |
| `agent_instructions` | str | None | Custom instructions |
| `model` | str | None | Model to use |
| `stream` | bool | False | Enable streaming |
| `tools` | tuple | () | Tools to provide |
## ProfilerConfig
Configuration for profiling behavior.
```python
from praisonai.cli.execution import ProfilerConfig
# Layer 0: Wall-clock only (< 1ms overhead)
config = ProfilerConfig(layer=0)
# Layer 1: + cProfile stats (~5% overhead)
config = ProfilerConfig(layer=1)
# Layer 2: + Call graph (~15% overhead)
config = ProfilerConfig(
layer=2,
show_callers=True,
show_callees=True,
)
# From CLI flags
config = ProfilerConfig.from_flags(
profile=True,
deep=True,
network=False,
)
```
### Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `layer` | int | 1 | Profiling layer (0, 1, or 2) |
| `limit` | int | 30 | Max functions to show |
| `sort_by` | str | "cumulative" | Sort by: cumulative, time, calls |
| `show_callers` | bool | False | Show caller functions |
| `show_callees` | bool | False | Show callee functions |
| `track_network` | bool | False | Track network requests |
| `output_format` | str | "text" | Output format: text, json |
| `save_path` | str | None | Path to save artifacts |
## Profiler
The main profiler class that wraps execution.
```python
from praisonai.cli.execution import Profiler, ProfilerConfig, ExecutionRequest
config = ProfilerConfig(layer=1)
profiler = Profiler(config)
request = ExecutionRequest(prompt="Hello")
result, report = profiler.profile_sync(request, invocation_method="api")
```
### Methods
#### `profile_sync(request, invocation_method="profile_command")`
Profile synchronous execution.
**Parameters:**
- `request`: ExecutionRequest - The execution request
- `invocation_method`: str - How this was invoked (for report)
**Returns:** Tuple of (ExecutionResult, ProfileReport)
## ProfileReport
The canonical profile report with timing and statistics.
```python
# Access timing breakdown
print(f"Total: {report.timing.total_ms}ms")
print(f"Imports: {report.timing.imports_ms}ms")
print(f"Agent Init: {report.timing.agent_init_ms}ms")
print(f"Execution: {report.timing.execution_ms}ms")
# Access function stats (layer 1+)
if report.functions:
for func in report.functions[:5]:
print(f"{func.name}: {func.cumulative_time_ms}ms")
# Access call graph (layer 2)
if report.call_graph:
print(f"Edges: {len(report.call_graph.edges)}")
# Export formats
json_str = report.to_json()
text_str = report.to_text()
dict_data = report.to_dict()
```
### Schema Version
The report uses schema version `1.0`. Schema is frozen after release - new fields are always optional with defaults.
### Mandatory Fields
- `schema_version`: str
- `run_id`: str
- `timestamp`: str (ISO 8601)
- `invocation`: InvocationInfo
- `timing`: TimingBreakdown
- `response_preview`: str
### Optional Fields
- `functions`: List[FunctionStat] (layer 1+)
- `call_graph`: CallGraph (layer 2)
- `network`: List[RequestTiming] (if track_network)
## ExecutionResult
Result of execution with output and metadata.
```python
# Access output
print(result.output)
print(result.success)
print(result.run_id)
# Check for errors
if result.error:
print(f"Error: {result.error}")
# Duration
print(f"Duration: {result.duration_ms}ms")
```
## Data Bounds
Profile data is strictly bounded to prevent memory issues:
| Data Type | Maximum |
|-----------|---------|
| Function stats | 1000 |
| Call graph edges | 5000 |
| Network requests | 100 |
## Example: Full Profiling Workflow
```python
from praisonai.cli.execution import (
ExecutionRequest,
Profiler,
ProfilerConfig,
)
# Configure deep profiling
config = ProfilerConfig(
layer=2,
limit=50,
show_callers=True,
show_callees=True,
)
# Create request
request = ExecutionRequest(
prompt="Explain quantum computing in simple terms",
model="gpt-4",
)
# Profile execution
profiler = Profiler(config)
result, report = profiler.profile_sync(request)
# Print text report
print(report.to_text())
# Save JSON report
with open("profile.json", "w") as f:
f.write(report.to_json())
# Analyze timing
print(f"\nTiming Analysis:")
print(f" Import overhead: {report.timing.imports_ms / report.timing.total_ms * 100:.1f}%")
print(f" Execution time: {report.timing.execution_ms / report.timing.total_ms * 100:.1f}%")
# Top 5 hotspots
if report.functions:
print(f"\nTop 5 Hotspots:")
for func in report.functions[:5]:
print(f" {func.name}: {func.cumulative_time_ms:.2f}ms ({func.calls} calls)")
```
## Invariants
The profiling module guarantees:
1. **Output Equivalence**: Profiled execution produces identical output to non-profiled execution
2. **Schema Stability**: Schema version 1.0 is frozen; new fields are always optional
3. **Bounded Data**: All profile data respects maximum bounds
4. **Zero Overhead When Disabled**: No profiling code runs unless explicitly enabled
-53
View File
@@ -1,53 +0,0 @@
# Templates CLI
```bash
praisonai templates list
```
```bash
praisonai templates search video
```
```bash
praisonai templates info ai-video-editor
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --output edited.mp4
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --output edited.mp4 --offline
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --output edited.mp4 --strict-tools
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --tools ./my_tools.py
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --tools-dir ./tools
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --tools-source praisonai_tools.video
```
```bash
praisonai templates run ai-video-editor --input video.mp4 --no-template-tools-py
```
```bash
praisonai templates init my-project --template ai-video-editor
```
```bash
praisonai templates cache clear
```
```bash
praisonai templates cache list
```
-104
View File
@@ -1,104 +0,0 @@
# Templates Module
## Loading Templates
```python
from praisonai.templates.loader import TemplateLoader
loader = TemplateLoader()
# Load from local path
template = loader.load("./my-template")
# Load from URI
template = loader.load("ai-video-editor")
# Load offline
template = loader.load("ai-video-editor", offline=True)
# Check requirements
missing = loader.check_requirements(template)
```
## Template Discovery
```python
from praisonai.templates.discovery import TemplateDiscovery
discovery = TemplateDiscovery(
include_package=True,
include_defaults=True,
)
# Find template
result = discovery.find_template("ai-video-editor")
# List all templates
templates = discovery.list_templates()
```
## Template Config
```python
from praisonai.templates.loader import TemplateConfig
# Access template properties
template.name
template.description
template.version
template.author
template.requires
template.workflow_file
template.agents_file
template.path
```
## Tool Override Integration
```python
from praisonai.templates.tool_override import (
create_tool_registry_with_overrides,
resolve_tools,
)
# Build registry with template sources
tools_sources = template.requires.get("tools_sources", [])
registry = create_tool_registry_with_overrides(
tools_sources=tools_sources,
template_dir=str(template.path),
include_defaults=True,
)
# Resolve tool names
tools = resolve_tools(["shell_tool"], registry=registry)
```
## TEMPLATE.yaml Schema
```yaml
name: my-template
version: "1.0.0"
description: Template description
requires:
tools: [shell_tool]
packages: [praisonai-tools]
env: [OPENAI_API_KEY]
tools_sources:
- praisonai_tools.video
- ./local_tools.py
workflow: workflow.yaml
agents: agents.yaml
config:
input:
type: string
required: true
output:
type: string
defaults:
preset: podcast
```
-65
View File
@@ -1,65 +0,0 @@
# Tools CLI
```bash
praisonai tools list
```
```bash
praisonai tools info shell_tool
```
```bash
praisonai tools info internet_search
```
```bash
praisonai tools search "web"
```
```bash
praisonai tools search "file"
```
```bash
praisonai tools doctor
```
```bash
praisonai tools doctor --json
```
```bash
praisonai tools resolve shell_tool
```
```bash
praisonai tools resolve internet_search --template ai-video-editor
```
```bash
praisonai tools resolve my_tool --tools ./my_tools.py
```
```bash
praisonai tools resolve my_tool --tools-dir ./tools
```
```bash
praisonai tools discover
```
```bash
praisonai tools discover --include praisonai_tools.video
```
```bash
praisonai tools discover --entrypoints
```
```bash
praisonai tools show-sources
```
```bash
praisonai tools show-sources --template ai-video-editor
```
-90
View File
@@ -1,90 +0,0 @@
# Tools Module
## Tool Registry
```python
from praisonai.templates.tool_override import (
create_tool_registry_with_overrides,
resolve_tools,
)
# Create registry with defaults
registry = create_tool_registry_with_overrides(include_defaults=True)
# Create registry with overrides
registry = create_tool_registry_with_overrides(
override_files=["my_tools.py"],
override_dirs=["./tools"],
include_defaults=True,
)
# Create registry with tools_sources
registry = create_tool_registry_with_overrides(
tools_sources=["praisonai_tools.video"],
template_dir="./my-template",
include_defaults=True,
)
# Resolve tool names to callables
tools = resolve_tools(
["shell_tool", "internet_search"],
registry=registry,
)
```
## Tool Override Loader
```python
from praisonai.templates.tool_override import ToolOverrideLoader
loader = ToolOverrideLoader()
# Load from file
tools = loader.load_from_file("my_tools.py")
# Load from directory
tools = loader.load_from_directory("./tools")
# Load from module
tools = loader.load_from_module("praisonai_tools.video")
# Get default tool directories
dirs = loader.get_default_tool_dirs()
```
## Resolution Order
1. CLI `--tools` files (highest priority)
2. CLI `--tools-dir` directories
3. Template `tools_sources` (from TEMPLATE.yaml)
4. Template-local `tools.py`
5. Default dirs (`~/.praison/tools`, `~/.config/praison/tools`)
6. Package discovery (`praisonai_tools`)
7. Built-in tools (lowest priority)
## Creating Custom Tools
```python
from praisonai_tools import tool
@tool
def my_custom_tool(query: str) -> str:
"""My custom tool description.
Args:
query: The search query
"""
return f"Result for: {query}"
```
## Template tools_sources
```yaml
# TEMPLATE.yaml
requires:
tools: [shell_tool]
tools_sources:
- praisonai_tools.video
- ./local_tools.py
- ./tools_dir/
```
+158
View File
@@ -0,0 +1,158 @@
"""
Agent-Centric API Example
Demonstrates the new consolidated feature parameters for PraisonAI Agents.
These params provide a cleaner, more intuitive API with progressive disclosure.
New Consolidated Params:
- memory: Union[bool, MemoryConfig, MemoryManager] - Memory and session management
- knowledge: Union[bool, List[str], KnowledgeConfig, KnowledgeBase] - RAG and retrieval
- planning: Union[bool, PlanningConfig] - Planning mode
- reflection: Union[bool, ReflectionConfig] - Self-reflection
- guardrails: Union[bool, Callable, GuardrailConfig] - Safety validation
- web: Union[bool, WebConfig] - Web search and fetch
- context: Union[bool, ManagerConfig, ContextManager] - Context management
"""
from praisonaiagents import (
Agent,
# Config classes for progressive disclosure
MemoryConfig,
KnowledgeConfig,
PlanningConfig,
ReflectionConfig,
GuardrailConfig,
WebConfig,
)
def example_simple_enable():
"""Example 1: Simple boolean enable (easiest)"""
print("\n=== Example 1: Simple Boolean Enable ===")
agent = Agent(
instructions="You are a helpful assistant",
memory=True, # Enable memory with defaults
reflection=True, # Enable self-reflection
web=True, # Enable web search + fetch
)
print(f"Agent created with memory={agent._memory_instance is not None}")
print(f" self_reflect={agent.self_reflect}")
print(f" web_search={agent.web_search}")
def example_with_config():
"""Example 2: Using config objects (more control)"""
print("\n=== Example 2: Using Config Objects ===")
agent = Agent(
instructions="You are a research assistant",
memory=MemoryConfig(
backend="file",
user_id="researcher_001",
auto_memory=True,
),
reflection=ReflectionConfig(
min_iterations=1,
max_iterations=5,
llm="gpt-4o-mini",
),
web=WebConfig(
search=True,
fetch=False, # Only search, no full page fetch
max_results=3,
),
)
print(f"Agent created with user_id={agent.user_id}")
print(f" {agent.max_reflect}")
print(f" web_search={agent.web_search}, web_fetch={agent.web_fetch}")
def example_guardrails():
"""Example 3: Guardrails with callable or config"""
print("\n=== Example 3: Guardrails ===")
# Option A: Direct callable
def my_validator(output):
"""Simple validator that checks response length."""
is_valid = len(output.raw) < 1000
return (is_valid, output if is_valid else "Response too long")
agent_a = Agent(
instructions="Be concise",
guardrails=my_validator, # Direct callable
)
print(f"Agent A: guardrail is callable = {callable(agent_a.guardrail)}")
# Option B: Using GuardrailConfig
agent_b = Agent(
instructions="Be helpful and safe",
guardrails=GuardrailConfig(
llm_validator="Ensure the response is helpful, accurate, and safe",
max_retries=3,
),
)
print(f"Agent B: guardrail = {agent_b.guardrail}")
print(f" max_retries = {agent_b.max_guardrail_retries}")
def example_planning():
"""Example 4: Planning mode"""
print("\n=== Example 4: Planning Mode ===")
agent = Agent(
instructions="You are a project planner",
planning=PlanningConfig(
reasoning=True,
read_only=True, # Plan mode - only read operations
),
)
print(f"Agent created with planning={agent.planning}")
print(f" plan_mode={agent.plan_mode}")
print(f" planning_reasoning={agent.planning_reasoning}")
def example_backward_compatible():
"""Example 5: Using new consolidated params"""
print("\n=== Example 5: Consolidated Params ===")
# New consolidated params
agent = Agent(
instructions="Test agent",
reflection=True,
web=WebConfig(search=True, fetch=False),
)
print(f"Consolidated params work: self_reflect={agent.self_reflect}")
print(f" web_search={agent.web_search}, web_fetch={agent.web_fetch}")
def example_real_chat():
"""Example 6: Real chat with new API"""
print("\n=== Example 6: Real Chat ===")
agent = Agent(
instructions="You are a helpful math tutor. Be concise.",
reflection=False,
web=False,
verbose=False,
)
response = agent.start("What is the square root of 144?")
print(f"Response: {response}")
if __name__ == "__main__":
print("=" * 60)
print("PraisonAI Agent-Centric API Examples")
print("=" * 60)
example_simple_enable()
example_with_config()
example_guardrails()
example_planning()
example_backward_compatible()
example_real_chat()
print("\n" + "=" * 60)
print("All examples completed!")
print("=" * 60)
+52 -49
View File
@@ -2,10 +2,10 @@
"""
Basic Background Tasks Example for PraisonAI Agents.
This example demonstrates how to use the background task system to:
1. Run tasks asynchronously
2. Track task progress
3. Cancel running tasks
This example demonstrates how to use the background task system with Agent:
1. Create an Agent with background task support
2. Run agent tasks asynchronously
3. Track task progress
4. Handle task completion
Usage:
@@ -13,74 +13,77 @@ Usage:
"""
import asyncio
import time
from praisonaiagents import Agent
from praisonaiagents.background import BackgroundRunner, BackgroundConfig, TaskStatus
async def main():
print("=" * 60)
print("Background Tasks Demo")
print("Agent-Centric Background Tasks Demo")
print("=" * 60)
# Create a background runner with config
config = BackgroundConfig(max_concurrent_tasks=3)
runner = BackgroundRunner(config=config)
runner = BackgroundRunner(config=BackgroundConfig(max_concurrent_tasks=3))
# Create an Agent with background task support
agent = Agent(
name="AsyncAssistant",
instructions="You are a helpful research assistant.",
background=runner
)
print("\n--- Agent with Background Support Created ---")
print(f"Agent: {agent.name}")
print(f"Background runner: {agent.background is not None}")
# Define some example tasks
def slow_task(duration: float, name: str) -> str:
"""A slow task that simulates work."""
print(f" 🔄 Task '{name}' starting (will take {duration}s)")
time.sleep(duration)
return f"Task '{name}' completed after {duration}s"
async def research_task(topic: str) -> str:
"""Simulate a research task."""
print(f" 🔄 Researching '{topic}'...")
await asyncio.sleep(1.5)
return f"Research on '{topic}' completed with 5 key findings"
async def async_task(duration: float, name: str) -> str:
"""An async task that simulates work."""
print(f" 🔄 Async task '{name}' starting (will take {duration}s)")
await asyncio.sleep(duration)
return f"Async task '{name}' completed after {duration}s"
async def analysis_task(data: str) -> str:
"""Simulate an analysis task."""
print(f" 🔄 Analyzing data...")
await asyncio.sleep(1.0)
return f"Analysis complete: {data}"
# Submit tasks
print("\n--- Submitting Tasks ---")
# Submit tasks via agent's background runner
print("\n--- Submitting Background Tasks ---")
task1 = await runner.submit(slow_task, args=(1.0, "quick-job"), name="task1")
print(f"✅ Submitted task 1: {task1.id[:8]}")
task1 = await agent.background.submit(
research_task,
args=("AI trends 2025",),
name="research_ai"
)
print(f"✅ Submitted research task: {task1.id[:8]}")
task2 = await runner.submit(slow_task, args=(2.0, "medium-job"), name="task2")
print(f"✅ Submitted task 2: {task2.id[:8]}")
task3 = await runner.submit(async_task, args=(1.5, "async-job"), name="task3")
print(f"✅ Submitted async task 3: {task3.id[:8]}")
task2 = await agent.background.submit(
analysis_task,
args=("market data",),
name="analyze_market"
)
print(f"✅ Submitted analysis task: {task2.id[:8]}")
# List all tasks
print("\n--- Task Status ---")
for task in runner.tasks:
for task in agent.background.tasks:
print(f" [{task.id[:8]}] {task.status.value}")
# Wait for task 1 to complete
print("\n--- Waiting for Task 1 ---")
await task1.wait(timeout=5.0)
print(f"✅ Task 1 result: {task1.result}")
# Check status of all tasks
print("\n--- Updated Task Status ---")
for task in [task1, task2, task3]:
status = "✅" if task.status == TaskStatus.COMPLETED else "🔄"
print(f" {status} [{task.id[:8]}] {task.status.value}")
# Wait for all remaining tasks
print("\n--- Waiting for All Tasks ---")
# Wait for tasks to complete
print("\n--- Waiting for Tasks ---")
await task1.wait(timeout=10.0)
await task2.wait(timeout=10.0)
await task3.wait(timeout=10.0)
# Final status
print("\n--- Final Results ---")
for task in [task1, task2, task3]:
if task.result:
print(f" ✅ {task.result}")
# Final results
print("\n--- Results ---")
for task in [task1, task2]:
status = "✅" if task.status == TaskStatus.COMPLETED else "❌"
print(f" {status} {task.result}")
# Cleanup
runner.clear_completed()
agent.background.clear_completed()
print("\n✅ Cleared completed tasks")
print("\n" + "=" * 60)
+35 -29
View File
@@ -2,10 +2,10 @@
"""
Basic Checkpoints Example for PraisonAI Agents.
This example demonstrates how to use the shadow git checkpointing system to:
1. Save checkpoints before making changes
2. List available checkpoints
3. View diffs between checkpoints
This example demonstrates how to use the shadow git checkpointing system with Agent:
1. Create an Agent with checkpoint support
2. Save checkpoints before making changes
3. List available checkpoints
4. Restore to a previous checkpoint
Usage:
@@ -16,6 +16,7 @@ import asyncio
import tempfile
from pathlib import Path
from praisonaiagents import Agent
from praisonaiagents.checkpoints import CheckpointService
@@ -23,7 +24,7 @@ async def main():
# Create a temporary workspace for demonstration
with tempfile.TemporaryDirectory() as workspace:
print("=" * 60)
print("Shadow Git Checkpointing Demo")
print("Agent-Centric Checkpointing Demo")
print("=" * 60)
print(f"\nWorkspace: {workspace}")
@@ -32,24 +33,35 @@ async def main():
test_file.write_text("# Initial content\nprint('Hello, World!')\n")
print(f"\n✅ Created initial file: {test_file.name}")
# Initialize the checkpoint service
service = CheckpointService(workspace_dir=workspace)
# Create checkpoint service
checkpoints = CheckpointService(workspace_dir=workspace)
# Create an Agent with checkpoint support
agent = Agent(
name="RefactorBot",
instructions="You are a code refactoring assistant.",
checkpoints=checkpoints
)
print("\n--- Agent with Checkpoint Support Created ---")
print(f"Agent: {agent.name}")
print(f"Checkpoints enabled: {agent.checkpoints is not None}")
try:
# Initialize the shadow git repository
print("\n--- Initializing Checkpoint Service ---")
await service.initialize()
await agent.checkpoints.initialize()
print("✅ Shadow git repository initialized")
# Save first checkpoint
print("\n--- Saving Checkpoint 1: Initial State ---")
result1 = await service.save("Initial state")
result1 = await agent.checkpoints.save("Initial state")
print(f"✅ Checkpoint saved: {result1.checkpoint.id[:8]}")
print(f" Message: {result1.checkpoint.message}")
# Make some changes
print("\n--- Making Changes ---")
test_file.write_text("# Modified content\nprint('Hello, PraisonAI!')\nprint('Checkpoints are awesome!')\n")
# Make some changes (simulating agent modifications)
print("\n--- Simulating Agent Changes ---")
test_file.write_text("# Refactored content\nprint('Hello, PraisonAI!')\nprint('Checkpoints are awesome!')\n")
print("✅ Modified example.py")
# Create a new file
@@ -58,38 +70,32 @@ async def main():
print("✅ Created utils.py")
# Save second checkpoint
print("\n--- Saving Checkpoint 2: After Changes ---")
result2 = await service.save("Added features")
print("\n--- Saving Checkpoint 2: After Refactoring ---")
result2 = await agent.checkpoints.save("Added features")
print(f"✅ Checkpoint saved: {result2.checkpoint.id[:8]}")
# List all checkpoints
print("\n--- Listing All Checkpoints ---")
checkpoints = await service.list_checkpoints()
for i, cp in enumerate(checkpoints, 1):
print(f" {i}. [{cp.id[:8]}] {cp.message} ({cp.timestamp})")
checkpoints_list = await agent.checkpoints.list_checkpoints()
for i, cp in enumerate(checkpoints_list, 1):
print(f" {i}. [{cp.id[:8]}] {cp.message}")
# Show diff
print("\n--- Current Diff (uncommitted changes) ---")
diff = await service.diff()
print("\n--- Current Diff ---")
diff = await agent.checkpoints.diff()
if diff.files:
for f in diff.files:
print(f" {f.status}: {f.path}")
else:
print(" No uncommitted changes")
# Make more changes to show diff
test_file.write_text("# Even more changes\nprint('This will be reverted')\n")
print("\n✅ Made more changes to example.py")
diff = await service.diff()
print("\n--- New Diff ---")
if diff.files:
for f in diff.files:
print(f" {f.status}: {f.path}")
# Make more changes
test_file.write_text("# Bad changes\nprint('This will be reverted')\n")
print("\n✅ Made problematic changes to example.py")
# Restore to checkpoint 2
print(f"\n--- Restoring to Checkpoint: {result2.checkpoint.id[:8]} ---")
await service.restore(result2.checkpoint.id)
await agent.checkpoints.restore(result2.checkpoint.id)
print("✅ Restored successfully!")
# Verify restoration
+74 -100
View File
@@ -2,140 +2,114 @@
"""
Basic Context Compaction Example for PraisonAI Agents.
This example demonstrates how to use context compaction to:
1. Manage conversation context length
2. Use different compaction strategies
3. Track compaction results
4. Preserve important messages
This example demonstrates how to use context compaction with Agent:
1. Create an Agent with context= parameter for compaction
2. Use different compaction strategies via ManagerConfig
3. Monitor compaction results
Usage:
python basic_compaction.py
"""
from praisonaiagents.compaction import (
ContextCompactor, CompactionConfig, CompactionStrategy, CompactionResult
)
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
def main():
print("=" * 60)
print("Context Compaction Demo")
print("Agent-Centric Context Compaction Demo")
print("=" * 60)
# ==========================================================================
# Sample Messages
# Available Strategies
# ==========================================================================
messages = [
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Hello! Can you help me with Python?"},
{"role": "assistant", "content": "Of course! I'd be happy to help you with Python. What would you like to know?"},
{"role": "user", "content": "How do I read a file in Python?"},
{"role": "assistant", "content": "To read a file in Python, you can use the built-in open() function. Here's an example:\n\nwith open('filename.txt', 'r') as f:\n content = f.read()\n\nThis opens the file, reads its content, and automatically closes it."},
{"role": "user", "content": "What about writing to a file?"},
{"role": "assistant", "content": "To write to a file, use the 'w' mode:\n\nwith open('filename.txt', 'w') as f:\n f.write('Hello, World!')\n\nUse 'a' mode to append instead of overwriting."},
{"role": "user", "content": "Can you explain list comprehensions?"},
{"role": "assistant", "content": "List comprehensions are a concise way to create lists. Instead of:\n\nresult = []\nfor x in range(10):\n result.append(x * 2)\n\nYou can write:\n\nresult = [x * 2 for x in range(10)]"},
print("\n--- Available Compaction Strategies ---")
strategies = [
("truncate", "Remove oldest messages first"),
("sliding_window", "Keep most recent messages within limit"),
("summarize", "Replace old messages with summary"),
("smart", "Intelligently select messages to keep"),
]
print(f"\n--- Original Messages ---")
print(f" Message count: {len(messages)}")
for name, desc in strategies:
print(f" {name}: {desc}")
# ==========================================================================
# Basic Compaction
# Agent with Context Compaction via context= param
# ==========================================================================
print("\n--- Basic Compaction (Truncate Strategy) ---")
print("\n--- Creating Agent with Context Compaction ---")
compactor = ContextCompactor(
max_tokens=50, # Low limit to trigger compaction
strategy=CompactionStrategy.TRUNCATE,
preserve_system=True,
preserve_recent=2
# Use context= parameter with ManagerConfig for compaction
agent = Agent(
name="LongChat",
instructions="You are a helpful assistant for extended conversations.",
context=ManagerConfig(
auto_compact=True,
compact_threshold=0.8,
strategy="sliding_window",
)
)
# Check stats before
stats = compactor.get_stats(messages)
print(f" Total tokens: {stats['total_tokens']}")
print(f" Max tokens: {stats['max_tokens']}")
print(f" Needs compaction: {stats['needs_compaction']}")
# Compact
compacted, result = compactor.compact(messages)
print(f"\n After compaction:")
print(f" Messages: {len(messages)} -> {len(compacted)}")
print(f" Tokens: {result.original_tokens} -> {result.compacted_tokens}")
print(f" Saved: {result.tokens_saved} tokens")
print(f" Compression: {result.compression_ratio:.1%}")
print(f"Agent: {agent.name}")
print(f"Context manager: {agent.context_manager is not None}")
if agent.context_manager:
print(f"Auto-compact: {agent.context_manager.config.auto_compact}")
print(f"Threshold: {agent.context_manager.config.compact_threshold}")
print(f"Strategy: {agent.context_manager.config.strategy}")
# ==========================================================================
# Sliding Window Strategy
# Different Strategy Examples via context= param
# ==========================================================================
print("\n--- Sliding Window Strategy ---")
print("\n--- Strategy Examples ---")
compactor = ContextCompactor(
max_tokens=50,
strategy=CompactionStrategy.SLIDING,
preserve_system=True,
preserve_recent=3
# Sliding window strategy
sliding_agent = Agent(
name="SlidingAgent",
instructions="You are helpful.",
context=ManagerConfig(
auto_compact=True,
strategy="sliding_window",
)
)
print(f" Sliding: strategy={sliding_agent.context_manager.config.strategy}")
compacted, result = compactor.compact(messages)
print(f" Messages kept: {result.messages_kept}")
print(f" Messages removed: {result.messages_removed}")
print(f" Strategy: {result.strategy_used.value}")
# ==========================================================================
# Summarize Strategy
# ==========================================================================
print("\n--- Summarize Strategy ---")
compactor = ContextCompactor(
max_tokens=50,
strategy=CompactionStrategy.SUMMARIZE,
preserve_system=True,
preserve_recent=2
# Summarize strategy
summarize_agent = Agent(
name="SummarizeAgent",
instructions="You are helpful.",
context=ManagerConfig(
auto_compact=True,
strategy="summarize",
)
)
print(f" Summarize: strategy={summarize_agent.context_manager.config.strategy}")
compacted, result = compactor.compact(messages)
print(f" Messages kept: {result.messages_kept}")
print(f" Strategy: {result.strategy_used.value}")
# Show what messages remain
print(f"\n Remaining message roles:")
for msg in compacted:
role = msg.get("role", "unknown")
content_preview = msg.get("content", "")[:50]
print(f" [{role}] {content_preview}...")
# ==========================================================================
# No Compaction Needed
# ==========================================================================
print("\n--- No Compaction Needed ---")
compactor = ContextCompactor(
max_tokens=10000, # High limit
strategy=CompactionStrategy.TRUNCATE
# Smart strategy
smart_agent = Agent(
name="SmartAgent",
instructions="You are helpful.",
context=ManagerConfig(
auto_compact=True,
strategy="smart",
)
)
compacted, result = compactor.compact(messages)
print(f" Was compacted: {result.was_compacted}")
print(f" Messages unchanged: {len(compacted) == len(messages)}")
print(f" Smart: strategy={smart_agent.context_manager.config.strategy}")
# ==========================================================================
# Result Serialization
# Context Manager Stats Demo
# ==========================================================================
print("\n--- Result Serialization ---")
print("\n--- Context Manager Stats Demo ---")
compactor = ContextCompactor(max_tokens=50)
_, result = compactor.compact(messages)
data = result.to_dict()
print(f" Original tokens: {data['original_tokens']}")
print(f" Compacted tokens: {data['compacted_tokens']}")
print(f" Compression ratio: {data['compression_ratio']:.2f}")
# Access context manager stats
if agent.context_manager:
stats = agent.context_manager.get_stats()
print(f" Model: {stats.get('model', 'N/A')}")
print(f" Model limit: {stats.get('model_limit', 'N/A')}")
print(f" Output reserve: {stats.get('output_reserve', 'N/A')}")
print(f" Usable budget: {stats.get('usable_budget', 'N/A')}")
else:
print(" Context manager not initialized")
print("\n" + "=" * 60)
print("Demo Complete!")
+41 -56
View File
@@ -1,87 +1,72 @@
"""
Fast Context with Agent - Basic Example
Fast Context - Basic Example
This is the simplest way to use Fast Context with an Agent.
Just set fast_context=True and the agent will automatically
have access to rapid code search capabilities.
FastContext provides rapid code search capabilities.
Use it directly from the context module for code search.
Fast Context provides:
FastContext provides:
- 10-20x faster code search than traditional methods
- Parallel execution (8 concurrent searches)
- Automatic caching for repeated queries
- Seamless integration with Agent workflows
Note: FastContext is now accessed via the context module directly,
not as Agent parameters. For context management with agents,
use Agent(context=True) or Agent(context=ManagerConfig(...)).
"""
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
def main():
print("=" * 60)
print("Agent with Fast Context - Basic Example")
print("Agent with Context Management - Basic Example")
print("=" * 60)
# Create an agent with Fast Context enabled
# This is the simplest configuration
# Create an agent with context management enabled
# This is the recommended way to use context features
agent = Agent(
name="CodeAssistant",
instructions="You are a helpful code assistant.",
fast_context=True, # Enable Fast Context
fast_context_path=".", # Search in current directory
context=True, # Enable context management with defaults
verbose=False
)
print("\n✓ Agent created with Fast Context enabled")
print(f" - fast_context_enabled: {agent.fast_context_enabled}")
print(f" - fast_context_path: {agent.fast_context_path}")
print(f" - fast_context_model: {agent.fast_context_model}")
print("\n✓ Agent created with context management enabled")
print(f" - context_manager: {agent.context_manager is not None}")
if agent.context_manager:
print(f" - auto_compact: {agent.context_manager.config.auto_compact}")
print(f" - strategy: {agent.context_manager.config.strategy}")
# Use delegate_to_fast_context to search code
# For FastContext code search, use the module directly
print("\n" + "-" * 60)
print("Searching for 'class Agent' in the codebase...")
print("Using FastContext for code search...")
print("-" * 60)
context = agent.delegate_to_fast_context("class Agent")
if context:
print(f"\n✓ Found relevant code!")
print(f" Context length: {len(context)} characters")
print("\nPreview of found context:")
print("-" * 40)
# Show first 500 characters
preview = context[:500]
print(preview)
if len(context) > 500:
print("...")
else:
print("\n✗ No relevant code found")
# Another search example
print("\n" + "-" * 60)
print("Searching for 'def chat' in the codebase...")
print("-" * 60)
context = agent.delegate_to_fast_context("def chat")
if context:
print(f"\n✓ Found relevant code!")
print(f" Context length: {len(context)} characters")
else:
print("\n✗ No relevant code found")
# Access FastContext directly for more control
print("\n" + "-" * 60)
print("Direct FastContext access for advanced usage...")
print("-" * 60)
fc = agent.fast_context
if fc:
result = fc.search("import")
print(f"\n✓ Direct search for 'import':")
try:
from praisonaiagents.context.fast import FastContext
fc = FastContext(
workspace_path=".",
model="gpt-4o-mini",
max_turns=4,
max_parallel=8,
)
result = fc.search("class Agent")
print(f"\n✓ Search for 'class Agent':")
print(f" Files found: {result.total_files}")
print(f" Search time: {result.search_time_ms}ms")
print(f" From cache: {result.from_cache}")
if result.total_files > 0:
context = fc.get_context_for_agent("class Agent")
print(f" Context length: {len(context)} characters")
except ImportError:
print("\n⚠ FastContext not available (optional feature)")
except Exception as e:
print(f"\n⚠ FastContext search failed: {e}")
print("\n" + "=" * 60)
print("Fast Context makes code search instant for your agents!")
print("Context management is now unified via context= param!")
print("=" * 60)
+61 -66
View File
@@ -1,103 +1,98 @@
"""
Example 4: Agent Integration with Fast Context
Example 4: Agent Integration with Context Management
This example shows how to use Fast Context integrated with the Agent class
for automatic code search delegation.
This example shows how to use context management with the Agent class.
Context management is now unified via the context= parameter.
Benefits:
- Seamless integration with existing Agent workflows
- Automatic context injection for code-related queries
- Configurable via Agent parameters or environment variables
- Automatic token tracking and optimization
- Configurable via context= parameter with ManagerConfig
"""
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
WORKSPACE = "/Users/praison/praisonai-package/src/praisonai-agents"
def main():
print("=" * 70)
print("Agent Integration with Fast Context")
print("Agent Integration with Context Management")
print("=" * 70)
# Create an agent with Fast Context enabled
print("\n1. Creating Agent with Fast Context")
# Create an agent with context management enabled
print("\n1. Creating Agent with Context Management")
print("-" * 40)
config = ManagerConfig(
auto_compact=True,
compact_threshold=0.8,
strategy="smart",
monitor_enabled=True,
monitor_path="./context_debug.txt",
)
agent = Agent(
name="CodeAssistant",
instructions="You are a helpful code assistant that can search and analyze code.",
fast_context=True,
fast_context_path=WORKSPACE,
fast_context_model="gpt-4o-mini",
fast_context_max_turns=4,
fast_context_parallelism=8,
fast_context_timeout=30.0,
instructions="You are a helpful code assistant.",
context=config,
verbose=False
)
print(f" Agent name: {agent.name}")
print(f" Fast Context enabled: {agent.fast_context_enabled}")
print(f" Fast Context path: {agent.fast_context_path}")
print(f" Fast Context model: {agent.fast_context_model}")
print(f" Max turns: {agent.fast_context_max_turns}")
print(f" Parallelism: {agent.fast_context_parallelism}")
print(f" Context manager: {agent.context_manager is not None}")
if agent.context_manager:
print(f" Auto-compact: {agent.context_manager.config.auto_compact}")
print(f" Threshold: {agent.context_manager.config.compact_threshold}")
print(f" Strategy: {agent.context_manager.config.strategy}")
# Use delegate_to_fast_context for code search
print("\n2. Delegating Code Search to Fast Context")
# For FastContext code search, use the module directly
print("\n2. Using FastContext for Code Search")
print("-" * 40)
queries = [
"find the Agent class definition",
"search for authentication handlers",
"locate memory management code",
]
for query in queries:
print(f"\n Query: '{query}'")
context = agent.delegate_to_fast_context(query)
try:
from praisonaiagents.context.fast import FastContext
if context:
# Show preview of context
lines = context.split('\n')
preview_lines = lines[:5]
print(f" Found context ({len(context)} chars):")
for line in preview_lines:
print(f" {line[:60]}{'...' if len(line) > 60 else ''}")
if len(lines) > 5:
print(f" ... and {len(lines) - 5} more lines")
else:
print(" No relevant code found")
# Access the FastContext instance directly
print("\n3. Direct FastContext Access")
print("-" * 40)
fc = agent.fast_context
if fc:
result = fc.search("def chat")
print(f" Direct search for 'def chat':")
print(f" Files found: {result.total_files}")
print(f" Search time: {result.search_time_ms}ms")
fc = FastContext(
workspace_path=WORKSPACE,
model="gpt-4o-mini",
max_turns=4,
max_parallel=8,
timeout=30.0,
)
for f in result.files[:3]:
print(f" - {f.path}")
queries = [
"find the Agent class definition",
"search for chat method",
]
for query in queries:
print(f"\n Query: '{query}'")
result = fc.search(query)
print(f" Files found: {result.total_files}")
print(f" Search time: {result.search_time_ms}ms")
except ImportError:
print(" FastContext not available (optional feature)")
except Exception as e:
print(f" FastContext error: {e}")
# Show configuration options
print("\n4. Configuration Options")
print("\n3. Configuration Options")
print("-" * 40)
print(" Agent parameters:")
print(" - fast_context=True # Enable Fast Context")
print(" - fast_context_path='.' # Workspace path")
print(" - fast_context_model='gpt-4o-mini' # Search model")
print(" - fast_context_max_turns=4 # Max search turns")
print(" - fast_context_parallelism=8 # Parallel calls")
print(" - fast_context_timeout=30.0 # Timeout (seconds)")
print(" Agent context= parameter:")
print(" - context=True # Enable with defaults")
print(" - context=ManagerConfig(...) # Custom configuration")
print(" - context=False # Disabled (default)")
print("\n Environment variables:")
print(" - FAST_CONTEXT_MODEL")
print(" - FAST_CONTEXT_MAX_TURNS")
print("\n ManagerConfig options:")
print(" - auto_compact=True # Auto-optimize context")
print(" - compact_threshold=0.8 # Trigger at 80%")
print(" - strategy='smart' # Optimization strategy")
print(" - monitor_enabled=True # Write snapshots")
print(" - monitor_path='./context.txt' # Snapshot path")
print(" - FAST_CONTEXT_PARALLELISM")
print(" - FAST_CONTEXT_TIMEOUT")
print(" - FAST_CONTEXT_CACHE")
+110 -2
View File
@@ -2,11 +2,22 @@
"""
Context Budgeting Example
Demonstrates how to use the ContextBudgeter to allocate token budgets
across different context segments for various models.
Demonstrates how to use context budgeting with Agent via context= param,
and also shows low-level ContextBudgeter usage for advanced scenarios.
Agent-Centric Quick Start:
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
agent = Agent(
instructions="You are helpful.",
context=ManagerConfig(output_reserve=16000), # Custom output reserve
)
"""
from praisonaiagents import Agent
from praisonaiagents.context import (
ManagerConfig,
ContextBudgeter,
BudgetAllocation,
get_model_limit,
@@ -14,6 +25,31 @@ from praisonaiagents.context import (
)
def agent_centric_example():
"""Agent-centric usage - recommended approach."""
print("=" * 60)
print("Agent-Centric Context Budgeting")
print("=" * 60)
# Simple: Enable context management with defaults
agent = Agent(
instructions="You are a helpful assistant.",
context=True,
)
print(f"Agent with context=True created")
print(f"Context manager: {agent.context_manager is not None}")
# Custom: Specify output reserve
agent2 = Agent(
instructions="You are a code assistant.",
context=ManagerConfig(output_reserve=16000),
)
print(f"Agent with custom output_reserve created")
if agent2.context_manager:
stats = agent2.context_manager.get_stats()
print(f"Output reserve: {stats.get('output_reserve', 'N/A')}")
def main():
print("=" * 60)
print("Context Budgeting Example")
@@ -92,6 +128,78 @@ def main():
budget_dict = budgeter.to_dict()
print(f"Budget as dict: {budget_dict}")
# Example 6: Overflow handling strategies overview
print("\n6. Overflow Handling Strategies")
print("-" * 40)
strategies = [
("truncate", "Remove oldest messages first", "Fast, simple", "Loses early context"),
("sliding_window", "Keep N most recent messages", "Preserves recent", "Loses early context"),
("prune_tools", "Truncate old tool outputs", "Keeps messages", "May lose tool details"),
("summarize", "Replace old messages with summary", "Preserves meaning", "Slower, uses API"),
("smart", "Combine strategies intelligently", "Best balance", "More complex"),
]
print(f"{'Strategy':<15} | {'Description':<30} | {'Pros':<18} | {'Cons'}")
print("-" * 90)
for name, desc, pros, cons in strategies:
print(f"{name:<15} | {desc:<30} | {pros:<18} | {cons}")
# Example 7: Simulated overflow playbook
print("\n7. Overflow Playbook (Thresholds → Actions)")
print("-" * 40)
playbook = [
(0.70, "INFO", "Monitor usage, no action needed"),
(0.80, "NOTICE", "Consider optimization soon"),
(0.90, "WARNING", "Trigger auto-compact if enabled"),
(0.95, "CRITICAL", "Aggressive optimization required"),
(1.00, "OVERFLOW", "Immediate truncation to prevent API error"),
]
print(f"{'Usage':<8} | {'Level':<10} | {'Action'}")
print("-" * 60)
for threshold, level, action in playbook:
print(f"{threshold:>6.0%} | {level:<10} | {action}")
# Example 8: Strategy comparison table
print("\n8. Strategy Comparison")
print("-" * 40)
print("Strategy | Preserves | Loses | Best For")
print("-" * 70)
print("truncate | Recent msgs | Old msgs | Simple chats")
print("sliding_window | Last N msgs | Earlier msgs | Long conversations")
print("prune_tools | All messages | Tool details | Tool-heavy agents")
print("summarize | Context meaning| Exact wording | Important history")
print("smart | Balanced | Minimal | Production use")
# Example 9: Default strategy for interactive mode
print("\n9. Default Strategy: Interactive Mode")
print("-" * 40)
print("Interactive mode default: context=False (zero overhead)")
print("To enable context management in interactive mode:")
print(" praisonai chat --context # Enable with defaults")
print(" Or in code:")
print(" agent = Agent(instructions='...', context=True)")
print("")
print("When enabled, defaults are:")
print(" - auto_compact: True")
print(" - compact_threshold: 0.8 (80% usage)")
print(" - strategy: smart")
print(" - output_reserve: model-specific (8K-16K)")
# Example 10: Default strategy for auto-agents mode
print("\n10. Default Strategy: Auto-Agents Mode")
print("-" * 40)
print("Auto-agents mode default: context=False (zero overhead)")
print("To enable for multi-agent workflows:")
print(" from praisonaiagents import PraisonAIAgents")
print(" agents = PraisonAIAgents(agents=[...], context=True)")
print("")
print("Recommended config for long tasks:")
print(" context=ManagerConfig(")
print(" auto_compact=True,")
print(" compact_threshold=0.7, # Earlier trigger for safety")
print(" strategy='smart',")
print(" output_reserve=16384, # Larger reserve for complex outputs")
print(" )")
print("\n" + "=" * 60)
print("✓ Context budgeting examples complete!")
print("=" * 60)
+38 -2
View File
@@ -2,13 +2,27 @@
"""
Context Monitoring Example
Demonstrates how to use the ContextMonitor to write runtime context
snapshots to disk for debugging and analysis.
Demonstrates how to use context monitoring with Agent via context= param,
and also shows low-level ContextMonitor usage for advanced scenarios.
Agent-Centric Quick Start:
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
agent = Agent(
instructions="You are helpful.",
context=ManagerConfig(
monitor_enabled=True,
monitor_path="./debug/context.json",
),
)
"""
import os
import tempfile
from praisonaiagents import Agent
from praisonaiagents.context import (
ManagerConfig,
ContextMonitor,
ContextLedger,
BudgetAllocation,
@@ -19,6 +33,28 @@ from praisonaiagents.context import (
)
def agent_centric_example():
"""Agent-centric usage - recommended approach."""
print("=" * 60)
print("Agent-Centric Context Monitoring")
print("=" * 60)
with tempfile.TemporaryDirectory() as tmpdir:
monitor_path = os.path.join(tmpdir, "context.json")
agent = Agent(
instructions="You are a helpful assistant.",
context=ManagerConfig(
monitor_enabled=True,
monitor_path=monitor_path,
monitor_format="json",
),
)
print(f"Agent with monitoring enabled")
print(f"Monitor path: {monitor_path}")
print(f"Context manager: {agent.context_manager is not None}")
def main():
print("=" * 60)
print("Context Monitoring Example")
+37 -2
View File
@@ -2,11 +2,25 @@
"""
Context Optimization Example
Demonstrates how to use various optimization strategies to reduce
context size when approaching model limits.
Demonstrates how to use context optimization with Agent via context= param,
and also shows low-level optimizer usage for advanced scenarios.
Agent-Centric Quick Start:
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
agent = Agent(
instructions="You are helpful.",
context=ManagerConfig(
auto_compact=True,
strategy="smart", # truncate, sliding_window, summarize, smart
),
)
"""
from praisonaiagents import Agent
from praisonaiagents.context import (
ManagerConfig,
get_optimizer,
OptimizerStrategy,
TruncateOptimizer,
@@ -17,6 +31,27 @@ from praisonaiagents.context import (
)
def agent_centric_example():
"""Agent-centric usage - recommended approach."""
print("=" * 60)
print("Agent-Centric Context Optimization")
print("=" * 60)
# Enable auto-compaction with smart strategy
agent = Agent(
instructions="You are a helpful assistant.",
context=ManagerConfig(
auto_compact=True,
compact_threshold=0.8,
strategy="smart",
),
)
print("Agent with auto-compaction enabled")
print(f"Strategy: smart")
print(f"Threshold: 0.8 (80% of context budget)")
print(f"Context manager: {agent.context_manager is not None}")
def main():
print("=" * 60)
print("Context Optimization Example")
+38 -2
View File
@@ -2,13 +2,24 @@
"""
Multi-Agent Context Isolation Example
Demonstrates how to use MultiAgentLedger and MultiAgentMonitor
for managing context across multiple agents with isolation.
Demonstrates how to use multi-agent context with PraisonAIAgents via context= param,
and also shows low-level MultiAgentLedger usage for advanced scenarios.
Agent-Centric Quick Start:
from praisonaiagents import Agent, PraisonAIAgents
from praisonaiagents.context import ManagerConfig
agents = PraisonAIAgents(
agents=[agent1, agent2],
context=ManagerConfig(policy="isolated"), # or "shared"
)
"""
import os
import tempfile
from praisonaiagents import Agent, PraisonAIAgents
from praisonaiagents.context import (
ManagerConfig,
MultiAgentLedger,
MultiAgentMonitor,
ContextLedgerManager,
@@ -17,6 +28,31 @@ from praisonaiagents.context import (
)
def agent_centric_example():
"""Agent-centric usage - recommended approach."""
print("=" * 60)
print("Agent-Centric Multi-Agent Context")
print("=" * 60)
# Create agents with context enabled
researcher = Agent(
name="Researcher",
instructions="You are a research specialist.",
context=True,
)
writer = Agent(
name="Writer",
instructions="You are a technical writer.",
context=True,
)
# Multi-agent setup with isolated context policy
# (each agent maintains its own context)
print("Created agents with context=True")
print("Use PraisonAIAgents(agents=[...], context=ManagerConfig(policy='isolated'))")
print("for multi-agent context isolation")
def main():
print("=" * 60)
print("Multi-Agent Context Isolation Example")
@@ -13,10 +13,10 @@ from praisonaiagents import Agent
from praisonaiagents.context import (
ContextBudgeter,
ContextLedgerManager,
ContextMonitor,
get_optimizer,
OptimizerStrategy,
estimate_messages_tokens,
format_percent,
)
@@ -37,7 +37,7 @@ def main():
# Get budget info
budget = budgeter.allocate()
print(f"\nModel: gpt-4o-mini")
print("\nModel: gpt-4o-mini")
print(f"Context budget: {budget.usable:,} tokens")
print(f"Output reserve: {budget.output_reserve:,} tokens")
@@ -76,7 +76,7 @@ def main():
current_tokens = ledger.get_total()
utilization = budgeter.get_utilization(current_tokens)
print(f"[Context: {current_tokens} tokens, {utilization:.1%} used]")
print(f"[Context: {current_tokens} tokens, {format_percent(utilization)} used]")
# Check if optimization needed
if utilization > 0.8:
@@ -101,7 +101,7 @@ def main():
final_utilization = budgeter.get_utilization(final_tokens)
print(f"Total tokens used: {final_tokens:,}")
print(f"Context utilization: {final_utilization:.1%}")
print(f"Context utilization: {format_percent(final_utilization)}")
print(f"Messages in conversation: {len(conversation)}")
print(f"Remaining capacity: {budgeter.get_remaining(final_tokens):,} tokens")
+217
View File
@@ -0,0 +1,217 @@
#!/usr/bin/env python3
"""
Agent-Centric Context Management Example
Demonstrates the recommended way to use context management with the new
`context=` parameter. This is the simplest and most powerful approach.
The `context=` parameter accepts:
- False (default): Zero overhead, no context management
- True: Enable with safe defaults (auto-compact, smart strategy)
- ManagerConfig: Custom configuration object
- ContextManager: Pre-configured manager instance
Requires: OPENAI_API_KEY environment variable
"""
import os
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
def example_1_simple_enable():
"""Simplest usage - just enable context management."""
print("=" * 60)
print("Example 1: Simple Enable (context=True)")
print("=" * 60)
# Create agent with context management enabled
agent = Agent(
instructions="You are a helpful assistant. Keep responses concise.",
llm="gpt-4o-mini",
context=True, # Enable with safe defaults
)
# The agent now automatically:
# - Tracks token usage
# - Optimizes when approaching limits (80% threshold)
# - Uses smart optimization strategy
print("Agent created with context management enabled")
print(f"Context manager initialized: {agent.context_manager is not None}")
if agent.context_manager:
print(f"Model: {agent.context_manager.model}")
print(f"Auto-compact: {agent.context_manager.config.auto_compact}")
print(f"Threshold: {agent.context_manager.config.compact_threshold}")
print(f"Strategy: {agent.context_manager.config.strategy}")
# Make a chat call
if os.environ.get("OPENAI_API_KEY"):
response = agent.chat("What is 2+2?")
print(f"\nResponse: {response}")
else:
print("\nSkipping API call (no OPENAI_API_KEY)")
print()
def example_2_custom_config():
"""Custom configuration with ManagerConfig."""
print("=" * 60)
print("Example 2: Custom Configuration (context=ManagerConfig)")
print("=" * 60)
# Create custom configuration
config = ManagerConfig(
auto_compact=True,
compact_threshold=0.7, # Trigger at 70% instead of 80%
strategy="smart",
monitor_enabled=True,
monitor_path="./context_debug.txt",
monitor_format="human",
redact_sensitive=True,
output_reserve=8000,
)
# Create agent with custom config
agent = Agent(
instructions="You are a code assistant.",
llm="gpt-4o-mini",
context=config,
)
print("Agent created with custom context config")
print(f"Threshold: {agent.context_manager.config.compact_threshold}")
print(f"Monitor enabled: {agent.context_manager.config.monitor_enabled}")
print(f"Monitor path: {agent.context_manager.config.monitor_path}")
if os.environ.get("OPENAI_API_KEY"):
response = agent.chat("Write a hello world in Python")
print(f"\nResponse: {response[:200]}...")
else:
print("\nSkipping API call (no OPENAI_API_KEY)")
print()
def example_3_disabled():
"""Disabled context management - zero overhead."""
print("=" * 60)
print("Example 3: Disabled (context=False)")
print("=" * 60)
# Create agent without context management (default)
agent = Agent(
instructions="You are a simple assistant.",
llm="gpt-4o-mini",
context=False, # Explicit disable (same as default)
)
print("Agent created without context management")
print(f"Context manager: {agent.context_manager}")
print("Zero overhead: No token tracking, no optimization")
if os.environ.get("OPENAI_API_KEY"):
response = agent.chat("Hello!")
print(f"\nResponse: {response}")
else:
print("\nSkipping API call (no OPENAI_API_KEY)")
print()
def example_4_access_stats():
"""Access context statistics and history."""
print("=" * 60)
print("Example 4: Access Context Stats")
print("=" * 60)
agent = Agent(
instructions="You are helpful.",
llm="gpt-4o-mini",
context=True,
)
manager = agent.context_manager
# Access budget info via internal attribute
budget = manager._budget
print(f"Model limit: {budget.model_limit:,} tokens")
print(f"Output reserve: {budget.output_reserve:,} tokens")
print(f"Usable budget: {budget.usable:,} tokens")
# Access config
print("\nConfiguration:")
print(f" Auto-compact: {manager.config.auto_compact}")
print(f" Threshold: {manager.config.compact_threshold}")
print(f" Strategy: {manager.config.strategy}")
print()
def example_5_multi_agent():
"""Multi-agent with shared context management."""
print("=" * 60)
print("Example 5: Multi-Agent Context Management")
print("=" * 60)
from praisonaiagents import PraisonAIAgents, Task
config = ManagerConfig(
auto_compact=True,
compact_threshold=0.8,
)
# Create agents
researcher = Agent(
name="Researcher",
instructions="You research topics thoroughly.",
llm="gpt-4o-mini",
)
writer = Agent(
name="Writer",
instructions="You write clear summaries.",
llm="gpt-4o-mini",
)
# Create tasks
task1 = Task(description="Research AI trends", agent=researcher)
task2 = Task(description="Write a summary", agent=writer)
# Create multi-agent system with context management
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2],
context=config, # Shared context config
)
print("Multi-agent system created")
print(f"Context manager: {agents.context_manager is not None}")
if agents.context_manager:
print("Type: MultiAgentContextManager")
print("Provides per-agent isolation with shared config")
print()
def main():
print("\n" + "=" * 60)
print("Agent-Centric Context Management Examples")
print("=" * 60 + "\n")
example_1_simple_enable()
example_2_custom_config()
example_3_disabled()
example_4_access_stats()
example_5_multi_agent()
print("=" * 60)
print("✓ All examples complete!")
print("=" * 60)
if __name__ == "__main__":
main()
+4 -4
View File
@@ -67,7 +67,7 @@ agent = Agent(
goal="Summarize web content concisely",
instructions="You are a helpful assistant that summarizes web content",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
self_reflect=False,
reflection=False,
verbose=True,
tools=[get_url_context] # FIX: tools must be a list, not a single function
)
@@ -77,7 +77,7 @@ task_with_guardrailresult = Task(
name="summarise article with GuardrailResult",
description="get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary below 500 characters",
agent=agent,
guardrail=validate_length_guardrailresult, # Using GuardrailResult
guardrails=validate_length_guardrailresult, # Using GuardrailResult
expected_output="summary of the article below 500 characters",
max_retries=3 # Will retry up to 3 times if guardrail fails
)
@@ -87,7 +87,7 @@ task_with_tuple = Task(
name="summarise article with tuple",
description="get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary below 500 characters",
agent=agent,
guardrail=validate_length_tuple, # Using Tuple[bool, Any]
guardrails=validate_length_tuple, # Using Tuple[bool, Any]
expected_output="summary of the article below 500 characters",
max_retries=3
)
@@ -97,7 +97,7 @@ task_with_llm_guardrail = Task(
name="summarise with LLM guardrail",
description="get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary",
agent=agent,
guardrail="Ensure the summary is professional, factual, and between 100-500 characters",
guardrails="Ensure the summary is professional, factual, and between 100-500 characters",
expected_output="professional summary of the article"
)
+210
View File
@@ -0,0 +1,210 @@
#!/usr/bin/env python3
"""
Example: Context Compression Demo
This example demonstrates:
1. Context compression for large retrieved content
2. Token budget management
3. Agent-centric knowledge retrieval with unique codes
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python compression_demo.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
# Unique verification code that CANNOT be guessed
SECRET_CODE = "COMPRESS-8K4M2"
def create_verbose_document(temp_dir: str) -> str:
"""Create a verbose document with a hidden unique code."""
filepath = os.path.join(temp_dir, 'verbose_policy.txt')
# Create a document with lots of filler text but one unique code
with open(filepath, 'w') as f:
f.write("""
ACME CORPORATION COMPREHENSIVE POLICY MANUAL
=============================================
CHAPTER 1: INTRODUCTION
-----------------------
This comprehensive policy manual outlines all procedures, guidelines, and
requirements for employees of Acme Corporation. All employees are expected
to read and understand this document in its entirety. This manual supersedes
all previous versions and is effective immediately upon publication.
The purpose of this manual is to provide clear guidance on company policies,
procedures, and expectations. It covers a wide range of topics including but
not limited to workplace conduct, benefits, leave policies, and security
protocols.
CHAPTER 2: WORKPLACE CONDUCT
----------------------------
All employees are expected to maintain professional conduct at all times.
This includes treating colleagues with respect, maintaining a clean workspace,
and adhering to the company dress code. Violations of workplace conduct
policies may result in disciplinary action up to and including termination.
Professional behavior includes punctuality, meeting deadlines, and effective
communication with team members. Employees should report any concerns about
workplace conduct to their immediate supervisor or Human Resources.
CHAPTER 3: BENEFITS AND COMPENSATION
------------------------------------
Acme Corporation offers a comprehensive benefits package to all full-time
employees. This includes health insurance, dental coverage, vision care,
and a 401(k) retirement plan with company matching up to 6% of salary.
Employees are eligible for benefits after completing 90 days of employment.
Open enrollment occurs annually in November. Changes to benefits outside
of open enrollment require a qualifying life event.
CHAPTER 4: LEAVE POLICIES
-------------------------
Employees accrue paid time off (PTO) based on years of service:
- 0-2 years: 15 days per year
- 3-5 years: 20 days per year
- 6+ years: 25 days per year
Sick leave is provided separately at 10 days per year. Unused sick leave
may be carried over to the following year up to a maximum of 30 days.
CHAPTER 5: SECURITY PROTOCOLS
-----------------------------
All employees must follow strict security protocols to protect company
assets and confidential information. This includes using strong passwords,
locking workstations when away, and reporting any suspicious activity.
IMPORTANT: The master security verification code is: COMPRESS-8K4M2
This code is required for accessing secure areas and systems. Do not share
this code with unauthorized personnel. Misuse of security credentials is
grounds for immediate termination.
CHAPTER 6: EMERGENCY PROCEDURES
-------------------------------
In case of emergency, employees should follow the evacuation procedures
posted throughout the building. Assembly points are located in the parking
lot on the north side of the building.
Fire drills are conducted quarterly. All employees must participate in
fire drills and know the location of the nearest emergency exit.
CHAPTER 7: CONCLUSION
---------------------
This policy manual is a living document and may be updated periodically.
Employees will be notified of any changes via email and updated copies
will be made available on the company intranet.
Questions about any policy should be directed to Human Resources.
Document Version: 3.2.1
Last Updated: 2024-12-01
""")
return filepath
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_compress_')
try:
print("=" * 60)
print("Example: Context Compression Demo")
print("=" * 60)
# Create verbose document
doc_path = create_verbose_document(temp_dir)
print(f"\nCreated verbose document: {doc_path}")
print(f"Secret code embedded: {SECRET_CODE}")
# Demonstrate compression
print("\n" + "-" * 40)
print("Compression Demo")
print("-" * 40)
try:
from praisonaiagents.rag import ContextCompressor
# Read the document
with open(doc_path, 'r') as f:
content = f.read()
print(f"\nOriginal document length: {len(content)} characters")
# Compress with different ratios
for ratio in [0.3, 0.5, 0.7]:
compressor = ContextCompressor(
max_tokens=2000,
target_ratio=ratio,
)
result = compressor.compress([content], query="security verification code")
print(f"\nCompression ratio {ratio}:")
print(f" Original tokens: {result.original_tokens}")
print(f" Compressed tokens: {result.compressed_tokens}")
print(f" Actual ratio: {result.compressed_tokens / max(result.original_tokens, 1):.2f}")
# Check if secret code is preserved
compressed_text = " ".join(result.chunks) if result.chunks else ""
if SECRET_CODE in compressed_text:
print(f" ✅ Secret code preserved in compressed output")
else:
print(f" ⚠️ Secret code may have been removed (query-focused compression)")
except ImportError as e:
print(f"Note: Compression module not available: {e}")
# Create agent with knowledge
print("\n" + "-" * 40)
print("Agent Knowledge Retrieval Test")
print("-" * 40)
agent = Agent(
name="PolicyExpert",
instructions="""You are a company policy expert.
Answer questions based ONLY on the provided knowledge context.
When asked about codes, provide the EXACT code from the documents.
Be concise but accurate.""",
knowledge=[temp_dir],
user_id="compression_demo_user",
verbose=True,
)
# Test retrieval of the unique code
print("\nQ: What is the master security verification code?")
response = agent.chat("What is the master security verification code?")
print(f"A: {response}")
if SECRET_CODE in response.upper():
print(f"\n✅ VERIFIED: Agent correctly retrieved the secret code!")
else:
print(f"\n❌ WARNING: Expected code {SECRET_CODE} not found in response")
print("\n" + "=" * 60)
print("Demo Complete")
print("=" * 60)
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""
Example: Knowledge Retrieval with Context-Required Questions
This example demonstrates:
1. Agent answers questions that REQUIRE retrieved context (not general knowledge)
2. Unique codes/secrets that cannot be guessed
3. Verification that context is text, not file path
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python context_required_example.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_ctx_')
try:
# Create test document with UNIQUE content that cannot be guessed
doc = os.path.join(temp_dir, 'company_policy.txt')
with open(doc, 'w') as f:
f.write("""
Acme Corporation Internal Policy Document
==========================================
Remote Work Policy:
- Employees may work from home up to 3 days per week
- Manager approval is required for remote work
- Manager approval code: ZEBRA-71
- All requests must be submitted via the HR portal
Expense Reimbursement:
- Maximum daily meal allowance: $47.50
- Mileage rate: $0.655 per mile
- Expense submission code: TIGER-42
- Receipts required for amounts over $25
IT Security:
- VPN access code prefix: FALCON-
- Password reset hotline: 555-0199
- Security incident code: HAWK-88
This document is confidential. Last updated: 2024-01-15.
""")
print("=" * 60)
print("Example: Context-Required Knowledge Retrieval")
print("=" * 60)
print(f"\nDocument location: {doc}")
print("\nThis example asks questions that CANNOT be answered")
print("without the retrieved context (unique codes/values).\n")
# Create agent with knowledge
agent = Agent(
name="PolicyExpert",
instructions="""You are a company policy expert.
Answer questions based ONLY on the provided knowledge context.
If the answer is not in the context, say 'I don't have that information.'
Be precise and quote exact values when asked about codes or numbers.""",
knowledge=[temp_dir],
user_id="demo_user",
verbose=True,
)
# Question 1: Unique code that requires context
print("-" * 40)
print("Question 1: What is the manager approval code for remote work?")
print("-" * 40)
response = agent.chat("What is the manager approval code for remote work?")
print(f"\nAnswer: {response}\n")
# Verify the answer contains the unique code
if "ZEBRA-71" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the unique code from context!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
# Question 2: Another unique value
print("-" * 40)
print("Question 2: What is the maximum daily meal allowance?")
print("-" * 40)
response = agent.chat("What is the maximum daily meal allowance?")
print(f"\nAnswer: {response}\n")
if "47.50" in response or "$47.50" in response:
print("✅ VERIFIED: Agent correctly retrieved the specific amount!\n")
else:
print("❌ WARNING: Agent did not find the amount - check retrieval\n")
# Question 3: Security code
print("-" * 40)
print("Question 3: What is the security incident code?")
print("-" * 40)
response = agent.chat("What is the security incident code?")
print(f"\nAnswer: {response}\n")
if "HAWK-88" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the security code!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
# Show context preview to prove it's text, not path
print("=" * 60)
print("Context Verification")
print("=" * 60)
context, _ = agent._get_knowledge_context("policy codes", use_rag=True)
print(f"\nRetrieved context preview (first 300 chars):")
print("-" * 40)
print(context[:300] if context else "NO CONTEXT")
print("-" * 40)
# Verify context is text, not path
if temp_dir in context:
print("\n❌ ERROR: Context contains file path instead of text!")
else:
print("\n✅ VERIFIED: Context contains actual text, not file path!")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,199 @@
#!/usr/bin/env python3
"""
Example: Hierarchical Summaries Demo
This example demonstrates:
1. Building multi-level summaries (file → folder → project)
2. Query routing to appropriate summary level
3. Agent-centric knowledge retrieval with unique codes
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python hierarchical_summaries_demo.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
# Unique verification codes per department
DEPT_CODES = {
"engineering": "ENG-7X2K9",
"marketing": "MKT-3M5N8",
"finance": "FIN-9P4Q1",
}
def create_hierarchical_corpus(temp_dir: str) -> dict:
"""Create a hierarchical corpus with departments and files."""
structure = {}
for dept, code in DEPT_CODES.items():
dept_dir = os.path.join(temp_dir, dept)
os.makedirs(dept_dir, exist_ok=True)
structure[dept] = []
# Create department overview
overview_path = os.path.join(dept_dir, "overview.txt")
with open(overview_path, 'w') as f:
f.write(f"""
{dept.upper()} DEPARTMENT OVERVIEW
{'=' * 40}
Department: {dept.title()}
Department Code: {code}
Mission Statement:
The {dept.title()} department is responsible for driving excellence
in our organization through innovative solutions and dedicated teamwork.
Key Responsibilities:
- Strategic planning and execution
- Cross-functional collaboration
- Continuous improvement initiatives
Access Code: {code}
Use this code for all department-related system access.
Last Updated: 2024-12-01
""")
structure[dept].append(overview_path)
# Create team files
for i, team in enumerate(["alpha", "beta", "gamma"]):
team_path = os.path.join(dept_dir, f"team_{team}.txt")
with open(team_path, 'w') as f:
f.write(f"""
{dept.upper()} - TEAM {team.upper()}
{'=' * 40}
Team: {team.title()}
Department: {dept.title()}
Team Size: {5 + i * 2} members
Current Projects:
- Project {team.upper()}-001: In Progress
- Project {team.upper()}-002: Planning
- Project {team.upper()}-003: Completed
Team Lead: {team.title()} Lead
Contact: {team}@acme.com
Note: For department access, use code: {code}
""")
structure[dept].append(team_path)
return structure
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_hierarchy_')
try:
print("=" * 60)
print("Example: Hierarchical Summaries Demo")
print("=" * 60)
# Create hierarchical corpus
structure = create_hierarchical_corpus(temp_dir)
total_files = sum(len(files) for files in structure.values())
print(f"\nCreated hierarchical corpus:")
print(f" Departments: {len(structure)}")
print(f" Total files: {total_files}")
print(f" Location: {temp_dir}")
for dept, files in structure.items():
print(f" - {dept}/: {len(files)} files (code: {DEPT_CODES[dept]})")
# Demonstrate hierarchical summarizer
print("\n" + "-" * 40)
print("Hierarchical Summarizer Demo")
print("-" * 40)
try:
from praisonaiagents.rag import HierarchicalSummarizer
# Collect all files
all_files = []
for files in structure.values():
all_files.extend(files)
# Build hierarchy
summarizer = HierarchicalSummarizer(max_levels=3)
result = summarizer.build_hierarchy(all_files, base_path=temp_dir)
print(f"\nHierarchy built:")
print(f" Levels: {summarizer.max_levels}")
if hasattr(result, 'nodes'):
print(f" Summary nodes: {len(result.nodes)}")
# Query at different levels
print("\nQuery routing demonstration:")
queries = [
("What is the engineering department code?", "engineering"),
("Tell me about marketing team alpha", "marketing"),
("What are the finance department responsibilities?", "finance"),
]
for query, expected_dept in queries:
print(f"\n Query: {query}")
# In a full implementation, this would route to the appropriate level
print(f" Expected department: {expected_dept}")
print(f" Expected code: {DEPT_CODES[expected_dept]}")
except ImportError as e:
print(f"Note: Hierarchical summarizer not available: {e}")
except Exception as e:
print(f"Note: Hierarchy building skipped: {e}")
# Create agent with knowledge
print("\n" + "-" * 40)
print("Agent Knowledge Retrieval Test")
print("-" * 40)
agent = Agent(
name="DepartmentExpert",
instructions="""You are a department information expert.
Answer questions based ONLY on the provided knowledge context.
When asked about department codes, provide the EXACT code from the documents.
Be specific about which department you're referring to.""",
knowledge=[temp_dir],
user_id="hierarchy_demo_user",
verbose=True,
)
# Test retrieval of department codes
test_cases = [
("What is the engineering department access code?", "ENG-7X2K9"),
("What is the marketing department code?", "MKT-3M5N8"),
("What is the finance department code?", "FIN-9P4Q1"),
]
print("\nTesting retrieval of department codes:\n")
for question, expected_code in test_cases:
print(f"Q: {question}")
response = agent.chat(question)
print(f"A: {response[:200]}...")
if expected_code in response.upper():
print(f"✅ VERIFIED: Found code {expected_code}\n")
else:
print(f"❌ WARNING: Expected code {expected_code} not found\n")
print("=" * 60)
print("Demo Complete")
print("=" * 60)
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,177 @@
#!/usr/bin/env python3
"""
Example: Large Corpus Strategy Selection Demo
This example demonstrates:
1. Automatic strategy selection based on corpus size
2. Different retrieval strategies (DIRECT, BASIC, HYBRID, RERANKED, COMPRESSED)
3. Agent-centric knowledge retrieval with unique codes
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python large_corpus_strategy_demo.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
# Unique verification codes that CANNOT be guessed
VERIFICATION_CODES = {
"project_alpha": "ALPHA-7X9K2",
"project_beta": "BETA-3M8N1",
"project_gamma": "GAMMA-5P2Q7",
"budget_code": "BUDGET-9R4T6",
"security_clearance": "CLEARANCE-2W8Y3",
}
def create_test_corpus(temp_dir: str, num_files: int = 10) -> list:
"""Create a test corpus with unique codes in each file."""
files = []
# Create main project files with unique codes
for i, (project, code) in enumerate(VERIFICATION_CODES.items()):
filepath = os.path.join(temp_dir, f"{project}_doc.txt")
with open(filepath, 'w') as f:
f.write(f"""
{project.upper().replace('_', ' ')} Documentation
{'=' * 50}
Project Overview:
This document contains confidential information about {project}.
The verification code for this project is: {code}
Key Details:
- Project started: 2024-0{i+1}-15
- Team size: {10 + i * 5} members
- Status: Active
- Priority: {'High' if i < 2 else 'Medium'}
Access Requirements:
To access this project, use verification code: {code}
All access attempts are logged for security purposes.
Last updated: 2024-12-01
""")
files.append(filepath)
# Create additional filler files to simulate larger corpus
for i in range(num_files - len(VERIFICATION_CODES)):
filepath = os.path.join(temp_dir, f"general_doc_{i}.txt")
with open(filepath, 'w') as f:
f.write(f"""
General Documentation File {i}
{'=' * 40}
This is a general documentation file containing various information
about company processes and procedures.
Section {i}.1: Overview
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Section {i}.2: Procedures
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
Duis aute irure dolor in reprehenderit in voluptate velit esse.
Section {i}.3: Guidelines
Excepteur sint occaecat cupidatat non proident, sunt in culpa.
Qui officia deserunt mollit anim id est laborum.
Document ID: DOC-{1000 + i}
""")
files.append(filepath)
return files
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_strategy_')
try:
print("=" * 60)
print("Example: Large Corpus Strategy Selection Demo")
print("=" * 60)
# Create test corpus
files = create_test_corpus(temp_dir, num_files=15)
print(f"\nCreated {len(files)} test documents in: {temp_dir}")
print(f"Unique verification codes embedded: {len(VERIFICATION_CODES)}")
# Show strategy selection based on corpus size
print("\n" + "-" * 40)
print("Strategy Selection Demo")
print("-" * 40)
try:
from praisonaiagents.rag import select_strategy, RetrievalStrategy
from praisonaiagents.knowledge.indexing import CorpusStats
# Get corpus stats
stats = CorpusStats.from_directory(temp_dir)
print(f"\nCorpus Statistics:")
print(f" Files: {stats.file_count}")
print(f" Estimated tokens: {stats.total_tokens}")
print(f" Recommended strategy: {stats.strategy_recommendation}")
# Show strategy selection for different corpus sizes
print(f"\nStrategy selection by corpus size:")
for size in [100, 1000, 10000, 50000, 100000]:
strategy = select_strategy(corpus_tokens=size)
print(f" {size:>6} tokens -> {strategy.value}")
except ImportError as e:
print(f"Note: Strategy module not available: {e}")
# Create agent with knowledge
print("\n" + "-" * 40)
print("Agent Knowledge Retrieval Test")
print("-" * 40)
agent = Agent(
name="ProjectExpert",
instructions="""You are a project documentation expert.
Answer questions based ONLY on the provided knowledge context.
When asked about verification codes, provide the EXACT code from the documents.
If information is not in the context, say 'Information not found in documents.'""",
knowledge=[temp_dir],
user_id="strategy_demo_user",
verbose=True,
)
# Test retrieval with unique codes
test_questions = [
("What is the verification code for Project Alpha?", "ALPHA-7X9K2"),
("What is the budget code?", "BUDGET-9R4T6"),
("What is the security clearance code?", "CLEARANCE-2W8Y3"),
]
print("\nTesting retrieval of unique codes:\n")
for question, expected_code in test_questions:
print(f"Q: {question}")
response = agent.chat(question)
print(f"A: {response[:200]}...")
if expected_code in response.upper():
print(f"✅ VERIFIED: Found code {expected_code}\n")
else:
print(f"❌ WARNING: Expected code {expected_code} not found\n")
print("=" * 60)
print("Demo Complete")
print("=" * 60)
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""
Example: Using mem0 Backend with Agent Retrieval
This example demonstrates:
1. Agent with knowledge using mem0 backend (default)
2. Proper scope identifiers (user_id/agent_id)
3. Handling of metadata normalization (mem0 returns metadata=None)
4. Agent-first retrieval API
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python mem0_backend_example.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
def main():
# Create temporary directory with test documents
temp_dir = tempfile.mkdtemp(prefix='praison_example_')
try:
# Create test documents with UNIQUE content that requires retrieval
doc1 = os.path.join(temp_dir, 'acme_policy.txt')
with open(doc1, 'w') as f:
f.write("""
Acme Corp Travel Policy Document
Flight booking code: FALCON-77
Hotel reservation prefix: HTL-
Maximum daily meal allowance: $52.75
Mileage reimbursement rate: $0.67 per mile
Travel approval required for trips over: $500
Emergency travel hotline: 555-ACME-911
""")
doc2 = os.path.join(temp_dir, 'project_info.txt')
with open(doc2, 'w') as f:
f.write("""
Project Phoenix Status Report
Project code: PHOENIX-2024
Lead engineer: Dr. Sarah Chen
Budget allocation: $2.4 million
Target completion: Q3 2025
Security clearance required: Level 3
Weekly standup: Tuesdays at 10am PST
""")
print("=" * 60)
print("Example: Agent with Knowledge (mem0 backend)")
print("=" * 60)
# Create agent with knowledge
# mem0 is the default backend - it requires scope identifiers
agent = Agent(
name="PolicyExpert",
instructions="You are a company policy expert. Answer questions based ONLY on the provided knowledge. Quote exact values when asked about codes or numbers.",
knowledge=[temp_dir], # Add directory with documents
user_id="example_user", # Required for mem0 backend
)
print("\n1. Testing Agent.chat() with context-required question:")
print("-" * 40)
# Question that REQUIRES the retrieved context (unique code)
response = agent.chat("What is the flight booking code?")
print("Q: What is the flight booking code?")
print(f"A: {response}")
# Verify the answer contains the unique code
if "FALCON-77" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the unique code!")
print("\n2. Testing Agent.chat() with another context-required question:")
print("-" * 40)
response = agent.chat("What is the maximum daily meal allowance?")
print("Q: What is the maximum daily meal allowance?")
print(f"A: {response}")
if "52.75" in response:
print("✅ VERIFIED: Agent correctly retrieved the specific amount!")
print("\n3. Testing Agent.retrieve() for context-only retrieval:")
print("-" * 40)
# retrieve() returns context without LLM generation
try:
context_pack = agent.retrieve("Project Phoenix")
print("Query: Project Phoenix")
print(f"Context preview: {context_pack.context[:200]}...")
print(f"Citations: {len(context_pack.citations)} found")
except Exception as e:
print(f"Note: retrieve() requires RAG module: {e}")
print("\n" + "=" * 60)
print("Example completed successfully!")
print("=" * 60)
finally:
# Cleanup
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""
Example: Using Scope Identifiers (user_id, agent_id, run_id)
This example demonstrates:
1. Multi-user knowledge isolation with user_id
2. Multi-agent knowledge sharing with agent_id
3. Run-specific context with run_id
4. Error handling for missing scope identifiers
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python scope_identifiers_example.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_scope_')
try:
# Create test document
doc = os.path.join(temp_dir, 'shared_knowledge.txt')
with open(doc, 'w') as f:
f.write("""
Company Policy Document
1. All employees must complete security training annually.
2. Remote work is allowed with manager approval.
3. Expense reports must be submitted within 30 days.
""")
print("=" * 60)
print("Example: Scope Identifiers for Knowledge Isolation")
print("=" * 60)
# Example 1: User-scoped knowledge
print("\n1. User-scoped knowledge (user_id):")
print("-" * 40)
user1_agent = Agent(
name="HR_Assistant_User1",
instructions="You are an HR assistant.",
knowledge=[temp_dir],
user_id="user_alice", # Knowledge scoped to Alice
)
response = user1_agent.chat("What is the policy on remote work?")
print("User Alice asks: What is the policy on remote work?")
print(f"Response: {response[:200]}...")
# Example 2: Agent-scoped knowledge
print("\n2. Agent-scoped knowledge (agent_id):")
print("-" * 40)
shared_agent = Agent(
name="Policy_Expert",
instructions="You are a policy expert.",
knowledge=[temp_dir],
agent_id="policy_agent_v1", # Knowledge scoped to this agent
)
response = shared_agent.chat("What are the expense report rules?")
print("Agent policy_agent_v1 query: What are the expense report rules?")
print(f"Response: {response[:200]}...")
# Example 3: Combined scoping
print("\n3. Combined scoping (user_id + agent_id):")
print("-" * 40)
combined_agent = Agent(
name="Personal_HR_Bot",
instructions="You are a personal HR assistant.",
knowledge=[temp_dir],
user_id="user_bob",
agent_id="hr_bot_v2",
)
response = combined_agent.chat("What training is required?")
print("User Bob with hr_bot_v2: What training is required?")
print(f"Response: {response[:200]}...")
print("\n" + "=" * 60)
print("Scope Identifier Summary:")
print("=" * 60)
print("""
- user_id: Isolates knowledge per user (multi-tenant)
- agent_id: Isolates knowledge per agent type/version
- run_id: Isolates knowledge per execution run (ephemeral)
Best Practices:
1. Always provide at least one scope identifier
2. Use user_id for user-specific data
3. Use agent_id for shared agent knowledge
4. Use run_id for temporary/session-specific context
""")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""
Example: Multi-Agent Scope Isolation
This example demonstrates:
1. Agent A ingests knowledge under user_id=U1
2. Agent B queries under user_id=U2 - should NOT find the knowledge
3. Agent B queries under user_id=U1 - should find the knowledge
4. Proves scope isolation works correctly
Requirements:
- pip install praisonaiagents[knowledge]
- OPENAI_API_KEY environment variable
Usage:
python scope_isolation_example.py
"""
import os
import tempfile
import shutil
from praisonaiagents import Agent, Knowledge
def main():
temp_dir = tempfile.mkdtemp(prefix='praison_scope_')
try:
# Create test document with UNIQUE secret
doc = os.path.join(temp_dir, 'secret.txt')
with open(doc, 'w') as f:
f.write("""
Project Codename: PHOENIX
Secret access code: WRENCH-992
Classification: Top Secret
Only authorized personnel with code WRENCH-992 may access this project.
""")
print("=" * 60)
print("Example: Multi-Agent Scope Isolation")
print("=" * 60)
# Step 1: Agent A ingests knowledge under user_id=U1
print("\n1. Agent A ingests knowledge under user_id='user_alice'")
print("-" * 40)
agent_a = Agent(
name="AgentA_Ingester",
instructions="You ingest and store knowledge.",
knowledge=[temp_dir],
user_id="user_alice", # Scoped to Alice
verbose=False,
)
# Ensure knowledge is processed
agent_a._ensure_knowledge_processed()
print(" Knowledge ingested by Agent A under user_alice scope")
# Step 2: Agent B queries under user_id=U2 - should NOT find
print("\n2. Agent B queries under user_id='user_bob' (different scope)")
print("-" * 40)
# Create a separate Knowledge instance for Agent B with different scope
knowledge_b = Knowledge()
search_result = knowledge_b.search("WRENCH-992", user_id="user_bob")
results = search_result.get('results', []) if isinstance(search_result, dict) else []
if len(results) == 0:
print(" ✅ CORRECT: Agent B (user_bob) found NO results")
print(" Scope isolation is working!")
else:
# Check if any result actually contains the secret
found_secret = any('wrench-992' in str(r.get('memory', '')).lower() for r in results)
if found_secret:
print(" ❌ ERROR: Agent B found the secret - scope isolation failed!")
else:
print(" ✅ CORRECT: Agent B found no matching content")
# Step 3: Agent B queries under user_id=U1 - should find
print("\n3. Agent B queries under user_id='user_alice' (same scope)")
print("-" * 40)
search_result = knowledge_b.search("WRENCH-992", user_id="user_alice")
results = search_result.get('results', []) if isinstance(search_result, dict) else []
if len(results) > 0:
memory = results[0].get('memory', '')
if 'wrench-992' in memory.lower():
print(" ✅ CORRECT: Agent B (as user_alice) found the secret!")
print(f" Retrieved: {memory[:80]}...")
else:
print(f" ⚠️ Found results but no secret: {memory[:80]}...")
else:
print(" ❌ ERROR: Agent B should have found results with user_alice scope")
# Step 4: Full Agent chat test with scope
print("\n4. Full Agent chat test with correct scope")
print("-" * 40)
agent_b = Agent(
name="AgentB_Querier",
instructions="Answer questions based on the provided knowledge. If you don't have the information, say so.",
knowledge=[temp_dir],
user_id="user_alice", # Same scope as Agent A
verbose=False,
)
response = agent_b.chat("What is the secret access code for Project PHOENIX?")
print(f" Question: What is the secret access code?")
print(f" Answer: {response}")
if "WRENCH-992" in response.upper():
print("\n ✅ VERIFIED: Agent correctly retrieved the secret code!")
else:
print("\n ⚠️ Agent may not have found the code")
print("\n" + "=" * 60)
print("Summary: Scope Isolation")
print("=" * 60)
print("""
- user_id='user_alice': Can access knowledge ingested by user_alice
- user_id='user_bob': Cannot access knowledge ingested by user_alice
- This enables multi-tenant knowledge isolation
""")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
main()
+33 -74
View File
@@ -2,9 +2,9 @@
"""
Basic Policy Engine Example for PraisonAI Agents.
This example demonstrates how to use the policy engine to:
1. Create policies with rules
2. Check if actions are allowed
This example demonstrates how to use the policy engine with Agent:
1. Create an Agent with policy enforcement
2. Define policies with rules
3. Block dangerous operations
4. Use convenience policy creators
@@ -12,126 +12,85 @@ Usage:
python basic_policy.py
"""
from praisonaiagents import Agent
from praisonaiagents.policy import (
PolicyEngine, Policy, PolicyRule, PolicyAction, PolicyConfig,
PolicyEngine, Policy, PolicyRule, PolicyAction,
create_deny_tools_policy, create_read_only_policy
)
def main():
print("=" * 60)
print("Policy Engine Demo")
print("Agent-Centric Policy Engine Demo")
print("=" * 60)
# Create a policy engine
# Create a policy engine with rules
engine = PolicyEngine()
# ==========================================================================
# Policy 1: Block dangerous tools
# ==========================================================================
# Add policy to block dangerous tools
print("\n--- Creating Policies ---")
dangerous_tools_policy = Policy(
engine.add_policy(Policy(
name="block_dangerous",
description="Block dangerous file operations",
rules=[
PolicyRule(
action=PolicyAction.DENY,
resource="tool:delete_*",
reason="Delete operations are blocked",
name="deny_delete"
reason="Delete operations are blocked"
),
PolicyRule(
action=PolicyAction.DENY,
resource="tool:rm_*",
reason="Remove operations are blocked",
name="deny_rm"
reason="Remove operations are blocked"
),
],
priority=100
)
engine.add_policy(dangerous_tools_policy)
print(f"✅ Added policy: {dangerous_tools_policy.name}")
))
print("✅ Added policy: block_dangerous")
# ==========================================================================
# Policy 2: Allow read operations
# ==========================================================================
read_policy = Policy(
name="allow_read",
description="Allow read operations",
rules=[
PolicyRule(
action=PolicyAction.ALLOW,
resource="tool:read_*",
name="allow_read"
),
PolicyRule(
action=PolicyAction.ALLOW,
resource="tool:list_*",
name="allow_list"
),
]
)
engine.add_policy(read_policy)
print(f"✅ Added policy: {read_policy.name}")
# Add read-only policy using convenience function
engine.add_policy(create_read_only_policy())
print("✅ Added policy: read_only")
# ==========================================================================
# Test policy checks
# ==========================================================================
# Create an Agent with policy enforcement
agent = Agent(
name="SecureAgent",
instructions="You are a file management assistant.",
policy=engine
)
print("\n--- Agent with Policy Enforcement Created ---")
print(f"Agent: {agent.name}")
print(f"Policy engine: {agent.policy is not None}")
# Test policy checks via agent's policy
print("\n--- Testing Policy Checks ---")
test_cases = [
("tool:read_file", "Reading a file"),
("tool:list_directory", "Listing directory"),
("tool:delete_file", "Deleting a file"),
("tool:rm_directory", "Removing directory"),
("tool:write_file", "Writing a file"),
]
for resource, description in test_cases:
result = engine.check(resource, {})
result = agent.policy.check(resource, {})
status = "✅ ALLOWED" if result.allowed else "🚫 DENIED"
reason = f" ({result.reason})" if result.reason else ""
print(f" {status}: {description}{reason}")
# ==========================================================================
# Using convenience functions
# ==========================================================================
print("\n--- Using Convenience Functions ---")
print("\n--- Convenience Policy Creators ---")
# Create a read-only policy
read_only = create_read_only_policy()
print(f"✅ Created read-only policy with {len(read_only.rules)} rules")
# Create a deny tools policy
deny_tools = create_deny_tools_policy(
["execute_*", "shell_*", "system_*"],
reason="System commands are blocked"
)
print(f"✅ Created deny-tools policy with {len(deny_tools.rules)} rules")
print(f"✅ create_deny_tools_policy: {len(deny_tools.rules)} rules")
# ==========================================================================
# Strict mode example
# ==========================================================================
print("\n--- Strict Mode Example ---")
strict_engine = PolicyEngine(PolicyConfig(strict_mode=True))
# In strict mode, unknown resources are denied
result = strict_engine.check("tool:unknown_operation", {})
print(f" Unknown operation in strict mode: {'DENIED' if not result.allowed else 'ALLOWED'}")
# ==========================================================================
# Policy serialization
# ==========================================================================
print("\n--- Policy Serialization ---")
policy_dict = dangerous_tools_policy.to_dict()
print(f" Serialized policy: {policy_dict['name']} with {len(policy_dict['rules'])} rules")
# Recreate from dict
restored = Policy.from_dict(policy_dict)
print(f" Restored policy: {restored.name}")
read_only = create_read_only_policy()
print(f"✅ create_read_only_policy: {len(read_only.rules)} rules")
print("\n" + "=" * 60)
print("Demo Complete!")
+6 -6
View File
@@ -32,9 +32,9 @@ code_writer = Agent(
goal="Write clean, efficient, and well-documented Python code",
backstory="You are a senior Python developer with 10+ years of experience in writing production-quality code.",
instructions="Write Python code that follows PEP 8 standards, includes proper error handling, and has clear documentation.",
self_reflect=True,
min_reflect=2,
max_reflect=5
reflection=True,
)
code_reviewer = Agent(
@@ -108,9 +108,9 @@ general_code_agent = Agent(
4. Refactor code for better performance
5. Add tests and documentation""",
tools=[code_interpreter, analyze_code_complexity],
self_reflect=True,
min_reflect=1,
max_reflect=3
reflection=True,
)
if __name__ == "__main__":
+1 -1
View File
@@ -14,7 +14,7 @@ image_agent = Agent(
backstory="""You are an expert in computer vision and image analysis.
You excel at describing images, detecting objects, and understanding visual content.""",
llm="gpt-5-nano",
self_reflect=False
reflection=False
)
# 1. Task with Image URL
@@ -14,7 +14,7 @@ image_agent = Agent(
backstory="""You are an expert in computer vision and image analysis.
You excel at describing images, detecting objects, and understanding visual content.""",
llm="gpt-5-nano",
self_reflect=False
reflection=False
)
# 1. Task with Image URL
+9 -9
View File
@@ -65,9 +65,9 @@ calculation_agent = Agent(
backstory="You are an expert mathematician who can handle complex calculations with precision.",
instructions="Perform the requested calculations step by step, showing your work clearly.",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
self_reflect=True,
min_reflect=1,
max_reflect=2
reflection=True,
)
problem_solver_agent = Agent(
@@ -81,9 +81,9 @@ problem_solver_agent = Agent(
3. Determine what needs to be found
4. Show each step of the solution
5. Verify the answer makes sense""",
self_reflect=True,
min_reflect=2,
max_reflect=4
reflection=True,
)
math_tutor_agent = Agent(
@@ -148,9 +148,9 @@ math_assistant = Agent(
Always show your work step by step.""",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
self_reflect=True,
min_reflect=1,
max_reflect=3
reflection=True,
)
if __name__ == "__main__":
+3 -2
View File
@@ -1,10 +1,11 @@
from praisonaiagents import Agent, Tools
from praisonaiagents import Agent, Tools, ReflectionConfig
from praisonaiagents.tools import execute_code, analyze_code, format_code, lint_code, disassemble_code # Code Tools
from praisonaiagents.tools import execute_command, list_processes, kill_process, get_system_info # Shell Tools
from praisonaiagents.tools import duckduckgo # Web Search Tool
agent = Agent(
instructions="You are a Programming Agent", self_reflect=True, min_reflect=5, max_reflect=10,
instructions="You are a Programming Agent",
reflection=ReflectionConfig(min_iterations=5, max_iterations=10),
tools=[execute_code, analyze_code, format_code, lint_code, disassemble_code, execute_command, list_processes, kill_process, get_system_info, duckduckgo]
)
agent.start(
+1 -1
View File
@@ -8,7 +8,7 @@ video_agent = Agent(
backstory="""You are an expert in computer vision and image analysis.
You excel at describing images, detecting objects, and understanding visual content.""",
llm="gpt-5-nano",
self_reflect=False
reflection=False
)
# Task with Video File
+3 -3
View File
@@ -4,9 +4,9 @@ from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_ran
agent = Agent(
instructions="You are a Wikipedia Agent",
tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language],
self_reflect=True,
min_reflect=3,
max_reflect=5,
reflection=True,
)
agent.start(
"What is the history of AI?"
+3 -3
View File
@@ -27,9 +27,9 @@ pdf_agent = Agent(
instructions="Read the provided PDF document and answer questions about its content. Be specific and cite relevant sections when possible.",
knowledge=["document.pdf"], # Replace with your PDF file path
knowledge_config=config,
self_reflect=True,
min_reflect=1,
max_reflect=3
reflection=True,
)
# Example usage
@@ -49,9 +49,9 @@ cot_agent = Agent(
6. Assess your confidence level
Always make your thinking process explicit and transparent.""",
self_reflect=True,
min_reflect=2,
max_reflect=3
reflection=True,
)
# Example 2: Reasoning Extractor Agent
@@ -224,7 +224,7 @@ pattern_reasoning_agent = Agent(
3. Counterfactual: Explore what-if scenarios
4. Systems thinking: Consider interconnections
5. Probabilistic: Assess likelihoods""",
self_reflect=True
reflection=True
)
# Example 8: Interactive Reasoning Extraction
+1 -1
View File
@@ -208,7 +208,7 @@ dynamic_router = Agent(
5. Current system load
Provide clear routing decisions with reasoning.""",
self_reflect=True
reflection=True
)
def dynamic_routing_example():
@@ -24,9 +24,9 @@ basic_reflection_agent = Agent(
3. Identify areas for improvement
4. Revise and enhance
5. Repeat until satisfied""",
self_reflect=True, # Enable self-reflection
min_reflect=1, # Minimum 1 reflection iteration
max_reflect=3 # Maximum 3 reflection iterations
reflection=True, # Enable self-reflection
# Minimum 1 reflection iteration
# Maximum 3 reflection iterations
)
# Example 2: Advanced Reflection with Different Strategies
@@ -41,9 +41,9 @@ quality_focused_agent = Agent(
- Clarity of presentation
- Logical flow
- Supporting evidence""",
self_reflect=True,
min_reflect=3, # Higher minimum for quality
max_reflect=5 # Allow more iterations
reflection=True,
# Higher minimum for quality
# Allow more iterations
)
efficiency_balanced_agent = Agent(
@@ -52,9 +52,9 @@ efficiency_balanced_agent = Agent(
goal="Achieve good quality with reasonable reflection time",
backstory="You balance quality with efficiency, knowing when good enough is sufficient.",
instructions="Create good content with 1-2 rounds of improvement.",
self_reflect=True,
min_reflect=1,
max_reflect=2 # Limit iterations for efficiency
reflection=True,
# Limit iterations for efficiency
)
# Example 3: No Reflection for Comparison
@@ -64,7 +64,7 @@ no_reflection_agent = Agent(
goal="Provide immediate responses without reflection",
backstory="You provide quick, first-draft responses.",
instructions="Respond immediately with your first thought.",
self_reflect=False # Disable reflection
reflection=False # Disable reflection
)
# Demonstration function to show reflection in action
@@ -113,9 +113,9 @@ def reflection_workflow_example():
role="Initial content creator",
goal="Create first draft with self-improvement",
backstory="You create initial drafts and refine them through reflection.",
self_reflect=True,
min_reflect=2,
max_reflect=3
reflection=True,
)
# Reviewer without reflection (provides feedback)
@@ -124,7 +124,7 @@ def reflection_workflow_example():
role="Content reviewer",
goal="Provide constructive feedback",
backstory="You review content and provide specific improvement suggestions.",
self_reflect=False # No reflection needed for reviewing
reflection=False # No reflection needed for reviewing
)
# Final polisher with reflection
@@ -133,9 +133,9 @@ def reflection_workflow_example():
role="Final content polisher",
goal="Perfect the content based on feedback",
backstory="You take feedback and create the final, polished version.",
self_reflect=True,
min_reflect=1,
max_reflect=2
reflection=True,
)
# Workflow tasks
@@ -202,9 +202,9 @@ custom_reflection_agent = Agent(
backstory="You adapt your reflection process based on content quality.",
instructions="""Create content and reflect on it.
Continue improving until quality standards are met or maximum iterations reached.""",
self_reflect=True,
min_reflect=1,
max_reflect=5
reflection=True,
)
# Example 6: Reflection Impact Analysis
@@ -17,10 +17,10 @@ reflection_agent = Agent(
goal="Demonstrate self-reflection and iterative improvement patterns",
backstory="Expert researcher with strong self-reflection and continuous improvement capabilities",
tools=[internet_search],
self_reflect=True,
min_reflect=2,
max_reflect=4,
reflect_llm="gpt-5-nano",
reflection=True,
verbose=True
)
+2 -2
View File
@@ -98,7 +98,7 @@ sync_agent = Agent(
goal="Perform synchronous searches and return structured results",
backstory="Expert in sync operations and data organization",
tools=[sync_search_tool],
self_reflect=False,
reflection=False,
verbose=True,
markdown=True
)
@@ -109,7 +109,7 @@ async_agent = Agent(
goal="Perform asynchronous searches and return structured results",
backstory="Expert in async operations and data organization",
tools=[async_search_tool],
self_reflect=False,
reflection=False,
verbose=True,
markdown=True
)
@@ -53,7 +53,7 @@ async_agent = Agent(
goal="Perform fast parallel searches with structured results",
backstory="Expert in efficient data retrieval and parallel search operations",
tools=[async_search_tool],
self_reflect=False,
reflection=False,
verbose=True,
markdown=True
)
@@ -63,7 +63,7 @@ summary_agent = Agent(
role="Research Synthesizer",
goal="Create concise summaries from multiple search results",
backstory="Expert in analyzing and synthesizing information from multiple sources",
self_reflect=True,
reflection=True,
verbose=True,
markdown=True
)
@@ -60,7 +60,7 @@ async_agent = Agent(
goal="Perform fast and efficient asynchronous searches with structured results",
backstory="Expert in parallel search operations and data retrieval",
tools=[async_search_tool],
self_reflect=False,
reflection=False,
verbose=True,
markdown=True
)
@@ -72,7 +72,7 @@ summary_agent = Agent(
backstory="""Expert in analyzing and synthesizing information from multiple sources.
Skilled at identifying patterns, trends, and connections between different topics.
Specializes in creating clear, structured summaries that highlight key insights.""",
self_reflect=True, # Enable self-reflection for better summary quality
reflection=True, # Enable self-reflection for better summary quality
verbose=True,
markdown=True
)
@@ -25,13 +25,13 @@ code_agent = Agent(
name="code_agent",
llm="gpt-5-nano",
backstory="Expert in writing Python scripts",
self_reflect=False
reflection=False
)
execution_agent = Agent(
name="execution_agent",
llm="gpt-5-nano",
backstory="Expert in executing Python scripts",
self_reflect=False,
reflection=False,
tools=[code_interpreter]
)
+3 -3
View File
@@ -129,9 +129,9 @@ def main():
tools=[internet_search_tool],
llm="gpt-5-nano",
markdown=True,
reflect_llm="gpt-5-nano",
min_reflect=2,
max_reflect=4
)
writer = Agent(
@@ -112,7 +112,7 @@ data_agent = Agent(
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organizing internet data from multiple sources.",
tools=[internet_search_tool, youtube_search_tool, wikipedia_search_tool],
self_reflect=False
reflection=False
)
# 3. Tasks
@@ -41,9 +41,9 @@ researcher = Agent(
tools=[internet_search_tool],
llm="gpt-5-nano",
markdown=True,
reflect_llm="gpt-5-nano",
min_reflect=2,
max_reflect=4
)
writer = Agent(
name="Writer",
+1 -1
View File
@@ -9,7 +9,7 @@ agent = Agent(
goal="Search for information from multiple sources",
backstory="I am an AI assistant that can search YouTube and Wikipedia.",
tools=[YouTubeSearchTool, WikipediaAPIWrapper],
self_reflect=False
reflection=False
)
# Create tasks to demonstrate both tools
+1 -1
View File
@@ -8,7 +8,7 @@ vision_agent = Agent(
backstory="""You are an expert in computer vision and image analysis.
You excel at describing images, detecting objects, and understanding visual content.""",
llm="gpt-5-nano",
self_reflect=False
reflection=False
)
# 1. Task with Image URL
@@ -15,7 +15,7 @@ researcher = Agent(
verbose=True,
llm="gpt-5-nano",
tools=[Tools.internet_search],
self_reflect=False
reflection=False
)
# Create an analyst agent
@@ -26,7 +26,7 @@ analyst = Agent(
backstory="Senior data analyst with expertise in pattern recognition",
verbose=True,
llm="gpt-5-nano",
self_reflect=False
reflection=False
)
# Define structured tasks
@@ -33,7 +33,7 @@ def get_structured_analysis(query: str, verbose: bool = True) -> AnalysisReport:
goal="Analyze topics and provide structured reports",
backstory="Expert at gathering information and providing structured analysis",
verbose=verbose,
self_reflect=True, # Enable self-reflection for better quality
reflection=True, # Enable self-reflection for better quality
markdown=True,
llm="gpt-5-nano"
)
+1 -1
View File
@@ -94,7 +94,7 @@ agent = Agent(
goal="Search for information about 'AI Agents Framework'",
backstory="I am an AI assistant that can search GitHub.",
tools=[EXASearchTool],
self_reflect=False
reflection=False
)
# Create tasks to demonstrate both tools
+1 -1
View File
@@ -8,7 +8,7 @@ news_agent = Agent(
goal="Collect and analyze news articles from various sources.",
backstory="Expert in news gathering and content analysis.",
tools=[get_article, get_news_sources, get_articles_from_source, get_trending_topics],
self_reflect=False
reflection=False
)
@@ -35,7 +35,7 @@ data_agent = Agent(
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organising internet data.",
tools=[internet_search_tool],
self_reflect=False
reflection=False
)
# 3. Tasks
@@ -158,7 +158,7 @@ data_agent = Agent(
goal="Collect and validate data about individuals",
backstory="Expert in gathering and validating demographic data",
tools=[random_data_of_individuals],
self_reflect=False
reflection=False
)
process_agent = Agent(
@@ -167,7 +167,7 @@ process_agent = Agent(
goal="Process and categorize individual data",
backstory="Expert in data analysis and categorization",
tools=[process_data_of_individuals],
self_reflect=False
reflection=False
)
# Modify tasks to use Pydantic models
@@ -125,7 +125,7 @@ email_task = Task(
description="Write a business proposal for a new software project, including contact information",
expected_output="Professional business proposal with contact email addresses",
agent=email_agent,
guardrail=email_format_guardrail,
guardrails=email_format_guardrail,
max_retries=3
)
@@ -135,7 +135,7 @@ content_task = Task(
description="Write an analysis of cloud computing benefits for small businesses",
expected_output="Comprehensive analysis of cloud computing benefits (minimum 50 words)",
agent=content_agent,
guardrail=word_count_guardrail,
guardrails=word_count_guardrail,
max_retries=3
)
@@ -145,7 +145,7 @@ business_task = Task(
description="Provide advice on handling difficult client relationships",
expected_output="Professional advice maintaining appropriate business tone",
agent=business_agent,
guardrail=professional_tone_guardrail,
guardrails=professional_tone_guardrail,
max_retries=3
)
@@ -155,7 +155,7 @@ research_task = Task(
description="Analyze the impact of artificial intelligence on job markets",
expected_output="Research-backed analysis with citations and evidence",
agent=research_agent,
guardrail=factual_accuracy_guardrail,
guardrails=factual_accuracy_guardrail,
max_retries=3
)
@@ -176,7 +176,7 @@ llm_guardrail_task = Task(
agent=llm_guardrail_agent,
# LLM-based guardrail using natural language validation
guardrail="Validate that this marketing content is: 1) Factually accurate with no false claims, 2) Compliant with advertising standards (no misleading statements), 3) Engaging and professional in tone, 4) Includes a clear call-to-action. If any criteria are not met, explain what needs to be improved.",
guardrails="Validate that this marketing content is: 1) Factually accurate with no false claims, 2) Compliant with advertising standards (no misleading statements), 3) Engaging and professional in tone, 4) Includes a clear call-to-action. If any criteria are not met, explain what needs to be improved.",
max_retries=2
)
@@ -38,7 +38,7 @@ production_agent = Agent(
goal="Generate safe, compliant content for production use",
backstory="Production-ready agent with built-in safety and compliance checks",
tools=[internet_search],
guardrail=content_guardrail,
guardrails=content_guardrail,
max_retries=3,
verbose=True
)
@@ -40,7 +40,7 @@ class DynamicAgentSystem:
goal=f"Conduct {inputs['depth']} research on {inputs['topic']} in {inputs['language']}",
backstory=f"Multilingual expert specializing in {inputs['depth']} analysis",
tools=[duckduckgo],
self_reflect=inputs['depth'] == 'detailed',
reflection=inputs['depth'] == 'detailed',
llm=self.llm_config
)
agents.append(research_agent)
@@ -19,7 +19,7 @@ search_agent = Agent(
goal=f"Find {num_results} relevant results about {topic}",
backstory="Expert in internet research and data collection",
tools=[duckduckgo],
self_reflect=False
reflection=False
)
# Create analysis agent
@@ -34,9 +34,7 @@ agent = Agent(
llm=llm_config, # Pass the detailed configuration
verbose=True, # Enable detailed output
markdown=True, # Format responses in markdown
self_reflect=True, # Enable self-reflection
max_reflect=3, # Maximum reflection iterations
min_reflect=1 # Minimum reflection iterations
reflection=True # Enable self-reflection
)
# Test the agent
@@ -4,7 +4,7 @@ from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-1.5-flash-8b",
self_reflect=True,
reflection=True,
verbose=True
)
@@ -3,7 +3,7 @@ from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-1.5-flash-8b",
self_reflect=True,
reflection=True,
verbose=True
)
@@ -87,7 +87,7 @@ def main():
and technology. You provide concise, accurate summaries of important news.""",
tools=[search_tool],
verbose=True,
self_reflect=False # Disable reflection for faster execution
reflection=False # Disable reflection for faster execution
)
# Define the task
@@ -113,7 +113,7 @@ initial_research = Task(
description="Research the current state of renewable energy adoption in the business sector, including trends, challenges, and opportunities",
expected_output="Comprehensive research report on renewable energy adoption in business",
agent=research_agent,
guardrail=data_quality_validator,
guardrails=data_quality_validator,
max_retries=3
)
@@ -124,7 +124,7 @@ quality_check = Task(
expected_output="Quality assessment report with go/no-go decision for proceeding to analysis",
agent=quality_checker,
context=[initial_research],
guardrail=completeness_validator,
guardrails=completeness_validator,
max_retries=2
)
@@ -153,7 +153,7 @@ additional_research = Task(
agent=research_agent,
context=[initial_research, quality_check],
tools=[duckduckgo],
guardrail=data_quality_validator,
guardrails=data_quality_validator,
max_retries=2
)
@@ -190,7 +190,7 @@ final_report = Task(
expected_output="Executive report with strategic recommendations for renewable energy adoption",
agent=report_writer,
context=[initial_research, quality_check, data_analysis, decision_task],
guardrail=completeness_validator,
guardrails=completeness_validator,
max_retries=2
)
+1 -2
View File
@@ -34,8 +34,7 @@ system_ops_agent = Agent(
backstory="""You are an expert system administrator with deep knowledge of Unix/Linux systems.
You excel at running complex system commands, managing processes, and handling system operations.
You always validate commands before execution and ensure they are safe to run.""",
min_reflect=6,
max_reflect=10,
reflection=True,
tools=[run_terminal_command, save_to_file],
llm="gpt-5-nano"
)
+1 -2
View File
@@ -53,8 +53,7 @@ web_scraper_agent = Agent(
role="Web Scraper",
goal="Extract URLs from https://news.ycombinator.com/",
backstory="An expert in data extraction from websites, adept at navigating and retrieving detailed information and saving it to a file. and just check if the file is created using run_terminal_command tool",
min_reflect=3,
max_reflect=5,
reflection=True,
tools=[code_interpret, save_to_file, run_terminal_command],
llm="gpt-5-nano"
)
@@ -28,7 +28,7 @@ hackathon_judge = Agent(
You excel at evaluating innovation, technical implementation, and presentation quality.
You provide constructive feedback and identify both strengths and areas for improvement.""",
llm="gpt-5-nano", # Using vision-capable model
self_reflect=False,
reflection=False,
knowledge=""
)
@@ -39,7 +39,7 @@ def get_hackathon_judge():
You excel at evaluating innovation, technical implementation, and presentation quality.
You provide constructive feedback and identify both strengths and areas for improvement.""",
llm="gpt-5-nano", # Using vision-capable model
self_reflect=False,
reflection=False,
knowledge=""
)
+36 -16
View File
@@ -88,17 +88,22 @@ def basic_citations():
print("BASIC CITATIONS IN RAG")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['source']}, {d['section']}]\n{d['content']}" for d in LEGAL_DOCUMENTS])
agent = Agent(
name="HR Policy Expert",
instructions="""You are an HR policy expert who answers employee questions.
instructions=f"""You are an HR policy expert who answers employee questions.
IMPORTANT: Always cite your sources using this format:
[Source: Document Name, Section]
Include the citation immediately after the relevant information.
If information comes from multiple sources, cite each one.""",
knowledge=LEGAL_DOCUMENTS,
user_id="citation_demo"
If information comes from multiple sources, cite each one.
POLICY DOCUMENTS:
{context}""",
verbose=False
)
queries = [
@@ -121,9 +126,12 @@ def structured_citations():
print("STRUCTURED CITATIONS")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['source']}, {d['section']}]\n{d['content']}" for d in LEGAL_DOCUMENTS])
agent = Agent(
name="Policy Researcher",
instructions="""You provide policy information with structured citations.
instructions=f"""You provide policy information with structured citations.
Format your response as:
@@ -134,9 +142,11 @@ def structured_citations():
1. [Document] - [Section] - [Relevant quote]
2. [Document] - [Section] - [Relevant quote]
Always include at least one source citation.""",
knowledge=LEGAL_DOCUMENTS,
user_id="structured_cite"
Always include at least one source citation.
POLICY DOCUMENTS:
{context}""",
verbose=False
)
query = "What are the requirements for remote work eligibility?"
@@ -153,9 +163,12 @@ def inline_citations():
print("INLINE CITATIONS (Academic Style)")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['source']}, {d['section']}]\n{d['content']}" for d in LEGAL_DOCUMENTS])
agent = Agent(
name="Academic Researcher",
instructions="""You write responses with inline citations like academic papers.
instructions=f"""You write responses with inline citations like academic papers.
Use numbered citations [1], [2], etc. in the text.
List full references at the end.
@@ -166,9 +179,11 @@ def inline_citations():
References:
[1] Employee Handbook v2024, Section 3.2
[2] Employee Handbook v2024, Section 4.1""",
knowledge=LEGAL_DOCUMENTS,
user_id="inline_cite"
[2] Employee Handbook v2024, Section 4.1
POLICY DOCUMENTS:
{context}""",
verbose=False
)
query = "Summarize the key employee benefits."
@@ -235,9 +250,12 @@ def multi_source_synthesis():
print("MULTI-SOURCE SYNTHESIS WITH CITATIONS")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['source']}, {d['section']}]\n{d['content']}" for d in LEGAL_DOCUMENTS])
agent = Agent(
name="Benefits Advisor",
instructions="""You synthesize information from multiple policy documents.
instructions=f"""You synthesize information from multiple policy documents.
When answering:
1. Gather relevant information from all sources
@@ -245,9 +263,11 @@ def multi_source_synthesis():
3. Cite each source for the specific information it provides
4. Note if sources have different or complementary information
Format: Include [Source: X] after each piece of information.""",
knowledge=LEGAL_DOCUMENTS,
user_id="multi_source"
Format: Include [Source: X] after each piece of information.
POLICY DOCUMENTS:
{context}""",
verbose=False
)
query = "Give me a complete overview of employee benefits and policies."
+27 -12
View File
@@ -110,17 +110,22 @@ def web_augmented_rag():
print("WEB-AUGMENTED RAG")
print("=" * 60)
# Build context from company knowledge
context = "\n\n".join([f"[{k['id']}]\n{k['content']}" for k in COMPANY_KNOWLEDGE])
# Create agent that can use web search
agent = Agent(
name="Research Assistant",
instructions="""You are a research assistant that combines internal knowledge
instructions=f"""You are a research assistant that combines internal knowledge
with web search results. When answering:
1. Check if the question requires current/real-time information
2. Use web search for news, weather, current events
3. Combine web results with your knowledge for comprehensive answers
4. Clearly indicate when information comes from web search.""",
knowledge=COMPANY_KNOWLEDGE,
user_id="web_demo"
4. Clearly indicate when information comes from web search.
COMPANY KNOWLEDGE:
{context}""",
verbose=False
)
# Simulate web-augmented queries
@@ -155,13 +160,18 @@ def database_augmented_rag():
print("DATABASE-AUGMENTED RAG")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{k['id']}]\n{k['content']}" for k in COMPANY_KNOWLEDGE])
agent = Agent(
name="Business Analyst",
instructions="""You are a business analyst with access to company databases.
instructions=f"""You are a business analyst with access to company databases.
Interpret data accurately and provide actionable insights.
When presenting numbers, add context and trends.""",
knowledge=COMPANY_KNOWLEDGE,
user_id="db_demo"
When presenting numbers, add context and trends.
COMPANY KNOWLEDGE:
{context}""",
verbose=False
)
queries = [
@@ -228,13 +238,18 @@ def hybrid_source_rag():
print("HYBRID SOURCE RAG")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{k['id']}]\n{k['content']}" for k in COMPANY_KNOWLEDGE])
agent = Agent(
name="Executive Assistant",
instructions="""You are an executive assistant with access to multiple data sources.
instructions=f"""You are an executive assistant with access to multiple data sources.
Synthesize information from static knowledge, databases, and APIs.
Provide comprehensive briefings that combine all relevant data.""",
knowledge=COMPANY_KNOWLEDGE,
user_id="hybrid_demo"
Provide comprehensive briefings that combine all relevant data.
COMPANY KNOWLEDGE:
{context}""",
verbose=False
)
query = "Give me a complete business briefing for today"
+25 -31
View File
@@ -143,19 +143,20 @@ def demonstrate_search_types():
def hybrid_rag_example():
"""Demonstrate hybrid retrieval with an agent."""
# Create agent with hybrid retrieval enabled
# Build context from troubleshooting guides
context = "\n\n".join([f"[{g['id']}]\n{g['content']}" for g in TROUBLESHOOTING_GUIDES])
# Create agent with context in instructions
agent = Agent(
name="Tech Support",
instructions="""You are a technical support specialist.
instructions=f"""You are a technical support specialist.
Help users troubleshoot errors using the knowledge base.
Always mention the specific error code when relevant.
Provide step-by-step resolution guidance.""",
knowledge=TROUBLESHOOTING_GUIDES,
knowledge_config={
"hybrid": True, # Enable hybrid search
"top_k": 3
},
user_id="hybrid_demo"
Provide step-by-step resolution guidance.
TROUBLESHOOTING KNOWLEDGE BASE:
{context}""",
verbose=False
)
# Test with different query types
@@ -191,35 +192,28 @@ def compare_retrieval_modes():
print("RETRIEVAL MODE COMPARISON")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{g['id']}]\n{g['content']}" for g in TROUBLESHOOTING_GUIDES])
query = "database transaction blocking issue"
# Dense-only agent
dense_agent = Agent(
name="Dense Search Agent",
instructions="Answer based on the knowledge provided.",
knowledge=TROUBLESHOOTING_GUIDES,
knowledge_config={"hybrid": False},
user_id="dense_demo"
)
# Hybrid agent
hybrid_agent = Agent(
name="Hybrid Search Agent",
instructions="Answer based on the knowledge provided.",
knowledge=TROUBLESHOOTING_GUIDES,
knowledge_config={"hybrid": True},
user_id="hybrid_demo"
# Agent with context
agent = Agent(
name="Search Agent",
instructions=f"""Answer based on the knowledge provided.
KNOWLEDGE BASE:
{context}""",
verbose=False
)
print(f"\n📝 Query: {query}")
print("\n🔍 Dense-Only Search:")
dense_response = dense_agent.chat(query)
print(f" {dense_response[:200]}...")
print("\n🔍 RAG Response:")
response = agent.chat(query)
print(f" {response[:200]}...")
print("\n🔍 Hybrid Search:")
hybrid_response = hybrid_agent.chat(query)
print(f" {hybrid_response[:200]}...")
print("\n💡 Note: Hybrid search combines semantic + keyword matching for best results.")
def hybrid_search_tuning():
+30 -14
View File
@@ -109,17 +109,22 @@ ENTITY_KNOWLEDGE = [
def graph_aware_rag():
"""Demonstrate relationship-aware retrieval."""
# Build context from entity knowledge
context = "\n\n".join([f"[{e['type'].upper()}: {e['id']}]\n{e['content']}" for e in ENTITY_KNOWLEDGE])
# Create agent with entity knowledge
agent = Agent(
name="Physics Historian",
instructions="""You are a physics historian who understands the relationships
instructions=f"""You are a physics historian who understands the relationships
between scientists, concepts, and institutions. When answering:
1. Trace connections between entities
2. Explain how ideas and people influenced each other
3. Reference specific relationships mentioned in the knowledge base
4. Build a narrative that shows the interconnected nature of physics history.""",
knowledge=ENTITY_KNOWLEDGE,
user_id="graph_demo"
4. Build a narrative that shows the interconnected nature of physics history.
KNOWLEDGE GRAPH:
{context}""",
verbose=False
)
# Relationship-focused queries
@@ -151,13 +156,18 @@ def multi_hop_reasoning():
print("MULTI-HOP REASONING")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{e['type'].upper()}: {e['id']}]\n{e['content']}" for e in ENTITY_KNOWLEDGE])
agent = Agent(
name="Connection Finder",
instructions="""You excel at finding indirect connections between entities.
instructions=f"""You excel at finding indirect connections between entities.
When asked about relationships, trace the path through intermediate entities.
Format your answer to show the chain of connections.""",
knowledge=ENTITY_KNOWLEDGE,
user_id="multihop_demo"
Format your answer to show the chain of connections.
KNOWLEDGE GRAPH:
{context}""",
verbose=False
)
# Multi-hop query requiring traversal
@@ -182,22 +192,28 @@ def entity_type_filtering():
# Filter to only people
people_only = [e for e in ENTITY_KNOWLEDGE if e['type'] == 'person']
people_context = "\n\n".join([f"[{e['id']}]\n{e['content']}" for e in people_only])
people_agent = Agent(
name="Biographer",
instructions="You are a biographer focused on scientists' lives and relationships.",
knowledge=people_only,
user_id="people_demo"
instructions=f"""You are a biographer focused on scientists' lives and relationships.
SCIENTISTS:
{people_context}""",
verbose=False
)
# Filter to only concepts
concepts_only = [e for e in ENTITY_KNOWLEDGE if e['type'] == 'concept']
concepts_context = "\n\n".join([f"[{e['id']}]\n{e['content']}" for e in concepts_only])
concept_agent = Agent(
name="Concept Explainer",
instructions="You explain scientific concepts and their relationships.",
knowledge=concepts_only,
user_id="concept_demo"
instructions=f"""You explain scientific concepts and their relationships.
CONCEPTS:
{concepts_context}""",
verbose=False
)
query = "Tell me about the major developments in early 20th century physics."
+28 -14
View File
@@ -99,15 +99,20 @@ ALL_DOCUMENTS = [CLIMATE_REPORT, ENERGY_REPORT, POLICY_REPORT, TECH_REPORT]
def multi_document_rag():
"""Demonstrate RAG across multiple documents."""
# Build context from all documents
context = "\n\n".join([f"[{d['source']}]\n{d['content']}" for d in ALL_DOCUMENTS])
# Create agent with multi-document knowledge
agent = Agent(
name="Climate Analyst",
instructions="""You are a climate and energy analyst.
instructions=f"""You are a climate and energy analyst.
Synthesize information from multiple reports to answer questions.
When information comes from different sources, mention which reports
you're drawing from. Provide comprehensive, well-rounded answers.""",
knowledge=ALL_DOCUMENTS,
user_id="multi_doc_demo"
you're drawing from. Provide comprehensive, well-rounded answers.
REPORTS:
{context}""",
verbose=False
)
# Questions that require multi-document synthesis
@@ -138,16 +143,21 @@ def cross_reference_example():
print("CROSS-REFERENCING ACROSS DOCUMENTS")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['source']}]\n{d['content']}" for d in ALL_DOCUMENTS])
agent = Agent(
name="Cross-Reference Analyst",
instructions="""You are an analyst who excels at finding connections
instructions=f"""You are an analyst who excels at finding connections
between different reports. When answering:
1. Identify relevant information from each source
2. Note any contradictions or complementary data
3. Synthesize a coherent narrative
4. Cite your sources explicitly.""",
knowledge=ALL_DOCUMENTS,
user_id="cross_ref_demo"
4. Cite your sources explicitly.
REPORTS:
{context}""",
verbose=False
)
query = """
@@ -171,16 +181,20 @@ def document_filtering():
# Create agents focused on specific document types
policy_agent = Agent(
name="Policy Expert",
instructions="You are a policy expert. Focus only on policy-related information.",
knowledge=[POLICY_REPORT],
user_id="policy_expert"
instructions=f"""You are a policy expert. Focus only on policy-related information.
POLICY REPORT:
{POLICY_REPORT['content']}""",
verbose=False
)
tech_agent = Agent(
name="Technology Expert",
instructions="You are a technology expert. Focus on technical innovations.",
knowledge=[TECH_REPORT],
user_id="tech_expert"
instructions=f"""You are a technology expert. Focus on technical innovations.
TECHNOLOGY REPORT:
{TECH_REPORT['content']}""",
verbose=False
)
query = "What progress has been made in addressing climate change?"
+66 -88
View File
@@ -1,104 +1,92 @@
"""
RAG Evaluation Example
This example demonstrates how to evaluate RAG retrieval quality
using golden queries and expected answers.
This example demonstrates how to evaluate RAG quality by testing
agent responses against expected answers.
Usage:
python rag_evaluation.py
Requirements:
pip install praisonaiagents[knowledge]
"""
import json
from pathlib import Path
from praisonaiagents import Knowledge
from praisonaiagents.rag import RAG, RAGConfig
from praisonaiagents import Agent
# Sample knowledge for evaluation
SAMPLE_KNOWLEDGE = """
Product Overview:
CloudManager Pro is an enterprise cloud management platform.
Its main purpose is to help organizations monitor, optimize, and secure their multi-cloud infrastructure.
Key Features:
- Real-time monitoring across AWS, Azure, and GCP
- Cost optimization with automated recommendations
- Security compliance checking
- Automated scaling and resource management
Getting Started:
1. Sign up at cloudmanager.example.com
2. Connect your cloud accounts
3. Configure monitoring dashboards
4. Set up alerts and policies
"""
def create_test_queries():
"""Create sample test queries for evaluation."""
return [
{
"query": "What is the main purpose of the system?",
"expected_doc": "overview.pdf",
"expected_answer_contains": "purpose",
"query": "What is the main purpose of CloudManager Pro?",
"expected_contains": ["monitor", "cloud", "infrastructure"],
},
{
"query": "What are the key features?",
"expected_doc": "features.pdf",
"expected_answer_contains": "feature",
"expected_contains": ["monitoring", "cost", "security"],
},
{
"query": "How do I get started?",
"expected_doc": "quickstart.pdf",
"expected_answer_contains": "start",
"expected_contains": ["sign up", "connect", "configure"],
},
]
def evaluate_rag(rag: RAG, test_queries: list) -> dict:
def evaluate_rag_agent(agent: Agent, test_queries: list) -> dict:
"""
Evaluate RAG performance on test queries.
Evaluate RAG agent performance on test queries.
Returns metrics including:
- Document retrieval accuracy
- Answer relevance
- Average retrieval score
Returns metrics including answer relevance accuracy.
"""
results = {
"total": len(test_queries),
"doc_hits": 0,
"answer_hits": 0,
"avg_score": 0.0,
"passed": 0,
"details": [],
}
total_score = 0.0
for test in test_queries:
query = test["query"]
expected_doc = test.get("expected_doc", "")
expected_contains = test.get("expected_answer_contains", "")
expected_terms = test.get("expected_contains", [])
# Run RAG query
result = rag.query(query)
# Get agent response
response = agent.chat(query)
response_lower = response.lower()
# Check if expected document is in citations
doc_found = any(
expected_doc in c.source for c in result.citations
) if expected_doc else True
# Check if response contains expected terms
terms_found = sum(1 for term in expected_terms if term.lower() in response_lower)
pass_rate = terms_found / len(expected_terms) if expected_terms else 1.0
passed = pass_rate >= 0.5 # At least 50% of terms found
# Check if answer contains expected text
answer_ok = (
expected_contains.lower() in result.answer.lower()
) if expected_contains else True
# Get average citation score
avg_citation_score = (
sum(c.score for c in result.citations) / len(result.citations)
if result.citations else 0.0
)
if doc_found:
results["doc_hits"] += 1
if answer_ok:
results["answer_hits"] += 1
total_score += avg_citation_score
if passed:
results["passed"] += 1
results["details"].append({
"query": query,
"doc_found": doc_found,
"answer_ok": answer_ok,
"citation_score": avg_citation_score,
"answer_preview": result.answer[:200],
"passed": passed,
"terms_found": terms_found,
"total_terms": len(expected_terms),
"pass_rate": pass_rate,
"answer_preview": response[:150],
})
results["avg_score"] = total_score / len(test_queries) if test_queries else 0.0
results["doc_accuracy"] = results["doc_hits"] / results["total"]
results["answer_accuracy"] = results["answer_hits"] / results["total"]
results["accuracy"] = results["passed"] / results["total"]
return results
@@ -109,20 +97,18 @@ def print_results(results: dict):
print("=" * 60)
print(f"\nTotal Queries: {results['total']}")
print(f"Document Retrieval Accuracy: {results['doc_accuracy']:.1%}")
print(f"Answer Relevance Accuracy: {results['answer_accuracy']:.1%}")
print(f"Average Citation Score: {results['avg_score']:.3f}")
print(f"Passed: {results['passed']}/{results['total']}")
print(f"Accuracy: {results['accuracy']:.1%}")
print("\n" + "-" * 60)
print("Detailed Results:")
print("-" * 60)
for i, detail in enumerate(results["details"], 1):
status = "PASS" if detail["doc_found"] and detail["answer_ok"] else "FAIL"
status = "✅ PASS" if detail["passed"] else "❌ FAIL"
print(f"\n[{i}] {status}: {detail['query']}")
print(f" Doc Found: {detail['doc_found']}")
print(f" Answer OK: {detail['answer_ok']}")
print(f" Score: {detail['citation_score']:.3f}")
print(f" Terms Found: {detail['terms_found']}/{detail['total_terms']}")
print(f" Answer: {detail['answer_preview']}...")
def main():
@@ -130,41 +116,33 @@ def main():
print("RAG Evaluation Example")
print("=" * 60)
# Create knowledge base
knowledge = Knowledge()
knowledge.add("documents/") # Add your documents
# Configure RAG
config = RAGConfig(
top_k=5,
min_score=0.3,
include_citations=True,
# Create agent with knowledge
agent = Agent(
name="Product Expert",
instructions=f"""You are a product expert who answers questions accurately.
Use only the following knowledge to answer questions.
KNOWLEDGE:
{SAMPLE_KNOWLEDGE}""",
verbose=False
)
rag = RAG(knowledge=knowledge, config=config)
# Get test queries
test_queries = create_test_queries()
# Run evaluation
print(f"\nEvaluating {len(test_queries)} queries...")
results = evaluate_rag(rag, test_queries)
results = evaluate_rag_agent(agent, test_queries)
# Print results
print_results(results)
# Save results to file
output_path = Path("evaluation_results.json")
with open(output_path, "w") as f:
json.dump(results, f, indent=2)
print(f"\nResults saved to {output_path}")
# Return exit code based on results
if results["doc_accuracy"] < 0.8 or results["answer_accuracy"] < 0.8:
print("\nWARNING: Evaluation metrics below threshold!")
# Return status
if results["accuracy"] < 0.8:
print("\n⚠️ WARNING: Accuracy below 80% threshold!")
return 1
print("\nEvaluation PASSED!")
print("\n✅ Evaluation PASSED!")
return 0
+70 -30
View File
@@ -1,86 +1,124 @@
"""
RAG Multi-Agent Example
This example demonstrates how multiple agents can share
the same knowledge base for collaborative RAG.
This example demonstrates how multiple agents can work together
on a shared knowledge base for collaborative RAG.
Usage:
python rag_multi_agent.py
Requirements:
pip install praisonaiagents[knowledge]
"""
from praisonaiagents import Agent, Knowledge, PraisonAIAgents, Task
from praisonaiagents import Agent, PraisonAIAgents, Task
# Shared knowledge base content
SHARED_KNOWLEDGE = """
# Climate Technology Report 2024
## Executive Summary
Global investment in climate technology reached $150 billion in 2024,
a 25% increase from the previous year. Key areas of growth include
renewable energy, carbon capture, and sustainable transportation.
## Key Findings
### Renewable Energy
- Solar capacity grew by 40% globally
- Wind energy now provides 15% of global electricity
- Battery storage costs decreased by 20%
### Carbon Capture
- Direct air capture costs fell to $400/ton
- 50 new carbon capture facilities announced
- Total captured CO2 reached 50 million tons
### Sustainable Transportation
- Electric vehicle sales reached 25 million units
- Hydrogen fuel cell technology advancing rapidly
- Sustainable aviation fuel production doubled
## Recommendations
1. Increase investment in grid infrastructure
2. Accelerate carbon capture deployment
3. Support EV charging network expansion
"""
def main():
print("=" * 60)
print("Multi-Agent RAG: Shared Knowledge Base")
print("Multi-Agent RAG: Collaborative Analysis")
print("=" * 60)
# Create shared knowledge base
shared_knowledge = Knowledge()
shared_knowledge.add("documents/") # Add your documents directory
# Create specialized agents sharing the same knowledge
# Create specialized agents with shared knowledge
researcher = Agent(
name="Researcher",
role="Research Analyst",
goal="Find and analyze relevant information from documents",
instructions="""You are a research analyst. Your job is to:
1. Search the knowledge base for relevant information
2. Extract key facts and data points
3. Provide detailed analysis with citations""",
knowledge=shared_knowledge,
goal="Find and analyze relevant information",
instructions=f"""You are a research analyst. Your job is to:
1. Extract key facts and data points
2. Provide detailed analysis
3. Cite specific sections
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
)
summarizer = Agent(
name="Summarizer",
role="Content Summarizer",
goal="Create concise summaries of complex information",
instructions="""You are a content summarizer. Your job is to:
goal="Create concise summaries",
instructions=f"""You are a content summarizer. Your job is to:
1. Take research findings and create clear summaries
2. Highlight the most important points
3. Make complex information accessible""",
knowledge=shared_knowledge,
3. Make complex information accessible
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
)
writer = Agent(
name="Writer",
role="Report Writer",
goal="Create well-structured reports",
instructions="""You are a report writer. Your job is to:
instructions=f"""You are a report writer. Your job is to:
1. Combine research and summaries into a cohesive report
2. Ensure proper structure and flow
3. Include citations where appropriate""",
knowledge=shared_knowledge,
3. Include key statistics
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
)
# Define tasks
research_task = Task(
description="Research the main themes and findings in the documents",
expected_output="Detailed research notes with citations",
description="Research the main investment trends and growth areas in climate technology",
expected_output="Detailed research notes with key statistics",
agent=researcher,
)
summary_task = Task(
description="Summarize the research findings into key points",
description="Summarize the research findings into 5 key bullet points",
expected_output="Bullet-point summary of main findings",
agent=summarizer,
)
report_task = Task(
description="Write a comprehensive report based on the research and summary",
expected_output="A well-structured report with introduction, findings, and conclusion",
description="Write a brief executive briefing based on the research and summary",
expected_output="A 2-paragraph executive briefing",
agent=writer,
)
# Run the multi-agent workflow
print("\n🔄 Running multi-agent workflow...")
agents = PraisonAIAgents(
agents=[researcher, summarizer, writer],
tasks=[research_task, summary_task, report_task],
process="sequential",
verbose=False
)
result = agents.start()
@@ -89,6 +127,8 @@ def main():
print("Final Report")
print("=" * 60)
print(result)
print("\n✅ Multi-agent RAG example completed!")
if __name__ == "__main__":
+97 -42
View File
@@ -1,73 +1,128 @@
"""
RAG PDF Q&A Example
RAG Document Q&A Example
This example demonstrates how to use RAG to answer questions
about PDF documents with citations.
about documents with proper context injection.
Usage:
python rag_pdf_qa.py
Requirements:
pip install praisonaiagents[knowledge]
"""
from praisonaiagents import Agent, Knowledge
from praisonaiagents.rag import RAG, RAGConfig
from praisonaiagents import Agent
# Sample document content (simulating PDF content)
DOCUMENT_CONTENT = """
# Research Study: Impact of Remote Work on Productivity
## Abstract
This study examines the effects of remote work arrangements on employee
productivity across 50 technology companies during 2023-2024.
## Methodology
- Survey of 5,000 employees across 50 companies
- Productivity metrics tracked over 12 months
- Control group: in-office workers
- Test group: remote and hybrid workers
## Key Findings
### Productivity Impact
- Remote workers showed 13% higher productivity on average
- Hybrid workers (3 days remote) showed 18% higher productivity
- Fully remote workers reported 25% better work-life balance
### Challenges Identified
- Communication delays increased by 15%
- Collaboration on complex projects took 10% longer
- New employee onboarding was 20% slower remotely
### Cost Analysis
- Companies saved average of $11,000 per remote employee annually
- Employees saved average of $4,500 in commuting costs
- Office space reduction saved 30% on real estate costs
## Recommendations
1. Implement hybrid work model (2-3 days remote)
2. Invest in collaboration tools
3. Create structured onboarding for remote hires
4. Establish clear communication protocols
## Conclusion
Remote work, when properly implemented, leads to improved productivity
and significant cost savings for both employers and employees.
"""
def main():
# Method 1: Simple Agent with Knowledge
# Method 1: Simple Agent with Context
print("=" * 60)
print("Method 1: Agent with Knowledge Parameter")
print("Method 1: Agent with Document Context")
print("=" * 60)
agent = Agent(
name="PDF Assistant",
instructions="You are a helpful assistant that answers questions about documents.",
knowledge=["sample.pdf"], # Add your PDF files here
name="Document Assistant",
instructions=f"""You are a helpful assistant that answers questions about documents.
Always cite specific sections when answering.
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
)
response = agent.start("What are the main topics covered in this document?")
print(f"\nAnswer: {response}\n")
query = "What are the main topics covered in this document?"
response = agent.chat(query)
print(f"\n📝 Query: {query}")
print(f"💡 Answer: {response}\n")
# Method 2: Explicit RAG Pipeline with Citations
# Method 2: Q&A with Citations
print("=" * 60)
print("Method 2: Explicit RAG Pipeline with Citations")
print("Method 2: Q&A with Citations")
print("=" * 60)
# Create knowledge base
knowledge = Knowledge()
knowledge.add("sample.pdf") # Add your PDF
# Configure RAG
config = RAGConfig(
top_k=5,
min_score=0.3,
include_citations=True,
max_context_tokens=4000,
citation_agent = Agent(
name="Citation Assistant",
instructions=f"""You answer questions about research documents.
Always include citations in format [Section: Name].
Be precise and reference specific data points.
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
)
# Create RAG pipeline
rag = RAG(knowledge=knowledge, config=config)
queries = [
"What methodology was used in this research?",
"What were the key findings about productivity?",
"What are the recommendations?"
]
# Query with citations
result = rag.query("What methodology was used in this research?")
for query in queries:
print(f"\n📝 Query: {query}")
response = citation_agent.chat(query)
print(f"💡 Answer: {response[:300]}...")
print(f"\nAnswer: {result.answer}")
print(f"\nSources ({len(result.citations)}):")
for citation in result.citations:
print(f" [{citation.id}] {citation.source} (score: {citation.score:.2f})")
print(f" {citation.text[:100]}...")
# Method 3: Streaming Response
# Method 3: Comparative Analysis
print("\n" + "=" * 60)
print("Method 3: Streaming Response")
print("Method 3: Analytical Questions")
print("=" * 60)
print("\nStreaming answer: ", end="")
for chunk in rag.stream("Summarize the key findings"):
print(chunk, end="", flush=True)
print("\n")
analyst = Agent(
name="Research Analyst",
instructions=f"""You are a research analyst who provides insights from documents.
Analyze data, identify trends, and provide actionable insights.
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
)
analysis_query = "Compare the benefits vs challenges of remote work based on this study"
print(f"\n📝 Query: {analysis_query}")
response = analyst.chat(analysis_query)
print(f"💡 Analysis:\n{response}")
print("\n✅ Document Q&A example completed!")
if __name__ == "__main__":
+25 -31
View File
@@ -118,19 +118,20 @@ def explain_reranking():
def rag_with_reranking():
"""Demonstrate RAG with reranking enabled."""
# Create agent with reranking
# Build context from research papers
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in RESEARCH_PAPERS])
# Create agent with context
agent = Agent(
name="Research Assistant",
instructions="""You are a research paper expert.
instructions=f"""You are a research paper expert.
Answer questions about machine learning research.
Reference specific papers when relevant.
Be precise about technical details.""",
knowledge=RESEARCH_PAPERS,
knowledge_config={
"rerank": True,
"top_k": 5
},
user_id="rerank_demo"
Be precise about technical details.
RESEARCH PAPERS:
{context}""",
verbose=False
)
queries = [
@@ -157,35 +158,28 @@ def compare_with_without_reranking():
print("RERANKING IMPACT COMPARISON")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in RESEARCH_PAPERS])
query = "How do transformers handle long-range dependencies?"
# Without reranking
agent_no_rerank = Agent(
name="No Rerank Agent",
instructions="Answer based on the research papers provided.",
knowledge=RESEARCH_PAPERS,
knowledge_config={"rerank": False, "top_k": 3},
user_id="no_rerank"
)
# With reranking
agent_with_rerank = Agent(
name="Rerank Agent",
instructions="Answer based on the research papers provided.",
knowledge=RESEARCH_PAPERS,
knowledge_config={"rerank": True, "top_k": 3},
user_id="with_rerank"
# Agent with context
agent = Agent(
name="Research Agent",
instructions=f"""Answer based on the research papers provided.
RESEARCH PAPERS:
{context}""",
verbose=False
)
print(f"\n📝 Query: {query}")
print("\n🔍 Without Reranking:")
response1 = agent_no_rerank.chat(query)
print(f" {response1[:250]}...")
print("\n🔍 RAG Response:")
response = agent.chat(query)
print(f" {response[:250]}...")
print("\n🔍 With Reranking:")
response2 = agent_with_rerank.chat(query)
print(f" {response2[:250]}...")
print("\n💡 Note: Reranking uses cross-encoders to improve precision after initial retrieval.")
def reranking_configuration():
+29 -15
View File
@@ -10,6 +10,7 @@ optimize both quality and efficiency.
"""
from praisonaiagents import Agent, AutoRagAgent, AutoRagConfig
from praisonaiagents.agents.auto_rag_agent import RetrievalPolicy
# Sample knowledge base: IT support documentation
IT_SUPPORT_DOCS = [
@@ -124,31 +125,36 @@ def policy_based_retrieval():
print("RETRIEVAL POLICY COMPARISON")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{d['id']}]\n{d['content']}" for d in IT_SUPPORT_DOCS])
base_agent = Agent(
name="IT Support",
instructions="You are an IT support agent. Help users with technical issues.",
knowledge=IT_SUPPORT_DOCS,
user_id="policy_demo"
instructions=f"""You are an IT support agent. Help users with technical issues.
IT SUPPORT DOCS:
{context}""",
verbose=False
)
query = "How do I set up VPN?"
policies = [
("auto", "Agent decides based on query analysis"),
("always", "Always retrieve, even for simple queries"),
("never", "Never retrieve, use only parametric knowledge"),
(RetrievalPolicy.AUTO, "Agent decides based on query analysis"),
(RetrievalPolicy.ALWAYS, "Always retrieve, even for simple queries"),
(RetrievalPolicy.NEVER, "Never retrieve, use only parametric knowledge"),
]
print(f"\n📝 Query: {query}\n")
for policy, description in policies:
config = AutoRagConfig(policy=policy)
config = AutoRagConfig(retrieval_policy=policy)
auto_agent = AutoRagAgent(agent=base_agent, config=config)
print(f"🔄 Policy: {policy.upper()}")
print(f"🔄 Policy: {policy.value.upper()}")
print(f" Description: {description}")
response = auto_agent.chat(query, user_id="policy_demo")
response = auto_agent.chat(query)
print(f" Response: {response[:150]}...")
print()
@@ -200,20 +206,28 @@ def priority_based_retrieval():
for doc in IT_SUPPORT_DOCS:
print(f" [{doc['priority'].upper():^6}] {doc['id']}")
# Build contexts
high_context = "\n\n".join([f"[{d['id']}]\n{d['content']}" for d in high_priority])
full_context = "\n\n".join([f"[{d['id']}]\n{d['content']}" for d in all_docs])
# High-priority agent (for urgent issues)
urgent_agent = Agent(
name="Urgent Support",
instructions="Handle urgent IT issues. Focus on critical procedures.",
knowledge=high_priority,
user_id="urgent_demo"
instructions=f"""Handle urgent IT issues. Focus on critical procedures.
HIGH PRIORITY DOCS:
{high_context}""",
verbose=False
)
# Full knowledge agent
full_agent = Agent(
name="Full Support",
instructions="Handle all IT support requests.",
knowledge=all_docs,
user_id="full_demo"
instructions=f"""Handle all IT support requests.
IT SUPPORT DOCS:
{full_context}""",
verbose=False
)
query = "I'm locked out of my account!"
+43 -18
View File
@@ -99,13 +99,18 @@ def json_output_rag():
print("JSON-STRUCTURED RAG OUTPUT")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in PRODUCT_CATALOG])
agent = Agent(
name="Product Assistant",
instructions="""You are a product assistant that provides structured information.
instructions=f"""You are a product assistant that provides structured information.
Always respond with valid JSON matching the requested format.
Extract accurate information from the product catalog.""",
knowledge=PRODUCT_CATALOG,
user_id="json_demo"
Extract accurate information from the product catalog.
PRODUCT CATALOG:
{context}""",
verbose=False
)
# Request structured product info
@@ -136,15 +141,20 @@ def pydantic_guided_rag():
# Generate schema from Pydantic model
schema = ProductComparison.model_json_schema()
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in PRODUCT_CATALOG])
agent = Agent(
name="Product Comparator",
instructions=f"""You compare products and provide structured recommendations.
Your response MUST be valid JSON matching this schema:
{schema}
Be objective and base recommendations on the product data.""",
knowledge=PRODUCT_CATALOG,
user_id="pydantic_demo"
Be objective and base recommendations on the product data.
PRODUCT CATALOG:
{context}""",
verbose=False
)
query = "Compare the UltraWidget Pro and SmartHome Hub. Which should I buy?"
@@ -163,13 +173,18 @@ def table_format_rag():
print("TABLE-FORMATTED RAG")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in PRODUCT_CATALOG])
agent = Agent(
name="Catalog Browser",
instructions="""You present product information in clean table format.
instructions=f"""You present product information in clean table format.
Use markdown tables for structured data.
Include relevant columns based on the query.""",
knowledge=PRODUCT_CATALOG,
user_id="table_demo"
Include relevant columns based on the query.
PRODUCT CATALOG:
{context}""",
verbose=False
)
query = "Show me all products in a table with name, price, and rating."
@@ -186,13 +201,18 @@ def list_format_rag():
print("LIST-FORMATTED RAG")
print("=" * 60)
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in PRODUCT_CATALOG])
agent = Agent(
name="Feature Lister",
instructions="""You extract and present information as organized lists.
instructions=f"""You extract and present information as organized lists.
Use bullet points and numbered lists appropriately.
Group related items together.""",
knowledge=PRODUCT_CATALOG,
user_id="list_demo"
Group related items together.
PRODUCT CATALOG:
{context}""",
verbose=False
)
queries = [
@@ -291,12 +311,17 @@ def multi_format_rag():
print(f"\n📝 Base Query: {query}")
print("\n🔄 Same query, different output formats:\n")
# Build context
context = "\n\n".join([f"[{p['id']}]\n{p['content']}" for p in base_knowledge])
for format_name, format_instruction in formats:
agent = Agent(
name=f"{format_name} Agent",
instructions=f"You are a product expert. {format_instruction}",
knowledge=base_knowledge,
user_id=f"format_{format_name.lower()}"
instructions=f"""You are a product expert. {format_instruction}
PRODUCT CATALOG:
{context}""",
verbose=False
)
response = agent.chat(query)
+105
View File
@@ -0,0 +1,105 @@
"""
Basic Agent Retrieval Example
Demonstrates the Agent-first retrieval API with knowledge and retrieval_config.
"""
import os
from praisonaiagents import Agent
# Ensure API key is set
if not os.environ.get("OPENAI_API_KEY"):
print("Please set OPENAI_API_KEY environment variable")
exit(1)
# Create a simple text file for testing
test_content = """
# PraisonAI Framework Overview
PraisonAI is an AI agents framework that enables building sophisticated AI applications.
## Key Features
1. **Agent-First Design**: Everything centers around the Agent class
2. **Knowledge Integration**: Agents can use knowledge bases for retrieval
3. **Unified Configuration**: Single retrieval_config for all retrieval settings
4. **Multi-Agent Support**: Multiple agents can share knowledge
## Retrieval Policies
- **auto**: Automatically decide when to retrieve based on query
- **always**: Always retrieve for every query
- **never**: Never retrieve (use for pure generation)
## Citation Modes
- **append**: Add citations at the end of the response
- **inline**: Include citations inline in the text
- **hidden**: Citations available in result but not in text
"""
# Write test content to a file
os.makedirs(".praison/test_docs", exist_ok=True)
with open(".praison/test_docs/overview.txt", "w") as f:
f.write(test_content)
print("=" * 60)
print("Basic Agent Retrieval Example")
print("=" * 60)
# Create agent with knowledge and retrieval config
agent = Agent(
name="Knowledge Agent",
instructions="You are a helpful assistant that answers questions based on the provided knowledge. Be concise.",
knowledge=[".praison/test_docs/overview.txt"],
retrieval_config={
"policy": "always", # Always retrieve for this demo
"top_k": 3, # Get top 3 chunks
"citations": True, # Include citations
"max_context_tokens": 2000,
}
)
# Test 1: Basic chat with retrieval
print("\n1. Basic Chat with Retrieval:")
print("-" * 40)
response = agent.chat("What are the key features of PraisonAI?")
print(response)
# Test 2: Query with structured result
print("\n2. Structured Query with Citations:")
print("-" * 40)
try:
result = agent.query("What are the retrieval policies?")
print(f"Answer: {result.answer}")
if result.citations:
print(f"\nCitations ({len(result.citations)}):")
for citation in result.citations:
print(f" - {citation.source}")
except Exception as e:
print(f"Query not available (RAG module may not be installed): {e}")
# Test 3: Retrieve only (no LLM generation)
print("\n3. Retrieve Only (No LLM):")
print("-" * 40)
try:
context = agent.retrieve("citation modes")
print(f"Found context: {len(context.context)} chars")
if context.citations:
print(f"Sources: {len(context.citations)}")
except Exception as e:
print(f"Retrieve not available (RAG module may not be installed): {e}")
# Test 4: Skip retrieval
print("\n4. Chat with skip_retrieval=True:")
print("-" * 40)
response = agent.chat("What is 2 + 2?", skip_retrieval=True)
print(response)
print("\n" + "=" * 60)
print("Example Complete!")
print("=" * 60)
# Cleanup
import shutil
shutil.rmtree(".praison/test_docs", ignore_errors=True)
+35 -53
View File
@@ -2,31 +2,29 @@
"""
Basic Thinking Budgets Example for PraisonAI Agents.
This example demonstrates how to use thinking budgets to:
1. Configure token budgets for extended thinking
This example demonstrates how to use thinking budgets with Agent:
1. Create an Agent with thinking budget
2. Use predefined budget levels
3. Track thinking usage
4. Adapt budgets based on complexity
Usage:
python basic_thinking.py
"""
from praisonaiagents.thinking import (
ThinkingBudget, ThinkingConfig, ThinkingUsage, ThinkingTracker
)
from praisonaiagents import Agent
from praisonaiagents.thinking import ThinkingBudget, ThinkingTracker
from praisonaiagents.thinking.budget import BudgetLevel
def main():
print("=" * 60)
print("Thinking Budgets Demo")
print("Agent-Centric Thinking Budgets Demo")
print("=" * 60)
# ==========================================================================
# Budget Levels
# ==========================================================================
print("\n--- Budget Levels ---")
print("\n--- Available Budget Levels ---")
levels = [
("Minimal", ThinkingBudget.minimal()),
@@ -39,10 +37,25 @@ def main():
for name, budget in levels:
print(f" {name}: {budget.max_tokens:,} tokens")
# ==========================================================================
# Agent with Thinking Budget
# ==========================================================================
print("\n--- Creating Agent with Thinking Budget ---")
agent = Agent(
name="DeepThinker",
instructions="You are a problem-solving assistant that thinks step by step.",
thinking_budget=ThinkingBudget.high() # 16,000 tokens for reasoning
)
print(f"Agent: {agent.name}")
print(f"Thinking budget: {agent.thinking_budget.max_tokens:,} tokens")
print(f"Adaptive: {agent.thinking_budget.adaptive}")
# ==========================================================================
# Custom Budget
# ==========================================================================
print("\n--- Custom Budget ---")
print("\n--- Custom Thinking Budget ---")
custom_budget = ThinkingBudget(
max_tokens=12000,
@@ -50,22 +63,24 @@ def main():
adaptive=True,
max_time_seconds=120.0
)
print(f" Max tokens: {custom_budget.max_tokens:,}")
print(f" Min tokens: {custom_budget.min_tokens:,}")
print(f" Adaptive: {custom_budget.adaptive}")
print(f" Time limit: {custom_budget.max_time_seconds}s")
agent_custom = Agent(
name="CustomThinker",
instructions="You solve complex problems.",
thinking_budget=custom_budget
)
print(f"Agent: {agent_custom.name}")
print(f"Max tokens: {agent_custom.thinking_budget.max_tokens:,}")
print(f"Min tokens: {agent_custom.thinking_budget.min_tokens:,}")
print(f"Time limit: {agent_custom.thinking_budget.max_time_seconds}s")
# ==========================================================================
# Complexity-Based Scaling
# ==========================================================================
print("\n--- Complexity-Based Token Allocation ---")
budget = ThinkingBudget(
max_tokens=16000,
min_tokens=2000,
adaptive=True
)
budget = agent.thinking_budget
complexities = [0.0, 0.25, 0.5, 0.75, 1.0]
for complexity in complexities:
tokens = budget.get_tokens_for_complexity(complexity)
@@ -75,34 +90,15 @@ def main():
# ==========================================================================
# Usage Tracking
# ==========================================================================
print("\n--- Usage Tracking ---")
usage = ThinkingUsage(
budget_tokens=8000,
tokens_used=5000,
time_seconds=30.0,
budget_time=60.0
)
print(f" Tokens used: {usage.tokens_used:,} / {usage.budget_tokens:,}")
print(f" Tokens remaining: {usage.tokens_remaining:,}")
print(f" Utilization: {usage.token_utilization:.1%}")
print(f" Over budget: {usage.is_over_budget}")
print(f" Time remaining: {usage.time_remaining}s")
# ==========================================================================
# Session Tracking
# ==========================================================================
print("\n--- Session Tracking ---")
tracker = ThinkingTracker()
# Simulate multiple thinking sessions
# Simulate thinking sessions
sessions = [
(8000, 4000, 20.0),
(8000, 6000, 35.0),
(16000, 12000, 60.0),
(8000, 9000, 45.0), # Over budget
]
for budget_tokens, tokens_used, time_seconds in sessions:
@@ -112,21 +108,7 @@ def main():
summary = tracker.get_summary()
print(f" Total sessions: {summary['session_count']}")
print(f" Total tokens used: {summary['total_tokens_used']:,}")
print(f" Average tokens/session: {summary['average_tokens_per_session']:,.0f}")
print(f" Average utilization: {summary['average_utilization']:.1%}")
print(f" Over-budget sessions: {summary['over_budget_count']}")
# ==========================================================================
# Serialization
# ==========================================================================
print("\n--- Serialization ---")
budget = ThinkingBudget.from_level(BudgetLevel.HIGH)
data = budget.to_dict()
print(f" Serialized: level={data['level']}, max_tokens={data['max_tokens']}")
restored = ThinkingBudget.from_dict(data)
print(f" Restored: {restored.max_tokens:,} tokens")
print("\n" + "=" * 60)
print("Demo Complete!")
-136
View File
@@ -1,136 +0,0 @@
#!/usr/bin/env python3
"""
Auto-generate CLI reference documentation from --help output.
Usage:
python scripts/generate_cli_docs.py > docs/cli/reference.mdx
python scripts/generate_cli_docs.py --output docs/cli/reference.mdx
This script runs `praisonai <cmd> --help` for key commands and generates
deterministic markdown documentation.
"""
import subprocess
import sys
import argparse
from datetime import datetime
from pathlib import Path
# Commands to document
COMMANDS = [
# Top-level
("praisonai", "--help"),
# Knowledge
("praisonai knowledge", "--help"),
("praisonai knowledge index", "--help"),
("praisonai knowledge search", "--help"),
("praisonai knowledge add", "--help"),
("praisonai knowledge list", "--help"),
# RAG
("praisonai rag", "--help"),
("praisonai rag index", "--help"),
("praisonai rag query", "--help"),
("praisonai rag chat", "--help"),
("praisonai rag eval", "--help"),
("praisonai rag serve", "--help"),
# Run
("praisonai run", "--help"),
# Chat
("praisonai chat", "--help"),
]
def run_help(cmd: str, flag: str = "--help") -> str:
"""Run a command with --help and capture output."""
full_cmd = f"{cmd} {flag}"
try:
result = subprocess.run(
full_cmd.split(),
capture_output=True,
text=True,
timeout=30,
)
return result.stdout or result.stderr or "(no output)"
except subprocess.TimeoutExpired:
return "(timeout)"
except FileNotFoundError:
return "(command not found)"
except Exception as e:
return f"(error: {e})"
def generate_markdown() -> str:
"""Generate markdown documentation."""
lines = [
"---",
"title: CLI Reference",
"description: Auto-generated CLI command reference",
"---",
"",
"# PraisonAI CLI Reference",
"",
"This page is auto-generated from `--help` output.",
"",
f"Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC",
"",
]
for cmd, flag in COMMANDS:
# Section header
cmd_name = cmd.replace("praisonai ", "").replace(" ", "-") or "main"
lines.append(f"## `{cmd}`")
lines.append("")
lines.append("```")
# Get help output
output = run_help(cmd, flag)
lines.append(output.strip())
lines.append("```")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Generate CLI docs from --help")
parser.add_argument(
"--output", "-o",
type=Path,
help="Output file path (default: stdout)",
)
parser.add_argument(
"--check",
action="store_true",
help="Check if output matches existing file (for CI)",
)
args = parser.parse_args()
content = generate_markdown()
if args.check and args.output:
if args.output.exists():
existing = args.output.read_text()
# Compare ignoring timestamp line
existing_lines = [l for l in existing.split("\n") if not l.startswith("Generated:")]
new_lines = [l for l in content.split("\n") if not l.startswith("Generated:")]
if existing_lines != new_lines:
print(f"ERROR: {args.output} is out of date. Run: python scripts/generate_cli_docs.py -o {args.output}", file=sys.stderr)
sys.exit(1)
print(f"OK: {args.output} is up to date")
sys.exit(0)
else:
print(f"ERROR: {args.output} does not exist", file=sys.stderr)
sys.exit(1)
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(content)
print(f"Written to {args.output}")
else:
print(content)
if __name__ == "__main__":
main()
@@ -78,26 +78,29 @@ def example_get_context_for_agent():
# =============================================
def example_agent_with_fast_context():
"""Use FastContext with an Agent."""
"""Use FastContext with an Agent via context= param."""
from praisonaiagents import Agent
from praisonaiagents.context.fast import FastContext
# Create agent with FastContext enabled
# Create FastContext instance
fc = FastContext(workspace_path=".")
# Search for code context
result = fc.search("find authentication code")
context = result.to_context_string() if result.files else None
# Create agent with context management enabled
agent = Agent(
name="CodeAssistant",
instructions="You are a helpful code assistant.",
fast_context=True,
fast_context_path=".", # Current directory
fast_context_model="gpt-4o-mini",
fast_context_max_turns=4,
fast_context_parallelism=8
context=True, # Enable context management
)
# Use delegate_to_fast_context for code search
context = agent.delegate_to_fast_context("find authentication code")
if context:
print("Found relevant code context:")
print(context[:300] + "...")
# Use the context in a chat
print(agent.chat(f"Based on this code:\n{context}\n\nExplain the authentication flow."))
else:
print("No relevant code found")
@@ -0,0 +1,310 @@
#!/usr/bin/env python3
"""
Large Context Knowledge Handling Example
Demonstrates the full pipeline:
1. Token Budget calculation
2. Incremental indexing with ignore patterns
3. Strategy selection based on corpus size
4. Smart retrieval with filtering
5. Contextual compression
6. Hierarchical summarization
Usage:
python examples/large_context_knowledge.py
"""
import os
import tempfile
from pathlib import Path
def create_sample_corpus(base_path: str) -> None:
"""Create a sample corpus for demonstration."""
# Create directory structure
src_dir = Path(base_path) / "src"
docs_dir = Path(base_path) / "docs"
src_dir.mkdir(parents=True, exist_ok=True)
docs_dir.mkdir(parents=True, exist_ok=True)
# Create Python files
(src_dir / "main.py").write_text("""
# Main application entry point
def main():
'''Start the application.'''
print("Hello from PraisonAI!")
if __name__ == "__main__":
main()
""")
(src_dir / "utils.py").write_text("""
# Utility functions
def helper_function(x: int) -> int:
'''Double the input value.'''
return x * 2
def format_output(text: str) -> str:
'''Format text for display.'''
return f"[OUTPUT] {text}"
""")
# Create documentation
(docs_dir / "readme.md").write_text("""
# Project Documentation
This is a sample project demonstrating large context knowledge handling.
## Features
- Token budget management
- Incremental indexing
- Smart retrieval
- Contextual compression
""")
(docs_dir / "api.md").write_text("""
# API Reference
## Functions
### main()
Entry point for the application.
### helper_function(x)
Doubles the input value.
### format_output(text)
Formats text for display.
""")
# Create .praisonignore
(Path(base_path) / ".praisonignore").write_text("""
# Ignore patterns
*.pyc
__pycache__/
.git/
""")
print(f"Created sample corpus at: {base_path}")
def demo_token_budget():
"""Demonstrate token budget calculation."""
print("\n" + "="*60)
print("1. TOKEN BUDGET CALCULATION")
print("="*60)
from praisonaiagents.rag.budget import TokenBudget, get_model_context_window
# Get context window for different models
models = ["gpt-4o", "claude-3.5-sonnet", "gemini-1.5-pro", "llama-3.1-70b"]
print("\nModel context windows:")
for model in models:
window = get_model_context_window(model)
print(f" {model}: {window:,} tokens")
# Create budget for GPT-4o
budget = TokenBudget.from_model("gpt-4o")
print(f"\nBudget for GPT-4o:")
print(f" Max tokens: {budget.model_max_tokens:,}")
print(f" Reserved for response: {budget.reserved_response_tokens:,}")
print(f" Max context tokens: {budget.max_context_tokens:,}")
# Dynamic budget calculation
available = budget.dynamic_budget(
prompt_tokens=500,
history_tokens=2000,
system_tokens=1000,
)
print(f"\nDynamic budget (with 500 prompt + 2000 history + 1000 system):")
print(f" Available for context: {available:,} tokens")
def demo_indexing(corpus_path: str):
"""Demonstrate incremental indexing."""
print("\n" + "="*60)
print("2. INCREMENTAL INDEXING")
print("="*60)
from praisonaiagents.knowledge import Knowledge
from praisonaiagents.knowledge.indexing import CorpusStats
# Get corpus stats before indexing
stats = CorpusStats.from_directory(corpus_path)
print(f"\nCorpus statistics:")
print(f" Files: {stats.file_count}")
print(f" Estimated tokens: {stats.total_tokens:,}")
print(f" Recommended strategy: {stats.strategy_recommendation}")
# Index the corpus
knowledge = Knowledge()
result = knowledge.index(
corpus_path,
user_id="demo_user",
incremental=False,
exclude_glob=["*.pyc"],
)
print(f"\nIndexing result:")
print(f" Files indexed: {result.files_indexed}")
print(f" Files skipped: {result.files_skipped}")
print(f" Chunks created: {result.chunks_created}")
print(f" Duration: {result.duration_seconds:.2f}s")
# Incremental re-index (should skip unchanged files)
print("\nRe-indexing (incremental)...")
result2 = knowledge.index(corpus_path, user_id="demo_user", incremental=True)
print(f" Files indexed: {result2.files_indexed}")
print(f" Files skipped: {result2.files_skipped}")
def demo_strategy_selection():
"""Demonstrate strategy selection."""
print("\n" + "="*60)
print("3. STRATEGY SELECTION")
print("="*60)
from praisonaiagents.rag.strategy import select_strategy, get_strategy_description, RetrievalStrategy
from praisonaiagents.knowledge.indexing import CorpusStats
# Show strategy for different corpus sizes
sizes = [5, 50, 500, 5000, 50000, 500000]
print("\nStrategy selection by corpus size:")
for size in sizes:
stats = CorpusStats(file_count=size)
strategy = select_strategy(stats)
print(f" {size:>7} files -> {strategy.value}")
# Show strategy descriptions
print("\nStrategy descriptions:")
for strategy in RetrievalStrategy:
desc = get_strategy_description(strategy)
print(f" {strategy.value}: {desc[:60]}...")
def demo_smart_retriever():
"""Demonstrate smart retriever with filtering."""
print("\n" + "="*60)
print("4. SMART RETRIEVER")
print("="*60)
from praisonaiagents.rag.retriever import SmartRetriever, SimpleReranker
# Create retriever with reranker
reranker = SimpleReranker()
retriever = SmartRetriever(reranker=reranker)
# Demonstrate filtering
chunks = [
{"text": "Python code for data processing", "metadata": {"filename": "process.py"}},
{"text": "JavaScript frontend code", "metadata": {"filename": "app.js"}},
{"text": "Python utilities and helpers", "metadata": {"filename": "utils.py"}},
{"text": "CSS styling rules", "metadata": {"filename": "styles.css"}},
]
print("\nFiltering by include pattern (*.py):")
filtered = retriever._apply_filters(chunks, include_glob=["*.py"])
for chunk in filtered:
print(f" - {chunk['metadata']['filename']}: {chunk['text'][:40]}...")
print("\nFiltering by exclude pattern (utils*):")
filtered = retriever._apply_filters(chunks, exclude_glob=["utils*"])
for chunk in filtered:
print(f" - {chunk['metadata']['filename']}: {chunk['text'][:40]}...")
# Demonstrate reranking
print("\nReranking for query 'Python data':")
reranked = reranker.rerank("Python data", chunks)
for i, chunk in enumerate(reranked[:3], 1):
print(f" {i}. [{chunk['score']:.2f}] {chunk['text'][:40]}...")
def demo_compression():
"""Demonstrate contextual compression."""
print("\n" + "="*60)
print("5. CONTEXTUAL COMPRESSION")
print("="*60)
from praisonaiagents.rag.compressor import ContextCompressor
compressor = ContextCompressor()
# Create chunks with some redundancy
chunks = [
{"text": "Python is a high-level programming language. It is known for its simplicity and readability. Python supports multiple programming paradigms."},
{"text": "Python is a high-level programming language. It is known for its simplicity and readability. Python supports multiple programming paradigms."}, # Duplicate
{"text": "JavaScript is the language of the web. It runs in browsers and on servers with Node.js."},
{"text": "Python has extensive libraries for data science, machine learning, and web development."},
]
print(f"\nOriginal chunks: {len(chunks)}")
result = compressor.compress(chunks, "Python programming", target_tokens=200)
print(f"After compression:")
print(f" Chunks: {len(result.chunks)}")
print(f" Original tokens: {result.original_tokens}")
print(f" Compressed tokens: {result.compressed_tokens}")
print(f" Compression ratio: {result.compression_ratio:.2%}")
print(f" Method: {result.method_used}")
def demo_hierarchical_summarizer(corpus_path: str):
"""Demonstrate hierarchical summarization."""
print("\n" + "="*60)
print("6. HIERARCHICAL SUMMARIZATION")
print("="*60)
from praisonaiagents.rag.summarizer import HierarchicalSummarizer
summarizer = HierarchicalSummarizer()
# Build hierarchy
print(f"\nBuilding hierarchy for: {corpus_path}")
result = summarizer.build_hierarchy(corpus_path, levels=2)
print(f"\nHierarchy result:")
print(f" Total files: {result.total_files}")
print(f" Total nodes: {len(result.nodes)}")
print(f" Total tokens: {result.total_tokens}")
# Show summaries
print("\nFile summaries:")
for path, node in result.nodes.items():
if node.level == 1: # File level
filename = node.metadata.get("filename", os.path.basename(path))
summary = node.summary[:60] + "..." if len(node.summary) > 60 else node.summary
print(f" {filename}: {summary}")
# Query hierarchy
print("\nQuerying hierarchy for 'function':")
results = summarizer.query_hierarchy("function", corpus_path, max_results=3)
for r in results:
print(f" - {os.path.basename(r['path'])}: score={r['score']}")
def main():
"""Run all demonstrations."""
print("="*60)
print("LARGE CONTEXT KNOWLEDGE HANDLING DEMO")
print("="*60)
# Create temporary corpus
with tempfile.TemporaryDirectory() as tmpdir:
create_sample_corpus(tmpdir)
# Run demos
demo_token_budget()
demo_indexing(tmpdir)
demo_strategy_selection()
demo_smart_retriever()
demo_compression()
demo_hierarchical_summarizer(tmpdir)
print("\n" + "="*60)
print("DEMO COMPLETE")
print("="*60)
if __name__ == "__main__":
main()
@@ -248,7 +248,35 @@ def __getattr__(name):
_lazy_cache[name] = LineRange
return LineRange
# RAG module (lazy loaded for zero performance impact)
# Retrieval configuration (Agent-first unified config)
elif name == "RetrievalConfig":
from praisonaiagents.rag.retrieval_config import RetrievalConfig
_lazy_cache[name] = RetrievalConfig
return RetrievalConfig
elif name == "RetrievalPolicy":
from praisonaiagents.rag.retrieval_config import RetrievalPolicy
_lazy_cache[name] = RetrievalPolicy
return RetrievalPolicy
elif name == "CitationsMode":
from praisonaiagents.rag.retrieval_config import CitationsMode
_lazy_cache[name] = CitationsMode
return CitationsMode
# RAG models (used by Agent.query() and Agent.retrieve())
elif name == "ContextPack":
from praisonaiagents.rag.models import ContextPack
_lazy_cache[name] = ContextPack
return ContextPack
elif name == "RAGResult":
from praisonaiagents.rag import RAGResult
_lazy_cache[name] = RAGResult
return RAGResult
elif name == "Citation":
from praisonaiagents.rag import Citation
_lazy_cache[name] = Citation
return Citation
# RAG internals (advanced use - prefer Agent.query() for normal usage)
elif name == "RAG":
from praisonaiagents.rag import RAG
_lazy_cache[name] = RAG
@@ -257,10 +285,6 @@ def __getattr__(name):
from praisonaiagents.rag import RAGConfig
_lazy_cache[name] = RAGConfig
return RAGConfig
elif name == "RAGResult":
from praisonaiagents.rag import RAGResult
_lazy_cache[name] = RAGResult
return RAGResult
elif name == "RAGCitation":
from praisonaiagents.rag import Citation
_lazy_cache[name] = Citation
@@ -348,6 +372,26 @@ def __getattr__(name):
except ImportError:
return None
# Feature Config classes (agent-centric API)
elif name in ("MemoryConfig", "KnowledgeConfig", "PlanningConfig",
"ReflectionConfig", "GuardrailConfig", "WebConfig",
"MemoryBackend", "ChunkingStrategy", "GuardrailAction",
"WebSearchProvider"):
from .config import feature_configs
result = getattr(feature_configs, name)
_lazy_cache[name] = result
return result
# Context management config (already exists)
elif name == "ManagerConfig":
from .context.manager import ManagerConfig
_lazy_cache[name] = ManagerConfig
return ManagerConfig
elif name == "ContextManager":
from .context.manager import ContextManager
_lazy_cache[name] = ContextManager
return ContextManager
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -503,4 +547,18 @@ __all__ = [
'RAGConfig',
'RAGResult',
'RAGCitation',
# Feature Configs (agent-centric API)
'MemoryConfig',
'KnowledgeConfig',
'PlanningConfig',
'ReflectionConfig',
'GuardrailConfig',
'WebConfig',
'MemoryBackend',
'ChunkingStrategy',
'GuardrailAction',
'WebSearchProvider',
# Context Management
'ManagerConfig',
'ContextManager',
]
@@ -267,43 +267,23 @@ class Agent:
response_template: Optional[str] = None,
allow_code_execution: Optional[bool] = False,
max_retry_limit: int = 2,
respect_context_window: bool = True,
code_execution_mode: Literal["safe", "unsafe"] = "safe",
embedder_config: Optional[Dict[str, Any]] = None,
knowledge: Optional[List[str]] = None,
knowledge: Optional[Union[bool, List[str], Any]] = None,
retrieval_config: Optional[Union[Dict[str, Any], Any]] = None,
knowledge_config: Optional[Dict[str, Any]] = None,
rag_config: Optional[Dict[str, Any]] = None,
embedder_config: Optional[Dict[str, Any]] = None,
user_id: Optional[str] = None,
use_system_prompt: Optional[bool] = True,
markdown: bool = True,
stream: bool = False,
metrics: bool = False,
self_reflect: bool = False,
max_reflect: int = 3,
min_reflect: int = 1,
reflect_llm: Optional[str] = None,
reflect_prompt: Optional[str] = None,
user_id: Optional[str] = None,
reasoning_steps: bool = False,
guardrail: Optional[Union[Callable[['TaskOutput'], Tuple[bool, Any]], str]] = None,
max_guardrail_retries: int = 3,
handoffs: Optional[List[Union['Agent', 'Handoff']]] = None,
base_url: Optional[str] = None,
api_key: Optional[str] = None,
web_search: Optional[Union[bool, Dict[str, Any]]] = None,
web_fetch: Optional[Union[bool, Dict[str, Any]]] = None,
prompt_caching: Optional[bool] = None,
claude_memory: Optional[Union[bool, Any]] = None,
plan_mode: bool = False,
planning: bool = False,
planning_tools: Optional[List[Any]] = None,
planning_reasoning: bool = False,
fast_context: bool = False,
fast_context_path: Optional[str] = None,
fast_context_model: str = "gpt-4o-mini",
fast_context_max_turns: int = 4,
fast_context_parallelism: int = 8,
fast_context_timeout: float = 30.0,
history_in_context: Optional[int] = None,
planning: Optional[Union[bool, Any]] = False,
auto_save: Optional[str] = None,
skills: Optional[List[str]] = None,
skills_dirs: Optional[List[str]] = None,
@@ -312,8 +292,20 @@ class Agent:
hooks: Optional[List[Any]] = None,
llm_config: Optional[Dict[str, Any]] = None,
rate_limiter: Optional[Any] = None,
auto_summarize: bool = False,
summarize_threshold: float = 0.8
# Agent-centric feature parameters (lazy loaded)
auto_memory: Optional[Any] = None,
policy: Optional[Any] = None,
background: Optional[Any] = None,
checkpoints: Optional[Any] = None,
output_style: Optional[Any] = None,
thinking_budget: Optional[Any] = None,
# Context management (single param for all context features)
context: Optional[Union[bool, Any]] = False,
# NEW: Consolidated feature params (agent-centric API)
# These take precedence over individual params when provided
reflection: Optional[Union[bool, Any]] = None, # Union[bool, ReflectionConfig]
guardrails: Optional[Union[bool, Callable, Any]] = None, # Union[bool, Callable, GuardrailConfig]
web: Optional[Union[bool, Any]] = None, # Union[bool, WebConfig]
):
"""Initialize an Agent instance.
@@ -362,8 +354,6 @@ class Agent:
snippets during task completion. Use with caution for security. Defaults to False.
max_retry_limit (int, optional): Maximum number of retry attempts for failed operations
before giving up. Helps handle transient errors. Defaults to 2.
respect_context_window (bool, optional): Automatically manage context window size
to prevent token limit errors with large conversations. Defaults to True.
code_execution_mode (Literal["safe", "unsafe"], optional): Safety mode for code execution.
"safe" restricts dangerous operations, "unsafe" allows full code execution. Defaults to "safe".
embedder_config (Optional[Dict[str, Any]], optional): Configuration dictionary for
@@ -431,6 +421,178 @@ class Agent:
Agent._configure_logging()
Agent._logging_configured = True
# ============================================================
# CONSOLIDATED PARAMS EXTRACTION (agent-centric API)
# New consolidated params take precedence over individual params
# Initialize removed params with defaults before extraction
# ============================================================
# Initialize removed params with defaults (these were removed from signature)
claude_memory = None
self_reflect = False
max_reflect = 3
min_reflect = 1
reflect_llm = None
reflect_prompt = None
guardrail = None
max_guardrail_retries = 3
web_search = None
web_fetch = None
plan_mode = False
planning_tools = None
planning_reasoning = False
# Extract memory settings from consolidated 'memory' param
# memory can be: False, True, MemoryConfig, or MemoryManager instance
_memory_user_id = user_id # Default from param
_memory_session_id = session_id # Default from param
_memory_db = db # Default from param
_memory_auto_memory = auto_memory # Default from param
_memory_claude_memory = claude_memory # Default from param
if memory is not None and memory is not False:
if hasattr(memory, 'user_id') and hasattr(memory, 'backend'):
# It's a MemoryConfig
if memory.user_id:
_memory_user_id = memory.user_id
if memory.session_id:
_memory_session_id = memory.session_id
if memory.db:
_memory_db = memory.db
_memory_auto_memory = memory.auto_memory
_memory_claude_memory = memory.claude_memory
# Convert MemoryConfig to appropriate memory init value
if memory.backend == "file" or (hasattr(memory.backend, 'value') and memory.backend.value == "file"):
memory = True # Use FileMemory
elif memory.config:
memory = memory.config # Use config dict
else:
backend_val = memory.backend.value if hasattr(memory.backend, 'value') else memory.backend
memory = backend_val # Use backend string
# Apply extracted memory values
user_id = _memory_user_id
session_id = _memory_session_id
db = _memory_db
auto_memory = _memory_auto_memory
claude_memory = _memory_claude_memory
# Extract knowledge settings from consolidated 'knowledge' param
# knowledge can be: False, True, List[str], KnowledgeConfig, or KnowledgeBase instance
_knowledge_sources = None
_knowledge_retrieval_config = retrieval_config
_knowledge_embedder_config = embedder_config
if knowledge is not None and knowledge is not False:
if isinstance(knowledge, list):
# It's a list of sources - use directly
_knowledge_sources = knowledge
elif hasattr(knowledge, 'search') and hasattr(knowledge, 'add'):
# It's a Knowledge/KnowledgeBase instance - pass through unchanged
# Don't modify knowledge, let the existing logic handle it
pass
elif hasattr(knowledge, 'sources') and hasattr(knowledge, 'embedder'):
# It's a KnowledgeConfig (has sources list and embedder string)
_knowledge_sources = knowledge.sources
_knowledge_embedder_config = knowledge.embedder_config or embedder_config
# Build retrieval config from KnowledgeConfig
if knowledge.config:
_knowledge_retrieval_config = knowledge.config
else:
_knowledge_retrieval_config = {
'top_k': knowledge.retrieval_k,
'threshold': knowledge.retrieval_threshold,
'rerank': knowledge.rerank,
'rerank_model': knowledge.rerank_model,
}
# Update knowledge to be the sources list for internal processing
knowledge = _knowledge_sources if _knowledge_sources else None
elif knowledge is True:
# Enable knowledge but no sources provided yet
knowledge = None
# Apply extracted knowledge values
retrieval_config = _knowledge_retrieval_config
embedder_config = _knowledge_embedder_config
# Extract planning settings from consolidated 'planning' param
# planning can be: False, True, or PlanningConfig
_planning_enabled = planning
_plan_mode = plan_mode
_planning_tools = planning_tools
_planning_reasoning = planning_reasoning
if planning is not None:
if planning is True:
_planning_enabled = True
elif planning is False:
_planning_enabled = False
elif hasattr(planning, 'reasoning'):
# It's a PlanningConfig
_planning_enabled = True
_planning_tools = planning.tools
_planning_reasoning = planning.reasoning
_plan_mode = planning.read_only
# planning param becomes True for internal use
planning = True
# Apply extracted planning values
plan_mode = _plan_mode
planning_tools = _planning_tools
planning_reasoning = _planning_reasoning
# Extract reflection settings from consolidated 'reflection' param
if reflection is not None:
if reflection is True:
# Enable with defaults
self_reflect = True
elif reflection is False:
self_reflect = False
elif hasattr(reflection, 'min_iterations'):
# It's a ReflectionConfig
self_reflect = True
min_reflect = reflection.min_iterations
max_reflect = reflection.max_iterations
reflect_llm = reflection.llm
reflect_prompt = reflection.prompt
# Extract guardrail settings from consolidated 'guardrails' param
if guardrails is not None:
if guardrails is True:
# Enable with defaults (no-op, user must provide validator)
pass
elif guardrails is False:
guardrail = None
elif callable(guardrails):
# Direct validator function
guardrail = guardrails
elif hasattr(guardrails, 'validator'):
# It's a GuardrailConfig
if guardrails.validator:
guardrail = guardrails.validator
elif guardrails.llm_validator:
guardrail = guardrails.llm_validator
max_guardrail_retries = guardrails.max_retries
# Extract web settings from consolidated 'web' param
if web is not None:
if web is True:
# Enable both search and fetch with defaults
web_search = True
web_fetch = True
elif web is False:
web_search = False
web_fetch = False
elif hasattr(web, 'search'):
# It's a WebConfig
web_search = web.search
web_fetch = web.fetch
# Could also extract search_config, fetch_config if needed
# ============================================================
# END CONSOLIDATED PARAMS EXTRACTION
# ============================================================
# If instructions are provided, use them to set role, goal, and backstory
if instructions:
self.name = name or "Agent"
@@ -465,12 +627,6 @@ class Agent:
# Store rate limiter (optional, zero overhead when None)
self._rate_limiter = rate_limiter
# Auto-summarization configuration
if auto_summarize and not (0.0 < summarize_threshold <= 1.0):
raise ValueError("summarize_threshold must be between 0.0 and 1.0")
self.auto_summarize = auto_summarize
self.summarize_threshold = summarize_threshold
# Store OpenAI client parameters for lazy initialization
self._openai_api_key = api_key
self._openai_base_url = base_url
@@ -586,7 +742,6 @@ class Agent:
self.response_template = response_template
self.allow_code_execution = allow_code_execution
self.max_retry_limit = max_retry_limit
self.respect_context_window = respect_context_window
self.code_execution_mode = code_execution_mode
self.embedder_config = embedder_config
self.knowledge = knowledge
@@ -627,7 +782,6 @@ Your Goal: {self.goal}
self.claude_memory = claude_memory
# Session management
self.history_in_context = history_in_context # Number of past sessions to include
self.auto_save = auto_save # Session name for auto-saving
# Initialize rules manager for persistent context (like Cursor/Windsurf)
@@ -666,30 +820,47 @@ Your Goal: {self.goal}
self.handoffs = handoffs if handoffs else []
self._process_handoffs()
# Initialize unified retrieval configuration
# retrieval_config is the SINGLE configuration surface (replaces knowledge_config + rag_config)
self._retrieval_config = None
if retrieval_config is not None:
from ..rag.retrieval_config import RetrievalConfig, create_retrieval_config
if isinstance(retrieval_config, RetrievalConfig):
self._retrieval_config = retrieval_config
elif isinstance(retrieval_config, dict):
self._retrieval_config = RetrievalConfig.from_dict(retrieval_config)
elif knowledge_config is not None or rag_config is not None:
# Legacy support: merge old configs into unified config
from ..rag.retrieval_config import create_retrieval_config
self._retrieval_config = create_retrieval_config(
knowledge_config=knowledge_config,
rag_config=rag_config,
)
# Check if knowledge parameter has any values
if not knowledge:
self.knowledge = None
self._knowledge_sources = None
self._knowledge_processed = True # No knowledge to process
self._rag_config = None
self._rag_instance = None
else:
# Store knowledge sources for lazy processing
self._knowledge_sources = knowledge
self._knowledge_processed = False
self._knowledge_config = knowledge_config
self._rag_config = rag_config # Store RAG config for citations/streaming
# Check if knowledge is a Knowledge instance (shared knowledge)
if hasattr(knowledge, 'search') and hasattr(knowledge, 'add'):
# It's a Knowledge instance - use directly
self.knowledge = knowledge
self._knowledge_sources = None
self._knowledge_processed = True
else:
# It's a list of sources - store for lazy processing
self._knowledge_sources = knowledge
self._knowledge_processed = False
self.knowledge = None # Will be initialized on first use
self._rag_instance = None # Lazy loaded RAG instance
self.knowledge = None # Will be initialized on first use
# Fast Context configuration (lazy loaded)
self.fast_context_enabled = fast_context
self._fast_context_path = fast_context_path # Store raw, resolve lazily
self.fast_context_model = fast_context_model
self.fast_context_max_turns = fast_context_max_turns
self.fast_context_parallelism = fast_context_parallelism
self.fast_context_timeout = fast_context_timeout
self._fast_context_instance = None # Lazy loaded
# Create default retrieval config if knowledge provided but no config
if self._retrieval_config is None:
from ..rag.retrieval_config import RetrievalConfig
self._retrieval_config = RetrievalConfig()
# Agent Skills configuration (lazy loaded for zero performance impact)
self._skills = skills
@@ -707,6 +878,135 @@ Your Goal: {self.goal}
self._session_store = None
self._session_store_initialized = False
# Agent-centric feature instances (lazy loaded for zero performance impact)
self._auto_memory = auto_memory
self._policy = policy
self._background = background
self._checkpoints = checkpoints
self._output_style = output_style
self._thinking_budget = thinking_budget
# Context management (lazy loaded for zero overhead when disabled)
self._context_param = context # Store raw param for lazy init
self._context_manager = None # Lazy initialized on first use
self._context_manager_initialized = False
@property
def auto_memory(self):
"""AutoMemory instance for automatic memory extraction."""
return self._auto_memory
@auto_memory.setter
def auto_memory(self, value):
self._auto_memory = value
@property
def policy(self):
"""PolicyEngine instance for execution control."""
return self._policy
@policy.setter
def policy(self, value):
self._policy = value
@property
def background(self):
"""BackgroundRunner instance for async task execution."""
return self._background
@background.setter
def background(self, value):
self._background = value
@property
def checkpoints(self):
"""CheckpointService instance for file-level undo/restore."""
return self._checkpoints
@checkpoints.setter
def checkpoints(self, value):
self._checkpoints = value
@property
def output_style(self):
"""OutputStyle instance for response formatting."""
return self._output_style
@output_style.setter
def output_style(self, value):
self._output_style = value
@property
def thinking_budget(self):
"""ThinkingBudget instance for extended thinking control."""
return self._thinking_budget
@thinking_budget.setter
def thinking_budget(self, value):
self._thinking_budget = value
@property
def context_manager(self):
"""
ContextManager instance for unified context management.
Lazy initialized on first access when context=True or context=ManagerConfig.
Returns None when context=False (zero overhead).
Example:
agent = Agent(instructions="...", context=True)
# Access manager for advanced operations
if agent.context_manager:
stats = agent.context_manager.get_stats()
"""
if self._context_manager_initialized:
return self._context_manager
# Initialize based on context param type
if self._context_param is False or self._context_param is None:
# Zero overhead - no context management
self._context_manager = None
self._context_manager_initialized = True
return None
# Lazy import to avoid overhead when not used
try:
from ..context import ContextManager, ManagerConfig
except ImportError:
# Context module not available
self._context_manager = None
self._context_manager_initialized = True
return None
if self._context_param is True:
# Enable with safe defaults
self._context_manager = ContextManager(
model=self.llm if isinstance(self.llm, str) else "gpt-4o-mini",
agent_name=self.name or "Agent",
)
elif isinstance(self._context_param, ManagerConfig):
# Use provided config
self._context_manager = ContextManager(
model=self.llm if isinstance(self.llm, str) else "gpt-4o-mini",
config=self._context_param,
agent_name=self.name or "Agent",
)
elif hasattr(self._context_param, 'process'):
# Already a ContextManager instance
self._context_manager = self._context_param
else:
# Unknown type, disable
self._context_manager = None
self._context_manager_initialized = True
return self._context_manager
@context_manager.setter
def context_manager(self, value):
"""Set context manager directly."""
self._context_manager = value
self._context_manager_initialized = True
@property
def console(self):
"""Lazily initialize Rich Console only when needed."""
@@ -715,17 +1015,6 @@ Your Goal: {self.goal}
self._console = Console()
return self._console
@property
def fast_context_path(self):
"""Lazily resolve fast_context_path - avoids os.getcwd() on every init."""
if self._fast_context_path is None:
self._fast_context_path = os.getcwd()
return self._fast_context_path
@fast_context_path.setter
def fast_context_path(self, value):
self._fast_context_path = value
@property
def skill_manager(self):
"""Lazily initialize SkillManager only when skills are accessed."""
@@ -912,57 +1201,6 @@ Your Goal: {self.goal}
return supports_prompt_caching(model_name)
@property
def fast_context(self):
"""Lazily initialize FastContext instance when needed.
Returns:
FastContext instance or None if not enabled
"""
if not self.fast_context_enabled:
return None
if self._fast_context_instance is None:
try:
from ..context.fast import FastContext
self._fast_context_instance = FastContext(
workspace_path=self.fast_context_path,
model=self.fast_context_model,
max_turns=self.fast_context_max_turns,
max_parallel=self.fast_context_parallelism,
timeout=self.fast_context_timeout,
verbose=self.verbose
)
except ImportError:
logging.warning("FastContext not available")
return None
return self._fast_context_instance
def delegate_to_fast_context(self, query: str) -> Optional[str]:
"""Delegate a code search query to FastContext subagent.
This method uses the FastContext subagent to rapidly search
the codebase and return relevant context.
Args:
query: Natural language search query
Returns:
Formatted context string or None if FastContext not available
"""
if not self.fast_context_enabled or self.fast_context is None:
return None
try:
result = self.fast_context.search(query)
if result.total_files > 0:
return self.fast_context.get_context_for_agent(query)
return None
except Exception as e:
logging.warning(f"FastContext search failed: {e}")
return None
@property
def rules_manager(self):
"""
@@ -1196,9 +1434,14 @@ Your Goal: {self.goal}
def _ensure_knowledge_processed(self):
"""Ensure knowledge is initialized and processed when first accessed."""
if not self._knowledge_processed and self._knowledge_sources:
# Initialize Knowledge with provided or default config
# Initialize Knowledge with config from retrieval_config
from praisonaiagents.knowledge import Knowledge
self.knowledge = Knowledge(self._knowledge_config or None)
knowledge_config = None
if self._retrieval_config is not None:
knowledge_config = self._retrieval_config.to_knowledge_config()
self.knowledge = Knowledge(knowledge_config)
# Process all knowledge sources
for source in self._knowledge_sources:
@@ -1206,22 +1449,28 @@ Your Goal: {self.goal}
self._knowledge_processed = True
@property
def retrieval_config(self):
"""Get the unified retrieval configuration."""
return self._retrieval_config
@property
def rag(self):
"""
Lazy-loaded RAG instance for advanced retrieval with citations.
Returns RAG instance configured with agent's knowledge and rag_config.
Returns None if no knowledge sources are configured.
Returns RAG instance configured with agent's knowledge and retrieval_config.
Returns None if no knowledge is configured.
Usage:
agent = Agent(knowledge=["doc.pdf"], rag_config={"include_citations": True})
agent = Agent(knowledge=["doc.pdf"], retrieval_config={"citations": True})
result = agent.rag.query("What is the main finding?")
print(result.answer)
for citation in result.citations:
print(f"[{citation.id}] {citation.source}")
"""
if not self._knowledge_sources:
# Check if we have knowledge (either sources or direct instance)
if not self._knowledge_sources and self.knowledge is None:
return None
if self._rag_instance is None:
@@ -1230,10 +1479,11 @@ Your Goal: {self.goal}
try:
from praisonaiagents.rag import RAG, RAGConfig
# Build RAGConfig from rag_config dict
# Build RAGConfig from unified retrieval_config
rag_config_obj = RAGConfig()
if self._rag_config:
for key, value in self._rag_config.items():
if self._retrieval_config is not None:
rag_dict = self._retrieval_config.to_rag_config()
for key, value in rag_dict.items():
if hasattr(rag_config_obj, key):
setattr(rag_config_obj, key, value)
@@ -1255,16 +1505,17 @@ Your Goal: {self.goal}
def _get_knowledge_context(self, query: str, use_rag: bool = False) -> tuple:
"""
Get knowledge context for a query.
Get knowledge context for a query using unified retrieval pipeline.
Args:
query: The user's question/prompt
use_rag: If True and rag_config is set, use RAG pipeline for citations
use_rag: If True, use RAG pipeline for token-aware context building
Returns:
Tuple of (context_string, citations_list or None)
"""
if not self._knowledge_sources:
# Check if we have knowledge (sources or direct instance)
if not self._knowledge_sources and self.knowledge is None:
return "", None
self._ensure_knowledge_processed()
@@ -1272,26 +1523,135 @@ Your Goal: {self.goal}
if not self.knowledge:
return "", None
# Use RAG if configured and requested
if use_rag and self._rag_config and self.rag:
# Use RAG pipeline for token-aware context building (DRY - single path)
if use_rag and self.rag:
try:
result = self.rag.query(query, user_id=self.user_id, agent_id=self.agent_id)
return result.context_used, result.citations
context_pack = self.rag.retrieve(query, user_id=self.user_id, agent_id=self.agent_id)
return context_pack.context, context_pack.citations
except Exception as e:
logging.warning(f"RAG query failed, falling back to basic retrieval: {e}")
logging.warning(f"RAG retrieve failed, falling back to basic retrieval: {e}")
# Basic retrieval (original behavior)
search_results = self.knowledge.search(query, agent_id=self.agent_id)
if not search_results:
return "", None
# Fallback: basic retrieval with token-aware formatting
try:
from praisonaiagents.rag.context import build_context
search_results = self.knowledge.search(query, agent_id=self.agent_id)
if not search_results:
return "", None
# Normalize results format (filter out None values, handle None metadata)
if isinstance(search_results, dict) and 'results' in search_results:
results = []
for r in search_results['results']:
if r is None:
continue
text = r.get('memory', '') or ''
metadata = r.get('metadata') or {} # Handle None metadata
if text:
results.append({"text": text, "metadata": metadata})
elif isinstance(search_results, list):
results = [{"text": str(r), "metadata": {}} for r in search_results if r is not None and str(r)]
else:
results = [{"text": str(search_results), "metadata": {}}] if search_results else []
# Use token-aware context building (same as RAG pipeline)
max_tokens = 4000
if self._retrieval_config:
max_tokens = self._retrieval_config.max_context_tokens
context, _ = build_context(results, max_tokens=max_tokens)
return context, None
except ImportError:
# Fallback to simple concatenation if RAG context module not available
search_results = self.knowledge.search(query, agent_id=self.agent_id)
if not search_results:
return "", None
if isinstance(search_results, dict) and 'results' in search_results:
# CRITICAL: Handle None results and get 'memory' field safely
parts = []
for result in search_results['results']:
if result is not None:
memory = result.get('memory', '') or ''
if memory:
parts.append(memory)
knowledge_content = "\n".join(parts)
else:
knowledge_content = "\n".join(search_results) if isinstance(search_results, list) else str(search_results)
return knowledge_content, None
def retrieve(self, query: str, **kwargs) -> "ContextPack":
"""
Retrieve context from knowledge without LLM generation.
# Format results
if isinstance(search_results, dict) and 'results' in search_results:
knowledge_content = "\n".join([result['memory'] for result in search_results['results']])
else:
knowledge_content = "\n".join(search_results) if isinstance(search_results, list) else str(search_results)
Returns a ContextPack that can be passed to chat_with_context().
This enables conditional retrieval - only retrieve when needed.
return knowledge_content, None
Args:
query: Search query
**kwargs: Additional arguments (top_k, rerank, etc.)
Returns:
ContextPack with context string and citations (no LLM call)
Raises:
ValueError: If no knowledge is configured
ImportError: If RAG module is not available
Usage:
agent = Agent(knowledge=["docs/"], retrieval_config={"citations": True})
context = agent.retrieve("What are the key findings?")
print(f"Found {len(context.citations)} sources")
response = agent.chat_with_context("Summarize", context)
"""
if not self._knowledge_sources and self.knowledge is None:
raise ValueError("No knowledge configured. Add knowledge=[] to Agent init.")
if not self.rag:
raise ImportError("RAG module not available. Install with: pip install 'praisonaiagents[rag]'")
kwargs.setdefault('user_id', self.user_id)
kwargs.setdefault('agent_id', self.agent_id)
return self.rag.retrieve(query, **kwargs)
def query(self, question: str, **kwargs) -> "RAGResult":
"""
Query knowledge and get a structured answer with citations.
This is the recommended method for getting answers with source citations.
Returns a structured result with answer, citations, context used, and metadata.
Args:
question: The question to answer
**kwargs: Additional arguments (top_k, rerank, etc.)
Returns:
RAGResult with answer, citations, context_used, and metadata
Raises:
ValueError: If no knowledge is configured
ImportError: If RAG module is not available
Usage:
agent = Agent(knowledge=["doc.pdf"], retrieval_config={"citations": True})
result = agent.query("What is the main finding?")
print(result.answer)
for citation in result.citations:
print(f" [{citation.id}] {citation.source}")
"""
if not self._knowledge_sources and self.knowledge is None:
raise ValueError("No knowledge configured. Add knowledge=[] to Agent init.")
if not self.rag:
raise ImportError("RAG module not available. Install with: pip install 'praisonaiagents[rag]'")
kwargs.setdefault('user_id', self.user_id)
kwargs.setdefault('agent_id', self.agent_id)
return self.rag.query(question, **kwargs)
def rag_query(self, question: str, **kwargs) -> "RAGResult":
"""
@@ -2403,7 +2763,7 @@ Your Goal: {self.goal}"""
"""Get the current session ID."""
return self._session_id
def chat(self, prompt, temperature=1.0, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=None, task_name=None, task_description=None, task_id=None, config=None):
def chat(self, prompt, temperature=1.0, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=None, task_name=None, task_description=None, task_id=None, config=None, force_retrieval=False, skip_retrieval=False):
# Apply rate limiter if configured (before any LLM call)
if self._rate_limiter is not None:
self._rate_limiter.acquire()
@@ -2442,23 +2802,42 @@ Your Goal: {self.goal}"""
# Use agent's stream setting if not explicitly provided
if stream is None:
stream = self.stream
# Search for existing knowledge if any knowledge is provided
if self._knowledge_sources and not self._knowledge_processed:
self._ensure_knowledge_processed()
if self.knowledge:
search_results = self.knowledge.search(prompt, agent_id=self.agent_id)
if search_results:
# Check if search_results is a list of dictionaries or strings
if isinstance(search_results, dict) and 'results' in search_results:
# Extract memory content from the results
knowledge_content = "\n".join([result['memory'] for result in search_results['results']])
else:
# If search_results is a list of strings, join them directly
knowledge_content = "\n".join(search_results)
# Append found knowledge to the prompt
prompt = f"{prompt}\n\nKnowledge: {knowledge_content}"
# Unified retrieval handling with policy-based decision
# Uses token-aware context building (DRY - same path as RAG pipeline)
if self._knowledge_sources or self.knowledge is not None:
if not self._knowledge_processed:
self._ensure_knowledge_processed()
# Determine if we should retrieve based on policy
should_retrieve = False
if self._retrieval_config is not None:
should_retrieve = self._retrieval_config.should_retrieve(
prompt if isinstance(prompt, str) else str(prompt),
force=force_retrieval,
skip=skip_retrieval
)
elif not skip_retrieval:
# No config but knowledge exists - retrieve by default unless skipped
should_retrieve = True if force_retrieval else (self.knowledge is not None)
if should_retrieve and self.knowledge:
# Use unified retrieval path with token-aware context building
knowledge_context, _ = self._get_knowledge_context(
prompt if isinstance(prompt, str) else str(prompt),
use_rag=True # Use RAG pipeline for token-aware context
)
if knowledge_context:
# Format with safety boundaries
if self._retrieval_config and self._retrieval_config.context_template:
formatted_context = self._retrieval_config.context_template.format(
context=knowledge_context
)
else:
formatted_context = f"<retrieved_context>\n{knowledge_context}\n</retrieved_context>"
# Append formatted knowledge to the prompt
prompt = f"{prompt}\n\n{formatted_context}"
if self._using_custom_llm:
try:
@@ -3343,7 +3722,7 @@ Write the complete compiled report:"""
def start(self, prompt: str, **kwargs):
"""Start the agent with a prompt. This is a convenience method that wraps chat()."""
# Load history from past sessions if history_in_context is set
# Load history from past sessions (now handled via context= param)
self._load_history_context()
# Check if planning mode is enabled
@@ -3363,37 +3742,13 @@ Write the complete compiled report:"""
return result
def _load_history_context(self):
"""Load history from past sessions into context if history_in_context is set."""
if not self.history_in_context or not self._memory_instance:
return
"""Load history from past sessions into context.
try:
sessions = self._memory_instance.list_sessions()
if not sessions:
return
# Get the last N sessions
sessions_to_load = sessions[:self.history_in_context]
for session_info in reversed(sessions_to_load):
try:
session_data = self._memory_instance.resume_session(session_info["name"])
history = session_data.get("conversation_history", [])
# Add history to chat_history with a marker
for msg in history:
if msg not in self.chat_history:
# Mark as from history to avoid duplication
msg_copy = msg.copy()
msg_copy["_from_history"] = True
self.chat_history.append(msg_copy)
except Exception:
continue
if sessions_to_load:
logging.debug(f"Loaded history from {len(sessions_to_load)} past session(s)")
except Exception as e:
logging.debug(f"Error loading history context: {e}")
Note: This functionality is now handled via context= param with ManagerConfig.
This method is kept for backward compatibility but is a no-op.
Use context=ManagerConfig(history_sessions=N) to load past sessions.
"""
pass
def _auto_save_session(self):
"""Auto-save session if auto_save is enabled."""
@@ -127,7 +127,34 @@ def process_task_context(context_item, verbose=0, user_id=None):
return str(context_item) # Fallback for unknown types
class PraisonAIAgents:
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None, memory=False, memory_config=None, embedder=None, user_id=None, max_iter=10, stream=True, name: Optional[str] = None, planning: bool = False, planning_llm: Optional[str] = None, auto_approve_plan: bool = False, planning_tools: Optional[List] = None, planning_reasoning: bool = False, on_task_start: Optional[Callable] = None, on_task_complete: Optional[Callable] = None, variables: Optional[Dict[str, Any]] = None):
def __init__(
self,
agents,
tasks=None,
verbose=0,
completion_checker=None,
max_retries=5,
process="sequential",
manager_llm=None,
# Consolidated feature params (agent-centric API)
memory: Optional[Any] = False, # Union[bool, MemoryConfig]
planning: Optional[Any] = False, # Union[bool, PlanningConfig]
context: Optional[Any] = False, # Union[bool, ManagerConfig, ContextManager]
# Legacy params (kept for backward compatibility)
memory_config=None,
embedder=None,
user_id=None,
max_iter=10,
stream=True,
name: Optional[str] = None,
planning_llm: Optional[str] = None,
auto_approve_plan: bool = False,
planning_tools: Optional[List] = None,
planning_reasoning: bool = False,
on_task_start: Optional[Callable] = None,
on_task_complete: Optional[Callable] = None,
variables: Optional[Dict[str, Any]] = None,
):
# Add check at the start if memory is requested
if memory:
try:
@@ -218,6 +245,11 @@ class PraisonAIAgents:
self._state = {} # Add state storage at PraisonAIAgents level
# Context management (single param for all context features)
self._context_param = context # Store raw param for lazy init
self._context_manager = None # Lazy initialized on first use
self._context_manager_initialized = False
# Initialize planning mode
self.planning = planning
self.planning_llm = planning_llm or "gpt-4o-mini"
@@ -296,6 +328,51 @@ class PraisonAIAgents:
cleaned = cleaned[:-3].strip()
return cleaned
@property
def context_manager(self):
"""
ContextManager instance for unified context management across all agents.
Lazy initialized on first access when context=True or context=ManagerConfig.
Returns None when context=False (zero overhead).
For multi-agent scenarios, uses MultiAgentContextManager for per-agent isolation.
"""
if self._context_manager_initialized:
return self._context_manager
# Initialize based on context param type
if self._context_param is False or self._context_param is None:
# Zero overhead - no context management
self._context_manager = None
self._context_manager_initialized = True
return None
# Lazy import to avoid overhead when not used
try:
from ..context import MultiAgentContextManager, ManagerConfig
except ImportError:
# Context module not available
self._context_manager = None
self._context_manager_initialized = True
return None
if self._context_param is True:
# Enable with safe defaults for multi-agent
self._context_manager = MultiAgentContextManager()
elif isinstance(self._context_param, ManagerConfig):
# Use provided config for all agents
self._context_manager = MultiAgentContextManager(config=self._context_param)
elif hasattr(self._context_param, 'get_agent_manager'):
# Already a MultiAgentContextManager instance
self._context_manager = self._context_param
else:
# Unknown type, disable
self._context_manager = None
self._context_manager_initialized = True
return self._context_manager
def default_completion_checker(self, task, agent_output):
if task.output_json and task.result and task.result.json_dict:
return True
@@ -56,7 +56,6 @@ class AutoAgents(PraisonAIAgents):
min_reflect: int = 1,
llm: Optional[str] = None,
function_calling_llm: Optional[str] = None,
respect_context_window: bool = True,
code_execution_mode: str = "safe",
embedder_config: Optional[Dict[str, Any]] = None,
knowledge_sources: Optional[List[Any]] = None,
@@ -94,7 +93,6 @@ class AutoAgents(PraisonAIAgents):
self.min_reflect = min_reflect
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
self.function_calling_llm = function_calling_llm
self.respect_context_window = respect_context_window
self.code_execution_mode = code_execution_mode
self.embedder_config = embedder_config
self.knowledge_sources = knowledge_sources
@@ -446,7 +444,6 @@ DO NOT use strings for tasks. Each task MUST be a complete object with all four
min_reflect=self.min_reflect,
llm=self.llm,
function_calling_llm=self.function_calling_llm,
respect_context_window=self.respect_context_window,
code_execution_mode=self.code_execution_mode,
embedder_config=self.embedder_config,
knowledge=self.knowledge_sources,
@@ -0,0 +1,43 @@
"""
Configuration Module for PraisonAI Agents.
Provides feature configuration classes for agent-centric API:
- MemoryConfig: Memory and session management
- KnowledgeConfig: RAG and knowledge retrieval
- PlanningConfig: Planning mode settings
- ReflectionConfig: Self-reflection settings
- GuardrailConfig: Safety and validation
- WebConfig: Web search and fetch
All imports are lazy-loaded for zero performance impact.
"""
__all__ = [
# Enums
"MemoryBackend",
"ChunkingStrategy",
"GuardrailAction",
"WebSearchProvider",
# Config classes
"MemoryConfig",
"KnowledgeConfig",
"PlanningConfig",
"ReflectionConfig",
"GuardrailConfig",
"WebConfig",
# Type aliases
"MemoryParam",
"KnowledgeParam",
"PlanningParam",
"ReflectionParam",
"GuardrailParam",
"WebParam",
]
def __getattr__(name: str):
"""Lazy load config classes."""
if name in __all__:
from . import feature_configs
return getattr(feature_configs, name)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,397 @@
"""
Feature Configuration Classes for PraisonAI Agents.
Provides dataclasses for consolidated feature configuration:
- MemoryConfig: Memory and session management
- KnowledgeConfig: RAG and knowledge retrieval
- PlanningConfig: Planning mode settings
- ReflectionConfig: Self-reflection settings
- GuardrailConfig: Safety and validation
- WebConfig: Web search and fetch
All configs follow the agent-centric pattern:
- False: Feature disabled (zero overhead)
- True: Feature enabled with safe defaults
- Config: Custom configuration
- Instance: Pre-configured manager/engine
Usage:
from praisonaiagents import Agent, MemoryConfig, KnowledgeConfig
# Simple enable
agent = Agent(instructions="...", memory=True)
# With config
agent = Agent(
instructions="...",
memory=MemoryConfig(backend="redis", user_id="user123"),
knowledge=KnowledgeConfig(sources=["docs/"], rerank=True),
)
"""
from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional, Callable, Tuple, Union
from enum import Enum
class MemoryBackend(str, Enum):
"""Memory storage backends."""
FILE = "file"
SQLITE = "sqlite"
REDIS = "redis"
POSTGRES = "postgres"
MEM0 = "mem0"
MONGODB = "mongodb"
@dataclass
class MemoryConfig:
"""
Configuration for agent memory and session management.
Consolidates: memory, auto_memory, claude_memory, user_id, session_id, db
Usage:
# Simple enable (uses FileMemory)
Agent(memory=True)
# With backend
Agent(memory=MemoryConfig(backend="redis"))
# Full config
Agent(memory=MemoryConfig(
backend="sqlite",
user_id="user123",
session_id="session456",
auto_memory=True,
))
"""
# Backend selection
backend: Union[str, MemoryBackend] = MemoryBackend.FILE
# Session identification
user_id: Optional[str] = None
session_id: Optional[str] = None
# Auto-memory extraction
auto_memory: bool = False
# Claude-specific memory (for Anthropic models)
claude_memory: bool = False
# Database adapter (for advanced use)
db: Optional[Any] = None
# Memory configuration dict (for provider-specific settings)
config: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"backend": self.backend.value if isinstance(self.backend, MemoryBackend) else self.backend,
"user_id": self.user_id,
"session_id": self.session_id,
"auto_memory": self.auto_memory,
"claude_memory": self.claude_memory,
"config": self.config,
}
class ChunkingStrategy(str, Enum):
"""Knowledge chunking strategies."""
FIXED = "fixed"
SEMANTIC = "semantic"
SENTENCE = "sentence"
PARAGRAPH = "paragraph"
@dataclass
class KnowledgeConfig:
"""
Configuration for RAG and knowledge retrieval.
Consolidates: knowledge, retrieval_config, knowledge_config, rag_config, embedder_config
Usage:
# Simple enable with sources
Agent(knowledge=["docs/", "data.pdf"])
# With config
Agent(knowledge=KnowledgeConfig(
sources=["docs/"],
embedder="openai",
chunking_strategy="semantic",
retrieval_k=5,
rerank=True,
))
"""
# Knowledge sources (files, directories, URLs)
sources: List[str] = field(default_factory=list)
# Embedder configuration
embedder: str = "openai"
embedder_config: Optional[Dict[str, Any]] = None
# Chunking
chunking_strategy: Union[str, ChunkingStrategy] = ChunkingStrategy.SEMANTIC
chunk_size: int = 1000
chunk_overlap: int = 200
# Retrieval
retrieval_k: int = 5
retrieval_threshold: float = 0.0
# Reranking
rerank: bool = False
rerank_model: Optional[str] = None
# Auto-retrieval (inject context automatically)
auto_retrieve: bool = True
# Full config dict (for advanced use)
config: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"sources": self.sources,
"embedder": self.embedder,
"embedder_config": self.embedder_config,
"chunking_strategy": self.chunking_strategy.value if isinstance(self.chunking_strategy, ChunkingStrategy) else self.chunking_strategy,
"chunk_size": self.chunk_size,
"chunk_overlap": self.chunk_overlap,
"retrieval_k": self.retrieval_k,
"retrieval_threshold": self.retrieval_threshold,
"rerank": self.rerank,
"rerank_model": self.rerank_model,
"auto_retrieve": self.auto_retrieve,
"config": self.config,
}
@dataclass
class PlanningConfig:
"""
Configuration for planning mode.
Consolidates: planning, plan_mode, planning_tools, planning_reasoning, planning_llm
Usage:
# Simple enable
Agent(planning=True)
# With config
Agent(planning=PlanningConfig(
llm="gpt-4o",
tools=[search_tool],
reasoning=True,
auto_approve=False,
))
"""
# Planning LLM (if different from main)
llm: Optional[str] = None
# Tools available during planning
tools: Optional[List[Any]] = None
# Enable reasoning during planning
reasoning: bool = False
# Auto-approve plans without user confirmation
auto_approve: bool = False
# Read-only mode (plan_mode) - only read operations allowed
read_only: bool = False
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"llm": self.llm,
"tools": [str(t) for t in (self.tools or [])],
"reasoning": self.reasoning,
"auto_approve": self.auto_approve,
"read_only": self.read_only,
}
@dataclass
class ReflectionConfig:
"""
Configuration for self-reflection.
Consolidates: self_reflect, max_reflect, min_reflect, reflect_llm, reflect_prompt
Usage:
# Simple enable
Agent(reflection=True)
# With config
Agent(reflection=ReflectionConfig(
min_iterations=1,
max_iterations=3,
llm="gpt-4o",
prompt="Evaluate your response for accuracy...",
))
"""
# Iteration limits
min_iterations: int = 1
max_iterations: int = 3
# Reflection LLM (if different from main)
llm: Optional[str] = None
# Custom reflection prompt
prompt: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"min_iterations": self.min_iterations,
"max_iterations": self.max_iterations,
"llm": self.llm,
"prompt": self.prompt,
}
class GuardrailAction(str, Enum):
"""Action to take when guardrail fails."""
RETRY = "retry"
SKIP = "skip"
RAISE = "raise"
@dataclass
class GuardrailConfig:
"""
Configuration for guardrails and safety validation.
Consolidates: guardrail, max_guardrail_retries, policy
Usage:
# With validator function
Agent(guardrails=my_validator_fn)
# With config
Agent(guardrails=GuardrailConfig(
validator=my_validator_fn,
max_retries=3,
on_fail="retry",
))
# With LLM-based validation
Agent(guardrails=GuardrailConfig(
llm_validator="Ensure response is helpful and safe",
max_retries=2,
))
"""
# Validator function: (TaskOutput) -> Tuple[bool, Any]
validator: Optional[Callable[..., Tuple[bool, Any]]] = None
# LLM-based validation prompt
llm_validator: Optional[str] = None
# Retry settings
max_retries: int = 3
on_fail: Union[str, GuardrailAction] = GuardrailAction.RETRY
# Policy engine (for advanced use)
policy: Optional[Any] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"validator": str(self.validator) if self.validator else None,
"llm_validator": self.llm_validator,
"max_retries": self.max_retries,
"on_fail": self.on_fail.value if isinstance(self.on_fail, GuardrailAction) else self.on_fail,
}
class WebSearchProvider(str, Enum):
"""Web search providers."""
DUCKDUCKGO = "duckduckgo"
GOOGLE = "google"
BING = "bing"
TAVILY = "tavily"
SERPER = "serper"
@dataclass
class WebConfig:
"""
Configuration for web search and fetch capabilities.
Consolidates: web_search, web_fetch
Usage:
# Simple enable
Agent(web=True)
# With config
Agent(web=WebConfig(
search=True,
fetch=True,
search_provider="duckduckgo",
max_results=5,
))
"""
# Enable web search
search: bool = True
# Enable web fetch (retrieve full page content)
fetch: bool = True
# Search provider
search_provider: Union[str, WebSearchProvider] = WebSearchProvider.DUCKDUCKGO
# Search settings
max_results: int = 5
# Search config dict (for provider-specific settings)
search_config: Optional[Dict[str, Any]] = None
# Fetch config dict
fetch_config: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"search": self.search,
"fetch": self.fetch,
"search_provider": self.search_provider.value if isinstance(self.search_provider, WebSearchProvider) else self.search_provider,
"max_results": self.max_results,
"search_config": self.search_config,
"fetch_config": self.fetch_config,
}
# Type aliases for Union types used in Agent.__init__
MemoryParam = Union[bool, MemoryConfig, Any] # Any = MemoryManager instance
KnowledgeParam = Union[bool, List[str], KnowledgeConfig, Any] # Any = KnowledgeBase instance
PlanningParam = Union[bool, PlanningConfig]
ReflectionParam = Union[bool, ReflectionConfig]
GuardrailParam = Union[bool, Callable[..., Tuple[bool, Any]], GuardrailConfig, Any] # Any = GuardrailEngine
WebParam = Union[bool, WebConfig]
__all__ = [
# Enums
"MemoryBackend",
"ChunkingStrategy",
"GuardrailAction",
"WebSearchProvider",
# Config classes
"MemoryConfig",
"KnowledgeConfig",
"PlanningConfig",
"ReflectionConfig",
"GuardrailConfig",
"WebConfig",
# Type aliases
"MemoryParam",
"KnowledgeParam",
"PlanningParam",
"ReflectionParam",
"GuardrailParam",
"WebParam",
]
@@ -41,7 +41,32 @@ Usage:
monitor.snapshot(ledger=result.ledger, budget=budget, messages=result.messages)
"""
def format_percent(value: float) -> str:
"""
Smart percentage formatting for context utilization display.
- For values < 0.1%: shows "<0.1%"
- For values < 1%: shows 2 decimal places (e.g., "0.02%")
- For values >= 1%: shows 1 decimal place (e.g., "5.3%")
Args:
value: A ratio (0.0 to 1.0+), NOT already multiplied by 100
Returns:
Formatted percentage string
"""
pct = value * 100
if pct < 0.1 and pct > 0:
return "<0.1%"
elif pct < 1.0:
return f"{pct:.2f}%"
else:
return f"{pct:.1f}%"
__all__ = [
# Utilities
"format_percent",
# Models
"ContextSegment",
"ContextLedger",
@@ -83,6 +108,23 @@ __all__ = [
"redact_sensitive",
"format_human_snapshot",
"format_json_snapshot",
"validate_monitor_path",
"should_include_content",
"load_ignore_patterns",
# Manager Facade (NEW)
"ContextManager",
"MultiAgentContextManager",
"create_context_manager",
"ManagerConfig",
"ContextPolicy",
"EstimationMode",
"ContextShareMode",
"ToolShareMode",
"OptimizationEvent",
"OptimizationEventType",
"EstimationMetrics",
"PerToolBudget",
"SnapshotHookData",
# Fast Context (legacy)
"FastContext",
"FastContextResult",
@@ -130,10 +172,19 @@ def __getattr__(name: str):
# Monitoring
if name in ("ContextMonitor", "MultiAgentMonitor", "redact_sensitive",
"format_human_snapshot", "format_json_snapshot"):
"format_human_snapshot", "format_json_snapshot",
"validate_monitor_path", "should_include_content", "load_ignore_patterns"):
from . import monitor
return getattr(monitor, name)
# Manager Facade
if name in ("ContextManager", "MultiAgentContextManager", "create_context_manager",
"ManagerConfig", "ContextPolicy", "EstimationMode", "ContextShareMode",
"ToolShareMode", "OptimizationEvent", "OptimizationEventType",
"EstimationMetrics", "PerToolBudget", "SnapshotHookData"):
from . import manager
return getattr(manager, name)
# Fast Context (legacy)
if name == "FastContext":
from praisonaiagents.context.fast.fast_context import FastContext
@@ -2,7 +2,7 @@
FastContext - High-level API for fast parallel code search.
This module provides the main FastContext class that can be used
standalone or integrated with the main Agent class.
standalone or integrated with the main Agent class via context= param.
Example usage:
# Standalone usage
@@ -12,13 +12,13 @@ Example usage:
result = fc.search("find authentication handlers")
print(result.to_context_string())
# With Agent integration
# With Agent integration via context= param
from praisonaiagents import Agent
from praisonaiagents.context import ManagerConfig
agent = Agent(
name="CodeAssistant",
fast_context=True,
fast_context_path="/path/to/project"
context=True, # Enable context management
)
"""
File diff suppressed because it is too large. Load diff
@@ -4,7 +4,7 @@ Context Monitor for PraisonAI Agents.
Writes runtime context to disk for inspection and debugging.
"""
from typing import Dict, List, Any, Optional, Literal
from typing import Dict, List, Any, Optional, Literal, Tuple
from pathlib import Path
from datetime import datetime
import json
@@ -15,16 +15,42 @@ from .models import (
)
def _format_pct(value: float) -> str:
"""Smart percentage formatting: <0.1% for tiny, 2 decimals for <1%, 1 decimal otherwise."""
if value < 0.1 and value > 0:
return "<0.1%"
elif value < 1.0:
return f"{value:.2f}%"
else:
return f"{value:.1f}%"
# Sensitive patterns for redaction
SENSITIVE_PATTERNS = [
r'sk-[a-zA-Z0-9]{20,}', # OpenAI API keys
r'sk-proj-[a-zA-Z0-9_-]+', # OpenAI project keys
# OpenAI API keys
r'sk-[a-zA-Z0-9]{20,}',
r'sk-proj-[a-zA-Z0-9_-]+',
# Anthropic API keys
r'sk-ant-[a-zA-Z0-9_-]+',
r'sk-ant-api\d+-[a-zA-Z0-9_-]+',
# Google API keys and tokens
r'AIza[a-zA-Z0-9_-]{30,}', # Google API keys (30+ chars after prefix)
r'ya29\.[a-zA-Z0-9_-]+', # Google OAuth tokens
r'goog-[a-zA-Z0-9_-]{20,}', # Google service tokens
# AWS credentials
r'AKIA[0-9A-Z]{16}', # AWS access keys
r'[a-zA-Z0-9]{32,}', # Generic long tokens (conservative)
r'ASIA[0-9A-Z]{16}', # AWS temporary access keys
r'aws_secret_access_key["\s:=]+["\']?[^"\'\s]+',
# Azure credentials
r'[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}', # Azure GUIDs
# Generic patterns
r'[a-zA-Z0-9]{40,}', # Generic long tokens (conservative)
r'password["\s:=]+["\']?[^"\'\s]+', # Password patterns
r'secret["\s:=]+["\']?[^"\'\s]+', # Secret patterns
r'token["\s:=]+["\']?[^"\'\s]+', # Token patterns
r'api[_-]?key["\s:=]+["\']?[^"\'\s]+', # API key patterns
r'bearer\s+[a-zA-Z0-9_-]+', # Bearer tokens
r'authorization["\s:=]+["\']?[^"\'\s]+', # Authorization headers
]
@@ -44,6 +70,153 @@ def redact_sensitive(text: str) -> str:
return result
def validate_monitor_path(
path: str,
allow_absolute: bool = False,
base_dir: Optional[str] = None,
) -> Tuple[bool, str]:
"""
Validate monitor output path for security.
Prevents path traversal attacks and enforces path restrictions.
Args:
path: Path to validate
allow_absolute: Whether to allow absolute paths
base_dir: Base directory for relative paths
Returns:
Tuple of (is_valid, error_message)
"""
from pathlib import Path as PathLib
path_obj = PathLib(path)
# Check for path traversal attempts
if '..' in path_obj.parts:
return False, "Path traversal (..) not allowed"
# Check absolute path restriction
if path_obj.is_absolute() and not allow_absolute:
return False, "Absolute paths not allowed (set allow_absolute_paths=True to override)"
# If base_dir specified, ensure path stays within it
if base_dir:
base = PathLib(base_dir).resolve()
try:
# For relative paths, resolve against base
if not path_obj.is_absolute():
full_path = (base / path_obj).resolve()
else:
full_path = path_obj.resolve()
# Check if resolved path is under base
if not str(full_path).startswith(str(base)):
return False, f"Path must be within {base_dir}"
except Exception as e:
return False, f"Path validation error: {e}"
# Check for suspicious patterns
suspicious = ['/etc/', '/var/', '/usr/', '/root/', '/home/', '~']
path_str = str(path_obj).lower()
for pattern in suspicious:
if pattern in path_str and not allow_absolute:
return False, f"Suspicious path pattern: {pattern}"
return True, ""
def should_include_content(
file_path: str,
ignore_patterns: Optional[List[str]] = None,
include_patterns: Optional[List[str]] = None,
) -> bool:
"""
Check if file content should be included based on ignore/include patterns.
Respects .praisonignore and .praisoninclude rules.
Args:
file_path: Path to check
ignore_patterns: Patterns to ignore (glob-style)
include_patterns: Patterns to include (glob-style)
Returns:
True if content should be included
"""
import fnmatch
from pathlib import Path as PathLib
path = PathLib(file_path)
name = path.name
# Default: include everything
if not ignore_patterns and not include_patterns:
return True
# Check include patterns first (whitelist)
if include_patterns:
included = False
for pattern in include_patterns:
if fnmatch.fnmatch(name, pattern) or fnmatch.fnmatch(str(path), pattern):
included = True
break
if not included:
return False
# Check ignore patterns (blacklist)
if ignore_patterns:
for pattern in ignore_patterns:
if fnmatch.fnmatch(name, pattern) or fnmatch.fnmatch(str(path), pattern):
return False
return True
def load_ignore_patterns(base_dir: str = ".") -> Tuple[List[str], List[str]]:
"""
Load ignore and include patterns from .praisonignore and .praisoninclude files.
Args:
base_dir: Directory to search for pattern files
Returns:
Tuple of (ignore_patterns, include_patterns)
"""
from pathlib import Path as PathLib
ignore_patterns = []
include_patterns = []
base = PathLib(base_dir)
# Load .praisonignore
ignore_file = base / ".praisonignore"
if ignore_file.exists():
try:
content = ignore_file.read_text()
for line in content.splitlines():
line = line.strip()
if line and not line.startswith('#'):
ignore_patterns.append(line)
except Exception:
pass
# Load .praisoninclude
include_file = base / ".praisoninclude"
if include_file.exists():
try:
content = include_file.read_text()
for line in content.splitlines():
line = line.strip()
if line and not line.startswith('#'):
include_patterns.append(line)
except Exception:
pass
return ignore_patterns, include_patterns
def format_human_snapshot(snapshot: ContextSnapshot, redact: bool = True) -> str:
"""
Format snapshot as human-readable text.
@@ -95,10 +268,10 @@ def format_human_snapshot(snapshot: ContextSnapshot, redact: bool = True) -> str
tokens = snapshot.ledger.get_segment(seg)
budget = snapshot.budget.get_segment_budget(seg)
pct = (tokens / budget * 100) if budget > 0 else 0
lines.append(f"{name:<20} | {tokens:>10,} | {budget:>10,} | {pct:>7.1f}%")
lines.append(f"{name:<20} | {tokens:>10,} | {budget:>10,} | {_format_pct(pct):>8}")
lines.append("-" * 20 + "-+-" + "-" * 10 + "-+-" + "-" * 10 + "-+-" + "-" * 8)
lines.append(f"{'TOTAL':<20} | {snapshot.ledger.total:>10,} | {snapshot.budget.usable:>10,} | {snapshot.utilization * 100:>7.1f}%")
lines.append(f"{'TOTAL':<20} | {snapshot.ledger.total:>10,} | {snapshot.budget.usable:>10,} | {_format_pct(snapshot.utilization * 100):>8}")
lines.append("")
@@ -365,17 +538,17 @@ class ContextMonitor:
utilization = ledger.total / budget.usable if budget.usable > 0 else 0
if utilization >= 1.0:
warnings.append(f"CRITICAL: Context overflow! {utilization*100:.1f}% of usable budget")
warnings.append(f"CRITICAL: Context overflow! {_format_pct(utilization*100)} of usable budget")
elif utilization >= 0.9:
warnings.append(f"WARNING: Context at {utilization*100:.1f}% of usable budget")
warnings.append(f"WARNING: Context at {_format_pct(utilization*100)} of usable budget")
elif utilization >= 0.8:
warnings.append(f"NOTICE: Context at {utilization*100:.1f}% of usable budget")
warnings.append(f"NOTICE: Context at {_format_pct(utilization*100)} of usable budget")
# Check individual segments
if budget.tool_outputs > 0:
tool_util = ledger.tool_outputs / budget.tool_outputs
if tool_util >= 0.9:
warnings.append(f"Tool outputs at {tool_util*100:.1f}% of budget")
warnings.append(f"Tool outputs at {_format_pct(tool_util*100)} of budget")
return warnings
@@ -16,6 +16,18 @@ from praisonaiagents.knowledge.chunking import Chunking
# Lazy loading for protocols and registries to avoid import overhead
_LAZY_IMPORTS = {
# Models and Protocols (new)
"SearchResultItem": ("praisonaiagents.knowledge.models", "SearchResultItem"),
"SearchResult": ("praisonaiagents.knowledge.models", "SearchResult"),
"AddResult": ("praisonaiagents.knowledge.models", "AddResult"),
"normalize_search_item": ("praisonaiagents.knowledge.models", "normalize_search_item"),
"normalize_search_result": ("praisonaiagents.knowledge.models", "normalize_search_result"),
"normalize_to_dict": ("praisonaiagents.knowledge.models", "normalize_to_dict"),
"KnowledgeStoreProtocol": ("praisonaiagents.knowledge.protocols", "KnowledgeStoreProtocol"),
"KnowledgeBackendError": ("praisonaiagents.knowledge.protocols", "KnowledgeBackendError"),
"ScopeRequiredError": ("praisonaiagents.knowledge.protocols", "ScopeRequiredError"),
"BackendNotAvailableError": ("praisonaiagents.knowledge.protocols", "BackendNotAvailableError"),
# Readers
"Document": ("praisonaiagents.knowledge.readers", "Document"),
"ReaderProtocol": ("praisonaiagents.knowledge.readers", "ReaderProtocol"),
@@ -85,6 +97,17 @@ __all__ = [
# Core
"Knowledge",
"Chunking",
# Models and Protocols
"SearchResultItem",
"SearchResult",
"AddResult",
"normalize_search_item",
"normalize_search_result",
"normalize_to_dict",
"KnowledgeStoreProtocol",
"KnowledgeBackendError",
"ScopeRequiredError",
"BackendNotAvailableError",
# Readers
"Document",
"ReaderProtocol",
@@ -0,0 +1,38 @@
"""
Knowledge Store Adapters for PraisonAI Agents.
Provides adapter implementations for various knowledge backends.
All adapters implement KnowledgeStoreProtocol.
Adapters are lazy-loaded to avoid importing heavy dependencies.
"""
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .mem0_adapter import Mem0Adapter
# Lazy loading for adapters
_LAZY_IMPORTS = {
"Mem0Adapter": ("praisonaiagents.knowledge.adapters.mem0_adapter", "Mem0Adapter"),
}
def __getattr__(name: str):
"""Lazy load adapters."""
if name in _LAZY_IMPORTS:
module_path, attr_name = _LAZY_IMPORTS[name]
import importlib
module = importlib.import_module(module_path)
return getattr(module, attr_name)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def __dir__():
"""List available attributes."""
return list(_LAZY_IMPORTS.keys())
__all__ = [
"Mem0Adapter",
]
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff. Show more