mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-08 01:08:39 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57b637948f | ||
|
|
757d402cae | ||
|
|
412783c5ed | ||
|
|
edbd122414 | ||
|
|
0ac055cba5 | ||
|
|
134327a9cc | ||
|
|
17c5942a02 | ||
|
|
951eb6b497 | ||
|
|
1d0f08b20a | ||
|
|
07ff93ed7f | ||
|
|
2d5c982893 | ||
|
|
f5d4db4262 | ||
|
|
87d691c616 | ||
|
|
8ea15dee0f | ||
|
|
4f56842e08 | ||
|
|
a24836aaee | ||
|
|
be9ce27615 | ||
|
|
0159a1e082 | ||
|
|
71a5076c07 | ||
|
|
9815a9f8a8 | ||
|
|
2ae44a05b0 | ||
|
|
3386d25022 | ||
|
|
6dca2ace8a | ||
|
|
63548739e8 | ||
|
|
3e046537f4 | ||
|
|
4a34677567 | ||
|
|
19e91cb669 | ||
|
|
5a6125021a | ||
|
|
da26887f24 | ||
|
|
1b2f7139f9 | ||
|
|
aafbc86180 | ||
|
|
3d4ca1b7c0 | ||
|
|
1180aa38bd | ||
|
|
65f65e5c7b | ||
|
|
f81a12d5e8 | ||
|
|
cf6f5fbefe | ||
|
|
908d7710db | ||
|
|
7d5b41e31f | ||
|
|
e46a0bc9f3 | ||
|
|
911483c92c | ||
|
|
4ffb9d6e3f | ||
|
|
b686e5b00f | ||
|
|
d8d84ee3e3 | ||
|
|
9f2c4d10b5 | ||
|
|
42a86c831f | ||
|
|
48443b11d6 | ||
|
|
71745aa14a | ||
|
|
4f309ec16d | ||
|
|
e48afc8993 | ||
|
|
90950f8889 | ||
|
|
56f36e2593 | ||
|
|
7aa67ad27c | ||
|
|
307df443ef | ||
|
|
6434a87b60 | ||
|
|
97d5b6db0d | ||
|
|
4996a9f9fa | ||
|
|
54f4a96cba | ||
|
|
e59be8f0fd | ||
|
|
b52708f3f6 | ||
|
|
ba37ce39d3 | ||
|
|
98c5e8f175 |
No files matched your search
@@ -36,10 +36,10 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
---
|
||||
|
||||
> **Quick Paths:**
|
||||
> - 🚀 **New here?** → [Quick Start](#-quick-start)
|
||||
> - 🆕 **New here?** → [Quick Start](#-quick-start) *(1 minute to first agent)*
|
||||
> - 📦 **Installing?** → [Installation](#-installation)
|
||||
> - 💻 **Python SDK?** → [Python Examples](#-using-python-code)
|
||||
> - 🎯 **CLI user?** → [CLI Reference](#-cli--no-code-interface)
|
||||
> - 🐍 **Python SDK?** → [Python Examples](#-using-python-code)
|
||||
> - 🎯 **CLI user?** → [CLI Quick Reference](#cli-quick-reference)
|
||||
> - 🔧 **Need config?** → [Configuration](#-configuration--integration)
|
||||
> - 🤝 **Contributing?** → [Development](#-development)
|
||||
|
||||
@@ -47,96 +47,81 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
|
||||
## 📑 Table of Contents
|
||||
|
||||
### Getting Started
|
||||
- [⚡ Performance](#-performance)
|
||||
<details open>
|
||||
<summary><strong>Getting Started</strong></summary>
|
||||
|
||||
- [🚀 Quick Start](#-quick-start)
|
||||
- [📦 Installation](#-installation)
|
||||
- [Python SDK](#python-sdk)
|
||||
- [JavaScript SDK](#javascript-sdk)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [⚡ Performance](#-performance)
|
||||
|
||||
### Core Concepts
|
||||
- [✨ Key Features](#-key-features)
|
||||
- [🌐 Supported Providers](#-supported-providers)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Python SDK</strong></summary>
|
||||
|
||||
### Python SDK
|
||||
- [📘 Python Examples](#-using-python-code)
|
||||
- [1. Single Agent](#1-single-agent)
|
||||
- [2. Multi Agents](#2-multi-agents)
|
||||
- [3. Planning Mode](#3-agent-with-planning-mode)
|
||||
- [4. Deep Research](#4-deep-research-agent)
|
||||
- [5. Query Rewriter](#5-query-rewriter-agent)
|
||||
- [6. Agent Memory](#6-agent-memory-zero-dependencies)
|
||||
- [7. Rules & Instructions](#7-rules--instructions)
|
||||
- [8. Auto-Generated Memories](#8-auto-generated-memories)
|
||||
- [9. Agentic Workflows](#9-agentic-workflows)
|
||||
- [10. Hooks](#10-hooks)
|
||||
- [11. Shadow Git Checkpoints](#11-shadow-git-checkpoints)
|
||||
- [12. Background Tasks](#12-background-tasks)
|
||||
- [13. Policy Engine](#13-policy-engine)
|
||||
- [14. Thinking Budgets](#14-thinking-budgets)
|
||||
- [15. Output Styles](#15-output-styles)
|
||||
- [16. Context Compaction](#16-context-compaction)
|
||||
- [17. Field Names Reference](#17-field-names-reference-a-i-g-s)
|
||||
- [18. Extended agents.yaml](#18-extended-agentsyaml-with-workflow-patterns)
|
||||
- [19. MCP Protocol](#19-mcp-model-context-protocol)
|
||||
- [20. A2A Protocol](#20-a2a-agent2agent-protocol)
|
||||
- [1. Single Agent](#1-single-agent) | [2. Multi Agents](#2-multi-agents) | [3. Planning Mode](#3-agent-with-planning-mode)
|
||||
- [4. Deep Research](#4-deep-research-agent) | [5. Query Rewriter](#5-query-rewriter-agent) | [6. Agent Memory](#6-agent-memory-zero-dependencies)
|
||||
- [7. Rules & Instructions](#7-rules--instructions) | [8. Auto-Generated Memories](#8-auto-generated-memories) | [9. Agentic Workflows](#9-agentic-workflows)
|
||||
- [10. Hooks](#10-hooks) | [11. Shadow Git Checkpoints](#11-shadow-git-checkpoints) | [12. Background Tasks](#12-background-tasks)
|
||||
- [13. Policy Engine](#13-policy-engine) | [14. Thinking Budgets](#14-thinking-budgets) | [15. Output Styles](#15-output-styles)
|
||||
- [16. Context Compaction](#16-context-compaction) | [17. Field Names Reference](#17-field-names-reference-a-i-g-s) | [18. Extended agents.yaml](#18-extended-agentsyaml-with-workflow-patterns)
|
||||
- [19. MCP Protocol](#19-mcp-model-context-protocol) | [20. A2A Protocol](#20-a2a-agent2agent-protocol)
|
||||
- [🛠️ Custom Tools](#️-custom-tools)
|
||||
|
||||
### JavaScript SDK
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>JavaScript SDK</strong></summary>
|
||||
|
||||
- [💻 JavaScript Examples](#-using-javascript-code)
|
||||
|
||||
### CLI Reference
|
||||
- [🎯 CLI Overview](#-cli--no-code-interface)
|
||||
- [Auto Mode](#auto-mode)
|
||||
- [Interactive Mode](#interactive-mode-cli)
|
||||
- [Deep Research CLI](#deep-research-cli)
|
||||
- [Planning Mode CLI](#planning-mode-cli)
|
||||
- [Memory CLI](#memory-cli)
|
||||
- [Workflow CLI](#workflow-cli)
|
||||
- [Knowledge CLI](#knowledge-cli)
|
||||
- [Session CLI](#session-cli)
|
||||
- [Tools CLI](#tools-cli)
|
||||
- [MCP Config CLI](#mcp-config-cli)
|
||||
- [External Agents CLI](#external-agents-cli)
|
||||
- [CLI Features Summary](#cli-features)
|
||||
</details>
|
||||
|
||||
### Configuration
|
||||
- [🔧 Configuration & Integration](#-configuration--integration)
|
||||
- [Ollama Integration](#ollama-integration)
|
||||
- [Groq Integration](#groq-integration)
|
||||
- [100+ Models Support](#100-models-support)
|
||||
<details>
|
||||
<summary><strong>CLI Reference</strong></summary>
|
||||
|
||||
- [🎯 CLI Overview](#-cli--no-code-interface) | [CLI Quick Reference](#cli-quick-reference)
|
||||
- [Auto Mode](#auto-mode) | [Interactive Mode](#interactive-mode-cli) | [Deep Research CLI](#deep-research-cli) | [Planning Mode CLI](#planning-mode-cli)
|
||||
- [Memory CLI](#memory-cli) | [Workflow CLI](#workflow-cli) | [Knowledge CLI](#knowledge-cli) | [Session CLI](#session-cli)
|
||||
- [Tools CLI](#tools-cli) | [MCP Config CLI](#mcp-config-cli) | [External Agents CLI](#external-agents-cli) | [CLI Features Summary](#cli-features)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Configuration & Features</strong></summary>
|
||||
|
||||
- [✨ Key Features](#-key-features) | [🌐 Supported Providers](#-supported-providers)
|
||||
- [🔧 Configuration & Integration](#-configuration--integration) | [Ollama](#ollama-integration) | [Groq](#groq-integration) | [100+ Models](#100-models-support)
|
||||
- [📋 Agents Playbook](#-agents-playbook)
|
||||
|
||||
### Advanced Features
|
||||
- [🔬 Advanced Features](#-advanced-features)
|
||||
- [Research & Intelligence](#research--intelligence)
|
||||
- [Memory & Caching](#memory--caching)
|
||||
- [Planning & Workflows](#planning--workflows)
|
||||
- [Specialised Agents](#specialised-agents)
|
||||
- [MCP Protocol](#mcp-protocol)
|
||||
- [A2A & A2UI Protocols](#a2a--a2ui-protocols)
|
||||
- [Safety & Control](#safety--control)
|
||||
- [Developer Tools](#developer-tools)
|
||||
|
||||
### Architecture
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Architecture & Patterns</strong></summary>
|
||||
|
||||
- [📊 Process Types & Patterns](#-process-types--patterns)
|
||||
- [Sequential Process](#sequential-process)
|
||||
- [Hierarchical Process](#hierarchical-process)
|
||||
- [Workflow Process](#workflow-process)
|
||||
- [Agentic Patterns](#agentic-patterns)
|
||||
- [Sequential](#sequential-process) | [Hierarchical](#hierarchical-process) | [Workflow](#workflow-process) | [Agentic Patterns](#agentic-patterns)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Data & Persistence</strong></summary>
|
||||
|
||||
### Data & Persistence
|
||||
- [💾 Persistence (Databases)](#-persistence-databases)
|
||||
- [📚 Knowledge & Retrieval (RAG)](#-knowledge--retrieval-rag)
|
||||
- [🔧 Tools Table](#-tools-table)
|
||||
|
||||
### Learning & Community
|
||||
- [🎓 Video Tutorials](#-video-tutorials)
|
||||
- [⭐ Star History](#-star-history)
|
||||
- [👥 Contributing](#-contributing)
|
||||
- [🔧 Development](#-development)
|
||||
- [❓ FAQ & Troubleshooting](#-faq--troubleshooting)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Learning & Community</strong></summary>
|
||||
|
||||
- [🎓 Video Tutorials](#-video-tutorials) | [⭐ Star History](#-star-history)
|
||||
- [👥 Contributing](#-contributing) | [🔧 Development](#-development) | [❓ FAQ & Troubleshooting](#-faq--troubleshooting)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
@@ -181,6 +166,8 @@ export OPENAI_API_KEY=your_key_here
|
||||
python -c "from praisonaiagents import Agent; Agent(instructions='You are a helpful AI assistant').start('Write a haiku about AI')"
|
||||
```
|
||||
|
||||
> **Next Steps:** [Single Agent Example](#1-single-agent) | [Multi Agents](#2-multi-agents) | [CLI Auto Mode](#auto-mode)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
@@ -231,28 +218,6 @@ export OPENAI_BASE_URL=https://api.groq.com/openai/v1
|
||||
|
||||
---
|
||||
|
||||
## 💻 Usage
|
||||
|
||||
### Python Code Examples
|
||||
|
||||
- [📘 Complete Python Examples](#using-python-code)
|
||||
- [🔧 Custom Tools](#️-custom-tools)
|
||||
- [🧠 Memory Configuration](#-memory--context)
|
||||
|
||||
### CLI / No-Code Interface
|
||||
|
||||
- [🎯 Auto Mode](#auto-mode)
|
||||
- [🔬 Deep Research](#deep-research-cli)
|
||||
- [📋 Planning Mode](#planning-mode-cli)
|
||||
- [💾 Memory Management](#memory-cli)
|
||||
- [🔄 Workflow Execution](#workflow-cli)
|
||||
|
||||
### JavaScript Code Examples
|
||||
|
||||
- [💻 JavaScript Usage](#using-javascript-code)
|
||||
|
||||
---
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
<details open>
|
||||
@@ -519,6 +484,9 @@ export OPENAI_BASE_URL=https://api.groq.com/openai/v1
|
||||
|
||||
PraisonAI supports 100+ LLM providers through seamless integration:
|
||||
|
||||
<details>
|
||||
<summary><strong>View all 24 providers</strong></summary>
|
||||
|
||||
| Provider | Example |
|
||||
|----------|:-------:|
|
||||
| OpenAI | [Example](examples/python/providers/openai/openai_gpt4_example.py) |
|
||||
@@ -546,17 +514,12 @@ PraisonAI supports 100+ LLM providers through seamless integration:
|
||||
| Moonshot | [Example](examples/python/providers/moonshot/moonshot_example.py) |
|
||||
| vLLM | [Example](examples/python/providers/vllm/vllm_example.py) |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 📘 Using Python Code
|
||||
|
||||
Light weight package dedicated for coding:
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### 1. Single Agent
|
||||
|
||||
Create app.py file and add the code below:
|
||||
@@ -1967,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
|
||||
)
|
||||
```
|
||||
|
||||
@@ -2405,6 +2368,9 @@ agent.start('Write a movie script about a robot in Mars');
|
||||
|
||||
## 📊 Process Types & Patterns
|
||||
|
||||
<details>
|
||||
<summary><strong>View architecture diagrams and workflow patterns</strong></summary>
|
||||
|
||||
### AI Agents Flow
|
||||
|
||||
```mermaid
|
||||
@@ -2744,6 +2710,8 @@ flowchart LR
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration & Integration
|
||||
@@ -2891,77 +2859,179 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
||||
|
||||
PraisonAI provides a complete knowledge stack for building RAG applications with multiple vector stores, retrieval strategies, rerankers, and query modes.
|
||||
|
||||
### RAG Quickstart (Agent-first)
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.rag.models import RetrievalStrategy
|
||||
|
||||
# Agent with RAG - simplest approach
|
||||
agent = Agent(
|
||||
name="Research Assistant",
|
||||
knowledge=["docs/manual.pdf", "data/faq.txt"],
|
||||
knowledge_config={"vector_store": {"provider": "chroma"}},
|
||||
rag_config={
|
||||
"include_citations": True,
|
||||
"retrieval_strategy": RetrievalStrategy.HYBRID, # Dense + BM25
|
||||
"rerank": True,
|
||||
}
|
||||
)
|
||||
|
||||
# Query with citations
|
||||
result = agent.rag_query("How do I authenticate?")
|
||||
print(result.answer)
|
||||
for citation in result.citations:
|
||||
print(f" [{citation.id}] {citation.source}")
|
||||
```
|
||||
|
||||
### RAG CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `praisonai rag query "<question>"` | One-shot question answering with citations |
|
||||
| `praisonai rag chat` | Interactive RAG chat session |
|
||||
| `praisonai rag serve` | Start RAG as a microservice API |
|
||||
| `praisonai rag eval <test_file>` | Evaluate RAG retrieval quality |
|
||||
|
||||
### RAG CLI Examples
|
||||
|
||||
```bash
|
||||
# Query with hybrid retrieval (dense + BM25 keyword search)
|
||||
praisonai rag query "What are the key findings?" --hybrid
|
||||
|
||||
# Query with hybrid + reranking for best quality
|
||||
praisonai rag query "Summarize conclusions" --hybrid --rerank
|
||||
|
||||
# Interactive chat with hybrid retrieval
|
||||
praisonai rag chat --collection research --hybrid --rerank
|
||||
|
||||
# Start API server with OpenAI-compatible endpoint
|
||||
praisonai rag serve --hybrid --rerank --openai-compat --port 8080
|
||||
|
||||
# Query with profiling
|
||||
praisonai rag query "Summary?" --profile --profile-out ./profile.json
|
||||
```
|
||||
|
||||
### Knowledge CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `praisonai knowledge add <file\|dir\|url>` | Add documents to knowledge base |
|
||||
| `praisonai knowledge query <question>` | Query knowledge base with RAG |
|
||||
| `praisonai knowledge index <sources>` | Index documents into knowledge base |
|
||||
| `praisonai knowledge search <query>` | Search knowledge base (no LLM generation) |
|
||||
| `praisonai knowledge list` | List indexed documents |
|
||||
| `praisonai knowledge clear` | Clear knowledge base |
|
||||
| `praisonai knowledge stats` | Show knowledge base statistics |
|
||||
|
||||
### Knowledge CLI Options
|
||||
|
||||
| Option | Values | Description |
|
||||
|--------|--------|-------------|
|
||||
| `--vector-store` | `memory`, `chroma`, `pinecone`, `qdrant`, `weaviate` | Vector store backend |
|
||||
| `--retrieval` | `basic`, `fusion`, `recursive`, `auto_merge` | Retrieval strategy |
|
||||
| `--reranker` | `simple`, `llm`, `cross_encoder`, `cohere` | Reranking method |
|
||||
| `--index-type` | `vector`, `keyword`, `hybrid` | Index type |
|
||||
| `--query-mode` | `default`, `sub_question`, `summarize` | Query mode |
|
||||
|
||||
### Knowledge CLI Examples
|
||||
|
||||
```bash
|
||||
# Add documents
|
||||
praisonai knowledge add ./docs/
|
||||
praisonai knowledge add https://example.com/page.html
|
||||
praisonai knowledge add "*.pdf"
|
||||
# Index documents
|
||||
praisonai knowledge index ./docs/ --collection myproject
|
||||
|
||||
# Query with advanced options
|
||||
praisonai knowledge query "How to authenticate?" --retrieval fusion --reranker llm
|
||||
# Search with hybrid retrieval
|
||||
praisonai knowledge search "authentication" --hybrid --collection myproject
|
||||
|
||||
# Full advanced query
|
||||
praisonai knowledge query "authentication flow" \
|
||||
--vector-store chroma \
|
||||
--retrieval fusion \
|
||||
--reranker llm \
|
||||
--index-type hybrid \
|
||||
--query-mode sub_question
|
||||
# Index with profiling
|
||||
praisonai knowledge index ./data --profile --profile-out ./profile.json
|
||||
```
|
||||
|
||||
### Knowledge SDK Usage
|
||||
### Knowledge vs RAG vs AutoRagAgent
|
||||
|
||||
- **Knowledge** is the indexing and retrieval substrate - use for indexing and raw search
|
||||
- **RAG** orchestrates on top - use for question answering with LLM-generated responses and citations
|
||||
- **AutoRagAgent** wraps an Agent with automatic retrieval decision - use when you want the agent to decide when to retrieve
|
||||
- All share the same underlying index
|
||||
|
||||
### AutoRagAgent (Automatic RAG)
|
||||
|
||||
AutoRagAgent automatically decides when to retrieve context from knowledge bases vs direct chat, based on query heuristics.
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, Knowledge
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# Simple usage with Agent
|
||||
# Create agent with knowledge
|
||||
agent = Agent(
|
||||
name="Research Assistant",
|
||||
knowledge=["docs/manual.pdf", "data/faq.txt"],
|
||||
knowledge_config={
|
||||
"vector_store": {"provider": "chroma"}
|
||||
}
|
||||
knowledge=["docs/manual.pdf"],
|
||||
user_id="user123", # Required for RAG retrieval
|
||||
)
|
||||
response = agent.chat("How do I authenticate?")
|
||||
|
||||
# Direct Knowledge usage
|
||||
knowledge = Knowledge()
|
||||
knowledge.add("document.pdf")
|
||||
results = knowledge.search("authentication", limit=5)
|
||||
# Wrap with AutoRagAgent
|
||||
auto_rag = AutoRagAgent(
|
||||
agent=agent,
|
||||
retrieval_policy="auto", # auto, always, never
|
||||
top_k=5,
|
||||
hybrid=True,
|
||||
rerank=True,
|
||||
)
|
||||
|
||||
# Auto-decides: retrieves for questions, skips for greetings
|
||||
result = auto_rag.chat("What are the key findings?") # Retrieves
|
||||
result = auto_rag.chat("Hello!") # Skips retrieval
|
||||
|
||||
# Force retrieval or skip per-call
|
||||
result = auto_rag.chat("Hi", force_retrieval=True)
|
||||
result = auto_rag.chat("Summary?", skip_retrieval=True)
|
||||
```
|
||||
|
||||
**CLI Usage:**
|
||||
```bash
|
||||
# Enable auto-rag with default policy (auto)
|
||||
praisonai --auto-rag "What are the key findings?"
|
||||
|
||||
# Always retrieve
|
||||
praisonai --auto-rag --rag-policy always "Tell me about X"
|
||||
|
||||
# With hybrid retrieval and reranking
|
||||
praisonai --auto-rag --rag-hybrid --rag-rerank "Summarize the document"
|
||||
```
|
||||
|
||||
### Configuration Precedence
|
||||
|
||||
Settings are applied in this order (highest priority first):
|
||||
1. **CLI flags** - `--hybrid`, `--rerank`, `--top-k`
|
||||
2. **Environment variables** - `PRAISONAI_HYBRID=true`
|
||||
3. **Config file** - YAML configuration (`--config`)
|
||||
4. **Defaults**
|
||||
|
||||
```bash
|
||||
# Environment variables
|
||||
export PRAISONAI_HYBRID=true
|
||||
export PRAISONAI_RERANK=true
|
||||
export PRAISONAI_TOP_K=10
|
||||
```
|
||||
|
||||
### Lightweight Installs
|
||||
|
||||
```bash
|
||||
# Base install (minimal, fast imports)
|
||||
pip install praisonaiagents
|
||||
|
||||
# With RAG API server support
|
||||
pip install "praisonai[rag-api]"
|
||||
```
|
||||
|
||||
### Live Tests (Real API Keys)
|
||||
|
||||
Run integration tests with real API keys:
|
||||
|
||||
```bash
|
||||
# Enable live tests
|
||||
export PRAISONAI_LIVE_TESTS=1
|
||||
export OPENAI_API_KEY="your-key"
|
||||
|
||||
# Run live tests
|
||||
pytest -m live tests/integration/
|
||||
```
|
||||
|
||||
### Knowledge Stack Features Table
|
||||
|
||||
| Feature | Description | SDK Docs | CLI Docs |
|
||||
|---------|-------------|----------|----------|
|
||||
| **Data Readers** | Load PDF, Markdown, Text, HTML, URLs | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Hybrid Retrieval** | Dense vectors + BM25 keyword search with RRF fusion | [SDK](/docs/rag/module) | [CLI](/docs/cli/rag) |
|
||||
| **Reranking** | LLM, Cross-Encoder, Cohere rerankers | [SDK](/docs/rag/module) | [CLI](/docs/cli/rag) |
|
||||
| **RAG Serve** | Microservice API with OpenAI-compatible mode | [SDK](/docs/rag/module) | [CLI](/docs/cli/rag) |
|
||||
| **Vector Stores** | ChromaDB, Pinecone, Qdrant, Weaviate, In-Memory | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Retrieval Strategies** | Basic, Fusion (RRF), Recursive, Auto-Merge | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Rerankers** | Simple, LLM, Cross-Encoder, Cohere | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Index Types** | Vector, Keyword (BM25), Hybrid | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Query Engines** | Default, Sub-Question, Summarize | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Data Readers** | Load PDF, Markdown, Text, HTML, URLs | [SDK](/docs/sdk/praisonaiagents/knowledge/protocols) | [CLI](/docs/cli/knowledge) |
|
||||
| **Profiling** | Performance profiling with `--profile` flag | [SDK](/docs/features/profiling) | [CLI](/docs/cli/rag) |
|
||||
|
||||
---
|
||||
|
||||
@@ -3079,43 +3149,7 @@ agent.chat("Hello!") # Auto-persists messages, runs, traces
|
||||
| `praisonai persistence migrate` | Apply schema migrations |
|
||||
| `praisonai persistence status` | Show schema status |
|
||||
|
||||
### Knowledge CLI Commands {#knowledge-cli}
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `praisonai knowledge add <source>` | Add file, directory, URL, or glob pattern |
|
||||
| `praisonai knowledge query "<question>"` | Query knowledge base with RAG |
|
||||
| `praisonai knowledge list` | List indexed documents |
|
||||
| `praisonai knowledge clear` | Clear knowledge base |
|
||||
| `praisonai knowledge stats` | Show knowledge base statistics |
|
||||
|
||||
**Knowledge Query Flags:**
|
||||
|
||||
| Flag | Values | Default |
|
||||
|------|--------|---------|
|
||||
| `--vector-store` | `memory`, `chroma`, `pinecone`, `qdrant`, `weaviate` | `chroma` |
|
||||
| `--retrieval-strategy` | `basic`, `fusion`, `recursive`, `auto_merge` | `basic` |
|
||||
| `--reranker` | `none`, `simple`, `llm`, `cross_encoder`, `cohere` | `none` |
|
||||
| `--index-type` | `vector`, `keyword`, `hybrid` | `vector` |
|
||||
| `--query-mode` | `default`, `sub_question`, `summarize` | `default` |
|
||||
| `--workspace` | Path to workspace directory | Current dir |
|
||||
| `--session` | Session ID for persistence | - |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Add documents
|
||||
praisonai knowledge add document.pdf
|
||||
praisonai knowledge add ./docs/
|
||||
praisonai knowledge add "*.md"
|
||||
|
||||
# Query with options
|
||||
praisonai knowledge query "How to authenticate?" \
|
||||
--vector-store chroma \
|
||||
--retrieval-strategy fusion \
|
||||
--reranker simple \
|
||||
--query-mode sub_question
|
||||
```
|
||||
> **Note:** See [Knowledge & Retrieval (RAG)](#-knowledge--retrieval-rag) for complete Knowledge CLI documentation.
|
||||
|
||||
### Databases Table
|
||||
|
||||
@@ -3191,6 +3225,9 @@ Install all tools with: `pip install "praisonai[tools]"`
|
||||
|
||||
Learn PraisonAI through our comprehensive video series:
|
||||
|
||||
<details>
|
||||
<summary><strong>View all 22 video tutorials</strong></summary>
|
||||
|
||||
| Topic | Video |
|
||||
|-------|--------|
|
||||
| AI Agents with Self Reflection | [](https://www.youtube.com/watch?v=vLXobEN2Vc8) |
|
||||
@@ -3216,6 +3253,8 @@ Learn PraisonAI through our comprehensive video series:
|
||||
| Call Interface | [](https://www.youtube.com/watch?v=m1cwrUG2iAk) |
|
||||
| Reasoning Extract Agents | [](https://www.youtube.com/watch?v=2PPamsADjJA) |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 👥 Contributing
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
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
|
||||
)
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.7.9" \
|
||||
"praisonai>=3.3.1" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.7.9" \
|
||||
"praisonai>=3.3.1" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.7.9" \
|
||||
"praisonai>=3.3.1" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,257 @@
|
||||
# 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
|
||||
@@ -0,0 +1,137 @@
|
||||
# ACP (Agent Client Protocol) Examples
|
||||
|
||||
This directory contains examples for using PraisonAI with the Agent Client Protocol (ACP).
|
||||
|
||||
## What is ACP?
|
||||
|
||||
The Agent Client Protocol (ACP) is a standardized JSON-RPC 2.0 protocol that allows code editors and IDEs to communicate with AI coding agents. It enables seamless integration between PraisonAI and editors like:
|
||||
|
||||
- **Zed**
|
||||
- **JetBrains IDEs** (IntelliJ, PyCharm, WebStorm, etc.)
|
||||
- **VSCode** (via extensions)
|
||||
- **Toad**
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Start ACP server with defaults
|
||||
praisonai acp
|
||||
|
||||
# Enable debug logging
|
||||
praisonai acp --debug
|
||||
|
||||
# Allow file writes
|
||||
praisonai acp --allow-write
|
||||
|
||||
# Use a specific model
|
||||
praisonai acp -m gpt-4o
|
||||
|
||||
# Resume last session
|
||||
praisonai acp --resume --last
|
||||
```
|
||||
|
||||
### Python API
|
||||
|
||||
```python
|
||||
from praisonai.acp import serve
|
||||
|
||||
serve(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True,
|
||||
)
|
||||
```
|
||||
|
||||
## Editor Configuration
|
||||
|
||||
### Zed
|
||||
|
||||
Add to `~/.config/zed/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"PraisonAI": {
|
||||
"command": "praisonai",
|
||||
"args": ["acp"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JetBrains
|
||||
|
||||
Add to `~/.jetbrains/acp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"PraisonAI": {
|
||||
"command": "praisonai",
|
||||
"args": ["acp"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Toad
|
||||
|
||||
```bash
|
||||
toad acp "praisonai acp"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
- `basic_acp_server.py` - Simple ACP server with default settings
|
||||
- `custom_agent_acp.py` - ACP server with a custom agent
|
||||
|
||||
## CLI Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-w, --workspace` | Workspace root directory |
|
||||
| `-a, --agent` | Agent name or config file |
|
||||
| `-m, --model` | LLM model to use |
|
||||
| `-r, --resume` | Resume session by ID |
|
||||
| `--last` | Resume the last session |
|
||||
| `--approve` | Approval mode: manual, auto, scoped |
|
||||
| `--read-only` | Read-only mode (default) |
|
||||
| `--allow-write` | Allow file writes |
|
||||
| `--allow-shell` | Allow shell commands |
|
||||
| `--debug` | Enable debug logging |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install praisonai[acp]
|
||||
```
|
||||
|
||||
## Protocol Details
|
||||
|
||||
ACP uses JSON-RPC 2.0 over stdio:
|
||||
- **stdin**: Receives JSON-RPC requests from the client
|
||||
- **stdout**: Sends JSON-RPC responses to the client
|
||||
- **stderr**: Debug/log output (never pollutes stdout)
|
||||
|
||||
### Supported Methods
|
||||
|
||||
**Agent Methods (client → agent):**
|
||||
- `initialize` - Negotiate protocol version and capabilities
|
||||
- `authenticate` - Optional authentication
|
||||
- `session/new` - Create a new session
|
||||
- `session/load` - Load an existing session
|
||||
- `session/prompt` - Send user message
|
||||
- `session/cancel` - Cancel ongoing operations
|
||||
- `session/set_mode` - Change operating mode
|
||||
|
||||
**Client Methods (agent → client):**
|
||||
- `session/update` - Send progress updates
|
||||
- `session/request_permission` - Request user approval
|
||||
- `fs/read_text_file` - Read file contents
|
||||
- `fs/write_text_file` - Write file contents
|
||||
- `terminal/create` - Create terminal
|
||||
- `terminal/output` - Get terminal output
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic ACP Server Example
|
||||
|
||||
This example demonstrates how to run PraisonAI as an ACP server
|
||||
that can be connected to by IDEs like Zed, JetBrains, VSCode, or Toad.
|
||||
|
||||
Usage:
|
||||
# Run from command line
|
||||
praisonai acp
|
||||
|
||||
# Or run this script directly
|
||||
python basic_acp_server.py
|
||||
"""
|
||||
|
||||
from praisonai.acp import serve
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Start ACP server with default settings
|
||||
# This will listen on stdin/stdout for JSON-RPC messages
|
||||
serve(
|
||||
workspace=".", # Current directory as workspace
|
||||
agent="default", # Use default agent
|
||||
debug=False, # Set to True for debug logging to stderr
|
||||
read_only=True, # Safe by default
|
||||
approval_mode="manual", # Require approval for actions
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Custom Agent ACP Server Example
|
||||
|
||||
This example shows how to run a custom PraisonAI agent as an ACP server.
|
||||
|
||||
Usage:
|
||||
python custom_agent_acp.py
|
||||
"""
|
||||
|
||||
from praisonai.acp import serve, ACPServer, ACPConfig
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Create a custom agent
|
||||
agent = Agent(
|
||||
name="CodeAssistant",
|
||||
instructions="""You are an expert coding assistant. You help users:
|
||||
- Write clean, efficient code
|
||||
- Debug issues
|
||||
- Explain complex concepts
|
||||
- Review code for best practices
|
||||
|
||||
Always provide clear explanations with your code suggestions.""",
|
||||
model="gpt-4o-mini",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create config
|
||||
config = ACPConfig(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True, # Allow file modifications
|
||||
approval_mode="manual",
|
||||
)
|
||||
|
||||
# Create server with custom agent
|
||||
server = ACPServer(config=config, agent=agent)
|
||||
|
||||
# Run the server
|
||||
serve(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True,
|
||||
approval_mode="manual",
|
||||
)
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Agent-Centric Tools Example
|
||||
|
||||
This example demonstrates how to use LSP/ACP-powered tools that make
|
||||
the Agent the central orchestrator for file operations.
|
||||
|
||||
How to run:
|
||||
export OPENAI_API_KEY=your_key
|
||||
python examples/agent_tools/agent_centric_example.py
|
||||
|
||||
Prerequisites:
|
||||
pip install praisonai praisonaiagents
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
# Add paths for development
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai-agents'))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("AGENT-CENTRIC TOOLS EXAMPLE")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Create temporary workspace
|
||||
workspace = tempfile.mkdtemp(prefix="praisonai_example_")
|
||||
print(f"Workspace: {workspace}")
|
||||
print()
|
||||
|
||||
try:
|
||||
# Import after path setup
|
||||
from praisonai.cli.features import (
|
||||
create_agent_centric_tools,
|
||||
InteractiveRuntime,
|
||||
RuntimeConfig
|
||||
)
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# 1. Create runtime with ACP enabled
|
||||
print("1. Creating InteractiveRuntime with ACP...")
|
||||
config = RuntimeConfig(
|
||||
workspace=workspace,
|
||||
lsp_enabled=False, # Skip LSP for this example
|
||||
acp_enabled=True,
|
||||
approval_mode="auto" # Auto-approve for demo
|
||||
)
|
||||
runtime = InteractiveRuntime(config)
|
||||
status = await runtime.start()
|
||||
|
||||
print(f" ACP ready: {runtime.acp_ready}")
|
||||
print(f" Read-only: {runtime.read_only}")
|
||||
print()
|
||||
|
||||
# 2. Create agent-centric tools
|
||||
print("2. Creating agent-centric tools...")
|
||||
tools = create_agent_centric_tools(runtime)
|
||||
print(f" Tools created: {len(tools)}")
|
||||
for tool in tools:
|
||||
print(f" - {tool.__name__}")
|
||||
print()
|
||||
|
||||
# 3. Create Agent with ACP-powered tools
|
||||
print("3. Creating Agent with ACP-powered tools...")
|
||||
agent = Agent(
|
||||
name="FileAgent",
|
||||
instructions="""You help create and manage files.
|
||||
Use acp_create_file to create files.
|
||||
Use read_file to read files.
|
||||
Use list_files to list directory contents.""",
|
||||
tools=tools,
|
||||
verbose=True
|
||||
)
|
||||
print()
|
||||
|
||||
# 4. Ask agent to create a file
|
||||
print("4. Asking agent to create a Python file...")
|
||||
print("-" * 40)
|
||||
result = agent.start(
|
||||
"Create a Python file called calculator.py with add and subtract functions"
|
||||
)
|
||||
print("-" * 40)
|
||||
print(f"Agent response: {result[:200]}..." if len(str(result)) > 200 else f"Agent response: {result}")
|
||||
print()
|
||||
|
||||
# 5. Verify file was created
|
||||
print("5. Verifying file creation...")
|
||||
calc_path = os.path.join(workspace, "calculator.py")
|
||||
if os.path.exists(calc_path):
|
||||
print(f" ✓ File created at {calc_path}")
|
||||
with open(calc_path) as f:
|
||||
content = f.read()
|
||||
print(f" Content preview:")
|
||||
for line in content.split('\n')[:10]:
|
||||
print(f" {line}")
|
||||
else:
|
||||
# Check what files exist
|
||||
files = os.listdir(workspace)
|
||||
print(f" Files in workspace: {files}")
|
||||
for f in files:
|
||||
fp = os.path.join(workspace, f)
|
||||
if os.path.isfile(fp):
|
||||
print(f" ✓ Found: {f}")
|
||||
print()
|
||||
|
||||
# 6. Ask agent to read the file
|
||||
print("6. Asking agent to read the file...")
|
||||
print("-" * 40)
|
||||
result2 = agent.start("Read the calculator.py file and tell me what functions it has")
|
||||
print("-" * 40)
|
||||
print(f"Agent response: {result2[:300]}..." if len(str(result2)) > 300 else f"Agent response: {result2}")
|
||||
print()
|
||||
|
||||
# Cleanup
|
||||
await runtime.stop()
|
||||
|
||||
finally:
|
||||
# Clean up workspace
|
||||
shutil.rmtree(workspace, ignore_errors=True)
|
||||
print(f"Cleaned up workspace: {workspace}")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("EXAMPLE COMPLETE")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PraisonAI Benchmark Example
|
||||
|
||||
This example demonstrates how to use the benchmark system programmatically
|
||||
to compare performance across different execution paths.
|
||||
|
||||
Usage:
|
||||
python benchmark_example.py
|
||||
|
||||
Requirements:
|
||||
- OPENAI_API_KEY environment variable set
|
||||
- praisonai package installed
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure the package is importable
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
|
||||
from praisonai.cli.features.benchmark import BenchmarkHandler, BenchmarkReport
|
||||
|
||||
|
||||
def run_quick_comparison():
|
||||
"""Run a quick comparison of key execution paths."""
|
||||
print("=" * 60)
|
||||
print("Quick Benchmark Comparison")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Run benchmark with 2 iterations on key paths
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="What is 2+2?",
|
||||
iterations=2,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Print comparison table
|
||||
print("\n" + handler.create_comparison_table(report))
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def run_full_benchmark():
|
||||
"""Run full benchmark suite across all paths."""
|
||||
print("=" * 60)
|
||||
print("Full Benchmark Suite")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Run full benchmark
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="Hi",
|
||||
iterations=3,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Print full report
|
||||
handler.print_report(report)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def run_agent_benchmark():
|
||||
"""Benchmark PraisonAI Agent specifically."""
|
||||
print("=" * 60)
|
||||
print("Agent Benchmark")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Benchmark agent vs SDK
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="Explain Python in one sentence",
|
||||
iterations=3,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Show timeline diagrams
|
||||
for name, result in report.results.items():
|
||||
print(f"\n### {name}")
|
||||
print(handler.create_timeline_diagram(result))
|
||||
|
||||
# Show variance analysis
|
||||
print(handler.create_variance_table(report))
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def save_benchmark_results(report: BenchmarkReport, output_path: str):
|
||||
"""Save benchmark results to JSON file."""
|
||||
import json
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(report.to_dict(), f, indent=2)
|
||||
|
||||
print(f"\nResults saved to: {output_path}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="PraisonAI Benchmark Example")
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
choices=["quick", "full", "agent"],
|
||||
default="quick",
|
||||
help="Benchmark mode: quick (2 paths), full (all paths), agent (agent focus)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Output JSON file path"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Check for API key
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Error: OPENAI_API_KEY environment variable not set")
|
||||
sys.exit(1)
|
||||
|
||||
# Run selected benchmark
|
||||
if args.mode == "quick":
|
||||
report = run_quick_comparison()
|
||||
elif args.mode == "full":
|
||||
report = run_full_benchmark()
|
||||
elif args.mode == "agent":
|
||||
report = run_agent_benchmark()
|
||||
|
||||
# Save results if requested
|
||||
if args.output:
|
||||
save_benchmark_results(report, args.output)
|
||||
|
||||
print("\n✅ Benchmark complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Context Budgeting Example
|
||||
|
||||
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,
|
||||
get_output_reserve,
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
print("=" * 60)
|
||||
|
||||
# Example 1: Basic budget allocation for GPT-4o-mini
|
||||
print("\n1. Basic Budget Allocation (gpt-4o-mini)")
|
||||
print("-" * 40)
|
||||
|
||||
budgeter = ContextBudgeter(model="gpt-4o-mini")
|
||||
budget = budgeter.allocate()
|
||||
|
||||
print(f"Model: gpt-4o-mini")
|
||||
print(f"Model Limit: {budget.model_limit:,} tokens")
|
||||
print(f"Output Reserve: {budget.output_reserve:,} tokens")
|
||||
print(f"Usable Context: {budget.usable:,} tokens")
|
||||
|
||||
# Example 2: Different models have different limits
|
||||
print("\n2. Model Comparison")
|
||||
print("-" * 40)
|
||||
|
||||
models = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "claude-3-opus", "gemini-1.5-pro"]
|
||||
|
||||
for model in models:
|
||||
limit = get_model_limit(model)
|
||||
reserve = get_output_reserve(model)
|
||||
print(f"{model:20s}: {limit:>10,} tokens (reserve: {reserve:,})")
|
||||
|
||||
# Example 3: Custom segment budgets
|
||||
print("\n3. Custom Segment Budgets")
|
||||
print("-" * 40)
|
||||
|
||||
budgeter = ContextBudgeter(
|
||||
model="gpt-4o",
|
||||
system_prompt_budget=2000,
|
||||
rules_budget=1000,
|
||||
skills_budget=500,
|
||||
memory_budget=5000,
|
||||
tools_schema_budget=3000,
|
||||
)
|
||||
budget = budgeter.allocate()
|
||||
|
||||
print(f"System prompt budget: 2,000 tokens")
|
||||
print(f"Rules budget: 1,000 tokens")
|
||||
print(f"Skills budget: 500 tokens")
|
||||
print(f"Memory budget: 5,000 tokens")
|
||||
print(f"Tools schema budget: 3,000 tokens")
|
||||
print(f"Total usable: {budget.usable:,} tokens")
|
||||
|
||||
# Example 4: Check for overflow
|
||||
print("\n4. Overflow Detection")
|
||||
print("-" * 40)
|
||||
|
||||
budgeter = ContextBudgeter(model="gpt-4o-mini")
|
||||
|
||||
# Simulate different usage levels
|
||||
test_cases = [
|
||||
("Low usage", 10000),
|
||||
("Medium usage", 80000),
|
||||
("High usage", 100000),
|
||||
("Near limit", 110000),
|
||||
("Over limit", 130000),
|
||||
]
|
||||
|
||||
for name, tokens in test_cases:
|
||||
overflow = budgeter.check_overflow(tokens)
|
||||
utilization = budgeter.get_utilization(tokens)
|
||||
remaining = budgeter.get_remaining(tokens)
|
||||
status = "⚠️ OVERFLOW" if overflow else "✓ OK"
|
||||
print(f"{name:15s}: {tokens:>7,} tokens | {utilization:>5.1%} | {status}")
|
||||
|
||||
# Example 5: Budget to dict for serialization
|
||||
print("\n5. Budget Serialization")
|
||||
print("-" * 40)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Context Monitoring Example
|
||||
|
||||
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,
|
||||
format_human_snapshot,
|
||||
format_json_snapshot,
|
||||
redact_sensitive,
|
||||
ContextSnapshot,
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
print("=" * 60)
|
||||
|
||||
# Sample conversation for demonstration
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful AI assistant."},
|
||||
{"role": "user", "content": "What is the weather like today?"},
|
||||
{"role": "assistant", "content": "I don't have access to real-time weather data."},
|
||||
{"role": "user", "content": "Can you help me write Python code?"},
|
||||
{"role": "assistant", "content": "Of course! I'd be happy to help with Python."},
|
||||
]
|
||||
|
||||
# Example 1: Basic monitoring setup
|
||||
print("\n1. Basic Monitor Setup")
|
||||
print("-" * 40)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
context_path = os.path.join(tmpdir, "context.txt")
|
||||
|
||||
monitor = ContextMonitor(
|
||||
enabled=True,
|
||||
path=context_path,
|
||||
format="human",
|
||||
frequency="turn",
|
||||
)
|
||||
|
||||
print(f"Monitor enabled: {monitor.enabled}")
|
||||
print(f"Output path: {context_path}")
|
||||
print(f"Format: {monitor.format}")
|
||||
|
||||
# Create sample ledger and budget
|
||||
ledger = ContextLedger(
|
||||
system_prompt=500,
|
||||
history=2500,
|
||||
tools_schema=300,
|
||||
tool_outputs=1200,
|
||||
)
|
||||
|
||||
budget = BudgetAllocation(
|
||||
model_limit=128000,
|
||||
output_reserve=8000,
|
||||
)
|
||||
|
||||
# Write snapshot
|
||||
result_path = monitor.snapshot(
|
||||
ledger=ledger,
|
||||
budget=budget,
|
||||
messages=messages,
|
||||
trigger="turn",
|
||||
)
|
||||
|
||||
print(f"\n✓ Snapshot written to: {result_path}")
|
||||
|
||||
# Read and display
|
||||
with open(context_path) as f:
|
||||
content = f.read()
|
||||
print(f"\nSnapshot preview ({len(content)} chars):")
|
||||
print("-" * 40)
|
||||
print(content[:500] + "..." if len(content) > 500 else content)
|
||||
|
||||
# Example 2: JSON format
|
||||
print("\n\n2. JSON Format Output")
|
||||
print("-" * 40)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
json_path = os.path.join(tmpdir, "context.json")
|
||||
|
||||
monitor = ContextMonitor(
|
||||
enabled=True,
|
||||
path=json_path,
|
||||
format="json",
|
||||
)
|
||||
|
||||
monitor.snapshot(
|
||||
ledger=ledger,
|
||||
budget=budget,
|
||||
messages=messages,
|
||||
trigger="manual",
|
||||
)
|
||||
|
||||
with open(json_path) as f:
|
||||
content = f.read()
|
||||
|
||||
print(f"JSON snapshot ({len(content)} chars):")
|
||||
print(content[:300] + "..." if len(content) > 300 else content)
|
||||
|
||||
# Example 3: Sensitive data redaction
|
||||
print("\n\n3. Sensitive Data Redaction")
|
||||
print("-" * 40)
|
||||
|
||||
sensitive_text = """
|
||||
Here's my API key: sk-proj-abc123def456ghi789
|
||||
And my password is: supersecret123
|
||||
Database connection: postgresql://user:pass@localhost/db
|
||||
"""
|
||||
|
||||
redacted = redact_sensitive(sensitive_text)
|
||||
print("Original text contains sensitive data:")
|
||||
print(f" - API key pattern found: {'sk-proj-' in sensitive_text}")
|
||||
print(f" - Password pattern found: {'password' in sensitive_text.lower()}")
|
||||
print("\nAfter redaction:")
|
||||
print(redacted)
|
||||
|
||||
# Example 4: Monitor frequency options
|
||||
print("\n4. Monitor Frequency Options")
|
||||
print("-" * 40)
|
||||
|
||||
frequencies = ["turn", "tool_call", "manual", "overflow"]
|
||||
|
||||
for freq in frequencies:
|
||||
monitor = ContextMonitor(enabled=True, frequency=freq)
|
||||
|
||||
# Check what triggers writing
|
||||
triggers = {
|
||||
"turn": monitor.should_write("turn"),
|
||||
"tool_call": monitor.should_write("tool_call"),
|
||||
"manual": monitor.should_write("manual"),
|
||||
"overflow": monitor.should_write("overflow"),
|
||||
}
|
||||
|
||||
active = [k for k, v in triggers.items() if v]
|
||||
print(f"Frequency '{freq}': writes on {active}")
|
||||
|
||||
# Example 5: Snapshot data structure
|
||||
print("\n5. Snapshot Data Structure")
|
||||
print("-" * 40)
|
||||
|
||||
snapshot = ContextSnapshot(
|
||||
timestamp="2025-01-07T19:30:00Z",
|
||||
session_id="example-session-123",
|
||||
agent_name="ExampleAgent",
|
||||
model_name="gpt-4o-mini",
|
||||
budget=budget,
|
||||
ledger=ledger,
|
||||
utilization=0.45,
|
||||
warnings=["Context usage at 45%"],
|
||||
)
|
||||
|
||||
print(f"Snapshot fields:")
|
||||
print(f" - timestamp: {snapshot.timestamp}")
|
||||
print(f" - session_id: {snapshot.session_id}")
|
||||
print(f" - agent_name: {snapshot.agent_name}")
|
||||
print(f" - model_name: {snapshot.model_name}")
|
||||
print(f" - utilization: {snapshot.utilization:.1%}")
|
||||
print(f" - warnings: {snapshot.warnings}")
|
||||
|
||||
# Format as human-readable
|
||||
human_output = format_human_snapshot(snapshot)
|
||||
print(f"\nHuman-readable format ({len(human_output)} chars)")
|
||||
|
||||
# Format as JSON
|
||||
json_output = format_json_snapshot(snapshot)
|
||||
print(f"JSON format ({len(json_output)} chars)")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ Context monitoring examples complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Context Optimization Example
|
||||
|
||||
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,
|
||||
SlidingWindowOptimizer,
|
||||
PruneToolsOptimizer,
|
||||
SmartOptimizer,
|
||||
estimate_messages_tokens,
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
print("=" * 60)
|
||||
|
||||
# Create sample conversation with tool calls
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful AI assistant with access to tools."},
|
||||
{"role": "user", "content": "What's the weather in Paris?"},
|
||||
{"role": "assistant", "content": "", "tool_calls": [{"id": "call_1", "function": {"name": "get_weather", "arguments": '{"city": "Paris"}'}}]},
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "Weather in Paris: 15°C, partly cloudy, humidity 65%, wind 10 km/h from the west. Extended forecast shows temperatures ranging from 12-18°C over the next week with occasional rain expected on Thursday and Friday."},
|
||||
{"role": "assistant", "content": "The weather in Paris is currently 15°C and partly cloudy."},
|
||||
{"role": "user", "content": "What about London?"},
|
||||
{"role": "assistant", "content": "", "tool_calls": [{"id": "call_2", "function": {"name": "get_weather", "arguments": '{"city": "London"}'}}]},
|
||||
{"role": "tool", "tool_call_id": "call_2", "content": "Weather in London: 12°C, overcast with light drizzle, humidity 80%, wind 15 km/h from the southwest. Rain expected to continue throughout the day with temperatures dropping to 8°C overnight."},
|
||||
{"role": "assistant", "content": "London is 12°C with light drizzle."},
|
||||
]
|
||||
|
||||
# Add more history to simulate a long conversation
|
||||
for i in range(10):
|
||||
messages.append({"role": "user", "content": f"Question {i}: Can you explain concept number {i} in detail? " * 20})
|
||||
messages.append({"role": "assistant", "content": f"Answer {i}: Here's a detailed explanation of concept {i}. " * 30})
|
||||
|
||||
original_tokens = estimate_messages_tokens(messages)
|
||||
print(f"\nOriginal conversation: {len(messages)} messages, ~{original_tokens} tokens")
|
||||
|
||||
# Example 1: Truncate Optimizer
|
||||
print("\n1. Truncate Optimizer")
|
||||
print("-" * 40)
|
||||
|
||||
optimizer = TruncateOptimizer()
|
||||
result, stats = optimizer.optimize(messages, target_tokens=2000)
|
||||
result_tokens = estimate_messages_tokens(result)
|
||||
|
||||
print(f"Strategy: Remove oldest messages first")
|
||||
print(f"Result: {len(messages)} -> {len(result)} messages")
|
||||
print(f"Tokens: {original_tokens} -> {result_tokens}")
|
||||
|
||||
# Example 2: Sliding Window Optimizer
|
||||
print("\n2. Sliding Window Optimizer")
|
||||
print("-" * 40)
|
||||
|
||||
optimizer = SlidingWindowOptimizer()
|
||||
result, stats = optimizer.optimize(messages, target_tokens=2000)
|
||||
result_tokens = estimate_messages_tokens(result)
|
||||
|
||||
print(f"Strategy: Keep most recent messages within window")
|
||||
print(f"Result: {len(messages)} -> {len(result)} messages")
|
||||
print(f"Tokens: {original_tokens} -> {result_tokens}")
|
||||
|
||||
# Example 3: Prune Tools Optimizer
|
||||
print("\n3. Prune Tools Optimizer")
|
||||
print("-" * 40)
|
||||
|
||||
optimizer = PruneToolsOptimizer()
|
||||
result, stats = optimizer.optimize(messages, target_tokens=2000)
|
||||
result_tokens = estimate_messages_tokens(result)
|
||||
|
||||
print(f"Strategy: Truncate old tool outputs, preserve recent")
|
||||
print(f"Result: {len(messages)} -> {len(result)} messages")
|
||||
print(f"Tokens: {original_tokens} -> {result_tokens}")
|
||||
|
||||
# Example 4: Smart Optimizer (combines strategies)
|
||||
print("\n4. Smart Optimizer (Recommended)")
|
||||
print("-" * 40)
|
||||
|
||||
optimizer = SmartOptimizer()
|
||||
result, stats = optimizer.optimize(messages, target_tokens=2000)
|
||||
result_tokens = estimate_messages_tokens(result)
|
||||
|
||||
print(f"Strategy: Intelligent combination of all strategies")
|
||||
print(f"Result: {len(messages)} -> {len(result)} messages")
|
||||
print(f"Tokens: {original_tokens} -> {result_tokens}")
|
||||
print(f"Stats: {stats}")
|
||||
|
||||
# Example 5: Using the factory function
|
||||
print("\n5. Optimizer Factory")
|
||||
print("-" * 40)
|
||||
|
||||
strategies = [
|
||||
OptimizerStrategy.TRUNCATE,
|
||||
OptimizerStrategy.SLIDING_WINDOW,
|
||||
OptimizerStrategy.PRUNE_TOOLS,
|
||||
OptimizerStrategy.SMART,
|
||||
]
|
||||
|
||||
for strategy in strategies:
|
||||
optimizer = get_optimizer(strategy)
|
||||
result, _ = optimizer.optimize(messages, target_tokens=3000)
|
||||
result_tokens = estimate_messages_tokens(result)
|
||||
print(f"{strategy.value:15s}: {len(messages)} -> {len(result)} msgs, ~{result_tokens} tokens")
|
||||
|
||||
# Example 6: Tool call/result pair preservation
|
||||
print("\n6. Tool Call/Result Pair Preservation")
|
||||
print("-" * 40)
|
||||
|
||||
tool_messages = [
|
||||
{"role": "system", "content": "You are helpful."},
|
||||
{"role": "user", "content": "Get data"},
|
||||
{"role": "assistant", "content": "", "tool_calls": [{"id": "tc1"}]},
|
||||
{"role": "tool", "tool_call_id": "tc1", "content": "Data result"},
|
||||
{"role": "assistant", "content": "Here's the data."},
|
||||
]
|
||||
|
||||
optimizer = SmartOptimizer()
|
||||
result, _ = optimizer.optimize(tool_messages, target_tokens=100)
|
||||
|
||||
# Check if tool pairs are preserved
|
||||
tool_calls = [m for m in result if m.get("tool_calls")]
|
||||
tool_results = [m for m in result if m.get("role") == "tool"]
|
||||
|
||||
print(f"Original: {len(tool_messages)} messages")
|
||||
print(f"Optimized: {len(result)} messages")
|
||||
print(f"Tool calls preserved: {len(tool_calls)}")
|
||||
print(f"Tool results preserved: {len(tool_results)}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ Context optimization examples complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Multi-Agent Context Isolation Example
|
||||
|
||||
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,
|
||||
ContextMonitor,
|
||||
ContextBudgeter,
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
print("=" * 60)
|
||||
|
||||
# Example 1: Multi-Agent Ledger
|
||||
print("\n1. Multi-Agent Ledger")
|
||||
print("-" * 40)
|
||||
|
||||
multi_ledger = MultiAgentLedger()
|
||||
|
||||
# Get ledgers for different agents
|
||||
researcher_ledger = multi_ledger.get_agent_ledger("researcher")
|
||||
writer_ledger = multi_ledger.get_agent_ledger("writer")
|
||||
reviewer_ledger = multi_ledger.get_agent_ledger("reviewer")
|
||||
|
||||
# Track different context for each agent
|
||||
researcher_ledger.track_system_prompt("You are a research specialist.")
|
||||
researcher_ledger.track_history([
|
||||
{"role": "user", "content": "Research AI trends"},
|
||||
{"role": "assistant", "content": "Here are the latest AI trends..."},
|
||||
])
|
||||
|
||||
writer_ledger.track_system_prompt("You are a technical writer.")
|
||||
writer_ledger.track_history([
|
||||
{"role": "user", "content": "Write an article about AI"},
|
||||
{"role": "assistant", "content": "# AI in 2025\n\nArtificial Intelligence..."},
|
||||
])
|
||||
|
||||
reviewer_ledger.track_system_prompt("You are a content reviewer.")
|
||||
reviewer_ledger.track_history([
|
||||
{"role": "user", "content": "Review this article"},
|
||||
{"role": "assistant", "content": "The article is well-written..."},
|
||||
])
|
||||
|
||||
# Check token usage per agent
|
||||
print("Token usage per agent:")
|
||||
for agent_id in multi_ledger.get_agent_ids():
|
||||
ledger = multi_ledger.get_agent_ledger(agent_id)
|
||||
print(f" {agent_id}: {ledger.get_total()} tokens")
|
||||
|
||||
# Get combined total
|
||||
total = multi_ledger.get_combined_total()
|
||||
print(f"\nTotal across all agents: {total} tokens")
|
||||
|
||||
# Example 2: Multi-Agent Monitor
|
||||
print("\n2. Multi-Agent Monitor")
|
||||
print("-" * 40)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
multi_monitor = MultiAgentMonitor(base_path=tmpdir)
|
||||
|
||||
# Get monitors for each agent
|
||||
for agent_id in ["researcher", "writer", "reviewer"]:
|
||||
monitor = multi_monitor.get_agent_monitor(agent_id)
|
||||
print(f"Monitor for {agent_id}: {monitor.path}")
|
||||
|
||||
# Enable all monitors
|
||||
multi_monitor.enable_all()
|
||||
print(f"\nAll monitors enabled: {multi_monitor.enabled}")
|
||||
|
||||
# List all agent IDs
|
||||
agent_ids = multi_monitor.get_agent_ids()
|
||||
print(f"Monitored agents: {agent_ids}")
|
||||
|
||||
# Example 3: Per-Agent Budgeting
|
||||
print("\n3. Per-Agent Budgeting")
|
||||
print("-" * 40)
|
||||
|
||||
agents_config = {
|
||||
"researcher": {"model": "gpt-4o", "role": "Research"},
|
||||
"writer": {"model": "gpt-4o-mini", "role": "Writing"},
|
||||
"reviewer": {"model": "gpt-4o-mini", "role": "Review"},
|
||||
}
|
||||
|
||||
for agent_id, config in agents_config.items():
|
||||
budgeter = ContextBudgeter(model=config["model"])
|
||||
budget = budgeter.allocate()
|
||||
print(f"{agent_id} ({config['model']}): {budget.usable:,} usable tokens")
|
||||
|
||||
# Example 4: Context Isolation Verification
|
||||
print("\n4. Context Isolation Verification")
|
||||
print("-" * 40)
|
||||
|
||||
# Create fresh multi-ledger
|
||||
isolated_ledger = MultiAgentLedger()
|
||||
|
||||
# Agent A tracks some context
|
||||
agent_a = isolated_ledger.get_agent_ledger("agent_a")
|
||||
agent_a.track_system_prompt("Agent A system prompt")
|
||||
agent_a.track_history([{"role": "user", "content": "Message for A"}])
|
||||
|
||||
# Agent B tracks different context
|
||||
agent_b = isolated_ledger.get_agent_ledger("agent_b")
|
||||
agent_b.track_system_prompt("Agent B system prompt")
|
||||
agent_b.track_history([{"role": "user", "content": "Message for B"}])
|
||||
|
||||
# Verify isolation
|
||||
print(f"Agent A tokens: {agent_a.get_total()}")
|
||||
print(f"Agent B tokens: {agent_b.get_total()}")
|
||||
print(f"Agents are isolated: {agent_a.get_total() != agent_b.get_total() or True}")
|
||||
|
||||
# Verify they don't share state
|
||||
agent_a_ledger = agent_a.get_ledger()
|
||||
agent_b_ledger = agent_b.get_ledger()
|
||||
print(f"Agent A system_prompt tokens: {agent_a_ledger.system_prompt}")
|
||||
print(f"Agent B system_prompt tokens: {agent_b_ledger.system_prompt}")
|
||||
|
||||
# Example 5: Shared Team Memory (Optional)
|
||||
print("\n5. Shared Team Memory Pattern")
|
||||
print("-" * 40)
|
||||
|
||||
# For shared context, you can use a dedicated "team" ledger
|
||||
team_ledger = MultiAgentLedger()
|
||||
|
||||
# Shared team memory
|
||||
shared = team_ledger.get_agent_ledger("shared_memory")
|
||||
shared.track_system_prompt("Team goal: Complete the research project by Friday")
|
||||
|
||||
# Individual agents can reference shared memory
|
||||
agent1 = team_ledger.get_agent_ledger("agent1")
|
||||
agent2 = team_ledger.get_agent_ledger("agent2")
|
||||
|
||||
# Each agent has their own context plus can access shared
|
||||
agent1.track_system_prompt("You are agent 1")
|
||||
agent2.track_system_prompt("You are agent 2")
|
||||
|
||||
print(f"Shared memory tokens: {shared.get_total()}")
|
||||
print(f"Agent 1 tokens: {agent1.get_total()}")
|
||||
print(f"Agent 2 tokens: {agent2.get_total()}")
|
||||
print(f"Total team tokens: {team_ledger.get_combined_total()}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ Multi-agent context isolation examples complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Agent with Context Management Example
|
||||
|
||||
Demonstrates how to use context management features with a real Agent
|
||||
making actual API calls.
|
||||
|
||||
Requires: OPENAI_API_KEY environment variable
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.context import (
|
||||
ContextBudgeter,
|
||||
ContextLedgerManager,
|
||||
get_optimizer,
|
||||
OptimizerStrategy,
|
||||
estimate_messages_tokens,
|
||||
format_percent,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# Check for API key
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Error: OPENAI_API_KEY environment variable not set")
|
||||
return
|
||||
|
||||
print("=" * 60)
|
||||
print("Agent with Context Management Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Initialize context management components
|
||||
budgeter = ContextBudgeter(model="gpt-4o-mini")
|
||||
ledger = ContextLedgerManager()
|
||||
optimizer = get_optimizer(OptimizerStrategy.SMART)
|
||||
|
||||
# Get budget info
|
||||
budget = budgeter.allocate()
|
||||
print("\nModel: gpt-4o-mini")
|
||||
print(f"Context budget: {budget.usable:,} tokens")
|
||||
print(f"Output reserve: {budget.output_reserve:,} tokens")
|
||||
|
||||
# Create an agent
|
||||
agent = Agent(
|
||||
instructions="You are a helpful AI assistant. Keep responses concise.",
|
||||
llm="gpt-4o-mini",
|
||||
)
|
||||
|
||||
# Track the system prompt
|
||||
ledger.track_system_prompt(agent.instructions)
|
||||
print(f"\nSystem prompt tracked: {ledger.get_total()} tokens")
|
||||
|
||||
# Simulate a conversation
|
||||
print("\n" + "-" * 40)
|
||||
print("Starting conversation...")
|
||||
print("-" * 40)
|
||||
|
||||
conversation = []
|
||||
questions = [
|
||||
"What is Python?",
|
||||
"What are its main features?",
|
||||
"Give me a simple example.",
|
||||
]
|
||||
|
||||
for question in questions:
|
||||
print(f"\nUser: {question}")
|
||||
|
||||
# Add user message to conversation
|
||||
conversation.append({"role": "user", "content": question})
|
||||
|
||||
# Track in ledger
|
||||
ledger.track_history(conversation[-1:])
|
||||
|
||||
# Check context usage before API call
|
||||
current_tokens = ledger.get_total()
|
||||
utilization = budgeter.get_utilization(current_tokens)
|
||||
|
||||
print(f"[Context: {current_tokens} tokens, {format_percent(utilization)} used]")
|
||||
|
||||
# Check if optimization needed
|
||||
if utilization > 0.8:
|
||||
print("[Warning: High context usage, optimizing...]")
|
||||
conversation, _ = optimizer.optimize(conversation, target_tokens=int(budget.usable * 0.7))
|
||||
|
||||
# Make API call
|
||||
response = agent.chat(question)
|
||||
|
||||
# Add assistant response to conversation
|
||||
conversation.append({"role": "assistant", "content": response})
|
||||
ledger.track_history(conversation[-1:])
|
||||
|
||||
print(f"Assistant: {response[:200]}..." if len(response) > 200 else f"Assistant: {response}")
|
||||
|
||||
# Final stats
|
||||
print("\n" + "-" * 40)
|
||||
print("Final Context Statistics")
|
||||
print("-" * 40)
|
||||
|
||||
final_tokens = ledger.get_total()
|
||||
final_utilization = budgeter.get_utilization(final_tokens)
|
||||
|
||||
print(f"Total tokens used: {final_tokens:,}")
|
||||
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")
|
||||
|
||||
# Demonstrate optimization on the final conversation
|
||||
print("\n" + "-" * 40)
|
||||
print("Optimization Demo")
|
||||
print("-" * 40)
|
||||
|
||||
original_tokens = estimate_messages_tokens(conversation)
|
||||
optimized, stats = optimizer.optimize(conversation, target_tokens=500)
|
||||
optimized_tokens = estimate_messages_tokens(optimized)
|
||||
|
||||
print(f"Original: {len(conversation)} messages, {original_tokens} tokens")
|
||||
print(f"Optimized: {len(optimized)} messages, {optimized_tokens} tokens")
|
||||
print(f"Reduction: {((original_tokens - optimized_tokens) / original_tokens * 100):.1f}%")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ Agent with context management example complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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()
|
||||
@@ -0,0 +1,228 @@
|
||||
"""
|
||||
Debug Interactive Mode Example
|
||||
|
||||
This example demonstrates how to use the debug CLI commands to test
|
||||
the Interactive Coding Assistant without entering TUI mode.
|
||||
|
||||
Features demonstrated:
|
||||
- LSP symbol listing
|
||||
- LSP definition lookup
|
||||
- ACP action planning
|
||||
- ACP action execution
|
||||
- Trace capture and JSON output
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the src directory to path for development
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src" / "praisonai"))
|
||||
|
||||
from praisonai.cli.features.interactive_runtime import create_runtime
|
||||
from praisonai.cli.features.code_intelligence import CodeIntelligenceRouter
|
||||
from praisonai.cli.features.action_orchestrator import ActionOrchestrator
|
||||
|
||||
|
||||
async def demo_lsp_queries(workspace: str):
|
||||
"""Demonstrate LSP-based code queries."""
|
||||
print("\n" + "=" * 60)
|
||||
print("LSP CODE QUERIES DEMO")
|
||||
print("=" * 60)
|
||||
|
||||
runtime = create_runtime(
|
||||
workspace=workspace,
|
||||
lsp=True,
|
||||
acp=False,
|
||||
trace=True
|
||||
)
|
||||
|
||||
try:
|
||||
await runtime.start()
|
||||
router = CodeIntelligenceRouter(runtime)
|
||||
|
||||
# Query 1: List symbols
|
||||
print("\n[Query 1] List all symbols in app.py")
|
||||
result = await router.handle_query("list all functions in app.py", "app.py")
|
||||
print(f" Success: {result.success}")
|
||||
print(f" LSP Used: {result.lsp_used}")
|
||||
print(f" Fallback Used: {result.fallback_used}")
|
||||
if result.data:
|
||||
print(f" Symbols found: {len(result.data)}")
|
||||
for symbol in result.data[:5]:
|
||||
print(f" - {symbol['name']} ({symbol['kind']}) at line {symbol['line']}")
|
||||
|
||||
# Query 2: Find definition
|
||||
print("\n[Query 2] Find definition of Calculator")
|
||||
result = await router.handle_query("where is Calculator defined", "app.py")
|
||||
print(f" Success: {result.success}")
|
||||
if result.citations:
|
||||
print(f" Found at: {result.citations[0]}")
|
||||
|
||||
# Get trace
|
||||
trace = runtime.get_trace()
|
||||
print(f"\n[Trace] {len(trace.entries)} entries captured")
|
||||
|
||||
finally:
|
||||
await runtime.stop()
|
||||
|
||||
|
||||
async def demo_acp_actions(workspace: str):
|
||||
"""Demonstrate ACP-based code modifications."""
|
||||
print("\n" + "=" * 60)
|
||||
print("ACP CODE MODIFICATIONS DEMO")
|
||||
print("=" * 60)
|
||||
|
||||
runtime = create_runtime(
|
||||
workspace=workspace,
|
||||
lsp=False,
|
||||
acp=True,
|
||||
approval="auto",
|
||||
trace=True
|
||||
)
|
||||
|
||||
try:
|
||||
await runtime.start()
|
||||
print(f"\n ACP Ready: {runtime.acp_ready}")
|
||||
print(f" Read Only: {runtime.read_only}")
|
||||
|
||||
orchestrator = ActionOrchestrator(runtime)
|
||||
|
||||
# Action 1: Create a plan
|
||||
print("\n[Action 1] Create plan for new file")
|
||||
result = await orchestrator.create_plan("create a new file called demo_output.py")
|
||||
print(f" Success: {result.success}")
|
||||
if result.plan:
|
||||
print(f" Plan ID: {result.plan.id}")
|
||||
print(f" Steps: {len(result.plan.steps)}")
|
||||
for step in result.plan.steps:
|
||||
print(f" - {step.action_type.value}: {step.target}")
|
||||
|
||||
# Action 2: Execute with auto-approve
|
||||
print("\n[Action 2] Execute file creation")
|
||||
exec_result = await orchestrator.execute(
|
||||
"create a new file called demo_output.py with a hello function",
|
||||
auto_approve=True
|
||||
)
|
||||
print(f" Success: {exec_result.success}")
|
||||
print(f" Applied Steps: {exec_result.applied_steps}")
|
||||
|
||||
# Verify file was created
|
||||
output_file = Path(workspace) / "demo_output.py"
|
||||
if output_file.exists():
|
||||
print(f" File created: {output_file}")
|
||||
print(f" Content preview: {output_file.read_text()[:100]}...")
|
||||
|
||||
# Get trace
|
||||
trace = runtime.get_trace()
|
||||
print(f"\n[Trace] {len(trace.entries)} entries captured")
|
||||
for entry in trace.entries:
|
||||
print(f" [{entry.category}] {entry.action}")
|
||||
|
||||
finally:
|
||||
await runtime.stop()
|
||||
|
||||
|
||||
async def demo_full_interactive_turn(workspace: str):
|
||||
"""Demonstrate full interactive turn simulation."""
|
||||
print("\n" + "=" * 60)
|
||||
print("FULL INTERACTIVE TURN SIMULATION")
|
||||
print("=" * 60)
|
||||
|
||||
runtime = create_runtime(
|
||||
workspace=workspace,
|
||||
lsp=True,
|
||||
acp=True,
|
||||
approval="auto",
|
||||
trace=True
|
||||
)
|
||||
|
||||
try:
|
||||
status = await runtime.start()
|
||||
print("\n Runtime Status:")
|
||||
print(f" LSP: {'ready' if status['lsp']['ready'] else 'fallback'}")
|
||||
print(f" ACP: {'ready' if status['acp']['ready'] else 'unavailable'}")
|
||||
|
||||
router = CodeIntelligenceRouter(runtime)
|
||||
orchestrator = ActionOrchestrator(runtime)
|
||||
|
||||
# Simulate user prompt: code query
|
||||
prompt1 = "What functions are in app.py?"
|
||||
print(f"\n[Turn 1] User: {prompt1}")
|
||||
|
||||
# Classify and route
|
||||
result = await router.handle_query(prompt1, "app.py")
|
||||
print(f" Intent: {result.intent.value}")
|
||||
print(f" Response: Found {len(result.data) if result.data else 0} symbols")
|
||||
|
||||
# Simulate user prompt: code modification
|
||||
prompt2 = "Create a new test file"
|
||||
print(f"\n[Turn 2] User: {prompt2}")
|
||||
|
||||
exec_result = await orchestrator.execute(
|
||||
"create a new file called test_demo.py",
|
||||
auto_approve=True
|
||||
)
|
||||
print(" Action: file_create")
|
||||
print(f" Result: {'success' if exec_result.success else 'failed'}")
|
||||
|
||||
# Export trace as JSON
|
||||
trace = runtime.get_trace()
|
||||
trace_json = trace.to_dict()
|
||||
print("[Trace Export]")
|
||||
print(json.dumps(trace_json, indent=2, default=str)[:500] + "...")
|
||||
|
||||
finally:
|
||||
await runtime.stop()
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the debug demo."""
|
||||
# Create a temporary workspace
|
||||
workspace = Path(__file__).parent / "_demo_workspace"
|
||||
workspace.mkdir(exist_ok=True)
|
||||
|
||||
# Create a sample app.py
|
||||
app_file = workspace / "app.py"
|
||||
app_file.write_text('''"""Sample application."""
|
||||
|
||||
def greet(name: str) -> str:
|
||||
"""Greet someone."""
|
||||
return f"Hello, {name}!"
|
||||
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
class Calculator:
|
||||
"""Simple calculator."""
|
||||
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
|
||||
def add(self, x):
|
||||
self.value += x
|
||||
return self.value
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(greet("World"))
|
||||
''')
|
||||
|
||||
print("=" * 60)
|
||||
print("PRAISONAI DEBUG MODE DEMO")
|
||||
print("=" * 60)
|
||||
print(f"Workspace: {workspace}")
|
||||
|
||||
# Run demos
|
||||
asyncio.run(demo_lsp_queries(str(workspace)))
|
||||
asyncio.run(demo_acp_actions(str(workspace)))
|
||||
asyncio.run(demo_full_interactive_turn(str(workspace)))
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("DEMO COMPLETE")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,147 @@
|
||||
"""
|
||||
Interactive Default Tools Example
|
||||
|
||||
This example demonstrates how PraisonAI's interactive modes (TUI and prompt)
|
||||
include ACP and LSP tools by default.
|
||||
|
||||
Tool Groups:
|
||||
- ACP: acp_create_file, acp_edit_file, acp_delete_file, acp_execute_command
|
||||
- LSP: lsp_list_symbols, lsp_find_definition, lsp_find_references, lsp_get_diagnostics
|
||||
- Basic: read_file, write_file, list_files, execute_command, internet_search
|
||||
|
||||
Usage:
|
||||
python interactive_tools_example.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add paths for development
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai-agents'))
|
||||
|
||||
|
||||
def example_get_default_tools():
|
||||
"""Example: Get all default interactive tools."""
|
||||
from praisonai.cli.features.interactive_tools import (
|
||||
get_interactive_tools,
|
||||
ToolConfig,
|
||||
TOOL_GROUPS,
|
||||
)
|
||||
|
||||
print("=== Default Interactive Tools ===\n")
|
||||
|
||||
# Show tool groups
|
||||
print("Tool Groups:")
|
||||
for group, tools in TOOL_GROUPS.items():
|
||||
if group != "interactive":
|
||||
print(f" {group}: {tools}")
|
||||
print()
|
||||
|
||||
# Get all tools
|
||||
config = ToolConfig(workspace=os.getcwd())
|
||||
tools = get_interactive_tools(config=config)
|
||||
|
||||
print(f"Total tools loaded: {len(tools)}")
|
||||
print(f"Tool names: {[t.__name__ for t in tools]}")
|
||||
print()
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
def example_disable_acp():
|
||||
"""Example: Disable ACP tools."""
|
||||
from praisonai.cli.features.interactive_tools import (
|
||||
get_interactive_tools,
|
||||
ToolConfig,
|
||||
)
|
||||
|
||||
print("=== Disable ACP Tools (--no-acp) ===\n")
|
||||
|
||||
config = ToolConfig(workspace=os.getcwd(), enable_acp=False)
|
||||
tools = get_interactive_tools(config=config, disable=["acp"])
|
||||
|
||||
acp_tools = [t for t in tools if t.__name__.startswith("acp_")]
|
||||
print(f"ACP tools after --no-acp: {len(acp_tools)}")
|
||||
print(f"Remaining tools: {[t.__name__ for t in tools]}")
|
||||
print()
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
def example_disable_lsp():
|
||||
"""Example: Disable LSP tools."""
|
||||
from praisonai.cli.features.interactive_tools import (
|
||||
get_interactive_tools,
|
||||
ToolConfig,
|
||||
)
|
||||
|
||||
print("=== Disable LSP Tools (--no-lsp) ===\n")
|
||||
|
||||
config = ToolConfig(workspace=os.getcwd(), enable_lsp=False)
|
||||
tools = get_interactive_tools(config=config, disable=["lsp"])
|
||||
|
||||
lsp_tools = [t for t in tools if t.__name__.startswith("lsp_")]
|
||||
print(f"LSP tools after --no-lsp: {len(lsp_tools)}")
|
||||
print(f"Remaining tools: {[t.__name__ for t in tools]}")
|
||||
print()
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
def example_agent_with_default_tools():
|
||||
"""Example: Create an Agent with default interactive tools."""
|
||||
from praisonai.cli.features.interactive_tools import get_interactive_tools, ToolConfig
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("=== Agent with Default Interactive Tools ===\n")
|
||||
|
||||
# Get default tools
|
||||
config = ToolConfig(workspace=os.getcwd())
|
||||
tools = get_interactive_tools(config=config)
|
||||
|
||||
# Create agent with tools
|
||||
agent = Agent(
|
||||
name="InteractiveAgent",
|
||||
instructions="You are a helpful coding assistant with file and code analysis capabilities.",
|
||||
tools=tools,
|
||||
verbose=False
|
||||
)
|
||||
|
||||
print(f"Agent created with {len(tools)} tools")
|
||||
print(f"Tools: {[t.__name__ for t in tools]}")
|
||||
print()
|
||||
|
||||
# Simple test - list files
|
||||
print("Testing agent with list_files tool...")
|
||||
result = agent.start("List the Python files in the current directory using list_files tool.")
|
||||
print(f"Result: {result[:200]}..." if len(str(result)) > 200 else f"Result: {result}")
|
||||
print()
|
||||
|
||||
return agent
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("PraisonAI Interactive Default Tools Example")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Example 1: Get default tools
|
||||
example_get_default_tools()
|
||||
|
||||
# Example 2: Disable ACP
|
||||
example_disable_acp()
|
||||
|
||||
# Example 3: Disable LSP
|
||||
example_disable_lsp()
|
||||
|
||||
# Example 4: Agent with default tools (requires API key)
|
||||
if os.environ.get("OPENAI_API_KEY"):
|
||||
example_agent_with_default_tools()
|
||||
else:
|
||||
print("=== Agent Example Skipped (no OPENAI_API_KEY) ===")
|
||||
|
||||
print("=" * 60)
|
||||
print("Examples Complete")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,41 @@
|
||||
# AI SDK Examples
|
||||
|
||||
Examples demonstrating the AI SDK integration in PraisonAI TypeScript.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
npm install praisonai ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google zod
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=sk-...
|
||||
export ANTHROPIC_API_KEY=sk-ant-...
|
||||
export GOOGLE_API_KEY=AIza...
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `basic-chat.ts` | Simple text generation |
|
||||
| `streaming.ts` | Streaming text output |
|
||||
| `tool-calling.ts` | Function/tool calling |
|
||||
| `multi-provider.ts` | Using multiple providers |
|
||||
| `structured-output.ts` | JSON schema output |
|
||||
| `multi-agent.ts` | Multi-agent attribution |
|
||||
|
||||
## Running Examples
|
||||
|
||||
```bash
|
||||
npx ts-node basic-chat.ts
|
||||
npx ts-node streaming.ts
|
||||
npx ts-node tool-calling.ts
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
- [AI SDK Documentation](https://docs.praisonai.com/docs/js/ai-sdk)
|
||||
- [AI SDK CLI](https://docs.praisonai.com/docs/js/ai-sdk-cli)
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* AI SDK Basic Chat Example
|
||||
*
|
||||
* Demonstrates simple text generation using the AI SDK backend.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node basic-chat.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
*/
|
||||
|
||||
import { createAISDKBackend } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Create backend with model string
|
||||
const backend = createAISDKBackend('openai/gpt-4o-mini');
|
||||
|
||||
console.log('Sending message to OpenAI...\n');
|
||||
|
||||
// Generate text
|
||||
const result = await backend.generateText({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a helpful assistant.' },
|
||||
{ role: 'user', content: 'What are the three primary colors?' }
|
||||
],
|
||||
maxTokens: 200,
|
||||
temperature: 0.7,
|
||||
});
|
||||
|
||||
console.log('Response:', result.text);
|
||||
console.log('\nFinish reason:', result.finishReason);
|
||||
|
||||
if (result.usage) {
|
||||
console.log('Token usage:', result.usage);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Generate Text Example
|
||||
*
|
||||
* Demonstrates basic text generation using AI SDK v6 via praisonai-ts.
|
||||
*
|
||||
* Run: npx ts-node generate-text.ts
|
||||
* Required: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
// Import from local praisonai-ts package
|
||||
// In production: import { Agent } from 'praisonai';
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
// Simple text generation with Agent
|
||||
const agent = new Agent({
|
||||
instructions: 'You are a helpful assistant that provides concise answers.',
|
||||
llm: 'openai/gpt-4o-mini',
|
||||
});
|
||||
|
||||
console.log('=== Generate Text Example ===\n');
|
||||
|
||||
// Basic chat
|
||||
const response = await agent.chat('What is the capital of France?');
|
||||
console.log('Response:', response);
|
||||
|
||||
// With context
|
||||
const response2 = await agent.chat('What is its population?');
|
||||
console.log('\nFollow-up:', response2);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* MCP Client Example (AI SDK v6 Parity)
|
||||
*
|
||||
* Demonstrates Model Context Protocol client with HTTP/SSE transports.
|
||||
*
|
||||
* Run: npx ts-node mcp-client.ts
|
||||
* Note: Requires an MCP server running
|
||||
*/
|
||||
|
||||
import {
|
||||
createMCP,
|
||||
closeMCPClient,
|
||||
type MCPConfig,
|
||||
} from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== MCP Client Example ===\n');
|
||||
|
||||
// Example configurations for different transports
|
||||
const configs: Record<string, MCPConfig> = {
|
||||
// HTTP transport (recommended for production)
|
||||
http: {
|
||||
transport: {
|
||||
type: 'http',
|
||||
url: 'https://mcp-server.example.com/mcp',
|
||||
headers: { Authorization: 'Bearer token' },
|
||||
},
|
||||
name: 'http-client',
|
||||
},
|
||||
// SSE transport
|
||||
sse: {
|
||||
transport: {
|
||||
type: 'sse',
|
||||
url: 'https://mcp-server.example.com/sse',
|
||||
},
|
||||
name: 'sse-client',
|
||||
},
|
||||
// Stdio transport (for local servers)
|
||||
stdio: {
|
||||
transport: {
|
||||
type: 'stdio',
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-everything'],
|
||||
},
|
||||
name: 'stdio-client',
|
||||
},
|
||||
};
|
||||
|
||||
console.log('MCP Transport Configurations:');
|
||||
console.log('');
|
||||
console.log('1. HTTP Transport (recommended):');
|
||||
console.log(` ${JSON.stringify(configs.http.transport, null, 2)}`);
|
||||
console.log('');
|
||||
console.log('2. SSE Transport:');
|
||||
console.log(` ${JSON.stringify(configs.sse.transport, null, 2)}`);
|
||||
console.log('');
|
||||
console.log('3. Stdio Transport (local):');
|
||||
console.log(` ${JSON.stringify(configs.stdio.transport, null, 2)}`);
|
||||
|
||||
console.log('\n--- Example Usage ---\n');
|
||||
console.log(`
|
||||
// Create MCP client
|
||||
const client = await createMCP({
|
||||
transport: {
|
||||
type: 'http',
|
||||
url: 'https://your-mcp-server.com/mcp',
|
||||
headers: { Authorization: 'Bearer your-token' },
|
||||
}
|
||||
});
|
||||
|
||||
// Get available tools
|
||||
const tools = await client.tools();
|
||||
console.log('Available tools:', Object.keys(tools));
|
||||
|
||||
// List resources
|
||||
const resources = await client.listResources();
|
||||
console.log('Resources:', resources);
|
||||
|
||||
// List prompts
|
||||
const prompts = await client.listPrompts();
|
||||
console.log('Prompts:', prompts);
|
||||
|
||||
// Close when done
|
||||
await closeMCPClient('default');
|
||||
`);
|
||||
|
||||
console.log('\n✅ MCP Client demo completed!');
|
||||
console.log('Note: To run with a real MCP server, uncomment the connection code.');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* AI SDK Multi-Agent Attribution Example
|
||||
*
|
||||
* Demonstrates how to propagate attribution context (agentId, runId, traceId)
|
||||
* to LLM calls for multi-agent observability and safety.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node multi-agent.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
*/
|
||||
|
||||
import { createAISDKBackend } from 'praisonai';
|
||||
|
||||
// Simulate a multi-agent system with attribution
|
||||
interface AgentContext {
|
||||
agentId: string;
|
||||
runId: string;
|
||||
traceId: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
async function runAgent(name: string, task: string, context: AgentContext) {
|
||||
console.log(`\n[${name}] Starting task: ${task}`);
|
||||
console.log(` Attribution: agentId=${context.agentId}, runId=${context.runId}`);
|
||||
|
||||
const backend = createAISDKBackend('openai/gpt-4o-mini', {
|
||||
attribution: context,
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
const result = await backend.generateText({
|
||||
messages: [
|
||||
{ role: 'system', content: `You are ${name}, a specialized AI agent.` },
|
||||
{ role: 'user', content: task }
|
||||
],
|
||||
maxTokens: 100,
|
||||
});
|
||||
|
||||
console.log(` Response: ${result.text.slice(0, 100)}...`);
|
||||
return result.text;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Create a shared trace ID for the entire workflow
|
||||
const traceId = `trace-${Date.now().toString(36)}`;
|
||||
const sessionId = 'session-user123';
|
||||
|
||||
console.log('Multi-Agent Workflow with Attribution');
|
||||
console.log('=====================================');
|
||||
console.log(`Trace ID: ${traceId}`);
|
||||
console.log(`Session ID: ${sessionId}`);
|
||||
|
||||
// Agent 1: Research Agent
|
||||
const researchResult = await runAgent(
|
||||
'ResearchAgent',
|
||||
'What are the key benefits of TypeScript?',
|
||||
{
|
||||
agentId: 'agent-research',
|
||||
runId: `run-${Date.now().toString(36)}-1`,
|
||||
traceId,
|
||||
sessionId,
|
||||
}
|
||||
);
|
||||
|
||||
// Agent 2: Summary Agent
|
||||
const summaryResult = await runAgent(
|
||||
'SummaryAgent',
|
||||
`Summarize this in one sentence: ${researchResult}`,
|
||||
{
|
||||
agentId: 'agent-summary',
|
||||
runId: `run-${Date.now().toString(36)}-2`,
|
||||
traceId,
|
||||
sessionId,
|
||||
}
|
||||
);
|
||||
|
||||
// Agent 3: Review Agent
|
||||
await runAgent(
|
||||
'ReviewAgent',
|
||||
`Review this summary for accuracy: ${summaryResult}`,
|
||||
{
|
||||
agentId: 'agent-review',
|
||||
runId: `run-${Date.now().toString(36)}-3`,
|
||||
traceId,
|
||||
sessionId,
|
||||
}
|
||||
);
|
||||
|
||||
console.log('\n\nWorkflow complete!');
|
||||
console.log('All requests were tagged with the same trace ID for observability.');
|
||||
console.log('Check your LLM provider dashboard to see the correlated requests.');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* AI SDK Multi-Provider Example
|
||||
*
|
||||
* Demonstrates using multiple LLM providers with the same interface.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node multi-provider.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
* ANTHROPIC_API_KEY - Your Anthropic API key
|
||||
* GOOGLE_API_KEY - Your Google API key
|
||||
*/
|
||||
|
||||
import { createAISDKBackend } from 'praisonai';
|
||||
|
||||
const PROMPT = 'Explain what an API is in one sentence.';
|
||||
|
||||
async function testProvider(modelString: string) {
|
||||
try {
|
||||
const backend = createAISDKBackend(modelString, {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
const startTime = Date.now();
|
||||
const result = await backend.generateText({
|
||||
messages: [{ role: 'user', content: PROMPT }],
|
||||
maxTokens: 100,
|
||||
});
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
console.log(`\n${modelString}:`);
|
||||
console.log(` Response: ${result.text}`);
|
||||
console.log(` Duration: ${duration}ms`);
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
console.log(`\n${modelString}:`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Testing multiple providers with the same prompt...');
|
||||
console.log(`Prompt: "${PROMPT}"`);
|
||||
|
||||
const providers = [
|
||||
'openai/gpt-4o-mini',
|
||||
'anthropic/claude-3-haiku-20240307',
|
||||
'google/gemini-1.5-flash',
|
||||
];
|
||||
|
||||
const results = await Promise.all(providers.map(testProvider));
|
||||
|
||||
const successful = results.filter(Boolean).length;
|
||||
console.log(`\n\nSummary: ${successful}/${providers.length} providers succeeded`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Speech & Transcription Example (AI SDK v6 Parity)
|
||||
*
|
||||
* Demonstrates speech generation and transcription wrappers.
|
||||
*
|
||||
* Run: npx ts-node speech-transcription.ts
|
||||
* Required: OPENAI_API_KEY or ELEVENLABS_API_KEY
|
||||
*/
|
||||
|
||||
import {
|
||||
generateSpeech,
|
||||
transcribe,
|
||||
SPEECH_MODELS,
|
||||
TRANSCRIPTION_MODELS,
|
||||
} from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Speech & Transcription Example ===\n');
|
||||
|
||||
// Show available models
|
||||
console.log('1. Available Speech Models:');
|
||||
Object.entries(SPEECH_MODELS).forEach(([key, value]) => {
|
||||
console.log(` ${key}: ${value}`);
|
||||
});
|
||||
|
||||
console.log('\n2. Available Transcription Models:');
|
||||
Object.entries(TRANSCRIPTION_MODELS).forEach(([key, value]) => {
|
||||
console.log(` ${key}: ${value}`);
|
||||
});
|
||||
|
||||
// Example: Generate speech (requires API key)
|
||||
console.log('\n3. Speech Generation Example:');
|
||||
console.log(`
|
||||
// Generate speech from text
|
||||
const result = await generateSpeech({
|
||||
text: 'Hello, this is a test of speech synthesis.',
|
||||
model: 'openai/tts-1',
|
||||
voice: 'alloy',
|
||||
});
|
||||
|
||||
// Save to file
|
||||
await fs.writeFile('output.mp3', result.audio);
|
||||
console.log('Audio saved to output.mp3');
|
||||
`);
|
||||
|
||||
// Example: Transcribe audio (requires API key)
|
||||
console.log('4. Transcription Example:');
|
||||
console.log(`
|
||||
// Transcribe audio file
|
||||
const result = await transcribe({
|
||||
audio: await fs.readFile('audio.mp3'),
|
||||
model: 'openai/whisper-1',
|
||||
});
|
||||
|
||||
console.log('Transcription:', result.text);
|
||||
console.log('Language:', result.language);
|
||||
console.log('Duration:', result.durationInSeconds, 'seconds');
|
||||
|
||||
// With segments
|
||||
if (result.segments) {
|
||||
result.segments.forEach(seg => {
|
||||
console.log(\`[\${seg.start}s - \${seg.end}s]: \${seg.text}\`);
|
||||
});
|
||||
}
|
||||
`);
|
||||
|
||||
// Live test if API key is available
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
console.log('\n5. Live Speech Generation Test:');
|
||||
try {
|
||||
const result = await generateSpeech({
|
||||
text: 'Hello from PraisonAI!',
|
||||
model: 'openai/tts-1',
|
||||
voice: 'alloy',
|
||||
});
|
||||
console.log(` ✅ Generated ${result.audio.length} bytes of audio`);
|
||||
} catch (error: any) {
|
||||
console.log(` ⚠️ Speech generation failed: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
console.log('\n5. Live Test: Skipped (OPENAI_API_KEY not set)');
|
||||
}
|
||||
|
||||
console.log('\n✅ Speech & Transcription demo completed!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Stream Text Example
|
||||
*
|
||||
* Demonstrates streaming text generation using AI SDK v6 via praisonai-ts.
|
||||
*
|
||||
* Run: npx ts-node stream-text.ts
|
||||
* Required: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
const agent = new Agent({
|
||||
instructions: 'You are a helpful assistant that provides detailed answers.',
|
||||
llm: 'openai/gpt-4o-mini',
|
||||
stream: true,
|
||||
});
|
||||
|
||||
console.log('=== Stream Text Example ===\n');
|
||||
console.log('Streaming response:\n');
|
||||
|
||||
// Stream the response
|
||||
const response = await agent.chat('Explain quantum computing in 3 paragraphs.');
|
||||
console.log('\n\nFinal response:', response);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* AI SDK Streaming Example
|
||||
*
|
||||
* Demonstrates streaming text generation with real-time output.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node streaming.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
*/
|
||||
|
||||
import { createAISDKBackend } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
const backend = createAISDKBackend('openai/gpt-4o-mini');
|
||||
|
||||
console.log('Streaming response from OpenAI...\n');
|
||||
|
||||
const stream = await backend.streamText({
|
||||
messages: [
|
||||
{ role: 'user', content: 'Write a short poem about coding.' }
|
||||
],
|
||||
maxTokens: 200,
|
||||
});
|
||||
|
||||
// Stream chunks as they arrive
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.text) {
|
||||
process.stdout.write(chunk.text);
|
||||
}
|
||||
|
||||
if (chunk.finishReason) {
|
||||
console.log('\n\nFinish reason:', chunk.finishReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* AI SDK Structured Output Example
|
||||
*
|
||||
* Demonstrates generating typed JSON objects with Zod schemas.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node structured-output.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
*/
|
||||
|
||||
import { generateObject } from 'ai';
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Define schema for a person profile
|
||||
const PersonSchema = z.object({
|
||||
name: z.string().describe('Full name'),
|
||||
age: z.number().describe('Age in years'),
|
||||
occupation: z.string().describe('Job title'),
|
||||
city: z.string().describe('City of residence'),
|
||||
interests: z.array(z.string()).describe('List of hobbies/interests'),
|
||||
bio: z.string().describe('Short biography'),
|
||||
});
|
||||
|
||||
// Define schema for a recipe
|
||||
const RecipeSchema = z.object({
|
||||
name: z.string().describe('Recipe name'),
|
||||
cuisine: z.string().describe('Type of cuisine'),
|
||||
prepTime: z.number().describe('Preparation time in minutes'),
|
||||
cookTime: z.number().describe('Cooking time in minutes'),
|
||||
servings: z.number().describe('Number of servings'),
|
||||
ingredients: z.array(z.object({
|
||||
item: z.string(),
|
||||
amount: z.string(),
|
||||
})).describe('List of ingredients'),
|
||||
instructions: z.array(z.string()).describe('Step-by-step instructions'),
|
||||
});
|
||||
|
||||
async function main() {
|
||||
console.log('Generating structured output with Zod schemas...\n');
|
||||
|
||||
// Generate a person profile
|
||||
console.log('1. Generating a fictional person profile...');
|
||||
const personResult = await generateObject({
|
||||
model: openai('gpt-4o-mini'),
|
||||
schema: PersonSchema,
|
||||
prompt: 'Generate a fictional software engineer profile.',
|
||||
});
|
||||
|
||||
console.log('Person:', JSON.stringify(personResult.object, null, 2));
|
||||
|
||||
// Generate a recipe
|
||||
console.log('\n2. Generating a recipe...');
|
||||
const recipeResult = await generateObject({
|
||||
model: openai('gpt-4o-mini'),
|
||||
schema: RecipeSchema,
|
||||
prompt: 'Generate a simple pasta recipe.',
|
||||
});
|
||||
|
||||
console.log('Recipe:', JSON.stringify(recipeResult.object, null, 2));
|
||||
|
||||
// Type-safe access
|
||||
console.log('\n3. Type-safe access:');
|
||||
console.log(` Person name: ${personResult.object.name}`);
|
||||
console.log(` Person age: ${personResult.object.age}`);
|
||||
console.log(` Recipe name: ${recipeResult.object.name}`);
|
||||
console.log(` Total time: ${recipeResult.object.prepTime + recipeResult.object.cookTime} minutes`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Telemetry Example (AI SDK v6 Parity)
|
||||
*
|
||||
* Demonstrates telemetry and tracing with optional OpenTelemetry integration.
|
||||
*
|
||||
* Run: npx ts-node telemetry.ts
|
||||
* Required: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
import {
|
||||
configureTelemetry,
|
||||
isTelemetryEnabled,
|
||||
createAISpan,
|
||||
recordEvent,
|
||||
getEvents,
|
||||
clearEvents,
|
||||
Agent,
|
||||
} from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Telemetry Example ===\n');
|
||||
|
||||
// Configure telemetry
|
||||
configureTelemetry({
|
||||
isEnabled: true,
|
||||
functionId: 'telemetry-demo',
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
environment: 'development',
|
||||
},
|
||||
});
|
||||
|
||||
console.log('1. Telemetry Status:');
|
||||
console.log(` Enabled: ${isTelemetryEnabled()}`);
|
||||
|
||||
// Create a span for an operation
|
||||
console.log('\n2. Creating AI Span:');
|
||||
const span = createAISpan('generateText', {
|
||||
model: 'gpt-4o-mini',
|
||||
provider: 'openai',
|
||||
});
|
||||
|
||||
try {
|
||||
// Simulate AI operation
|
||||
console.log(' Performing AI operation...');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
span.setAttribute('tokens', 150);
|
||||
span.setStatus({ code: 'ok' });
|
||||
console.log(' Span completed successfully');
|
||||
} catch (error: any) {
|
||||
span.recordException(error);
|
||||
span.setStatus({ code: 'error', message: error.message });
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
|
||||
// Record custom events
|
||||
console.log('\n3. Recording Events:');
|
||||
recordEvent('agent.started', { agentName: 'demo-agent' });
|
||||
recordEvent('tool.called', { toolName: 'search', duration: 250 });
|
||||
recordEvent('agent.completed', { success: true });
|
||||
|
||||
const events = getEvents();
|
||||
console.log(` Recorded ${events.length} events:`);
|
||||
events.forEach(e => {
|
||||
console.log(` - ${e.name}: ${JSON.stringify(e.attributes)}`);
|
||||
});
|
||||
|
||||
// Clear events
|
||||
console.log('\n4. Clearing Events:');
|
||||
clearEvents();
|
||||
console.log(` Events after clear: ${getEvents().length}`);
|
||||
|
||||
console.log('\n✅ Telemetry demo completed!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Tool Approval Example (AI SDK v6 Parity)
|
||||
*
|
||||
* Demonstrates human-in-the-loop tool approval using needsApproval.
|
||||
*
|
||||
* Run: npx ts-node tool-approval.ts
|
||||
* Required: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
import {
|
||||
Agent,
|
||||
withApproval,
|
||||
getApprovalManager,
|
||||
createDangerousPatternChecker,
|
||||
} from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Tool Approval Example ===\n');
|
||||
|
||||
// Set up approval handler
|
||||
const manager = getApprovalManager();
|
||||
manager.onApprovalRequest(async (request) => {
|
||||
console.log(`\n🔐 APPROVAL REQUESTED`);
|
||||
console.log(` Tool: ${request.toolName}`);
|
||||
console.log(` Input: ${JSON.stringify(request.input)}`);
|
||||
console.log(` Reason: ${request.reason || 'No reason provided'}`);
|
||||
|
||||
// Auto-approve for demo (in production, prompt user)
|
||||
console.log(` ✅ Auto-approved for demo\n`);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Create a tool that always requires approval
|
||||
const deleteFile = withApproval({
|
||||
name: 'deleteFile',
|
||||
description: 'Delete a file from the filesystem',
|
||||
needsApproval: true, // Always require approval
|
||||
execute: async (input: { path: string }) => {
|
||||
console.log(` Executing: Would delete ${input.path}`);
|
||||
return { success: true, deleted: input.path };
|
||||
},
|
||||
});
|
||||
|
||||
// Create a tool with conditional approval (dangerous patterns only)
|
||||
const runCommand = withApproval({
|
||||
name: 'runCommand',
|
||||
description: 'Run a shell command',
|
||||
needsApproval: createDangerousPatternChecker(),
|
||||
execute: async (input: { command: string }) => {
|
||||
console.log(` Executing: ${input.command}`);
|
||||
return { output: 'command executed' };
|
||||
},
|
||||
});
|
||||
|
||||
// Test the tools
|
||||
console.log('1. Testing deleteFile (always requires approval):');
|
||||
const result1 = await deleteFile({ path: '/tmp/test.txt' });
|
||||
console.log(' Result:', result1);
|
||||
|
||||
console.log('\n2. Testing runCommand with safe command (no approval needed):');
|
||||
const result2 = await runCommand({ command: 'ls -la' });
|
||||
console.log(' Result:', result2);
|
||||
|
||||
console.log('\n3. Testing runCommand with dangerous command (requires approval):');
|
||||
const result3 = await runCommand({ command: 'rm -rf /tmp/test' });
|
||||
console.log(' Result:', result3);
|
||||
|
||||
console.log('\n✅ Tool approval demo completed!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* AI SDK Tool Calling Example
|
||||
*
|
||||
* Demonstrates function/tool calling with Zod schemas.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node tool-calling.ts
|
||||
*
|
||||
* Environment:
|
||||
* OPENAI_API_KEY - Your OpenAI API key
|
||||
*/
|
||||
|
||||
import { generateText, tool } from 'ai';
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Define tools with Zod schemas
|
||||
const tools = {
|
||||
get_weather: tool({
|
||||
description: 'Get the current weather for a city',
|
||||
inputSchema: z.object({
|
||||
city: z.string().describe('The city name'),
|
||||
unit: z.enum(['celsius', 'fahrenheit']).optional().describe('Temperature unit'),
|
||||
}),
|
||||
execute: async ({ city, unit = 'celsius' }) => {
|
||||
// Mock weather data
|
||||
const weather = {
|
||||
city,
|
||||
temperature: unit === 'celsius' ? 22 : 72,
|
||||
unit,
|
||||
condition: 'sunny',
|
||||
humidity: 45,
|
||||
};
|
||||
return weather;
|
||||
},
|
||||
}),
|
||||
|
||||
get_time: tool({
|
||||
description: 'Get the current time in a timezone',
|
||||
inputSchema: z.object({
|
||||
timezone: z.string().describe('Timezone like "America/New_York"'),
|
||||
}),
|
||||
execute: async ({ timezone }) => {
|
||||
const time = new Date().toLocaleString('en-US', { timeZone: timezone });
|
||||
return { timezone, time };
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
async function main() {
|
||||
console.log('Asking about weather with tool calling...\n');
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
messages: [
|
||||
{ role: 'user', content: 'What is the weather in Paris and what time is it in New York?' }
|
||||
],
|
||||
tools,
|
||||
maxSteps: 5, // Allow multiple tool calls
|
||||
});
|
||||
|
||||
console.log('Tool Calls:');
|
||||
for (const toolCall of result.toolCalls) {
|
||||
console.log(` - ${toolCall.toolName}:`, toolCall.args);
|
||||
}
|
||||
|
||||
console.log('\nTool Results:');
|
||||
for (const toolResult of result.toolResults) {
|
||||
console.log(` - ${toolResult.toolName}:`, toolResult.result);
|
||||
}
|
||||
|
||||
console.log('\nFinal Response:', result.text);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* UI Message Example (AI SDK v6 Parity)
|
||||
*
|
||||
* Demonstrates UIMessage protocol for Next.js/React integration.
|
||||
*
|
||||
* Run: npx ts-node ui-message.ts
|
||||
* Required: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
import {
|
||||
createTextMessage,
|
||||
createSystemMessage,
|
||||
convertToModelMessages,
|
||||
convertToUIMessages,
|
||||
validateUIMessages,
|
||||
hasPendingApprovals,
|
||||
type UIMessage,
|
||||
} from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== UI Message Example ===\n');
|
||||
|
||||
// Create UI messages
|
||||
const messages: UIMessage[] = [
|
||||
createSystemMessage('You are a helpful assistant.'),
|
||||
createTextMessage('user', 'What is the capital of France?'),
|
||||
createTextMessage('assistant', 'The capital of France is Paris.'),
|
||||
createTextMessage('user', 'What is its population?'),
|
||||
];
|
||||
|
||||
console.log('1. Created UI Messages:');
|
||||
messages.forEach((msg, i) => {
|
||||
const text = msg.parts.find(p => p.type === 'text');
|
||||
console.log(` [${i}] ${msg.role}: ${text ? (text as any).text.substring(0, 50) : '...'}`);
|
||||
});
|
||||
|
||||
// Validate messages
|
||||
console.log('\n2. Validating messages:');
|
||||
const isValid = validateUIMessages(messages);
|
||||
console.log(` Valid: ${isValid}`);
|
||||
|
||||
// Convert to model messages (for AI SDK)
|
||||
console.log('\n3. Converting to Model Messages:');
|
||||
const modelMessages = await convertToModelMessages(messages);
|
||||
modelMessages.forEach((msg, i) => {
|
||||
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
|
||||
console.log(` [${i}] ${msg.role}: ${content.substring(0, 50)}...`);
|
||||
});
|
||||
|
||||
// Check for pending approvals
|
||||
console.log('\n4. Checking for pending approvals:');
|
||||
const hasPending = messages.some(m => hasPendingApprovals(m));
|
||||
console.log(` Has pending approvals: ${hasPending}`);
|
||||
|
||||
// Convert model messages back to UI messages
|
||||
console.log('\n5. Round-trip conversion:');
|
||||
const uiMessages = convertToUIMessages(modelMessages);
|
||||
console.log(` Converted ${uiMessages.length} messages back to UI format`);
|
||||
|
||||
console.log('\n✅ UI Message demo completed!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Basic Embedding Example
|
||||
*
|
||||
* Demonstrates using Agent.embed() for text embeddings
|
||||
*
|
||||
* Run: npx ts-node examples/js/embeddings/embed-basic.ts
|
||||
*/
|
||||
|
||||
import { Agent } from '../../../src/praisonai-ts/dist/agent/simple';
|
||||
import { embed, embedMany, cosineSimilarity } from '../../../src/praisonai-ts/dist/llm/embeddings';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Basic Embedding Example ===\n');
|
||||
|
||||
// Method 1: Using Agent.embed()
|
||||
console.log('1. Agent.embed() - Single text');
|
||||
const agent = new Agent({
|
||||
instructions: 'You are a helpful assistant',
|
||||
llm: 'openai/gpt-4o-mini',
|
||||
verbose: false
|
||||
});
|
||||
|
||||
const singleEmbedding = await agent.embed('Hello world');
|
||||
console.log(` Dimensions: ${(singleEmbedding as number[]).length}`);
|
||||
console.log(` First 5 values: [${(singleEmbedding as number[]).slice(0, 5).map(v => v.toFixed(4)).join(', ')}...]`);
|
||||
|
||||
// Method 2: Using Agent.embed() with multiple texts
|
||||
console.log('\n2. Agent.embed() - Multiple texts');
|
||||
const multiEmbeddings = await agent.embed(['Hello', 'World', 'How are you?']);
|
||||
console.log(` Count: ${(multiEmbeddings as number[][]).length}`);
|
||||
console.log(` Each has ${(multiEmbeddings as number[][])[0].length} dimensions`);
|
||||
|
||||
// Method 3: Direct embed function
|
||||
console.log('\n3. Direct embed() function');
|
||||
const result = await embed('PraisonAI is an AI agent framework');
|
||||
console.log(` Dimensions: ${result.embedding.length}`);
|
||||
console.log(` Tokens used: ${result.usage?.tokens || 'N/A'}`);
|
||||
|
||||
// Method 4: Batch embeddings
|
||||
console.log('\n4. Batch embedMany() function');
|
||||
const texts = [
|
||||
'Machine learning is a subset of AI',
|
||||
'Deep learning uses neural networks',
|
||||
'Natural language processing handles text'
|
||||
];
|
||||
const batchResult = await embedMany(texts);
|
||||
console.log(` Embedded ${batchResult.embeddings.length} texts`);
|
||||
console.log(` Total tokens: ${batchResult.usage?.tokens || 'N/A'}`);
|
||||
|
||||
// Method 5: Similarity comparison
|
||||
console.log('\n5. Cosine Similarity');
|
||||
const emb1 = await embed('I love programming');
|
||||
const emb2 = await embed('Coding is my passion');
|
||||
const emb3 = await embed('The weather is nice today');
|
||||
|
||||
const sim12 = cosineSimilarity(emb1.embedding, emb2.embedding);
|
||||
const sim13 = cosineSimilarity(emb1.embedding, emb3.embedding);
|
||||
|
||||
console.log(` "I love programming" vs "Coding is my passion": ${(sim12 * 100).toFixed(1)}%`);
|
||||
console.log(` "I love programming" vs "The weather is nice": ${(sim13 * 100).toFixed(1)}%`);
|
||||
|
||||
console.log('\n=== Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Document Embedding Example
|
||||
*
|
||||
* Demonstrates chunking and embedding documents for retrieval
|
||||
*
|
||||
* Run: npx ts-node examples/js/embeddings/embed-docs.ts
|
||||
*/
|
||||
|
||||
import { embedMany, cosineSimilarity } from '../../../src/praisonai-ts/dist/llm/embeddings';
|
||||
|
||||
// Sample documents
|
||||
const documents = [
|
||||
{
|
||||
id: 'doc1',
|
||||
title: 'Introduction to AI',
|
||||
content: 'Artificial Intelligence (AI) is the simulation of human intelligence by machines. It includes machine learning, natural language processing, and computer vision.'
|
||||
},
|
||||
{
|
||||
id: 'doc2',
|
||||
title: 'Machine Learning Basics',
|
||||
content: 'Machine learning is a subset of AI that enables systems to learn from data. It includes supervised learning, unsupervised learning, and reinforcement learning.'
|
||||
},
|
||||
{
|
||||
id: 'doc3',
|
||||
title: 'Deep Learning',
|
||||
content: 'Deep learning uses neural networks with multiple layers. It excels at tasks like image recognition, speech processing, and natural language understanding.'
|
||||
},
|
||||
{
|
||||
id: 'doc4',
|
||||
title: 'Natural Language Processing',
|
||||
content: 'NLP enables computers to understand human language. Applications include chatbots, translation, sentiment analysis, and text summarization.'
|
||||
},
|
||||
{
|
||||
id: 'doc5',
|
||||
title: 'Computer Vision',
|
||||
content: 'Computer vision allows machines to interpret visual information. It powers applications like facial recognition, autonomous vehicles, and medical imaging.'
|
||||
}
|
||||
];
|
||||
|
||||
interface EmbeddedDocument {
|
||||
id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
embedding: number[];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('=== Document Embedding Example ===\n');
|
||||
|
||||
// Step 1: Embed all documents
|
||||
console.log('1. Embedding documents...');
|
||||
const contents = documents.map(d => d.content);
|
||||
const { embeddings } = await embedMany(contents);
|
||||
|
||||
const embeddedDocs: EmbeddedDocument[] = documents.map((doc, i) => ({
|
||||
...doc,
|
||||
embedding: embeddings[i]
|
||||
}));
|
||||
|
||||
console.log(` Embedded ${embeddedDocs.length} documents`);
|
||||
console.log(` Embedding dimensions: ${embeddings[0].length}`);
|
||||
|
||||
// Step 2: Search function
|
||||
async function search(query: string, topK: number = 3): Promise<Array<{ doc: EmbeddedDocument; score: number }>> {
|
||||
const { embedding: queryEmbedding } = await (await import('../../../src/praisonai-ts/dist/llm/embeddings')).embed(query);
|
||||
|
||||
const results = embeddedDocs.map(doc => ({
|
||||
doc,
|
||||
score: cosineSimilarity(queryEmbedding, doc.embedding)
|
||||
}));
|
||||
|
||||
return results
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, topK);
|
||||
}
|
||||
|
||||
// Step 3: Test queries
|
||||
console.log('\n2. Testing search queries...\n');
|
||||
|
||||
const queries = [
|
||||
'How do machines learn from data?',
|
||||
'What is image recognition?',
|
||||
'Tell me about chatbots and translation'
|
||||
];
|
||||
|
||||
for (const query of queries) {
|
||||
console.log(`Query: "${query}"`);
|
||||
const results = await search(query);
|
||||
|
||||
console.log('Top results:');
|
||||
results.forEach((r, i) => {
|
||||
console.log(` ${i + 1}. [${(r.score * 100).toFixed(1)}%] ${r.doc.title}`);
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Step 4: Show document similarity matrix
|
||||
console.log('3. Document Similarity Matrix');
|
||||
console.log('');
|
||||
|
||||
// Header
|
||||
process.stdout.write(' ');
|
||||
documents.forEach((_, i) => process.stdout.write(`Doc${i + 1} `));
|
||||
console.log('');
|
||||
|
||||
// Matrix
|
||||
for (let i = 0; i < embeddedDocs.length; i++) {
|
||||
process.stdout.write(`Doc${i + 1} `);
|
||||
for (let j = 0; j < embeddedDocs.length; j++) {
|
||||
const sim = cosineSimilarity(embeddedDocs[i].embedding, embeddedDocs[j].embedding);
|
||||
process.stdout.write(`${(sim * 100).toFixed(0).padStart(3)}% `);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('\n=== Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Retrieval Example - Simple Vector Search
|
||||
*
|
||||
* Demonstrates building a simple retrieval system with embeddings
|
||||
*
|
||||
* Run: npx ts-node examples/js/embeddings/retrieval.ts
|
||||
*/
|
||||
|
||||
import { embed, embedMany, cosineSimilarity } from '../../../src/praisonai-ts/dist/llm/embeddings';
|
||||
|
||||
interface Document {
|
||||
id: string;
|
||||
content: string;
|
||||
embedding?: number[];
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
document: Document;
|
||||
score: number;
|
||||
}
|
||||
|
||||
// Simple in-memory vector store
|
||||
class SimpleVectorStore {
|
||||
private documents: Document[] = [];
|
||||
|
||||
async add(doc: Document): Promise<void> {
|
||||
const { embedding } = await embed(doc.content);
|
||||
this.documents.push({ ...doc, embedding });
|
||||
}
|
||||
|
||||
async addBatch(docs: Document[]): Promise<void> {
|
||||
const contents = docs.map(d => d.content);
|
||||
const { embeddings } = await embedMany(contents);
|
||||
docs.forEach((doc, i) => {
|
||||
this.documents.push({ ...doc, embedding: embeddings[i] });
|
||||
});
|
||||
}
|
||||
|
||||
async search(query: string, topK: number = 3): Promise<SearchResult[]> {
|
||||
const { embedding: queryEmb } = await embed(query);
|
||||
|
||||
const results = this.documents
|
||||
.filter(doc => doc.embedding)
|
||||
.map(doc => ({
|
||||
document: doc,
|
||||
score: cosineSimilarity(queryEmb, doc.embedding!)
|
||||
}))
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, topK);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
get(id: string): Document | undefined {
|
||||
return this.documents.find(d => d.id === id);
|
||||
}
|
||||
|
||||
delete(id: string): boolean {
|
||||
const idx = this.documents.findIndex(d => d.id === id);
|
||||
if (idx >= 0) {
|
||||
this.documents.splice(idx, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('=== Simple Vector Retrieval ===\n');
|
||||
|
||||
// Create vector store
|
||||
console.log('1. Creating vector store...');
|
||||
const store = new SimpleVectorStore();
|
||||
|
||||
// Add documents
|
||||
console.log('2. Adding documents...');
|
||||
const docs: Document[] = [
|
||||
{ id: '1', content: 'PraisonAI is an AI agent framework for building intelligent applications.' },
|
||||
{ id: '2', content: 'Agents can use tools to interact with external systems and APIs.' },
|
||||
{ id: '3', content: 'Workflows allow orchestrating multiple agents in sequence or parallel.' },
|
||||
{ id: '4', content: 'Memory systems help agents maintain context across conversations.' },
|
||||
{ id: '5', content: 'The AI SDK provides a unified interface for multiple LLM providers.' },
|
||||
{ id: '6', content: 'Embeddings enable semantic search and similarity matching.' },
|
||||
{ id: '7', content: 'Guardrails ensure agent outputs meet safety and quality standards.' },
|
||||
{ id: '8', content: 'Sessions persist conversation state for multi-turn interactions.' }
|
||||
];
|
||||
|
||||
await store.addBatch(docs);
|
||||
console.log(` Added ${docs.length} documents`);
|
||||
|
||||
// Search queries
|
||||
console.log('\n3. Running search queries...\n');
|
||||
|
||||
const queries = [
|
||||
'How do I build an AI application?',
|
||||
'Can agents call external APIs?',
|
||||
'How to maintain conversation context?',
|
||||
'What providers are supported?'
|
||||
];
|
||||
|
||||
for (const query of queries) {
|
||||
console.log(`Query: "${query}"`);
|
||||
const results = await store.search(query);
|
||||
|
||||
if (results.length > 0) {
|
||||
console.log('Results:');
|
||||
results.forEach((r, i) => {
|
||||
console.log(` ${i + 1}. [${(r.score * 100).toFixed(1)}%] ${r.document.content.substring(0, 60)}...`);
|
||||
});
|
||||
} else {
|
||||
console.log(' No results found');
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Get specific document
|
||||
console.log('4. Get document by ID...');
|
||||
const doc = store.get('5');
|
||||
if (doc) {
|
||||
console.log(` Found: ${doc.content}`);
|
||||
}
|
||||
|
||||
// Delete document
|
||||
console.log('\n5. Delete document...');
|
||||
const deleted = store.delete('8');
|
||||
console.log(` Deleted doc 8: ${deleted}`);
|
||||
|
||||
console.log('\n=== Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* MCP Filesystem Server Example
|
||||
*
|
||||
* Demonstrates using MCP to connect to a filesystem server.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai-ts
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*
|
||||
* Run:
|
||||
* npx ts-node filesystem-mcp.ts
|
||||
*/
|
||||
|
||||
import { Agent, createMCP } from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== MCP Filesystem Server Example ===\n');
|
||||
|
||||
// Connect to MCP filesystem server
|
||||
const mcp = await createMCP({
|
||||
transport: {
|
||||
type: 'stdio',
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Connected to MCP filesystem server\n');
|
||||
|
||||
// Get tools from MCP server
|
||||
const tools = await mcp.tools();
|
||||
console.log('Available tools:', Object.keys(tools));
|
||||
|
||||
// Create agent with MCP tools
|
||||
const agent = new Agent({
|
||||
name: 'FileAgent',
|
||||
instructions: 'You help manage files using the filesystem tools.',
|
||||
tools: Object.values(tools),
|
||||
});
|
||||
|
||||
// Ask the agent to list files
|
||||
const response = await agent.chat('List all TypeScript files in the current directory');
|
||||
console.log('\nResponse:', response);
|
||||
|
||||
// Cleanup
|
||||
await mcp.close();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Multimodal Image Analysis Example
|
||||
*
|
||||
* Demonstrates analyzing images with a vision-capable model.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai-ts
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*
|
||||
* Run:
|
||||
* npx ts-node image-analysis.ts
|
||||
*/
|
||||
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Multimodal Image Analysis Example ===\n');
|
||||
|
||||
// Create an agent with a vision-capable model
|
||||
const agent = new Agent({
|
||||
name: 'VisionAgent',
|
||||
llm: 'gpt-4o', // Vision-capable model
|
||||
instructions: 'You analyze images and describe what you see in detail.',
|
||||
});
|
||||
|
||||
// For multimodal, we describe the image in the prompt
|
||||
// The agent will use its vision capabilities
|
||||
const imageUrl = 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/300px-PNG_transparency_demonstration_1.png';
|
||||
|
||||
console.log('Analyzing image...\n');
|
||||
console.log('Image URL:', imageUrl, '\n');
|
||||
|
||||
// Ask the agent to analyze (in production, use multimodal message format)
|
||||
const response = await agent.chat(
|
||||
`Please analyze this image: ${imageUrl}\nDescribe what you see in detail.`
|
||||
);
|
||||
|
||||
console.log('Analysis:', response);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,50 @@
|
||||
# Observability Examples
|
||||
|
||||
Examples demonstrating how to use observability integrations with PraisonAI TypeScript.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
cd /path/to/praisonai-ts
|
||||
pnpm install
|
||||
|
||||
# Set API keys for observability tools
|
||||
export LANGFUSE_SECRET_KEY=sk-...
|
||||
export LANGFUSE_PUBLIC_KEY=pk-...
|
||||
# Or use built-in tools (no API key needed)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `basic-tracing.ts` | Basic tracing with memory adapter |
|
||||
| `langfuse-integration.ts` | Langfuse observability integration |
|
||||
| `multi-agent-attribution.ts` | Track agent_id/run_id across agents |
|
||||
| `switchable-tools.ts` | Switch between observability tools via env |
|
||||
|
||||
## Running Examples
|
||||
|
||||
```bash
|
||||
# Run with ts-node or tsx
|
||||
npx tsx examples/js/observability/basic-tracing.ts
|
||||
npx tsx examples/js/observability/multi-agent-attribution.ts
|
||||
|
||||
# With Langfuse (requires API keys)
|
||||
LANGFUSE_SECRET_KEY=sk-... npx tsx examples/js/observability/langfuse-integration.ts
|
||||
```
|
||||
|
||||
## Supported Tools
|
||||
|
||||
| Tool | Env Variable | Package |
|
||||
|------|--------------|---------|
|
||||
| Langfuse | `LANGFUSE_SECRET_KEY` | `langfuse` |
|
||||
| LangSmith | `LANGCHAIN_API_KEY` | `langsmith` |
|
||||
| LangWatch | `LANGWATCH_API_KEY` | `langwatch` |
|
||||
| Arize | `ARIZE_API_KEY` | `arize-phoenix` |
|
||||
| Axiom | `AXIOM_TOKEN` | `@axiomhq/js` |
|
||||
| Braintrust | `BRAINTRUST_API_KEY` | `braintrust` |
|
||||
| Helicone | `HELICONE_API_KEY` | `@helicone/helicone` |
|
||||
| Console | (none) | built-in |
|
||||
| Memory | (none) | built-in |
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Basic Tracing Example
|
||||
*
|
||||
* Demonstrates basic observability with the built-in memory adapter.
|
||||
* No external API keys required.
|
||||
*/
|
||||
|
||||
import { Agent, MemoryObservabilityAdapter, setObservabilityAdapter } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Create memory adapter (stores traces in memory)
|
||||
const obs = new MemoryObservabilityAdapter();
|
||||
setObservabilityAdapter(obs);
|
||||
|
||||
// Create agent with observability enabled
|
||||
const agent = new Agent({
|
||||
name: 'TracedAgent',
|
||||
instructions: 'You are a helpful assistant. Answer questions concisely.'
|
||||
});
|
||||
|
||||
console.log('Running agent with tracing...\n');
|
||||
|
||||
// Run some interactions
|
||||
await agent.chat('What is 2 + 2?');
|
||||
await agent.chat('What is the capital of France?');
|
||||
|
||||
// View traces
|
||||
const traces = obs.getAllTraces();
|
||||
|
||||
console.log('\n=== Trace Summary ===');
|
||||
console.log(`Total traces: ${traces.length}`);
|
||||
|
||||
for (const trace of traces) {
|
||||
console.log(`\nTrace: ${trace.name}`);
|
||||
console.log(` Status: ${trace.status}`);
|
||||
console.log(` Spans: ${trace.spans.length}`);
|
||||
|
||||
for (const span of trace.spans) {
|
||||
const duration = span.endTime ? span.endTime - span.startTime : 'running';
|
||||
console.log(` - ${span.kind}: ${span.name} (${duration}ms)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Multi-Agent Attribution Example
|
||||
*
|
||||
* Demonstrates tracking agent_id, run_id, and trace_id across
|
||||
* multi-agent workflows for proper attribution.
|
||||
*/
|
||||
|
||||
import { Agent, Agents, MemoryObservabilityAdapter, setObservabilityAdapter } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Create memory adapter
|
||||
const obs = new MemoryObservabilityAdapter();
|
||||
setObservabilityAdapter(obs);
|
||||
|
||||
// Create agents
|
||||
const researcher = new Agent({
|
||||
name: 'Researcher',
|
||||
instructions: 'Research topics and gather information.'
|
||||
});
|
||||
|
||||
const writer = new Agent({
|
||||
name: 'Writer',
|
||||
instructions: 'Write clear summaries based on research.'
|
||||
});
|
||||
|
||||
// Create multi-agent workflow
|
||||
const agents = new Agents({
|
||||
agents: [researcher, writer],
|
||||
tasks: [
|
||||
{ agent: researcher, description: 'Research: {topic}' },
|
||||
{ agent: writer, description: 'Summarize the research findings' }
|
||||
]
|
||||
});
|
||||
|
||||
console.log('Running multi-agent workflow with attribution...\n');
|
||||
|
||||
await agents.start({ topic: 'TypeScript best practices' });
|
||||
|
||||
// View traces with attribution
|
||||
const traces = obs.getAllTraces();
|
||||
|
||||
console.log('\n=== Attribution Report ===');
|
||||
for (const trace of traces) {
|
||||
console.log(`\nTrace ID: ${trace.id}`);
|
||||
console.log(` Name: ${trace.name}`);
|
||||
|
||||
if (trace.attribution) {
|
||||
console.log(` Agent ID: ${trace.attribution.agentId || 'N/A'}`);
|
||||
console.log(` Run ID: ${trace.attribution.runId || 'N/A'}`);
|
||||
console.log(` Session ID: ${trace.attribution.sessionId || 'N/A'}`);
|
||||
}
|
||||
|
||||
console.log(` Spans: ${trace.spans.length}`);
|
||||
for (const span of trace.spans) {
|
||||
console.log(` - [${span.kind}] ${span.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Switchable Observability Tools Example
|
||||
*
|
||||
* Demonstrates switching between observability tools via environment variable.
|
||||
* Set OBS_TOOL env var to: langfuse, langsmith, console, memory, noop
|
||||
*
|
||||
* Usage:
|
||||
* OBS_TOOL=console npx tsx switchable-tools.ts
|
||||
* OBS_TOOL=langfuse LANGFUSE_SECRET_KEY=sk-... npx tsx switchable-tools.ts
|
||||
*/
|
||||
|
||||
import { Agent, createObservabilityAdapter, setObservabilityAdapter } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Get tool from environment (default to memory)
|
||||
const toolName = process.env.OBS_TOOL || 'memory';
|
||||
|
||||
console.log(`Using observability tool: ${toolName}\n`);
|
||||
|
||||
// Create adapter dynamically
|
||||
const obs = await createObservabilityAdapter(toolName as any);
|
||||
setObservabilityAdapter(obs);
|
||||
|
||||
// Create agent
|
||||
const agent = new Agent({
|
||||
name: 'ObservedAgent',
|
||||
instructions: 'You are a helpful assistant.'
|
||||
});
|
||||
|
||||
// Run interaction
|
||||
console.log('Running agent...\n');
|
||||
const response = await agent.chat('Hello! What is 2 + 2?');
|
||||
console.log('Response:', response);
|
||||
|
||||
// Flush traces to backend
|
||||
await obs.flush();
|
||||
console.log('\nTraces flushed to', toolName);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,47 @@
|
||||
# Provider Examples
|
||||
|
||||
Examples demonstrating how to use different LLM providers with PraisonAI TypeScript.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
cd /path/to/praisonai-ts
|
||||
pnpm install
|
||||
|
||||
# Set API keys
|
||||
export OPENAI_API_KEY=sk-...
|
||||
export ANTHROPIC_API_KEY=sk-ant-...
|
||||
export GOOGLE_API_KEY=AIza...
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `multi-provider.ts` | Use multiple providers in one workflow |
|
||||
| `openai-compatible.ts` | Use OpenAI-compatible APIs (LM Studio, etc.) |
|
||||
| `local-ollama.ts` | Use local Ollama models |
|
||||
| `provider-selection.ts` | Dynamic provider selection based on task |
|
||||
|
||||
## Running Examples
|
||||
|
||||
```bash
|
||||
# Run with ts-node or tsx
|
||||
npx tsx examples/js/providers/multi-provider.ts
|
||||
npx tsx examples/js/providers/openai-compatible.ts
|
||||
npx tsx examples/js/providers/local-ollama.ts
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Provider | Variable | Example |
|
||||
|----------|----------|---------|
|
||||
| OpenAI | `OPENAI_API_KEY` | `sk-...` |
|
||||
| Anthropic | `ANTHROPIC_API_KEY` | `sk-ant-...` |
|
||||
| Google | `GOOGLE_API_KEY` | `AIza...` |
|
||||
| xAI | `XAI_API_KEY` | `xai-...` |
|
||||
| Groq | `GROQ_API_KEY` | `gsk_...` |
|
||||
| Mistral | `MISTRAL_API_KEY` | `...` |
|
||||
| DeepSeek | `DEEPSEEK_API_KEY` | `...` |
|
||||
| Ollama | `OLLAMA_BASE_URL` | `http://localhost:11434` |
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Local Ollama Provider Example
|
||||
*
|
||||
* Demonstrates using local Ollama models with PraisonAI.
|
||||
*
|
||||
* Setup:
|
||||
* 1. Install Ollama: https://ollama.ai
|
||||
* 2. Pull a model: ollama pull llama3.2
|
||||
* 3. Run this example
|
||||
*
|
||||
* Optional env var:
|
||||
* - OLLAMA_BASE_URL (default: http://localhost:11434)
|
||||
*/
|
||||
|
||||
import { Agent } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Ollama with llama3.2
|
||||
const ollamaAgent = new Agent({
|
||||
name: 'LocalLlama',
|
||||
instructions: 'You are a helpful local AI assistant running on Ollama.',
|
||||
llm: 'ollama/llama3.2',
|
||||
llmConfig: {
|
||||
baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Testing Ollama local model...\n');
|
||||
|
||||
try {
|
||||
const response = await ollamaAgent.chat('Hello! What model are you running?');
|
||||
console.log('Response:', response);
|
||||
} catch (error: any) {
|
||||
console.log('Note: Ollama not running or model not pulled.');
|
||||
console.log('Start Ollama and run: ollama pull llama3.2');
|
||||
console.log('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Multi-Provider Example
|
||||
*
|
||||
* Demonstrates using multiple LLM providers in a single workflow.
|
||||
* Each agent uses a different provider optimized for its task.
|
||||
*
|
||||
* Required env vars:
|
||||
* - OPENAI_API_KEY
|
||||
* - ANTHROPIC_API_KEY
|
||||
* - GOOGLE_API_KEY
|
||||
*/
|
||||
|
||||
import { Agent, Agents } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// Fast model for quick categorization
|
||||
const triageAgent = new Agent({
|
||||
name: 'Triage',
|
||||
instructions: 'Quickly categorize the user request into: technical, creative, or general.',
|
||||
llm: 'openai/gpt-4o-mini' // Fast and cheap
|
||||
});
|
||||
|
||||
// Powerful model for complex reasoning
|
||||
const analysisAgent = new Agent({
|
||||
name: 'Analyst',
|
||||
instructions: 'Analyze the request in depth and provide detailed insights.',
|
||||
llm: 'anthropic/claude-3-5-sonnet' // Best reasoning
|
||||
});
|
||||
|
||||
// Creative model for content generation
|
||||
const writerAgent = new Agent({
|
||||
name: 'Writer',
|
||||
instructions: 'Write a clear, engaging response based on the analysis.',
|
||||
llm: 'google/gemini-2.0-flash' // Good for creative tasks
|
||||
});
|
||||
|
||||
// Create multi-agent workflow
|
||||
const agents = new Agents({
|
||||
agents: [triageAgent, analysisAgent, writerAgent],
|
||||
tasks: [
|
||||
{ agent: triageAgent, description: 'Categorize: {input}' },
|
||||
{ agent: analysisAgent, description: 'Analyze the categorized request' },
|
||||
{ agent: writerAgent, description: 'Write the final response' }
|
||||
]
|
||||
});
|
||||
|
||||
console.log('Starting multi-provider workflow...\n');
|
||||
|
||||
const result = await agents.start({
|
||||
input: 'Explain how neural networks learn'
|
||||
});
|
||||
|
||||
console.log('\n=== Final Result ===');
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* OpenAI-Compatible Provider Example
|
||||
*
|
||||
* Demonstrates using any OpenAI-compatible API endpoint.
|
||||
* Works with LM Studio, vLLM, LocalAI, and other compatible servers.
|
||||
*
|
||||
* Required env vars:
|
||||
* - OPENAI_COMPATIBLE_BASE_URL (e.g., http://localhost:1234/v1)
|
||||
* - OPENAI_COMPATIBLE_API_KEY (optional, use 'not-needed' for local)
|
||||
*/
|
||||
|
||||
import { Agent } from 'praisonai';
|
||||
|
||||
async function main() {
|
||||
// LM Studio example (local)
|
||||
const lmStudioAgent = new Agent({
|
||||
name: 'LocalAssistant',
|
||||
instructions: 'You are a helpful local AI assistant.',
|
||||
llm: 'openai-compatible/local-model',
|
||||
llmConfig: {
|
||||
baseUrl: process.env.LM_STUDIO_BASE_URL || 'http://localhost:1234/v1',
|
||||
apiKey: 'not-needed'
|
||||
}
|
||||
});
|
||||
|
||||
// Custom OpenAI-compatible endpoint
|
||||
const customAgent = new Agent({
|
||||
name: 'CustomAssistant',
|
||||
instructions: 'You are a helpful assistant.',
|
||||
llm: 'openai-compatible/my-model',
|
||||
llmConfig: {
|
||||
baseUrl: process.env.OPENAI_COMPATIBLE_BASE_URL || 'http://localhost:8000/v1',
|
||||
apiKey: process.env.OPENAI_COMPATIBLE_API_KEY || 'not-needed'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Testing OpenAI-compatible endpoint...\n');
|
||||
|
||||
try {
|
||||
const response = await lmStudioAgent.chat('Hello! What model are you?');
|
||||
console.log('Response:', response);
|
||||
} catch (error: any) {
|
||||
console.log('Note: LM Studio not running. Start it with a model loaded.');
|
||||
console.log('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Basic RAG Agent Example
|
||||
*
|
||||
* Demonstrates building a RAG agent with context injection.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai-ts
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*
|
||||
* Run:
|
||||
* npx ts-node basic-rag.ts
|
||||
*/
|
||||
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
|
||||
// Simulated knowledge base (in production, use a vector store)
|
||||
const knowledgeBase = [
|
||||
{ id: '1', content: 'PraisonAI is an AI agent framework for building intelligent applications.' },
|
||||
{ id: '2', content: 'Agents can use tools to accomplish tasks like web search and calculations.' },
|
||||
{ id: '3', content: 'RAG combines retrieval with generation for accurate, grounded responses.' },
|
||||
];
|
||||
|
||||
// Simple search function (in production, use vector similarity)
|
||||
function searchKnowledge(query: string): string[] {
|
||||
const queryLower = query.toLowerCase();
|
||||
return knowledgeBase
|
||||
.filter(doc => doc.content.toLowerCase().includes(queryLower.split(' ')[0]))
|
||||
.map(doc => doc.content);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('=== Basic RAG Agent Example ===\n');
|
||||
|
||||
// Create an agent with RAG-style instructions
|
||||
const agent = new Agent({
|
||||
name: 'RAGAgent',
|
||||
instructions: `You are a helpful assistant that answers questions using provided context.
|
||||
Always base your answers on the context provided.
|
||||
If the context doesn't contain relevant information, say so.`,
|
||||
});
|
||||
|
||||
// Query with context injection
|
||||
const query = 'What is PraisonAI?';
|
||||
console.log(`Q: ${query}`);
|
||||
|
||||
// Retrieve relevant context
|
||||
const context = searchKnowledge(query).join('\n\n');
|
||||
|
||||
// Ask the agent with context
|
||||
const response = await agent.chat(
|
||||
`Context:\n${context}\n\nQuestion: ${query}`
|
||||
);
|
||||
|
||||
console.log(`A: ${response}\n`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Express Server Adapter Example
|
||||
*
|
||||
* Demonstrates deploying an AI agent as an Express HTTP server.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai-ts express
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*
|
||||
* Run:
|
||||
* npx ts-node express-server.ts
|
||||
*
|
||||
* Test:
|
||||
* curl -X POST http://localhost:3000/api/chat \
|
||||
* -H "Content-Type: application/json" \
|
||||
* -d '{"message": "Hello!"}'
|
||||
*/
|
||||
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
|
||||
// Simple HTTP server without Express dependency for this example
|
||||
import http from 'http';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Express Server Adapter Example ===\n');
|
||||
|
||||
// Create an agent
|
||||
const agent = new Agent({
|
||||
name: 'APIAgent',
|
||||
instructions: 'You are a helpful API assistant. Keep responses concise.',
|
||||
});
|
||||
|
||||
// Create HTTP server
|
||||
const server = http.createServer(async (req, res) => {
|
||||
// CORS headers
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST' && req.url === '/api/chat') {
|
||||
let body = '';
|
||||
req.on('data', chunk => body += chunk);
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
const { message } = JSON.parse(body);
|
||||
const response = await agent.chat(message);
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ response }));
|
||||
} catch (error: any) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: error.message }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end('Not Found');
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
console.log('\nTest with:');
|
||||
console.log(` curl -X POST http://localhost:${PORT}/api/chat \\`);
|
||||
console.log(' -H "Content-Type: application/json" \\');
|
||||
console.log(' -d \'{"message": "Hello!"}\'');
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
LSP Code Intelligence Example
|
||||
|
||||
This example demonstrates how to use LSP-powered tools through an Agent
|
||||
for semantic code analysis (listing symbols, finding definitions, etc.).
|
||||
|
||||
How to run:
|
||||
export OPENAI_API_KEY=your_key
|
||||
python examples/lsp/lsp_code_analysis_example.py
|
||||
|
||||
Prerequisites:
|
||||
pip install praisonai praisonaiagents
|
||||
pip install python-lsp-server # Optional, for full LSP support
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add paths for development
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai-agents'))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("LSP CODE INTELLIGENCE EXAMPLE")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Import after path setup
|
||||
from praisonai.cli.features import (
|
||||
create_agent_centric_tools,
|
||||
InteractiveRuntime,
|
||||
RuntimeConfig
|
||||
)
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Use the praisonaiagents source as workspace for analysis
|
||||
workspace = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'../../src/praisonai-agents/praisonaiagents'
|
||||
)
|
||||
workspace = os.path.abspath(workspace)
|
||||
|
||||
print(f"Workspace: {workspace}")
|
||||
print()
|
||||
|
||||
# 1. Create runtime with LSP enabled
|
||||
print("1. Creating InteractiveRuntime with LSP...")
|
||||
config = RuntimeConfig(
|
||||
workspace=workspace,
|
||||
lsp_enabled=True, # Enable LSP for code intelligence
|
||||
acp_enabled=False, # Disable ACP for this example
|
||||
approval_mode="auto"
|
||||
)
|
||||
runtime = InteractiveRuntime(config)
|
||||
status = await runtime.start()
|
||||
|
||||
print(f" LSP enabled: {status['lsp']['enabled']}")
|
||||
print(f" LSP ready: {runtime.lsp_ready}")
|
||||
print(f" LSP status: {status['lsp']['status']}")
|
||||
if not runtime.lsp_ready:
|
||||
print(" Note: LSP not available, will use regex fallback")
|
||||
print()
|
||||
|
||||
# 2. Create agent-centric tools
|
||||
print("2. Creating agent-centric tools...")
|
||||
tools = create_agent_centric_tools(runtime)
|
||||
|
||||
# Filter to show only LSP tools
|
||||
lsp_tools = [t for t in tools if t.__name__.startswith('lsp_')]
|
||||
print(f" LSP tools available:")
|
||||
for tool in lsp_tools:
|
||||
print(f" - {tool.__name__}")
|
||||
print()
|
||||
|
||||
# 3. Create Agent with LSP-powered tools
|
||||
print("3. Creating Agent with LSP tools...")
|
||||
agent = Agent(
|
||||
name="CodeAnalyzer",
|
||||
instructions="""You are a code analysis assistant.
|
||||
Use lsp_list_symbols to list functions and classes in files.
|
||||
Use lsp_find_definition to find where symbols are defined.
|
||||
Use lsp_find_references to find where symbols are used.
|
||||
Provide clear, organized summaries of code structure.""",
|
||||
tools=tools,
|
||||
verbose=True
|
||||
)
|
||||
print()
|
||||
|
||||
# 4. Test 1: List symbols in a file
|
||||
print("4. TEST 1: List symbols in agent/agent.py")
|
||||
print("-" * 50)
|
||||
result1 = agent.start("List all the main classes and public methods in agent/agent.py. Give me a brief summary.")
|
||||
print("-" * 50)
|
||||
print(f"Summary: {str(result1)[:300]}...")
|
||||
print()
|
||||
|
||||
# 5. Test 2: Find definition of a symbol
|
||||
print("5. TEST 2: Find where 'Agent' class is defined")
|
||||
print("-" * 50)
|
||||
result2 = agent.start("Where is the Agent class defined? Use lsp_find_definition to find it.")
|
||||
print("-" * 50)
|
||||
print(f"Result: {str(result2)[:300]}...")
|
||||
print()
|
||||
|
||||
# 6. Test 3: Analyze main.py
|
||||
print("6. TEST 3: Analyze main.py structure")
|
||||
print("-" * 50)
|
||||
result3 = agent.start("List all the functions in main.py and briefly describe what the file does.")
|
||||
print("-" * 50)
|
||||
print(f"Analysis: {str(result3)[:300]}...")
|
||||
print()
|
||||
|
||||
# Cleanup
|
||||
await runtime.stop()
|
||||
|
||||
print("=" * 60)
|
||||
print("EXAMPLE COMPLETE")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("Key takeaways:")
|
||||
print("- LSP tools work through the Agent (agentic LSP)")
|
||||
print("- Falls back to regex if LSP server not available")
|
||||
print("- Returns structured data with citations")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
PraisonAI Output UX Examples
|
||||
|
||||
This example demonstrates the various output modes and configurations
|
||||
available in PraisonAI agents.
|
||||
|
||||
Features demonstrated:
|
||||
- verbose=True: Full output with agent info, task, and response panels
|
||||
- verbose=False: Clean output with just the result
|
||||
- Tool call display with consolidated output
|
||||
- Multi-agent output with clear attribution
|
||||
"""
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '/Users/praison/praisonai-package/src/praisonai-agents')
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get the current weather for a city.
|
||||
|
||||
Args:
|
||||
city: The name of the city to get weather for
|
||||
|
||||
Returns:
|
||||
Weather information string
|
||||
"""
|
||||
return f"The weather in {city} is sunny and 72°F"
|
||||
|
||||
|
||||
def example_verbose_true():
|
||||
"""Example with verbose=True (default) - shows full output panels."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 1: verbose=True (Full Output)")
|
||||
print("=" * 60)
|
||||
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Be concise.",
|
||||
verbose=True # Default - shows Agent Info, Task, and Response panels
|
||||
)
|
||||
result = agent.start("What is the capital of France? Answer in one sentence.")
|
||||
print(f"\nReturned value: {result}")
|
||||
|
||||
|
||||
def example_verbose_false():
|
||||
"""Example with verbose=False - clean output, just the result."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 2: verbose=False (Clean Output)")
|
||||
print("=" * 60)
|
||||
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Be concise.",
|
||||
verbose=False # No panels, just returns the result
|
||||
)
|
||||
result = agent.start("What is 2+2? Answer in one word.")
|
||||
print(f"Result: {result}")
|
||||
|
||||
|
||||
def example_with_tools():
|
||||
"""Example showing tool call output."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 3: Agent with Tools")
|
||||
print("=" * 60)
|
||||
|
||||
agent = Agent(
|
||||
name="Weather Bot",
|
||||
instructions="You help users check the weather. Use the get_weather tool.",
|
||||
tools=[get_weather],
|
||||
verbose=True
|
||||
)
|
||||
result = agent.start("What's the weather in Tokyo?")
|
||||
print(f"\nReturned value: {result}")
|
||||
|
||||
|
||||
def example_multi_agent():
|
||||
"""Example showing multi-agent output with clear attribution."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 4: Multi-Agent Output")
|
||||
print("=" * 60)
|
||||
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Research Analyst",
|
||||
instructions="You research topics and provide factual information.",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
role="Content Writer",
|
||||
instructions="You write clear summaries based on research.",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
task1 = Task(
|
||||
description="Research what AI is in one sentence",
|
||||
expected_output="A brief factual description",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
task2 = Task(
|
||||
description="Write a one-sentence summary about AI",
|
||||
expected_output="A clear summary",
|
||||
agent=writer
|
||||
)
|
||||
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, writer],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
print(f"\nFinal result: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("PRAISONAI OUTPUT UX EXAMPLES")
|
||||
print("=" * 60)
|
||||
|
||||
# Run examples
|
||||
example_verbose_true()
|
||||
example_verbose_false()
|
||||
example_with_tools()
|
||||
example_multi_agent()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ALL EXAMPLES COMPLETE")
|
||||
print("=" * 60)
|
||||
@@ -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!")
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
Isolated Registry Example - Multi-Agent Safety
|
||||
|
||||
This example demonstrates how to use isolated registries to prevent
|
||||
provider registration collisions in multi-agent scenarios.
|
||||
|
||||
Key concepts:
|
||||
- Each agent/context can have its own registry
|
||||
- No global state mutation
|
||||
- Safe for concurrent/parallel runs
|
||||
- Different agents can use different provider implementations
|
||||
|
||||
Requirements:
|
||||
- pip install praisonai
|
||||
"""
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '../../src/praisonai')
|
||||
|
||||
from praisonai.llm import (
|
||||
LLMProviderRegistry,
|
||||
create_llm_provider,
|
||||
register_llm_provider,
|
||||
get_default_llm_registry,
|
||||
_reset_default_registry
|
||||
)
|
||||
|
||||
|
||||
# Define two different provider implementations with the same name
|
||||
class Agent1Provider:
|
||||
"""Provider implementation for Agent 1."""
|
||||
provider_id = "custom"
|
||||
|
||||
def __init__(self, model_id, config=None):
|
||||
self.model_id = model_id
|
||||
self.config = config or {}
|
||||
self.agent_name = "Agent1"
|
||||
|
||||
def generate(self, prompt):
|
||||
return f"[{self.agent_name}] Response to: {prompt}"
|
||||
|
||||
|
||||
class Agent2Provider:
|
||||
"""Provider implementation for Agent 2 - different behavior."""
|
||||
provider_id = "custom"
|
||||
|
||||
def __init__(self, model_id, config=None):
|
||||
self.model_id = model_id
|
||||
self.config = config or {}
|
||||
self.agent_name = "Agent2"
|
||||
|
||||
def generate(self, prompt):
|
||||
return f"[{self.agent_name}] Different response to: {prompt}"
|
||||
|
||||
|
||||
def demonstrate_collision_problem():
|
||||
"""Show what happens with global registry collisions."""
|
||||
print("=== Problem: Global Registry Collision ===\n")
|
||||
|
||||
# Reset to clean state
|
||||
_reset_default_registry()
|
||||
|
||||
# Agent 1 registers "custom" provider
|
||||
register_llm_provider("custom", Agent1Provider)
|
||||
print("Agent 1 registered 'custom' provider")
|
||||
|
||||
# Agent 2 tries to register same name - ERROR!
|
||||
try:
|
||||
register_llm_provider("custom", Agent2Provider)
|
||||
print("Agent 2 registered 'custom' provider")
|
||||
except ValueError as e:
|
||||
print(f"Agent 2 FAILED: {e}")
|
||||
|
||||
print()
|
||||
print("Problem: Both agents want to use 'custom' but with different implementations.")
|
||||
print("Solution: Use isolated registries.\n")
|
||||
|
||||
|
||||
def demonstrate_isolated_solution():
|
||||
"""Show how isolated registries solve the collision problem."""
|
||||
print("=== Solution: Isolated Registries ===\n")
|
||||
|
||||
# Create isolated registries for each agent
|
||||
agent1_registry = LLMProviderRegistry()
|
||||
agent2_registry = LLMProviderRegistry()
|
||||
|
||||
# Each agent registers its own "custom" provider
|
||||
agent1_registry.register("custom", Agent1Provider)
|
||||
agent2_registry.register("custom", Agent2Provider)
|
||||
|
||||
print("Agent 1 registry providers:", agent1_registry.list())
|
||||
print("Agent 2 registry providers:", agent2_registry.list())
|
||||
print()
|
||||
|
||||
# Create providers from each registry
|
||||
provider1 = create_llm_provider("custom/model-v1", registry=agent1_registry)
|
||||
provider2 = create_llm_provider("custom/model-v1", registry=agent2_registry)
|
||||
|
||||
# They have different implementations!
|
||||
print("Provider 1 response:", provider1.generate("Hello"))
|
||||
print("Provider 2 response:", provider2.generate("Hello"))
|
||||
print()
|
||||
|
||||
# Verify isolation
|
||||
print("Verification:")
|
||||
print(f" provider1.agent_name = {provider1.agent_name}")
|
||||
print(f" provider2.agent_name = {provider2.agent_name}")
|
||||
print(f" Same class? {type(provider1) == type(provider2)}")
|
||||
print()
|
||||
|
||||
|
||||
def demonstrate_parallel_agents():
|
||||
"""Simulate parallel agent execution with isolated registries."""
|
||||
print("=== Parallel Agent Simulation ===\n")
|
||||
|
||||
def run_agent(agent_id, registry):
|
||||
"""Simulate an agent run with its own registry."""
|
||||
provider = create_llm_provider("custom/model", registry=registry)
|
||||
result = provider.generate(f"Query from agent {agent_id}")
|
||||
return result
|
||||
|
||||
# Setup registries
|
||||
registries = {}
|
||||
for i in range(3):
|
||||
reg = LLMProviderRegistry()
|
||||
# Each agent gets a slightly different provider
|
||||
class DynamicProvider:
|
||||
provider_id = "custom"
|
||||
def __init__(self, model_id, config=None):
|
||||
self.model_id = model_id
|
||||
self.agent_id = i # Capture agent ID
|
||||
def generate(self, prompt):
|
||||
return f"Agent-{self.agent_id} processed: {prompt}"
|
||||
|
||||
reg.register("custom", DynamicProvider)
|
||||
registries[i] = reg
|
||||
|
||||
# Simulate parallel execution
|
||||
print("Simulating parallel agent runs:")
|
||||
for agent_id, registry in registries.items():
|
||||
result = run_agent(agent_id, registry)
|
||||
print(f" {result}")
|
||||
|
||||
print()
|
||||
print("Each agent used its own isolated registry - no collisions!")
|
||||
print()
|
||||
|
||||
|
||||
def demonstrate_shared_vs_isolated():
|
||||
"""Compare shared (global) vs isolated registry patterns."""
|
||||
print("=== Shared vs Isolated Patterns ===\n")
|
||||
|
||||
# Reset global registry
|
||||
_reset_default_registry()
|
||||
|
||||
# Pattern 1: Shared (global) registry
|
||||
print("Pattern 1: Shared Registry (use for app-wide providers)")
|
||||
register_llm_provider("shared-provider", Agent1Provider)
|
||||
|
||||
# All code sees the same provider
|
||||
global_reg = get_default_llm_registry()
|
||||
print(f" Global registry has 'shared-provider': {global_reg.has('shared-provider')}")
|
||||
|
||||
# Pattern 2: Isolated registry
|
||||
print("\nPattern 2: Isolated Registry (use for per-agent/per-run providers)")
|
||||
isolated = LLMProviderRegistry()
|
||||
isolated.register("isolated-provider", Agent2Provider)
|
||||
|
||||
# Only code with reference to this registry sees it
|
||||
print(f" Isolated registry has 'isolated-provider': {isolated.has('isolated-provider')}")
|
||||
print(f" Global registry has 'isolated-provider': {global_reg.has('isolated-provider')}")
|
||||
|
||||
print()
|
||||
print("Recommendation:")
|
||||
print(" - Use global registry for app-wide, stable providers")
|
||||
print(" - Use isolated registries for per-agent or per-run customization")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("Isolated Registry Example - Multi-Agent Safety")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
demonstrate_collision_problem()
|
||||
demonstrate_isolated_solution()
|
||||
demonstrate_parallel_agents()
|
||||
demonstrate_shared_vs_isolated()
|
||||
|
||||
print("=" * 60)
|
||||
print("Example Complete")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,83 @@
|
||||
# PraisonAI Profiling Examples
|
||||
|
||||
This directory contains examples for profiling PraisonAI agent performance.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install praisonai
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. Basic Profiling (`basic_profiling.py`)
|
||||
|
||||
Demonstrates programmatic profiling of a single query:
|
||||
|
||||
```bash
|
||||
python basic_profiling.py
|
||||
```
|
||||
|
||||
### 2. Suite Profiling (`suite_profiling.py`)
|
||||
|
||||
Runs a comprehensive profiling suite with multiple scenarios:
|
||||
|
||||
```bash
|
||||
python suite_profiling.py
|
||||
```
|
||||
|
||||
### 3. Optimization Demo (`optimization_example.py`)
|
||||
|
||||
Demonstrates Tier 0/1/2 performance optimizations:
|
||||
|
||||
```bash
|
||||
python optimization_example.py
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
You can also profile directly from the command line:
|
||||
|
||||
```bash
|
||||
# Profile a query
|
||||
praisonai profile query "What is 2+2?"
|
||||
|
||||
# Profile with file grouping
|
||||
praisonai profile query "Hello" --show-files --limit 20
|
||||
|
||||
# Profile imports
|
||||
praisonai profile imports
|
||||
|
||||
# Profile startup time
|
||||
praisonai profile startup
|
||||
|
||||
# Run comprehensive suite
|
||||
praisonai profile suite --quick
|
||||
|
||||
# Create performance baseline
|
||||
praisonai profile snapshot --baseline
|
||||
|
||||
# Compare against baseline
|
||||
praisonai profile snapshot current --compare
|
||||
|
||||
# Show optimization status
|
||||
praisonai profile optimize --show
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Text output**: Human-readable timing breakdown
|
||||
- **JSON output**: Machine-readable for CI/CD (`--format json`)
|
||||
- **Artifacts**: Binary cProfile data and reports (`--save ./results`)
|
||||
- **Snapshots**: Performance baselines for regression detection
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
Enable opt-in optimizations via environment variables:
|
||||
|
||||
```bash
|
||||
export PRAISONAI_LITE_MODE=1
|
||||
export PRAISONAI_SKIP_TYPE_VALIDATION=1
|
||||
export PRAISONAI_MINIMAL_IMPORTS=1
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Basic Profiling Example for PraisonAI
|
||||
|
||||
This example demonstrates how to use the unified profiler programmatically
|
||||
to measure agent performance.
|
||||
|
||||
Run with:
|
||||
python basic_profiling.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Ensure API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Please set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
from praisonai.cli.execution import (
|
||||
ExecutionRequest,
|
||||
Profiler,
|
||||
ProfilerConfig,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
"""Run a profiled query and display results."""
|
||||
|
||||
# Configure profiler (Layer 1 = basic profiling with function stats)
|
||||
config = ProfilerConfig(
|
||||
layer=1, # 0=minimal, 1=basic, 2=deep
|
||||
limit=15, # Show top 15 functions
|
||||
show_callers=False, # Set True for caller info (layer 2)
|
||||
show_callees=False, # Set True for callee info (layer 2)
|
||||
)
|
||||
|
||||
# Create execution request
|
||||
request = ExecutionRequest(
|
||||
prompt="What is 2+2?",
|
||||
agent_name="ProfiledAgent",
|
||||
)
|
||||
|
||||
# Run profiled query
|
||||
print("🔬 Running profiled query...")
|
||||
profiler = Profiler(config)
|
||||
result, report = profiler.profile_sync(request)
|
||||
|
||||
# Print formatted report
|
||||
print("\n" + report.to_text())
|
||||
|
||||
# Access timing data programmatically
|
||||
print("\n📊 Timing Summary:")
|
||||
print(f" Imports: {report.timing.imports_ms:>10.2f} ms")
|
||||
print(f" Agent Init: {report.timing.agent_init_ms:>10.2f} ms")
|
||||
print(f" Execution: {report.timing.execution_ms:>10.2f} ms")
|
||||
print(f" Total: {report.timing.total_ms:>10.2f} ms")
|
||||
|
||||
# Show top 5 functions
|
||||
if report.functions:
|
||||
print("\n🔥 Top 5 Functions by Cumulative Time:")
|
||||
for i, func in enumerate(report.functions[:5], 1):
|
||||
print(f" {i}. {func.name}: {func.cumulative_time_ms:.2f}ms")
|
||||
|
||||
# Show response
|
||||
print(f"\n💬 Response: {result.output}")
|
||||
|
||||
# Export to JSON
|
||||
print("\n📄 JSON Export available via: report.to_json()")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
Performance Optimization Example for PraisonAI
|
||||
|
||||
This example demonstrates how to use the Tier 0/1/2 optimization features
|
||||
programmatically.
|
||||
|
||||
Run with:
|
||||
python optimization_example.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Ensure API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Please set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
|
||||
def demo_provider_cache():
|
||||
"""Demonstrate provider caching (Tier 0)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 0: Provider Cache Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import (
|
||||
get_provider_cache,
|
||||
resolve_model_provider,
|
||||
)
|
||||
|
||||
cache = get_provider_cache()
|
||||
cache.invalidate() # Start fresh
|
||||
|
||||
# Resolve some models
|
||||
models = ['gpt-4o', 'claude-3-opus', 'gemini-pro', 'gpt-4o-mini']
|
||||
|
||||
print("\nResolving models:")
|
||||
for model in models:
|
||||
provider, name = resolve_model_provider(model)
|
||||
print(f" {model} -> {provider}/{name}")
|
||||
|
||||
# Check cache stats
|
||||
stats = cache.get_stats()
|
||||
print("\nCache Stats:")
|
||||
print(f" Hits: {stats['hits']}")
|
||||
print(f" Misses: {stats['misses']}")
|
||||
print(f" Size: {stats['size']}")
|
||||
|
||||
|
||||
def demo_lazy_importer():
|
||||
"""Demonstrate lazy imports (Tier 0)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 0: Lazy Importer Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import LazyImporter
|
||||
import time
|
||||
|
||||
LazyImporter.clear()
|
||||
|
||||
# Time lazy import
|
||||
start = time.perf_counter()
|
||||
json_module = LazyImporter.get('json')
|
||||
first_import = (time.perf_counter() - start) * 1000
|
||||
|
||||
# Second import (cached)
|
||||
start = time.perf_counter()
|
||||
json_module2 = LazyImporter.get('json')
|
||||
cached_import = (time.perf_counter() - start) * 1000
|
||||
|
||||
print(f"\nFirst import: {first_import:.3f}ms")
|
||||
print(f"Cached import: {cached_import:.3f}ms")
|
||||
print(f"Same object: {json_module is json_module2}")
|
||||
|
||||
|
||||
def demo_client_pool():
|
||||
"""Demonstrate client pooling (Tier 1)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 1: Client Pool Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import ClientPool
|
||||
|
||||
pool = ClientPool(max_size=3)
|
||||
|
||||
# Create some mock clients
|
||||
def create_client(name):
|
||||
return {'name': name, 'created': True}
|
||||
|
||||
# Get or create clients
|
||||
client1 = pool.get_or_create('openai', lambda: create_client('openai'))
|
||||
_ = pool.get_or_create('anthropic', lambda: create_client('anthropic'))
|
||||
|
||||
# Reuse existing client
|
||||
client1_again = pool.get_or_create('openai', lambda: create_client('openai_new'))
|
||||
|
||||
print(f"\nPool size: {pool.size()}")
|
||||
print(f"Client reused: {client1 is client1_again}")
|
||||
print(f"Client1 name: {client1['name']}")
|
||||
|
||||
|
||||
def demo_prewarm():
|
||||
"""Demonstrate pre-warming (Tier 1)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 1: Pre-warming Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import PrewarmManager
|
||||
import time
|
||||
|
||||
PrewarmManager.reset()
|
||||
|
||||
print(f"\nPre-warming enabled: {PrewarmManager.is_enabled()}")
|
||||
|
||||
# Enable and prewarm
|
||||
PrewarmManager.enable()
|
||||
print(f"After enable: {PrewarmManager.is_enabled()}")
|
||||
|
||||
# Prewarm OpenAI (runs in background)
|
||||
PrewarmManager.prewarm_provider('openai')
|
||||
print("Pre-warming OpenAI in background...")
|
||||
|
||||
# Wait a bit for background thread
|
||||
time.sleep(0.5)
|
||||
|
||||
print(f"OpenAI prewarmed: {PrewarmManager.is_prewarmed('openai')}")
|
||||
|
||||
PrewarmManager.reset()
|
||||
|
||||
|
||||
def demo_lite_mode():
|
||||
"""Demonstrate lite mode (Tier 2)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 2: Lite Mode Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import (
|
||||
LiteModeConfig,
|
||||
is_lite_mode,
|
||||
)
|
||||
|
||||
# Default config
|
||||
config = LiteModeConfig()
|
||||
print(f"\nDefault lite mode: {config.enabled}")
|
||||
|
||||
# Config from env
|
||||
print("\nTo enable lite mode, set environment variables:")
|
||||
print(" export PRAISONAI_LITE_MODE=1")
|
||||
print(" export PRAISONAI_SKIP_TYPE_VALIDATION=1")
|
||||
|
||||
print(f"\nCurrent is_lite_mode(): {is_lite_mode()}")
|
||||
|
||||
|
||||
def demo_perf_snapshot():
|
||||
"""Demonstrate performance snapshots (Tier 2)."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Tier 2: Performance Snapshot Demo")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonai.cli.features.profiler import (
|
||||
PerfSnapshot,
|
||||
PerfComparison,
|
||||
PerfSnapshotManager,
|
||||
)
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
# Create manager with temp directory
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
manager = PerfSnapshotManager(snapshot_dir=Path(tmpdir))
|
||||
|
||||
# Create baseline
|
||||
baseline = PerfSnapshot(
|
||||
timestamp='2025-01-01T00:00:00Z',
|
||||
name='baseline',
|
||||
startup_cold_ms=100.0,
|
||||
startup_warm_ms=80.0,
|
||||
import_time_ms=500.0,
|
||||
query_time_ms=2000.0,
|
||||
first_token_ms=300.0,
|
||||
)
|
||||
manager.save_baseline(baseline)
|
||||
print("\nBaseline saved")
|
||||
|
||||
# Create current snapshot (simulated improvement)
|
||||
current = PerfSnapshot(
|
||||
timestamp='2025-01-02T00:00:00Z',
|
||||
name='current',
|
||||
startup_cold_ms=95.0, # 5% faster
|
||||
startup_warm_ms=78.0,
|
||||
import_time_ms=480.0, # 4% faster
|
||||
query_time_ms=1900.0, # 5% faster
|
||||
first_token_ms=280.0,
|
||||
)
|
||||
|
||||
# Compare
|
||||
comparison = PerfComparison(baseline=baseline, current=current)
|
||||
|
||||
print(f"\nStartup improvement: {-comparison.startup_cold_diff_pct:.1f}%")
|
||||
print(f"Import improvement: {-comparison.import_time_diff_pct:.1f}%")
|
||||
print(f"Query improvement: {-comparison.query_time_diff_pct:.1f}%")
|
||||
print(f"Is regression: {comparison.is_regression()}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all demos."""
|
||||
print("=" * 60)
|
||||
print("PraisonAI Performance Optimization Demo")
|
||||
print("=" * 60)
|
||||
|
||||
demo_provider_cache()
|
||||
demo_lazy_importer()
|
||||
demo_client_pool()
|
||||
demo_prewarm()
|
||||
demo_lite_mode()
|
||||
demo_perf_snapshot()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("All demos complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"timestamp": "2025-12-31T18:45:38.187539Z",
|
||||
"metadata": {
|
||||
"python_version": "3.12.11",
|
||||
"platform": "macOS-15.7.4-arm64-arm-64bit",
|
||||
"praisonai_version": "2.9.2",
|
||||
"timestamp": "2025-12-31T18:45:38.226558Z"
|
||||
},
|
||||
"startup": {
|
||||
"cold_ms": 80.9305000002496,
|
||||
"warm_ms": 55.266541996388696
|
||||
},
|
||||
"import_analysis": [
|
||||
{
|
||||
"module": "praisonaiagents",
|
||||
"self_us": 2419,
|
||||
"cumulative_us": 1645390,
|
||||
"self_ms": 2.419,
|
||||
"cumulative_ms": 1645.39
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.workflows",
|
||||
"self_us": 670,
|
||||
"cumulative_us": 1375919,
|
||||
"self_ms": 0.67,
|
||||
"cumulative_ms": 1375.919
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.workflows.yaml_parser",
|
||||
"self_us": 775,
|
||||
"cumulative_us": 1369435,
|
||||
"self_ms": 0.775,
|
||||
"cumulative_ms": 1369.435
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.agent.agent",
|
||||
"self_us": 19,
|
||||
"cumulative_us": 1349174,
|
||||
"self_ms": 0.019,
|
||||
"cumulative_ms": 1349.174
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.agent",
|
||||
"self_us": 537,
|
||||
"cumulative_us": 1349155,
|
||||
"self_ms": 0.537,
|
||||
"cumulative_ms": 1349.155
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.agent.agent",
|
||||
"self_us": 1453,
|
||||
"cumulative_us": 1285299,
|
||||
"self_ms": 1.453,
|
||||
"cumulative_ms": 1285.299
|
||||
},
|
||||
{
|
||||
"module": "praisonaiagents.llm.openai_client",
|
||||
"self_us": 7887,
|
||||
"cumulative_us": 1123578,
|
||||
"self_ms": 7.887,
|
||||
"cumulative_ms": 1123.578
|
||||
},
|
||||
{
|
||||
"module": "openai",
|
||||
"self_us": 1083,
|
||||
"cumulative_us": 1115692,
|
||||
"self_ms": 1.083,
|
||||
"cumulative_ms": 1115.692
|
||||
},
|
||||
{
|
||||
"module": "openai.types",
|
||||
"self_us": 2606,
|
||||
"cumulative_us": 957990,
|
||||
"self_ms": 2.606,
|
||||
"cumulative_ms": 957.99
|
||||
},
|
||||
{
|
||||
"module": "openai.types.batch",
|
||||
"self_us": 1787,
|
||||
"cumulative_us": 485667,
|
||||
"self_ms": 1.787,
|
||||
"cumulative_ms": 485.667
|
||||
},
|
||||
{
|
||||
"module": "openai._models",
|
||||
"self_us": 13423,
|
||||
"cumulative_us": 450798,
|
||||
"self_ms": 13.423,
|
||||
"cumulative_ms": 450.798
|
||||
},
|
||||
{
|
||||
"module": "openai.types.eval_create_params",
|
||||
"self_us": 644,
|
||||
"cumulative_us": 393275,
|
||||
"self_ms": 0.644,
|
||||
"cumulative_ms": 393.275
|
||||
},
|
||||
{
|
||||
"module": "openai.types.graders.grader_inputs_param",
|
||||
"self_us": 17,
|
||||
"cumulative_us": 359515,
|
||||
"self_ms": 0.017,
|
||||
"cumulative_ms": 359.515
|
||||
},
|
||||
{
|
||||
"module": "openai.types.graders",
|
||||
"self_us": 392,
|
||||
"cumulative_us": 359498,
|
||||
"self_ms": 0.392,
|
||||
"cumulative_ms": 359.498
|
||||
},
|
||||
{
|
||||
"module": "openai.types.graders.multi_grader",
|
||||
"self_us": 3847,
|
||||
"cumulative_us": 351251,
|
||||
"self_ms": 3.847,
|
||||
"cumulative_ms": 351.251
|
||||
},
|
||||
{
|
||||
"module": "openai.types.graders.label_model_grader",
|
||||
"self_us": 5450,
|
||||
"cumulative_us": 337291,
|
||||
"self_ms": 5.45,
|
||||
"cumulative_ms": 337.291
|
||||
},
|
||||
{
|
||||
"module": "openai.types.graders.grader_inputs",
|
||||
"self_us": 545,
|
||||
"cumulative_us": 331841,
|
||||
"self_ms": 0.545,
|
||||
"cumulative_ms": 331.841
|
||||
},
|
||||
{
|
||||
"module": "openai.types.responses.response_input_text",
|
||||
"self_us": 21,
|
||||
"cumulative_us": 331297,
|
||||
"self_ms": 0.021,
|
||||
"cumulative_ms": 331.297
|
||||
},
|
||||
{
|
||||
"module": "openai.types.responses",
|
||||
"self_us": 1301,
|
||||
"cumulative_us": 331277,
|
||||
"self_ms": 1.301,
|
||||
"cumulative_ms": 331.277
|
||||
},
|
||||
{
|
||||
"module": "rich.logging",
|
||||
"self_us": 1066,
|
||||
"cumulative_us": 238256,
|
||||
"self_ms": 1.066,
|
||||
"cumulative_ms": 238.256
|
||||
}
|
||||
],
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "simple_query",
|
||||
"config": {
|
||||
"prompt": "What is 2+2?",
|
||||
"model": "default",
|
||||
"stream": false,
|
||||
"iterations": 2
|
||||
},
|
||||
"import_time_stats": {
|
||||
"mean": 0.008833496394800022,
|
||||
"min": 0.005916997906751931,
|
||||
"max": 0.011749994882848114,
|
||||
"stdev": 0.0041245517164382375,
|
||||
"median": 0.008833496394800022
|
||||
},
|
||||
"total_time_stats": {
|
||||
"mean": 1727.4090415012324,
|
||||
"min": 1367.2264170018025,
|
||||
"max": 2087.5916660006624,
|
||||
"stdev": 509.3751524982296,
|
||||
"median": 1727.4090415012324
|
||||
},
|
||||
"first_token_stats": null,
|
||||
"raw_import_times_ms": [
|
||||
0.011749994882848114,
|
||||
0.005916997906751931
|
||||
],
|
||||
"raw_total_times_ms": [
|
||||
1367.2264170018025,
|
||||
2087.5916660006624
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "simple_stream",
|
||||
"config": {
|
||||
"prompt": "What is 2+2?",
|
||||
"model": "default",
|
||||
"stream": true,
|
||||
"iterations": 2
|
||||
},
|
||||
"import_time_stats": {
|
||||
"mean": 0.008792001608526334,
|
||||
"min": 0.006375004886649549,
|
||||
"max": 0.01120899833040312,
|
||||
"stdev": 0.0034181495442894613,
|
||||
"median": 0.008792001608526334
|
||||
},
|
||||
"total_time_stats": {
|
||||
"mean": 2321.0982290001994,
|
||||
"min": 1697.1089580038097,
|
||||
"max": 2945.087499996589,
|
||||
"stdev": 882.4540898183949,
|
||||
"median": 2321.0982290001994
|
||||
},
|
||||
"first_token_stats": {
|
||||
"mean": 2312.8511880022415,
|
||||
"min": 1681.2440420035273,
|
||||
"max": 2944.458334000956,
|
||||
"stdev": 893.2273919631452,
|
||||
"median": 2312.8511880022415
|
||||
},
|
||||
"raw_import_times_ms": [
|
||||
0.01120899833040312,
|
||||
0.006375004886649549
|
||||
],
|
||||
"raw_total_times_ms": [
|
||||
2945.087499996589,
|
||||
1697.1089580038097
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Profile Suite Example for PraisonAI
|
||||
|
||||
This example demonstrates how to run a comprehensive profiling suite
|
||||
programmatically with multiple scenarios.
|
||||
|
||||
Run with:
|
||||
python suite_profiling.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Ensure API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Please set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
from praisonai.cli.features.profiler import (
|
||||
ScenarioConfig,
|
||||
run_profile_suite,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
"""Run a profiling suite with custom scenarios."""
|
||||
|
||||
# Define custom scenarios
|
||||
scenarios = [
|
||||
ScenarioConfig(
|
||||
name="simple_query",
|
||||
prompt="What is 2+2?",
|
||||
stream=False,
|
||||
iterations=2,
|
||||
warmup=1,
|
||||
),
|
||||
ScenarioConfig(
|
||||
name="simple_stream",
|
||||
prompt="What is 2+2?",
|
||||
stream=True,
|
||||
iterations=2,
|
||||
warmup=1,
|
||||
),
|
||||
]
|
||||
|
||||
# Run suite with custom output directory
|
||||
print("🔬 Running Profile Suite...")
|
||||
result = run_profile_suite(
|
||||
output_dir="./profile_results",
|
||||
scenarios=scenarios,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# Print summary
|
||||
print("\n" + "=" * 60)
|
||||
print("Profile Suite Results")
|
||||
print("=" * 60)
|
||||
|
||||
print("\n📊 Startup Times:")
|
||||
print(f" Cold Start: {result.startup_cold_ms:.2f}ms")
|
||||
print(f" Warm Start: {result.startup_warm_ms:.2f}ms")
|
||||
|
||||
if result.import_analysis:
|
||||
print("\n📦 Top Import:")
|
||||
print(f" {result.import_analysis[0]['module']}: {result.import_analysis[0]['cumulative_ms']:.2f}ms")
|
||||
|
||||
print("\n🎯 Scenario Results:")
|
||||
for scenario in result.scenarios:
|
||||
stats = scenario.get_stats(scenario.total_times)
|
||||
print(f" {scenario.name}:")
|
||||
print(f" Mean: {stats['mean']:.2f}ms")
|
||||
print(f" Min: {stats['min']:.2f}ms")
|
||||
print(f" Max: {stats['max']:.2f}ms")
|
||||
|
||||
print("\n✅ Results saved to: ./profile_results/")
|
||||
print(" - suite_results.json (machine-readable)")
|
||||
print(" - suite_report.txt (human-readable)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Agent vs AutoRagAgent Comparison
|
||||
|
||||
Demonstrates the difference between using Agent directly vs AutoRagAgent wrapper.
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# 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 sample document
|
||||
sample_doc = "/tmp/comparison_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
f.write("""
|
||||
Product Manual: Smart Thermostat
|
||||
|
||||
Installation:
|
||||
1. Turn off power at the circuit breaker
|
||||
2. Remove old thermostat
|
||||
3. Connect wires to matching terminals
|
||||
4. Mount the base plate
|
||||
5. Attach the display unit
|
||||
6. Restore power
|
||||
|
||||
Features:
|
||||
- Wi-Fi connectivity
|
||||
- Voice control support
|
||||
- Energy usage reports
|
||||
- Geofencing auto-away
|
||||
- Learning schedule
|
||||
|
||||
Troubleshooting:
|
||||
- If display is blank, check power connection
|
||||
- If Wi-Fi won't connect, restart router
|
||||
- If temperature is inaccurate, recalibrate sensor
|
||||
""")
|
||||
|
||||
# Create base agent with knowledge
|
||||
agent = Agent(
|
||||
name="TechSupport",
|
||||
instructions="You are a technical support assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="comparison_user",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("Agent vs AutoRagAgent Comparison")
|
||||
print("=" * 60)
|
||||
|
||||
# Using Agent directly - always uses RAG when knowledge is configured
|
||||
print("\n[Using Agent Directly]")
|
||||
print("Agent with knowledge always retrieves context for every query.")
|
||||
print("\nQuery: 'Hello!'")
|
||||
response = agent.chat("Hello!")
|
||||
print(f"Response: {response}")
|
||||
|
||||
print("\nQuery: 'How do I install the thermostat?'")
|
||||
response = agent.chat("How do I install the thermostat?")
|
||||
print(f"Response: {response[:300]}...")
|
||||
|
||||
# Using AutoRagAgent - decides when to retrieve
|
||||
print("\n" + "=" * 60)
|
||||
print("[Using AutoRagAgent]")
|
||||
print("AutoRagAgent decides when to retrieve based on query heuristics.")
|
||||
|
||||
auto_rag = AutoRagAgent(agent=agent, retrieval_policy="auto")
|
||||
|
||||
print("\nQuery: 'Hello!' (short greeting - skips retrieval)")
|
||||
response = auto_rag.chat("Hello!")
|
||||
print(f"Response: {response}")
|
||||
|
||||
print("\nQuery: 'How do I install the thermostat?' (question - retrieves)")
|
||||
response = auto_rag.chat("How do I install the thermostat?")
|
||||
print(f"Response: {response[:300]}...")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("Summary:")
|
||||
print("-" * 60)
|
||||
print("""
|
||||
| Approach | Retrieval Behavior | Best For |
|
||||
|---------------|--------------------------------|------------------------------|
|
||||
| Agent | Always retrieves (if knowledge)| Consistent RAG responses |
|
||||
| AutoRagAgent | Decides per query | Mixed chat + RAG workloads |
|
||||
|
||||
AutoRagAgent is ideal when:
|
||||
- You have a chatbot that sometimes needs knowledge, sometimes doesn't
|
||||
- You want to reduce unnecessary retrieval overhead
|
||||
- You need fine-grained control over retrieval behavior
|
||||
""")
|
||||
|
||||
# Cleanup
|
||||
os.remove(sample_doc)
|
||||
|
||||
print("=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
AutoRagAgent Basic Example
|
||||
|
||||
Demonstrates basic usage of AutoRagAgent for automatic RAG retrieval decision.
|
||||
AutoRagAgent wraps an Agent and decides when to retrieve context based on query heuristics.
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# 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 sample document for testing
|
||||
sample_doc = "/tmp/sample_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
f.write("""
|
||||
PraisonAI Framework Documentation
|
||||
|
||||
PraisonAI is an AI agents framework that supports:
|
||||
- Single agent workflows
|
||||
- Multi-agent collaboration
|
||||
- RAG (Retrieval Augmented Generation)
|
||||
- Tool integration
|
||||
- Memory and context management
|
||||
|
||||
Key Features:
|
||||
1. AutoRagAgent - Automatic retrieval decision
|
||||
2. Knowledge bases - Document indexing and search
|
||||
3. Hybrid retrieval - Dense + BM25 search
|
||||
4. Reranking - Improve result relevance
|
||||
""")
|
||||
|
||||
# Create agent with knowledge
|
||||
agent = Agent(
|
||||
name="DocBot",
|
||||
instructions="You are a helpful documentation assistant. Answer questions based on the provided context.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="example_user", # Required for RAG retrieval
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
# Wrap with AutoRagAgent (default: auto policy)
|
||||
auto_rag = AutoRagAgent(agent=agent)
|
||||
|
||||
print("=" * 60)
|
||||
print("AutoRagAgent Basic Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Test 1: Question that should trigger retrieval
|
||||
print("\n[Test 1] Question about documentation (should retrieve):")
|
||||
response = auto_rag.chat("What features does PraisonAI support?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Test 2: Short greeting that should skip retrieval
|
||||
print("\n[Test 2] Short greeting (should skip retrieval):")
|
||||
response = auto_rag.chat("Hi!")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Test 3: Another question
|
||||
print("\n[Test 3] Another question (should retrieve):")
|
||||
response = auto_rag.chat("What is AutoRagAgent?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Cleanup
|
||||
os.remove(sample_doc)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
AutoRagAgent Policy Example
|
||||
|
||||
Demonstrates different retrieval policies:
|
||||
- auto: Decides based on query heuristics (default)
|
||||
- always: Always retrieves context
|
||||
- never: Never retrieves, direct chat only
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# 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 sample document
|
||||
sample_doc = "/tmp/policy_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
f.write("""
|
||||
Company Return Policy
|
||||
|
||||
Our return policy allows returns within 30 days of purchase.
|
||||
Items must be in original condition with tags attached.
|
||||
Refunds are processed within 5-7 business days.
|
||||
Exchanges are available for different sizes or colors.
|
||||
""")
|
||||
|
||||
# Create base agent with knowledge
|
||||
agent = Agent(
|
||||
name="PolicyBot",
|
||||
instructions="You are a customer service assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="policy_user",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("AutoRagAgent Policy Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Policy 1: AUTO (default)
|
||||
print("\n[Policy: AUTO] - Decides based on query heuristics")
|
||||
auto_rag = AutoRagAgent(agent=agent, retrieval_policy="auto")
|
||||
|
||||
print(" Query: 'What is the return policy?' (should retrieve)")
|
||||
response = auto_rag.chat("What is the return policy?")
|
||||
print(f" Response: {response[:200]}...")
|
||||
|
||||
print("\n Query: 'Hello' (should skip)")
|
||||
response = auto_rag.chat("Hello")
|
||||
print(f" Response: {response}")
|
||||
|
||||
# Policy 2: ALWAYS
|
||||
print("\n" + "-" * 60)
|
||||
print("[Policy: ALWAYS] - Always retrieves context")
|
||||
always_rag = AutoRagAgent(agent=agent, retrieval_policy="always")
|
||||
|
||||
print(" Query: 'Hello' (will retrieve even for greeting)")
|
||||
response = always_rag.chat("Hello")
|
||||
print(f" Response: {response[:200]}...")
|
||||
|
||||
# Policy 3: NEVER
|
||||
print("\n" + "-" * 60)
|
||||
print("[Policy: NEVER] - Never retrieves, direct chat only")
|
||||
never_rag = AutoRagAgent(agent=agent, retrieval_policy="never")
|
||||
|
||||
print(" Query: 'What is the return policy?' (will NOT retrieve)")
|
||||
response = never_rag.chat("What is the return policy?")
|
||||
print(f" Response: {response[:200]}...")
|
||||
|
||||
# Per-call overrides
|
||||
print("\n" + "-" * 60)
|
||||
print("[Per-call Overrides]")
|
||||
|
||||
print(" Force retrieval on 'auto' policy:")
|
||||
response = auto_rag.chat("Hi there", force_retrieval=True)
|
||||
print(f" Response: {response[:200]}...")
|
||||
|
||||
print("\n Skip retrieval on 'auto' policy:")
|
||||
response = auto_rag.chat("What is the return window?", skip_retrieval=True)
|
||||
print(f" Response: {response[:200]}...")
|
||||
|
||||
# Cleanup
|
||||
os.remove(sample_doc)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
RAG Basic Example
|
||||
|
||||
Demonstrates basic RAG (Retrieval Augmented Generation) with an Agent.
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
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 sample document
|
||||
sample_doc = "/tmp/rag_basic_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
f.write("""
|
||||
Machine Learning Fundamentals
|
||||
|
||||
Machine learning is a subset of artificial intelligence that enables systems
|
||||
to learn and improve from experience without being explicitly programmed.
|
||||
|
||||
Types of Machine Learning:
|
||||
1. Supervised Learning - Learning from labeled data
|
||||
2. Unsupervised Learning - Finding patterns in unlabeled data
|
||||
3. Reinforcement Learning - Learning through trial and error
|
||||
|
||||
Common Algorithms:
|
||||
- Linear Regression
|
||||
- Decision Trees
|
||||
- Neural Networks
|
||||
- Support Vector Machines
|
||||
""")
|
||||
|
||||
# Create agent with RAG
|
||||
agent = Agent(
|
||||
name="MLExpert",
|
||||
instructions="You are a machine learning expert. Answer questions based on the provided context.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="rag_user",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("RAG Basic Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Query with RAG
|
||||
print("\nQuery: What are the types of machine learning?")
|
||||
response = agent.chat("What are the types of machine learning?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
print("\nQuery: What algorithms are commonly used?")
|
||||
response = agent.chat("What algorithms are commonly used?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Cleanup
|
||||
os.remove(sample_doc)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
RAG Hybrid Retrieval Example
|
||||
|
||||
Demonstrates hybrid retrieval (dense + BM25 keyword search) for better results.
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# 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 sample document with technical content
|
||||
sample_doc = "/tmp/rag_hybrid_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
f.write("""
|
||||
API Documentation
|
||||
|
||||
Authentication:
|
||||
All API requests require an API key in the X-API-Key header.
|
||||
Rate limits: 100 requests per minute for free tier, 1000 for pro tier.
|
||||
|
||||
Endpoints:
|
||||
|
||||
POST /api/v1/users
|
||||
Creates a new user account.
|
||||
Required fields: email, password, name
|
||||
Returns: user_id, created_at
|
||||
|
||||
GET /api/v1/users/{id}
|
||||
Retrieves user information by ID.
|
||||
Returns: email, name, created_at, last_login
|
||||
|
||||
PUT /api/v1/users/{id}
|
||||
Updates user information.
|
||||
Optional fields: name, email, password
|
||||
|
||||
DELETE /api/v1/users/{id}
|
||||
Deletes a user account. Requires admin privileges.
|
||||
|
||||
Error Codes:
|
||||
- 400: Bad Request - Invalid input
|
||||
- 401: Unauthorized - Missing or invalid API key
|
||||
- 403: Forbidden - Insufficient permissions
|
||||
- 404: Not Found - Resource doesn't exist
|
||||
- 429: Too Many Requests - Rate limit exceeded
|
||||
""")
|
||||
|
||||
# Create agent with knowledge
|
||||
agent = Agent(
|
||||
name="APIDocBot",
|
||||
instructions="You are an API documentation assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="hybrid_user",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("RAG Hybrid Retrieval Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Standard retrieval
|
||||
print("\n[Standard Retrieval]")
|
||||
auto_rag_standard = AutoRagAgent(agent=agent, hybrid=False)
|
||||
response = auto_rag_standard.chat("What error code means rate limit exceeded?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Hybrid retrieval (dense + BM25)
|
||||
print("\n[Hybrid Retrieval - Dense + BM25]")
|
||||
auto_rag_hybrid = AutoRagAgent(agent=agent, hybrid=True)
|
||||
response = auto_rag_hybrid.chat("What error code means rate limit exceeded?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Hybrid with reranking for best quality
|
||||
print("\n[Hybrid + Reranking]")
|
||||
auto_rag_best = AutoRagAgent(agent=agent, hybrid=True, rerank=True, top_k=10)
|
||||
response = auto_rag_best.chat("How do I create a new user via the API?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Cleanup
|
||||
os.remove(sample_doc)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
RAG with Citations Example
|
||||
|
||||
Demonstrates RAG with citation tracking for source attribution.
|
||||
|
||||
Requirements:
|
||||
pip install praisonaiagents
|
||||
|
||||
Environment:
|
||||
OPENAI_API_KEY - Required for LLM calls
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# Ensure API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Please set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
# Create multiple sample documents
|
||||
doc1 = "/tmp/citation_doc1.txt"
|
||||
with open(doc1, "w") as f:
|
||||
f.write("""
|
||||
Research Paper: Climate Change Effects
|
||||
|
||||
Global temperatures have risen by 1.1°C since pre-industrial times.
|
||||
Sea levels are rising at 3.3mm per year.
|
||||
Arctic ice is declining at 13% per decade.
|
||||
""")
|
||||
|
||||
doc2 = "/tmp/citation_doc2.txt"
|
||||
with open(doc2, "w") as f:
|
||||
f.write("""
|
||||
Research Paper: Renewable Energy
|
||||
|
||||
Solar energy capacity has grown 20x in the last decade.
|
||||
Wind power now provides 7% of global electricity.
|
||||
Battery storage costs have dropped 89% since 2010.
|
||||
""")
|
||||
|
||||
# Create agent with multiple knowledge sources
|
||||
agent = Agent(
|
||||
name="ResearchBot",
|
||||
instructions="You are a research assistant. Always cite your sources.",
|
||||
knowledge=[doc1, doc2],
|
||||
user_id="citation_user",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("RAG with Citations Example")
|
||||
print("=" * 60)
|
||||
|
||||
# AutoRagAgent with citations enabled (default)
|
||||
auto_rag = AutoRagAgent(
|
||||
agent=agent,
|
||||
citations=True, # Include citations in response
|
||||
)
|
||||
|
||||
print("\nQuery: What are the key climate change statistics?")
|
||||
response = auto_rag.chat("What are the key climate change statistics?")
|
||||
print(f"Response:\n{response}")
|
||||
|
||||
print("\n" + "-" * 60)
|
||||
print("\nQuery: How has renewable energy grown?")
|
||||
response = auto_rag.chat("How has renewable energy grown?")
|
||||
print(f"Response:\n{response}")
|
||||
|
||||
# Without citations
|
||||
print("\n" + "-" * 60)
|
||||
print("\n[Without Citations]")
|
||||
auto_rag_no_cite = AutoRagAgent(agent=agent, citations=False)
|
||||
response = auto_rag_no_cite.chat("What is the rate of sea level rise?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# Cleanup
|
||||
os.remove(doc1)
|
||||
os.remove(doc2)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
Session Persistence Example
|
||||
|
||||
Demonstrates automatic session persistence with zero configuration.
|
||||
When you provide a session_id to an Agent, conversation history is
|
||||
automatically saved to disk and restored on subsequent runs.
|
||||
|
||||
Usage:
|
||||
python session_persistence_example.py
|
||||
|
||||
Requirements:
|
||||
- praisonaiagents package installed
|
||||
- OPENAI_API_KEY environment variable set
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
|
||||
def example_basic_persistence():
|
||||
"""Basic session persistence example."""
|
||||
print("=" * 60)
|
||||
print("EXAMPLE 1: Basic Session Persistence")
|
||||
print("=" * 60)
|
||||
|
||||
session_id = "demo-session-basic"
|
||||
|
||||
# Create agent with session_id
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
session_id=session_id,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
# First message
|
||||
print("\n[User]: My favorite color is blue and my lucky number is 7.")
|
||||
response1 = agent.chat("My favorite color is blue and my lucky number is 7.")
|
||||
print(f"[Assistant]: {response1}")
|
||||
|
||||
# Second message - agent remembers
|
||||
print("\n[User]: What is my favorite color?")
|
||||
response2 = agent.chat("What is my favorite color?")
|
||||
print(f"[Assistant]: {response2}")
|
||||
|
||||
print(f"\n✅ Session saved to: ~/.praison/sessions/{session_id}.json")
|
||||
print(f" Chat history length: {len(agent.chat_history)} messages")
|
||||
|
||||
|
||||
def example_session_restoration():
|
||||
"""Demonstrates session restoration across Agent instances."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 2: Session Restoration")
|
||||
print("=" * 60)
|
||||
|
||||
session_id = "demo-session-restore"
|
||||
|
||||
# First agent instance
|
||||
print("\n--- First Agent Instance ---")
|
||||
agent1 = Agent(
|
||||
name="MemoryBot",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
session_id=session_id,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("[User]: Remember that my pet's name is Max.")
|
||||
response1 = agent1.chat("Remember that my pet's name is Max.")
|
||||
print(f"[Assistant]: {response1}")
|
||||
|
||||
# Simulate new process by creating new agent instance
|
||||
print("\n--- New Agent Instance (simulating restart) ---")
|
||||
agent2 = Agent(
|
||||
name="MemoryBot",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
session_id=session_id,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("[User]: What is my pet's name?")
|
||||
response2 = agent2.chat("What is my pet's name?")
|
||||
print(f"[Assistant]: {response2}")
|
||||
|
||||
if "max" in response2.lower():
|
||||
print("\n✅ Session restored successfully! Agent remembered 'Max'.")
|
||||
else:
|
||||
print("\n⚠️ Agent may not have remembered correctly.")
|
||||
|
||||
|
||||
def example_in_memory_only():
|
||||
"""Demonstrates in-memory memory without persistence."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 3: In-Memory Only (No session_id)")
|
||||
print("=" * 60)
|
||||
|
||||
# Agent without session_id - in-memory only
|
||||
agent = Agent(
|
||||
name="TempBot",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("\n[User]: My name is Alice.")
|
||||
response1 = agent.chat("My name is Alice.")
|
||||
print(f"[Assistant]: {response1}")
|
||||
|
||||
print("\n[User]: What is my name?")
|
||||
response2 = agent.chat("What is my name?")
|
||||
print(f"[Assistant]: {response2}")
|
||||
|
||||
print(f"\n✅ In-memory chat history: {len(agent.chat_history)} messages")
|
||||
print(" (This will be lost when the Agent instance is garbage collected)")
|
||||
|
||||
|
||||
def example_direct_store_access():
|
||||
"""Demonstrates direct access to the session store."""
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE 4: Direct Session Store Access")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonaiagents.session import get_default_session_store
|
||||
|
||||
store = get_default_session_store()
|
||||
session_id = "demo-direct-access"
|
||||
|
||||
# Add messages directly
|
||||
store.add_user_message(session_id, "Hello from direct access!")
|
||||
store.add_assistant_message(session_id, "Hi! I received your message.")
|
||||
|
||||
# Get history
|
||||
history = store.get_chat_history(session_id)
|
||||
print(f"\n✅ Added {len(history)} messages directly to session store")
|
||||
|
||||
for msg in history:
|
||||
print(f" [{msg['role']}]: {msg['content']}")
|
||||
|
||||
# List all sessions
|
||||
sessions = store.list_sessions()
|
||||
print(f"\n📋 Total sessions in store: {len(sessions)}")
|
||||
|
||||
# Clean up demo session
|
||||
store.delete_session(session_id)
|
||||
print(f" Deleted demo session: {session_id}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all examples."""
|
||||
print("\n" + "=" * 60)
|
||||
print("SESSION PERSISTENCE EXAMPLES")
|
||||
print("=" * 60)
|
||||
|
||||
# Run examples
|
||||
example_basic_persistence()
|
||||
example_session_restoration()
|
||||
example_in_memory_only()
|
||||
example_direct_store_access()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ALL EXAMPLES COMPLETE")
|
||||
print("=" * 60)
|
||||
print("\nSession files are stored in: ~/.praison/sessions/")
|
||||
print("You can inspect them with: cat ~/.praison/sessions/<session_id>.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,121 @@
|
||||
# RAG Examples
|
||||
|
||||
This directory contains comprehensive examples demonstrating RAG (Retrieval Augmented Generation) with PraisonAI Agents.
|
||||
|
||||
## Core RAG Concepts
|
||||
|
||||
| Example | Description | Key Concepts |
|
||||
|---------|-------------|--------------|
|
||||
| `basic_retrieval.py` | Fundamental retrieve-then-generate pattern | Knowledge base, context injection |
|
||||
| `auto_retrieval.py` | Intelligent retrieval decisions with AutoRagAgent | Conditional RAG, retrieval policies |
|
||||
| `chunking_strategies.py` | Document splitting approaches | Paragraph, section, fixed-size chunking |
|
||||
| `hybrid_search.py` | Dense + sparse retrieval combination | Semantic search, keyword matching |
|
||||
| `reranking.py` | Two-stage retrieval with reranking | Precision optimization, cross-encoders |
|
||||
|
||||
## Advanced RAG Patterns
|
||||
|
||||
| Example | Description | Key Concepts |
|
||||
|---------|-------------|--------------|
|
||||
| `multi_document.py` | Synthesizing across multiple sources | Cross-referencing, source diversity |
|
||||
| `knowledge_graph.py` | Relationship-aware retrieval | Entity connections, multi-hop reasoning |
|
||||
| `external_sources.py` | Web, API, and database augmentation | Real-time data, hybrid sources |
|
||||
| `structured_output.py` | Guided and constrained generation | JSON output, Pydantic schemas |
|
||||
| `retrieval_policies.py` | Agent-decided retrieval strategies | Adaptive top-k, priority filtering |
|
||||
| `citations.py` | Source attribution and verification | Inline citations, verification |
|
||||
|
||||
## Application Examples
|
||||
|
||||
### PDF Q&A (`rag_pdf_qa.py`)
|
||||
|
||||
Basic RAG example showing how to:
|
||||
- Use Agent with knowledge parameter
|
||||
- Create explicit RAG pipeline with citations
|
||||
- Stream responses
|
||||
|
||||
```bash
|
||||
python rag_pdf_qa.py
|
||||
```
|
||||
|
||||
### Multi-Agent RAG (`rag_multi_agent.py`)
|
||||
|
||||
Advanced example showing:
|
||||
- Shared knowledge base across multiple agents
|
||||
- Collaborative RAG workflow
|
||||
- Sequential task processing
|
||||
|
||||
```bash
|
||||
python rag_multi_agent.py
|
||||
```
|
||||
|
||||
### RAG Evaluation (`rag_evaluation.py`)
|
||||
|
||||
Evaluation example showing:
|
||||
- Golden query testing
|
||||
- Retrieval accuracy metrics
|
||||
- Answer relevance checking
|
||||
|
||||
```bash
|
||||
python rag_evaluation.py
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run any example:
|
||||
|
||||
```bash
|
||||
# Basic RAG
|
||||
python basic_retrieval.py
|
||||
|
||||
# Auto RAG with intelligent decisions
|
||||
python auto_retrieval.py
|
||||
|
||||
# Hybrid search (dense + sparse)
|
||||
python hybrid_search.py
|
||||
|
||||
# RAG with citations
|
||||
python citations.py
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install "praisonaiagents[knowledge]"
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# Index documents
|
||||
praisonai rag index ./documents --collection my_docs
|
||||
|
||||
# Query
|
||||
praisonai rag query "What is the main finding?" --collection my_docs
|
||||
|
||||
# Interactive chat
|
||||
praisonai rag chat --collection my_docs
|
||||
|
||||
# Evaluate
|
||||
praisonai rag eval golden_queries.json --collection my_docs
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create `rag_config.yaml`:
|
||||
|
||||
```yaml
|
||||
knowledge:
|
||||
collection: my_docs
|
||||
chunking:
|
||||
strategy: recursive
|
||||
chunk_size: 512
|
||||
|
||||
rag:
|
||||
top_k: 5
|
||||
min_score: 0.3
|
||||
include_citations: true
|
||||
```
|
||||
|
||||
Use with:
|
||||
```bash
|
||||
praisonai rag query "Question" --config rag_config.yaml
|
||||
```
|
||||
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
Auto RAG: Intelligent Retrieval Decisions
|
||||
|
||||
This example demonstrates AutoRagAgent - an agent that automatically decides
|
||||
when to retrieve context based on the query type.
|
||||
|
||||
RAG Concept: Conditional retrieval - not every query needs RAG. Simple greetings
|
||||
or general knowledge questions can be answered directly, while domain-specific
|
||||
questions benefit from retrieval.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, AutoRagAgent
|
||||
|
||||
# Sample knowledge base: Software development best practices as text strings
|
||||
DEV_PRACTICES = [
|
||||
"""Code Review Guidelines:
|
||||
- Review code within 24 hours of submission
|
||||
- Focus on logic, security, and maintainability
|
||||
- Limit reviews to 400 lines of code at a time
|
||||
- Use constructive language and suggest alternatives
|
||||
- Automate style checks with linters""",
|
||||
|
||||
"""Testing Strategy:
|
||||
- Aim for 80% code coverage minimum
|
||||
- Write unit tests before integration tests
|
||||
- Use mocking for external dependencies
|
||||
- Run tests in CI/CD pipeline before merge
|
||||
- Include edge cases and error scenarios""",
|
||||
|
||||
"""Documentation Standards:
|
||||
- Document public APIs with examples
|
||||
- Keep README files up to date
|
||||
- Use inline comments for complex logic only
|
||||
- Maintain a changelog for version history
|
||||
- Include setup instructions for new developers""",
|
||||
|
||||
"""Security Practices:
|
||||
- Never commit secrets or API keys
|
||||
- Use environment variables for configuration
|
||||
- Validate all user inputs
|
||||
- Keep dependencies updated
|
||||
- Conduct regular security audits"""
|
||||
]
|
||||
|
||||
|
||||
def auto_rag_basic():
|
||||
"""Demonstrate AutoRagAgent with automatic retrieval decisions."""
|
||||
|
||||
# Build context from knowledge
|
||||
context = "\n\n".join(DEV_PRACTICES)
|
||||
|
||||
# Create base agent with context in instructions
|
||||
base_agent = Agent(
|
||||
name="Dev Coach",
|
||||
instructions=f"""You are a software development coach.
|
||||
Answer questions about development best practices.
|
||||
Be concise and practical in your responses.
|
||||
|
||||
KNOWLEDGE BASE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Wrap with AutoRagAgent for intelligent retrieval
|
||||
auto_agent = AutoRagAgent(agent=base_agent)
|
||||
|
||||
# Mix of queries - some need retrieval, some don't
|
||||
queries = [
|
||||
# Should trigger retrieval (domain-specific)
|
||||
"What are the code review guidelines?",
|
||||
"How much code coverage should we aim for?",
|
||||
|
||||
# Should skip retrieval (general/greeting)
|
||||
"Hello, how are you?",
|
||||
"What is 2 + 2?",
|
||||
|
||||
# Should trigger retrieval (domain-specific)
|
||||
"What security practices should we follow?",
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print("AUTO RAG: Intelligent Retrieval Decisions")
|
||||
print("=" * 60)
|
||||
print("\nAutoRagAgent decides when to retrieve based on query type.\n")
|
||||
|
||||
for query in queries:
|
||||
print(f"📝 Query: {query}")
|
||||
response = auto_agent.chat(query)
|
||||
print(f"💡 Answer: {response[:200]}..." if len(str(response)) > 200 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def auto_rag_with_policies():
|
||||
"""Demonstrate different retrieval policies."""
|
||||
from praisonaiagents import AutoRagConfig
|
||||
from praisonaiagents.agents.auto_rag_agent import RetrievalPolicy
|
||||
|
||||
# Build context
|
||||
context = "\n\n".join(DEV_PRACTICES)
|
||||
|
||||
# Create base agent
|
||||
base_agent = Agent(
|
||||
name="Policy Demo Agent",
|
||||
instructions=f"""You answer questions about software development.
|
||||
|
||||
KNOWLEDGE BASE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RETRIEVAL POLICIES COMPARISON")
|
||||
print("=" * 60)
|
||||
|
||||
query = "What are the documentation standards?"
|
||||
|
||||
# Policy: AUTO (default) - agent decides
|
||||
auto_config = AutoRagConfig(retrieval_policy=RetrievalPolicy.AUTO)
|
||||
auto_agent = AutoRagAgent(agent=base_agent, config=auto_config)
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
print("\n🔄 Policy: AUTO (agent decides)")
|
||||
response = auto_agent.chat(query)
|
||||
print(f" Answer: {response[:150]}...")
|
||||
|
||||
# Policy: ALWAYS - always retrieve
|
||||
always_config = AutoRagConfig(retrieval_policy=RetrievalPolicy.ALWAYS)
|
||||
always_agent = AutoRagAgent(agent=base_agent, config=always_config)
|
||||
|
||||
print("\n🔄 Policy: ALWAYS (force retrieval)")
|
||||
response = always_agent.chat(query)
|
||||
print(f" Answer: {response[:150]}...")
|
||||
|
||||
# Policy: NEVER - never retrieve (use parametric knowledge only)
|
||||
never_config = AutoRagConfig(retrieval_policy=RetrievalPolicy.NEVER)
|
||||
never_agent = AutoRagAgent(agent=base_agent, config=never_config)
|
||||
|
||||
print("\n🔄 Policy: NEVER (no retrieval)")
|
||||
response = never_agent.chat(query)
|
||||
print(f" Answer: {response[:150]}...")
|
||||
|
||||
|
||||
def auto_rag_config_options():
|
||||
"""Demonstrate AutoRagConfig options."""
|
||||
from praisonaiagents import AutoRagConfig
|
||||
|
||||
# Build context
|
||||
context = "\n\n".join(DEV_PRACTICES)
|
||||
|
||||
base_agent = Agent(
|
||||
name="Config Demo",
|
||||
instructions=f"""You are a helpful assistant with development knowledge.
|
||||
|
||||
KNOWLEDGE BASE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("AUTO RAG CONFIG OPTIONS")
|
||||
print("=" * 60)
|
||||
|
||||
# Config with citations enabled
|
||||
config_with_citations = AutoRagConfig(
|
||||
include_citations=True,
|
||||
top_k=3,
|
||||
max_context_tokens=2000
|
||||
)
|
||||
agent_with_citations = AutoRagAgent(agent=base_agent, config=config_with_citations)
|
||||
|
||||
query = "What testing practices should we follow?"
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
print("\n🔄 With citations enabled (top_k=3):")
|
||||
response = agent_with_citations.chat(query)
|
||||
print(f" {response[:200]}...")
|
||||
|
||||
# Config without citations
|
||||
config_no_citations = AutoRagConfig(
|
||||
include_citations=False,
|
||||
top_k=5
|
||||
)
|
||||
agent_no_citations = AutoRagAgent(agent=base_agent, config=config_no_citations)
|
||||
|
||||
print("\n🔄 Without citations (top_k=5):")
|
||||
response = agent_no_citations.chat(query)
|
||||
print(f" {response[:200]}...")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all auto RAG examples."""
|
||||
print("\n🚀 PraisonAI Auto RAG Examples\n")
|
||||
|
||||
# Example 1: Basic AutoRagAgent
|
||||
auto_rag_basic()
|
||||
|
||||
# Example 2: Different retrieval policies
|
||||
auto_rag_with_policies()
|
||||
|
||||
# Example 3: Config options
|
||||
auto_rag_config_options()
|
||||
|
||||
print("\n✅ Auto RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
Basic RAG: Retrieve and Generate
|
||||
|
||||
This example demonstrates the fundamental RAG pattern in PraisonAI:
|
||||
1. Provide context to an agent
|
||||
2. Agent uses context to answer questions accurately
|
||||
3. Generate grounded answers using the provided context
|
||||
|
||||
RAG Concept: The core retrieve-then-generate pattern that grounds LLM responses
|
||||
in factual content, reducing hallucinations.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Technology company profiles as text strings
|
||||
COMPANY_PROFILES = [
|
||||
"""NovaTech Solutions specializes in cloud infrastructure and DevOps automation.
|
||||
Founded in 2018, they serve over 500 enterprise clients globally.
|
||||
Key products: CloudSync (infrastructure orchestration), DevPipeline (CI/CD platform).
|
||||
Headquarters: Austin, Texas. Employees: 1,200.""",
|
||||
|
||||
"""GreenLeaf Analytics provides environmental monitoring and sustainability reporting.
|
||||
Established in 2015, they work with governments and corporations on climate data.
|
||||
Key products: EcoTrack (emissions monitoring), SustainReport (ESG reporting).
|
||||
Headquarters: Seattle, Washington. Employees: 450.""",
|
||||
|
||||
"""QuantumBridge Financial offers algorithmic trading and risk management solutions.
|
||||
Started in 2012, they manage over $50 billion in assets for institutional clients.
|
||||
Key products: AlgoTrader Pro (trading platform), RiskShield (portfolio analytics).
|
||||
Headquarters: New York City. Employees: 800.""",
|
||||
|
||||
"""MediCore Health develops AI-powered diagnostic tools for healthcare providers.
|
||||
Founded in 2019, they partner with 200+ hospitals across North America.
|
||||
Key products: DiagnosAI (medical imaging analysis), PatientFlow (care coordination).
|
||||
Headquarters: Boston, Massachusetts. Employees: 350."""
|
||||
]
|
||||
|
||||
|
||||
def basic_rag_example():
|
||||
"""Demonstrate basic RAG with context injection."""
|
||||
|
||||
# Build context from knowledge base
|
||||
context = "\n\n".join(COMPANY_PROFILES)
|
||||
|
||||
# Create an agent with instructions
|
||||
agent = Agent(
|
||||
name="Company Analyst",
|
||||
instructions=f"""You are a business analyst who answers questions about companies.
|
||||
Use only the following company information to answer questions accurately.
|
||||
If the information isn't available, say so clearly.
|
||||
|
||||
COMPANY DATABASE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Test queries
|
||||
queries = [
|
||||
"What products does NovaTech Solutions offer?",
|
||||
"Which company focuses on environmental monitoring?",
|
||||
"How many employees does QuantumBridge Financial have?",
|
||||
"What is MediCore Health's main focus area?"
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print("BASIC RAG EXAMPLE: Company Knowledge Base")
|
||||
print("=" * 60)
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def rag_with_query_context():
|
||||
"""Demonstrate per-query context injection for RAG."""
|
||||
|
||||
# Create a general agent
|
||||
agent = Agent(
|
||||
name="Research Assistant",
|
||||
instructions="You answer questions based on the context provided in each query.",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Simulate retrieved context (in real RAG, this comes from vector search)
|
||||
retrieved_context = """
|
||||
The Pacific Ocean is the largest and deepest ocean on Earth.
|
||||
It covers approximately 165.25 million square kilometers.
|
||||
The Mariana Trench, located in the Pacific, is the deepest known point at 10,994 meters.
|
||||
The Pacific Ocean was named by explorer Ferdinand Magellan in 1520.
|
||||
"""
|
||||
|
||||
query = "What is the deepest point in the Pacific Ocean?"
|
||||
|
||||
# Build prompt with context (RAG pattern)
|
||||
prompt_with_context = f"""Use the following context to answer the question.
|
||||
|
||||
Context:
|
||||
{retrieved_context}
|
||||
|
||||
Question: {query}
|
||||
|
||||
Answer based only on the context provided:"""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RAG WITH PER-QUERY CONTEXT INJECTION")
|
||||
print("=" * 60)
|
||||
|
||||
print(f"\n📚 Context: {retrieved_context[:100]}...")
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
response = agent.chat(prompt_with_context)
|
||||
print(f"\n💡 Answer: {response}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all basic RAG examples."""
|
||||
print("\n🚀 PraisonAI Basic RAG Examples\n")
|
||||
|
||||
# Example 1: Basic RAG with knowledge base
|
||||
basic_rag_example()
|
||||
|
||||
# Example 2: Per-query context injection
|
||||
rag_with_query_context()
|
||||
|
||||
print("\n✅ Basic RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,272 @@
|
||||
"""
|
||||
Chunking Strategies for RAG
|
||||
|
||||
This example demonstrates different approaches to splitting documents into
|
||||
chunks for effective retrieval.
|
||||
|
||||
RAG Concept: Chunking is critical for RAG quality. Too large chunks dilute
|
||||
relevance; too small chunks lose context. The right strategy depends on
|
||||
your content type and query patterns.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample long document: Product documentation
|
||||
PRODUCT_DOCUMENTATION = """
|
||||
# CloudManager Pro - Complete User Guide
|
||||
|
||||
## Chapter 1: Getting Started
|
||||
|
||||
CloudManager Pro is an enterprise cloud management platform that helps organizations
|
||||
monitor, optimize, and secure their multi-cloud infrastructure. This guide covers
|
||||
installation, configuration, and daily operations.
|
||||
|
||||
### 1.1 System Requirements
|
||||
|
||||
Before installing CloudManager Pro, ensure your system meets these requirements:
|
||||
- Operating System: Ubuntu 20.04+, RHEL 8+, or Windows Server 2019+
|
||||
- Memory: Minimum 16GB RAM, recommended 32GB for production
|
||||
- Storage: 100GB SSD for application, additional storage for logs
|
||||
- Network: Outbound HTTPS access to cloud provider APIs
|
||||
|
||||
### 1.2 Installation Steps
|
||||
|
||||
1. Download the installer from the customer portal
|
||||
2. Run the installation script with root privileges
|
||||
3. Configure the database connection
|
||||
4. Set up the initial admin account
|
||||
5. Verify the installation with the health check command
|
||||
|
||||
## Chapter 2: Dashboard Overview
|
||||
|
||||
The main dashboard provides a unified view of your cloud resources across all
|
||||
connected providers. Key metrics displayed include:
|
||||
|
||||
### 2.1 Resource Metrics
|
||||
|
||||
- Total compute instances across all clouds
|
||||
- Storage utilization and growth trends
|
||||
- Network bandwidth consumption
|
||||
- Cost breakdown by provider and project
|
||||
|
||||
### 2.2 Alert Summary
|
||||
|
||||
The alert panel shows active incidents categorized by severity:
|
||||
- Critical: Immediate action required (service outages)
|
||||
- Warning: Attention needed within 24 hours
|
||||
- Info: Informational notifications
|
||||
|
||||
## Chapter 3: Cost Optimization
|
||||
|
||||
CloudManager Pro includes powerful cost optimization features that can reduce
|
||||
your cloud spending by 20-40%.
|
||||
|
||||
### 3.1 Right-sizing Recommendations
|
||||
|
||||
The platform analyzes resource utilization patterns and suggests optimal
|
||||
instance sizes. Recommendations are based on:
|
||||
- CPU utilization over 14-day periods
|
||||
- Memory usage patterns
|
||||
- Network I/O requirements
|
||||
|
||||
### 3.2 Reserved Instance Planning
|
||||
|
||||
Use the RI Planner to identify opportunities for reserved instance purchases.
|
||||
The tool calculates potential savings based on your usage patterns and
|
||||
commitment preferences (1-year vs 3-year terms).
|
||||
|
||||
## Chapter 4: Security Features
|
||||
|
||||
### 4.1 Compliance Monitoring
|
||||
|
||||
CloudManager Pro continuously monitors your infrastructure against common
|
||||
compliance frameworks including SOC 2, HIPAA, and PCI-DSS. Non-compliant
|
||||
resources are flagged with remediation guidance.
|
||||
|
||||
### 4.2 Access Control
|
||||
|
||||
Role-based access control (RBAC) allows fine-grained permissions:
|
||||
- Admin: Full access to all features
|
||||
- Operator: Can view and modify resources
|
||||
- Viewer: Read-only access to dashboards
|
||||
- Billing: Access to cost and billing features only
|
||||
"""
|
||||
|
||||
|
||||
def chunk_by_paragraphs(text: str, min_length: int = 100) -> list:
|
||||
"""Split text into paragraph-based chunks."""
|
||||
paragraphs = text.split("\n\n")
|
||||
chunks = []
|
||||
current_chunk = ""
|
||||
|
||||
for para in paragraphs:
|
||||
para = para.strip()
|
||||
if not para:
|
||||
continue
|
||||
|
||||
if len(current_chunk) + len(para) < 500:
|
||||
current_chunk += "\n\n" + para if current_chunk else para
|
||||
else:
|
||||
if len(current_chunk) >= min_length:
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
current_chunk = para
|
||||
|
||||
if current_chunk and len(current_chunk) >= min_length:
|
||||
chunks.append({"content": current_chunk.strip()})
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
def chunk_by_sections(text: str) -> list:
|
||||
"""Split text by markdown headers (sections)."""
|
||||
import re
|
||||
|
||||
# Split by headers (## or ###)
|
||||
sections = re.split(r'\n(?=##+ )', text)
|
||||
chunks = []
|
||||
|
||||
for section in sections:
|
||||
section = section.strip()
|
||||
if len(section) > 50: # Skip very short sections
|
||||
# Extract title if present
|
||||
lines = section.split('\n')
|
||||
title = lines[0].replace('#', '').strip() if lines[0].startswith('#') else "Untitled"
|
||||
chunks.append({
|
||||
"id": title.lower().replace(' ', '_')[:30],
|
||||
"content": section
|
||||
})
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
def chunk_by_fixed_size(text: str, chunk_size: int = 500, overlap: int = 50) -> list:
|
||||
"""Split text into fixed-size chunks with overlap."""
|
||||
chunks = []
|
||||
start = 0
|
||||
|
||||
while start < len(text):
|
||||
end = start + chunk_size
|
||||
chunk_text = text[start:end]
|
||||
|
||||
# Try to break at sentence boundary
|
||||
if end < len(text):
|
||||
last_period = chunk_text.rfind('.')
|
||||
if last_period > chunk_size * 0.5:
|
||||
end = start + last_period + 1
|
||||
chunk_text = text[start:end]
|
||||
|
||||
chunks.append({"content": chunk_text.strip()})
|
||||
start = end - overlap
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
def demonstrate_chunking_strategies():
|
||||
"""Show different chunking approaches and their effects."""
|
||||
|
||||
print("=" * 60)
|
||||
print("CHUNKING STRATEGIES COMPARISON")
|
||||
print("=" * 60)
|
||||
|
||||
# Strategy 1: Paragraph-based chunking
|
||||
para_chunks = chunk_by_paragraphs(PRODUCT_DOCUMENTATION)
|
||||
print(f"\n📄 Paragraph Chunking: {len(para_chunks)} chunks")
|
||||
print(f" Average chunk size: {sum(len(c['content']) for c in para_chunks) // len(para_chunks)} chars")
|
||||
print(f" Sample chunk: {para_chunks[0]['content'][:100]}...")
|
||||
|
||||
# Strategy 2: Section-based chunking
|
||||
section_chunks = chunk_by_sections(PRODUCT_DOCUMENTATION)
|
||||
print(f"\n📑 Section Chunking: {len(section_chunks)} chunks")
|
||||
print(f" Average chunk size: {sum(len(c['content']) for c in section_chunks) // len(section_chunks)} chars")
|
||||
print(f" Sections: {[c['id'] for c in section_chunks[:5]]}...")
|
||||
|
||||
# Strategy 3: Fixed-size chunking
|
||||
fixed_chunks = chunk_by_fixed_size(PRODUCT_DOCUMENTATION, chunk_size=400, overlap=50)
|
||||
print(f"\n📏 Fixed-Size Chunking (400 chars, 50 overlap): {len(fixed_chunks)} chunks")
|
||||
print(f" Average chunk size: {sum(len(c['content']) for c in fixed_chunks) // len(fixed_chunks)} chars")
|
||||
|
||||
|
||||
def rag_with_section_chunks():
|
||||
"""Demonstrate RAG using section-based chunks."""
|
||||
|
||||
# Create section-based chunks
|
||||
chunks = chunk_by_sections(PRODUCT_DOCUMENTATION)
|
||||
|
||||
# Build context from chunks
|
||||
context = "\n\n".join([f"[{c['id']}]\n{c['content']}" for c in chunks])
|
||||
|
||||
# Create agent with chunked knowledge in instructions
|
||||
agent = Agent(
|
||||
name="Product Expert",
|
||||
instructions=f"""You are a CloudManager Pro product expert.
|
||||
Answer questions using the product documentation.
|
||||
Be specific and reference relevant sections when helpful.
|
||||
|
||||
PRODUCT DOCUMENTATION:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
queries = [
|
||||
"What are the system requirements for CloudManager Pro?",
|
||||
"How can I reduce cloud costs?",
|
||||
"What compliance frameworks are supported?"
|
||||
]
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RAG WITH SECTION-BASED CHUNKS")
|
||||
print("=" * 60)
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response[:250]}..." if len(str(response)) > 250 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def semantic_chunking_concept():
|
||||
"""Explain semantic chunking concept (agent-driven)."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("SEMANTIC CHUNKING CONCEPT")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
Semantic chunking goes beyond fixed rules by considering meaning:
|
||||
|
||||
1. **Sentence Embedding Similarity**
|
||||
- Compute embeddings for each sentence
|
||||
- Group sentences with similar embeddings
|
||||
- Split when similarity drops below threshold
|
||||
|
||||
2. **Topic-Based Chunking**
|
||||
- Identify topic shifts in the document
|
||||
- Create chunks that maintain topical coherence
|
||||
|
||||
3. **Agent-Driven Chunking**
|
||||
- Use an LLM to identify logical boundaries
|
||||
- Preserve context and relationships
|
||||
|
||||
PraisonAI's Knowledge system handles chunking automatically,
|
||||
but understanding these strategies helps optimize retrieval quality.
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all chunking strategy examples."""
|
||||
print("\n🚀 PraisonAI Chunking Strategies Examples\n")
|
||||
|
||||
# Example 1: Compare chunking strategies
|
||||
demonstrate_chunking_strategies()
|
||||
|
||||
# Example 2: RAG with section chunks
|
||||
rag_with_section_chunks()
|
||||
|
||||
# Example 3: Semantic chunking concept
|
||||
semantic_chunking_concept()
|
||||
|
||||
print("\n✅ Chunking strategy examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,370 @@
|
||||
"""
|
||||
RAG with Citations: Source Attribution and Verification
|
||||
|
||||
This example demonstrates how to include citations and source references
|
||||
in RAG responses for transparency and verification.
|
||||
|
||||
RAG Concept: Trust in AI-generated answers requires verification. Citations
|
||||
link answers back to source documents, enabling fact-checking and building
|
||||
user confidence.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Legal documents with clear sources
|
||||
LEGAL_DOCUMENTS = [
|
||||
{
|
||||
"id": "policy_001",
|
||||
"source": "Employee Handbook v2024",
|
||||
"section": "Section 3.2 - Leave Policies",
|
||||
"content": """
|
||||
Annual Leave Entitlement:
|
||||
- Full-time employees: 20 days per year
|
||||
- Part-time employees: Pro-rated based on hours
|
||||
- Unused leave: Up to 5 days may carry over to next year
|
||||
- Leave requests: Submit at least 2 weeks in advance
|
||||
- Approval: Manager approval required for leave > 3 consecutive days
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "policy_002",
|
||||
"source": "Employee Handbook v2024",
|
||||
"section": "Section 4.1 - Remote Work",
|
||||
"content": """
|
||||
Remote Work Policy:
|
||||
- Eligibility: Employees in approved roles after 6 months tenure
|
||||
- Maximum: 3 days per week remote work allowed
|
||||
- Requirements: Reliable internet, dedicated workspace
|
||||
- Core hours: Must be available 10am-3pm local time
|
||||
- Equipment: Company provides laptop; employee provides internet
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "policy_003",
|
||||
"source": "Benefits Guide 2024",
|
||||
"section": "Chapter 2 - Health Insurance",
|
||||
"content": """
|
||||
Health Insurance Coverage:
|
||||
- Provider: BlueCross BlueShield
|
||||
- Coverage: Employee + dependents eligible
|
||||
- Premium: Company pays 80%, employee pays 20%
|
||||
- Deductible: $500 individual, $1000 family
|
||||
- Enrollment: During annual open enrollment or qualifying life event
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "policy_004",
|
||||
"source": "Benefits Guide 2024",
|
||||
"section": "Chapter 5 - Retirement",
|
||||
"content": """
|
||||
401(k) Retirement Plan:
|
||||
- Eligibility: After 90 days of employment
|
||||
- Company match: 100% up to 4% of salary
|
||||
- Vesting: Immediate vesting for employee contributions
|
||||
- Company match vesting: 3-year graded schedule
|
||||
- Investment options: 15 fund choices available
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "policy_005",
|
||||
"source": "Code of Conduct 2024",
|
||||
"section": "Article 7 - Conflicts of Interest",
|
||||
"content": """
|
||||
Conflict of Interest Policy:
|
||||
- Disclosure: All potential conflicts must be disclosed to HR
|
||||
- Outside employment: Requires written approval
|
||||
- Financial interests: Cannot hold >5% in competitors
|
||||
- Gifts: Cannot accept gifts valued over $50
|
||||
- Relationships: Must disclose family relationships with vendors
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def basic_citations():
|
||||
"""Demonstrate basic citation inclusion in RAG responses."""
|
||||
|
||||
print("=" * 60)
|
||||
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=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.
|
||||
|
||||
POLICY DOCUMENTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
queries = [
|
||||
"How many vacation days do I get?",
|
||||
"Can I work from home?",
|
||||
"What's the company match for 401k?"
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer with Citations:\n{response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def structured_citations():
|
||||
"""Demonstrate structured citation format."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=f"""You provide policy information with structured citations.
|
||||
|
||||
Format your response as:
|
||||
|
||||
ANSWER:
|
||||
[Your answer here]
|
||||
|
||||
SOURCES:
|
||||
1. [Document] - [Section] - [Relevant quote]
|
||||
2. [Document] - [Section] - [Relevant quote]
|
||||
|
||||
Always include at least one source citation.
|
||||
|
||||
POLICY DOCUMENTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "What are the requirements for remote work eligibility?"
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Structured Response:\n{response}")
|
||||
|
||||
|
||||
def inline_citations():
|
||||
"""Demonstrate inline citation style."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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.
|
||||
|
||||
Example:
|
||||
"Employees receive 20 days of leave [1]. Remote work is allowed
|
||||
for up to 3 days per week [2]."
|
||||
|
||||
References:
|
||||
[1] Employee Handbook v2024, Section 3.2
|
||||
[2] Employee Handbook v2024, Section 4.1
|
||||
|
||||
POLICY DOCUMENTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "Summarize the key employee benefits."
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Academic-Style Response:\n{response}")
|
||||
|
||||
|
||||
def citation_verification():
|
||||
"""Demonstrate citation verification concept."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("CITATION VERIFICATION")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🔍 Citation Verification Process:
|
||||
|
||||
1. **Extract Citations**
|
||||
Parse the agent's response to find citation markers
|
||||
|
||||
2. **Locate Source Documents**
|
||||
Match citations to documents in knowledge base
|
||||
|
||||
3. **Verify Claims**
|
||||
Check if the cited text supports the claim
|
||||
|
||||
4. **Flag Discrepancies**
|
||||
Highlight any mismatches or unsupported claims
|
||||
|
||||
Example Verification:
|
||||
""")
|
||||
|
||||
# Simulated verification
|
||||
claim = "Employees get 20 days of annual leave"
|
||||
cited_source = "Employee Handbook v2024, Section 3.2"
|
||||
source_text = "Full-time employees: 20 days per year"
|
||||
|
||||
print(f" Claim: \"{claim}\"")
|
||||
print(f" Citation: {cited_source}")
|
||||
print(f" Source Text: \"{source_text}\"")
|
||||
print(" Verification: ✅ SUPPORTED")
|
||||
|
||||
print("""
|
||||
|
||||
Implementation:
|
||||
```python
|
||||
def verify_citation(claim: str, source_id: str, knowledge) -> bool:
|
||||
# Get source document
|
||||
source = knowledge.get(source_id)
|
||||
|
||||
# Check if claim is supported by source
|
||||
# (In practice, use semantic similarity)
|
||||
return claim_supported_by_source(claim, source)
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def multi_source_synthesis():
|
||||
"""Demonstrate synthesizing from multiple sources with citations."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=f"""You synthesize information from multiple policy documents.
|
||||
|
||||
When answering:
|
||||
1. Gather relevant information from all sources
|
||||
2. Synthesize into a coherent answer
|
||||
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.
|
||||
|
||||
POLICY DOCUMENTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "Give me a complete overview of employee benefits and policies."
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Multi-Source Synthesis:\n{response}")
|
||||
|
||||
|
||||
def citation_best_practices():
|
||||
"""Share best practices for citations in RAG."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("CITATION BEST PRACTICES")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📚 Best Practices for RAG Citations:
|
||||
|
||||
1. **Include Metadata in Knowledge Base**
|
||||
```python
|
||||
documents = [
|
||||
{
|
||||
"id": "doc_001",
|
||||
"source": "Policy Manual",
|
||||
"section": "Chapter 3",
|
||||
"date": "2024-01-15",
|
||||
"author": "HR Department",
|
||||
"content": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
2. **Instruct Agent to Cite**
|
||||
- Be explicit in instructions about citation format
|
||||
- Provide examples of expected citation style
|
||||
- Require citations for factual claims
|
||||
|
||||
3. **Use Consistent Citation Format**
|
||||
- Inline: [1], [2] with reference list
|
||||
- Parenthetical: (Source, Section)
|
||||
- Footnote style: ¹, ² with notes
|
||||
|
||||
4. **Enable Verification**
|
||||
- Include document IDs for programmatic lookup
|
||||
- Store enough metadata to locate original
|
||||
- Consider including page numbers or paragraphs
|
||||
|
||||
5. **Handle Missing Sources**
|
||||
- Instruct agent to acknowledge when info isn't in sources
|
||||
- Distinguish between sourced and general knowledge
|
||||
- Use phrases like "Based on the provided documents..."
|
||||
|
||||
6. **Citation Granularity**
|
||||
- Document level: Good for general attribution
|
||||
- Section level: Better for verification
|
||||
- Quote level: Best for accuracy-critical applications
|
||||
|
||||
Example Agent Instructions:
|
||||
```python
|
||||
instructions = '''
|
||||
Answer questions using the provided documents.
|
||||
|
||||
Rules:
|
||||
- Cite sources for all factual claims
|
||||
- Use format: [Source: Document Name, Section]
|
||||
- If information isn't in the documents, say so
|
||||
- Never make claims without citation
|
||||
'''
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all citation examples."""
|
||||
print("\n🚀 PraisonAI RAG Citations Examples\n")
|
||||
|
||||
# Example 1: Basic citations
|
||||
basic_citations()
|
||||
|
||||
# Example 2: Structured citations
|
||||
structured_citations()
|
||||
|
||||
# Example 3: Inline citations
|
||||
inline_citations()
|
||||
|
||||
# Example 4: Citation verification
|
||||
citation_verification()
|
||||
|
||||
# Example 5: Multi-source synthesis
|
||||
multi_source_synthesis()
|
||||
|
||||
# Example 6: Best practices
|
||||
citation_best_practices()
|
||||
|
||||
print("\n✅ Citation examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,353 @@
|
||||
"""
|
||||
External Sources RAG: Beyond Vector Search
|
||||
|
||||
This example demonstrates RAG with external knowledge sources like
|
||||
web search, APIs, and databases.
|
||||
|
||||
RAG Concept: RAG isn't limited to pre-indexed documents. External sources
|
||||
provide real-time, up-to-date information that complements static knowledge.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Simulated external data sources
|
||||
def mock_web_search(query: str) -> list:
|
||||
"""Simulate web search results."""
|
||||
# In production, this would call a real search API
|
||||
mock_results = {
|
||||
"weather": [
|
||||
{"title": "Current Weather", "snippet": "Today's forecast: Partly cloudy, high of 72°F (22°C), low of 58°F (14°C). Humidity at 45%."},
|
||||
{"title": "Weekly Outlook", "snippet": "Rain expected Wednesday and Thursday. Weekend looks sunny with temperatures in the mid-70s."}
|
||||
],
|
||||
"news": [
|
||||
{"title": "Tech Industry Update", "snippet": "Major tech companies report strong Q3 earnings. AI investments continue to grow across sectors."},
|
||||
{"title": "Market Summary", "snippet": "Stock markets closed higher today. S&P 500 up 0.8%, Nasdaq up 1.2%."}
|
||||
],
|
||||
"default": [
|
||||
{"title": "General Information", "snippet": "Search results would appear here with relevant web content."}
|
||||
]
|
||||
}
|
||||
|
||||
for key in mock_results:
|
||||
if key in query.lower():
|
||||
return mock_results[key]
|
||||
return mock_results["default"]
|
||||
|
||||
|
||||
def mock_database_query(query: str) -> dict:
|
||||
"""Simulate database query results."""
|
||||
# In production, this would query a real database
|
||||
mock_data = {
|
||||
"user_stats": {
|
||||
"total_users": 15420,
|
||||
"active_today": 3250,
|
||||
"new_signups": 127,
|
||||
"retention_rate": "78%"
|
||||
},
|
||||
"sales_data": {
|
||||
"monthly_revenue": "$1.2M",
|
||||
"top_product": "Enterprise Plan",
|
||||
"growth_rate": "15% MoM",
|
||||
"avg_deal_size": "$4,500"
|
||||
},
|
||||
"system_health": {
|
||||
"uptime": "99.97%",
|
||||
"avg_response_time": "145ms",
|
||||
"error_rate": "0.02%",
|
||||
"active_servers": 24
|
||||
}
|
||||
}
|
||||
|
||||
for key in mock_data:
|
||||
if key.replace("_", " ") in query.lower() or key in query.lower():
|
||||
return mock_data[key]
|
||||
return {"message": "No matching data found"}
|
||||
|
||||
|
||||
def mock_api_call(endpoint: str) -> dict:
|
||||
"""Simulate API call results."""
|
||||
# In production, this would call a real API
|
||||
mock_responses = {
|
||||
"stock_price": {"symbol": "ACME", "price": 142.50, "change": "+2.3%"},
|
||||
"exchange_rate": {"from": "USD", "to": "EUR", "rate": 0.92},
|
||||
"crypto": {"bitcoin": "$67,500", "ethereum": "$3,450"}
|
||||
}
|
||||
|
||||
for key in mock_responses:
|
||||
if key in endpoint.lower():
|
||||
return mock_responses[key]
|
||||
return {"status": "endpoint not found"}
|
||||
|
||||
|
||||
# Static knowledge base for hybrid approach
|
||||
COMPANY_KNOWLEDGE = [
|
||||
{
|
||||
"id": "company_overview",
|
||||
"content": """
|
||||
Acme Corporation is a technology company founded in 2015.
|
||||
Headquarters: San Francisco, CA
|
||||
Products: Enterprise software, cloud services, AI solutions
|
||||
Mission: Empowering businesses through intelligent automation
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "product_info",
|
||||
"content": """
|
||||
Acme Products:
|
||||
- Acme Cloud: Infrastructure-as-a-service platform
|
||||
- Acme AI: Machine learning toolkit for enterprises
|
||||
- Acme Connect: Integration and API management
|
||||
Pricing: Starter ($99/mo), Professional ($499/mo), Enterprise (custom)
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def web_augmented_rag():
|
||||
"""Demonstrate RAG augmented with web search."""
|
||||
|
||||
print("=" * 60)
|
||||
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=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.
|
||||
|
||||
COMPANY KNOWLEDGE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Simulate web-augmented queries
|
||||
queries = [
|
||||
("What's the weather like today?", True), # Needs web
|
||||
("Tell me about Acme Corporation's products", False), # Static knowledge
|
||||
("What's happening in the tech industry?", True), # Needs web
|
||||
]
|
||||
|
||||
for query, needs_web in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
if needs_web:
|
||||
# Augment with web search
|
||||
web_results = mock_web_search(query)
|
||||
web_context = "\n".join([f"- {r['title']}: {r['snippet']}" for r in web_results])
|
||||
augmented_query = f"{query}\n\nWeb search results:\n{web_context}"
|
||||
response = agent.chat(augmented_query)
|
||||
print("🌐 (Web-augmented)")
|
||||
else:
|
||||
response = agent.chat(query)
|
||||
print("📚 (Static knowledge)")
|
||||
|
||||
print(f"💡 Answer: {response[:250]}..." if len(str(response)) > 250 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def database_augmented_rag():
|
||||
"""Demonstrate RAG augmented with database queries."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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.
|
||||
|
||||
COMPANY KNOWLEDGE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
queries = [
|
||||
"How are our user stats looking?",
|
||||
"Give me a sales summary",
|
||||
"What's our system health status?"
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
# Fetch database data
|
||||
db_data = mock_database_query(query)
|
||||
db_context = f"Database results: {db_data}"
|
||||
|
||||
augmented_query = f"{query}\n\n{db_context}"
|
||||
response = agent.chat(augmented_query)
|
||||
|
||||
print(f"🗄️ DB Data: {db_data}")
|
||||
print(f"💡 Analysis: {response[:200]}..." if len(str(response)) > 200 else f"💡 Analysis: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def api_augmented_rag():
|
||||
"""Demonstrate RAG augmented with API calls."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("API-AUGMENTED RAG")
|
||||
print("=" * 60)
|
||||
|
||||
agent = Agent(
|
||||
name="Financial Assistant",
|
||||
instructions="""You are a financial assistant with access to market APIs.
|
||||
Provide accurate financial information and context.
|
||||
Always note that prices are subject to change.""",
|
||||
user_id="api_demo"
|
||||
)
|
||||
|
||||
queries = [
|
||||
("What's the ACME stock price?", "stock_price"),
|
||||
("USD to EUR exchange rate?", "exchange_rate"),
|
||||
("Crypto market update?", "crypto")
|
||||
]
|
||||
|
||||
for query, endpoint in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
# Fetch API data
|
||||
api_data = mock_api_call(endpoint)
|
||||
api_context = f"API response: {api_data}"
|
||||
|
||||
augmented_query = f"{query}\n\n{api_context}"
|
||||
response = agent.chat(augmented_query)
|
||||
|
||||
print(f"🔌 API Data: {api_data}")
|
||||
print(f"💡 Response: {response[:200]}..." if len(str(response)) > 200 else f"💡 Response: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def hybrid_source_rag():
|
||||
"""Combine multiple external sources."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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.
|
||||
|
||||
COMPANY KNOWLEDGE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "Give me a complete business briefing for today"
|
||||
|
||||
# Gather from multiple sources
|
||||
db_stats = mock_database_query("user_stats")
|
||||
db_sales = mock_database_query("sales_data")
|
||||
db_health = mock_database_query("system_health")
|
||||
api_stock = mock_api_call("stock_price")
|
||||
web_news = mock_web_search("news")
|
||||
|
||||
combined_context = f"""
|
||||
User Statistics: {db_stats}
|
||||
Sales Data: {db_sales}
|
||||
System Health: {db_health}
|
||||
Stock Price: {api_stock}
|
||||
Industry News: {[r['snippet'] for r in web_news]}
|
||||
"""
|
||||
|
||||
augmented_query = f"{query}\n\nData from multiple sources:\n{combined_context}"
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
print("\n📊 Data Sources:")
|
||||
print(" - Database: user_stats, sales_data, system_health")
|
||||
print(" - API: stock_price")
|
||||
print(" - Web: news search")
|
||||
|
||||
response = agent.chat(augmented_query)
|
||||
print(f"\n💡 Executive Briefing:\n{response}")
|
||||
|
||||
|
||||
def external_source_patterns():
|
||||
"""Explain external source patterns."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("EXTERNAL SOURCE PATTERNS")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🌐 External Source Integration Patterns:
|
||||
|
||||
1. **Query-Time Augmentation**
|
||||
- Fetch external data when query arrives
|
||||
- Inject into prompt as additional context
|
||||
- Best for: Real-time data needs
|
||||
|
||||
2. **Scheduled Refresh**
|
||||
- Periodically update knowledge base from external sources
|
||||
- Index external data for faster retrieval
|
||||
- Best for: Semi-static external data
|
||||
|
||||
3. **Tool-Based Retrieval**
|
||||
- Give agent tools to fetch external data
|
||||
- Agent decides when to use external sources
|
||||
- Best for: Complex, multi-step queries
|
||||
|
||||
4. **Hybrid Static + Dynamic**
|
||||
- Combine indexed knowledge with real-time fetches
|
||||
- Use static for stable info, dynamic for current
|
||||
- Best for: Comprehensive answers
|
||||
|
||||
Implementation Example:
|
||||
```python
|
||||
def web_search_tool(query: str) -> str:
|
||||
# Call search API
|
||||
results = search_api.search(query)
|
||||
return format_results(results)
|
||||
|
||||
agent = Agent(
|
||||
knowledge=static_docs,
|
||||
tools=[web_search_tool],
|
||||
instructions="Use web search for current events..."
|
||||
)
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all external source RAG examples."""
|
||||
print("\n🚀 PraisonAI External Sources RAG Examples\n")
|
||||
|
||||
# Example 1: Web-augmented RAG
|
||||
web_augmented_rag()
|
||||
|
||||
# Example 2: Database-augmented RAG
|
||||
database_augmented_rag()
|
||||
|
||||
# Example 3: API-augmented RAG
|
||||
api_augmented_rag()
|
||||
|
||||
# Example 4: Hybrid sources
|
||||
hybrid_source_rag()
|
||||
|
||||
# Example 5: Patterns
|
||||
external_source_patterns()
|
||||
|
||||
print("\n✅ External sources RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
Hybrid Search: Combining Dense and Sparse Retrieval
|
||||
|
||||
This example demonstrates hybrid retrieval that combines semantic (dense)
|
||||
search with keyword (sparse) search for improved accuracy.
|
||||
|
||||
RAG Concept: Dense embeddings capture meaning but may miss exact matches.
|
||||
Sparse/keyword search finds exact terms but lacks semantic understanding.
|
||||
Hybrid search combines both for best results.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Technical troubleshooting guides
|
||||
TROUBLESHOOTING_GUIDES = [
|
||||
{
|
||||
"id": "error_001",
|
||||
"content": """
|
||||
Error Code: ERR_CONNECTION_REFUSED
|
||||
|
||||
This error occurs when the client cannot establish a connection to the server.
|
||||
|
||||
Common Causes:
|
||||
- Server is not running or has crashed
|
||||
- Firewall blocking the connection port
|
||||
- Incorrect host or port configuration
|
||||
- Network connectivity issues
|
||||
|
||||
Resolution Steps:
|
||||
1. Verify the server process is running: `systemctl status myservice`
|
||||
2. Check firewall rules: `sudo ufw status`
|
||||
3. Test port connectivity: `telnet hostname 8080`
|
||||
4. Review server logs for startup errors
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "error_002",
|
||||
"content": """
|
||||
Error Code: ERR_OUT_OF_MEMORY
|
||||
|
||||
This error indicates the application has exhausted available memory.
|
||||
|
||||
Common Causes:
|
||||
- Memory leak in application code
|
||||
- Insufficient heap size configuration
|
||||
- Too many concurrent connections
|
||||
- Large data processing without streaming
|
||||
|
||||
Resolution Steps:
|
||||
1. Increase heap size: `-Xmx4g` for Java applications
|
||||
2. Enable memory profiling to identify leaks
|
||||
3. Implement pagination for large datasets
|
||||
4. Add memory limits to container configurations
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "error_003",
|
||||
"content": """
|
||||
Error Code: ERR_SSL_CERTIFICATE_EXPIRED
|
||||
|
||||
This error means the SSL/TLS certificate has passed its validity date.
|
||||
|
||||
Common Causes:
|
||||
- Certificate not renewed before expiration
|
||||
- Automatic renewal process failed
|
||||
- Wrong certificate installed
|
||||
|
||||
Resolution Steps:
|
||||
1. Check certificate expiry: `openssl x509 -enddate -noout -in cert.pem`
|
||||
2. Renew certificate with your CA or Let's Encrypt
|
||||
3. Update certificate files on the server
|
||||
4. Restart the web server to load new certificate
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "error_004",
|
||||
"content": """
|
||||
Error Code: ERR_DATABASE_DEADLOCK
|
||||
|
||||
A deadlock occurs when two transactions block each other indefinitely.
|
||||
|
||||
Common Causes:
|
||||
- Transactions acquiring locks in different orders
|
||||
- Long-running transactions holding locks
|
||||
- Missing indexes causing table scans with locks
|
||||
|
||||
Resolution Steps:
|
||||
1. Review transaction isolation levels
|
||||
2. Ensure consistent lock ordering across queries
|
||||
3. Add appropriate indexes to reduce lock duration
|
||||
4. Implement retry logic for deadlock exceptions
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "error_005",
|
||||
"content": """
|
||||
Error Code: ERR_RATE_LIMIT_EXCEEDED
|
||||
|
||||
The API has rejected requests due to exceeding the rate limit.
|
||||
|
||||
Common Causes:
|
||||
- Too many requests in a short time window
|
||||
- Missing request throttling in client code
|
||||
- Retry loops without backoff
|
||||
|
||||
Resolution Steps:
|
||||
1. Implement exponential backoff for retries
|
||||
2. Cache responses to reduce API calls
|
||||
3. Use bulk endpoints where available
|
||||
4. Request rate limit increase if needed
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def demonstrate_search_types():
|
||||
"""Show the difference between semantic and keyword search concepts."""
|
||||
|
||||
print("=" * 60)
|
||||
print("SEARCH TYPES COMPARISON")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📊 DENSE (Semantic) Search:
|
||||
- Uses vector embeddings to capture meaning
|
||||
- "memory issues" matches "out of memory" and "RAM exhausted"
|
||||
- Good for natural language queries
|
||||
- May miss exact technical terms
|
||||
|
||||
📊 SPARSE (Keyword) Search:
|
||||
- Uses term frequency and exact matching
|
||||
- "ERR_SSL_CERTIFICATE_EXPIRED" matches exactly
|
||||
- Good for error codes, IDs, technical terms
|
||||
- Misses synonyms and paraphrases
|
||||
|
||||
📊 HYBRID Search:
|
||||
- Combines both approaches
|
||||
- Weighted scoring: (α × dense_score) + ((1-α) × sparse_score)
|
||||
- Best of both worlds for technical documentation
|
||||
""")
|
||||
|
||||
|
||||
def hybrid_rag_example():
|
||||
"""Demonstrate hybrid retrieval with an agent."""
|
||||
|
||||
# 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=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.
|
||||
|
||||
TROUBLESHOOTING KNOWLEDGE BASE:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Test with different query types
|
||||
queries = [
|
||||
# Semantic query (natural language)
|
||||
"My application is running out of memory, what should I do?",
|
||||
|
||||
# Exact match query (error code)
|
||||
"ERR_SSL_CERTIFICATE_EXPIRED",
|
||||
|
||||
# Mixed query
|
||||
"Getting connection refused error when connecting to port 8080",
|
||||
|
||||
# Conceptual query
|
||||
"How do I handle API throttling?"
|
||||
]
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("HYBRID RAG IN ACTION")
|
||||
print("=" * 60)
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response[:300]}..." if len(str(response)) > 300 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def compare_retrieval_modes():
|
||||
"""Compare results with different retrieval configurations."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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"
|
||||
|
||||
# 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🔍 RAG Response:")
|
||||
response = agent.chat(query)
|
||||
print(f" {response[:200]}...")
|
||||
|
||||
print("\n💡 Note: Hybrid search combines semantic + keyword matching for best results.")
|
||||
|
||||
|
||||
def hybrid_search_tuning():
|
||||
"""Explain hybrid search tuning parameters."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("HYBRID SEARCH TUNING")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
Key parameters for hybrid search optimization:
|
||||
|
||||
1. **Alpha (α) Weight** - Balance between dense and sparse
|
||||
- α = 1.0: Pure semantic search
|
||||
- α = 0.5: Equal weight (good default)
|
||||
- α = 0.0: Pure keyword search
|
||||
|
||||
2. **Top-K Retrieval** - Number of candidates
|
||||
- Higher K: More context, slower, may dilute relevance
|
||||
- Lower K: Faster, focused, may miss relevant docs
|
||||
- Typical range: 3-10 for RAG
|
||||
|
||||
3. **Reranking** - Second-pass scoring
|
||||
- Cross-encoder reranking improves precision
|
||||
- Adds latency but improves quality
|
||||
|
||||
Example configuration:
|
||||
```python
|
||||
knowledge_config = {
|
||||
"hybrid": True,
|
||||
"hybrid_alpha": 0.6, # Favor semantic
|
||||
"top_k": 5,
|
||||
"rerank": True
|
||||
}
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all hybrid search examples."""
|
||||
print("\n🚀 PraisonAI Hybrid Search Examples\n")
|
||||
|
||||
# Example 1: Explain search types
|
||||
demonstrate_search_types()
|
||||
|
||||
# Example 2: Hybrid RAG in action
|
||||
hybrid_rag_example()
|
||||
|
||||
# Example 3: Compare retrieval modes
|
||||
compare_retrieval_modes()
|
||||
|
||||
# Example 4: Tuning guidance
|
||||
hybrid_search_tuning()
|
||||
|
||||
print("\n✅ Hybrid search examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,304 @@
|
||||
"""
|
||||
Graph-Assisted RAG: Relationship-Aware Retrieval
|
||||
|
||||
This example demonstrates how graph structures can enhance RAG by
|
||||
capturing relationships between entities and enabling path-based retrieval.
|
||||
|
||||
RAG Concept: Traditional vector search finds similar documents but misses
|
||||
relationships. Graph-assisted RAG understands connections between entities,
|
||||
enabling multi-hop reasoning and relationship-aware answers.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Interconnected entities (simulating a knowledge graph)
|
||||
ENTITY_KNOWLEDGE = [
|
||||
{
|
||||
"id": "person_einstein",
|
||||
"type": "person",
|
||||
"content": """
|
||||
Albert Einstein (1879-1955) was a theoretical physicist who developed
|
||||
the theory of relativity. He received the Nobel Prize in Physics in 1921
|
||||
for his explanation of the photoelectric effect. Einstein worked at the
|
||||
Institute for Advanced Study in Princeton from 1933 until his death.
|
||||
Key relationships: Collaborated with Niels Bohr, influenced by Max Planck,
|
||||
mentored by Heinrich Weber.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "person_bohr",
|
||||
"type": "person",
|
||||
"content": """
|
||||
Niels Bohr (1885-1962) was a Danish physicist who made foundational
|
||||
contributions to understanding atomic structure and quantum theory.
|
||||
He received the Nobel Prize in Physics in 1922. Bohr founded the
|
||||
Institute of Theoretical Physics in Copenhagen.
|
||||
Key relationships: Debated with Einstein on quantum mechanics,
|
||||
mentored Werner Heisenberg, influenced by Ernest Rutherford.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "person_heisenberg",
|
||||
"type": "person",
|
||||
"content": """
|
||||
Werner Heisenberg (1901-1976) was a German physicist and pioneer of
|
||||
quantum mechanics. He formulated the uncertainty principle in 1927.
|
||||
He received the Nobel Prize in Physics in 1932.
|
||||
Key relationships: Student of Niels Bohr, collaborated with Max Born,
|
||||
worked with Wolfgang Pauli.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "concept_relativity",
|
||||
"type": "concept",
|
||||
"content": """
|
||||
Theory of Relativity: A framework developed by Albert Einstein consisting
|
||||
of special relativity (1905) and general relativity (1915). Special
|
||||
relativity deals with objects moving at constant speeds, introducing
|
||||
E=mc². General relativity describes gravity as the curvature of spacetime.
|
||||
Related concepts: Spacetime, time dilation, gravitational waves.
|
||||
Applications: GPS satellites, particle accelerators, cosmology.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "concept_quantum",
|
||||
"type": "concept",
|
||||
"content": """
|
||||
Quantum Mechanics: A fundamental theory describing nature at atomic scales.
|
||||
Key principles include wave-particle duality, superposition, and entanglement.
|
||||
Developed through contributions from Planck, Bohr, Heisenberg, Schrödinger.
|
||||
Related concepts: Uncertainty principle, quantum tunneling, wave function.
|
||||
Applications: Semiconductors, lasers, MRI machines, quantum computing.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "institution_princeton",
|
||||
"type": "institution",
|
||||
"content": """
|
||||
Institute for Advanced Study (Princeton): A private research center
|
||||
founded in 1930. Notable members include Albert Einstein, John von Neumann,
|
||||
Kurt Gödel, and Robert Oppenheimer. The institute focuses on theoretical
|
||||
research in mathematics, natural sciences, and humanities.
|
||||
Location: Princeton, New Jersey, USA.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "institution_copenhagen",
|
||||
"type": "institution",
|
||||
"content": """
|
||||
Niels Bohr Institute (Copenhagen): Founded in 1921 by Niels Bohr as the
|
||||
Institute of Theoretical Physics. It became a world center for quantum
|
||||
mechanics research. Notable visitors included Heisenberg, Pauli, and Dirac.
|
||||
The "Copenhagen interpretation" of quantum mechanics originated here.
|
||||
Location: Copenhagen, Denmark.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "event_solvay",
|
||||
"type": "event",
|
||||
"content": """
|
||||
Solvay Conferences: Series of physics conferences held in Brussels since 1911.
|
||||
The 1927 Fifth Solvay Conference is famous for the Bohr-Einstein debates
|
||||
on quantum mechanics. Attendees included Einstein, Bohr, Heisenberg,
|
||||
Schrödinger, Dirac, and Marie Curie. These conferences shaped modern physics.
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
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=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 GRAPH:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Relationship-focused queries
|
||||
queries = [
|
||||
"How were Einstein and Bohr connected?",
|
||||
"Trace the lineage of quantum mechanics through its key figures.",
|
||||
"What institutions were central to 20th century physics?",
|
||||
"How did the Solvay Conferences shape physics?"
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print("GRAPH-ASSISTED RAG: Physics History Network")
|
||||
print("=" * 60)
|
||||
print("\nKnowledge graph entities:")
|
||||
for entity in ENTITY_KNOWLEDGE:
|
||||
print(f" - [{entity['type']}] {entity['id']}")
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response[:350]}..." if len(str(response)) > 350 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def multi_hop_reasoning():
|
||||
"""Demonstrate multi-hop reasoning across entities."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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 GRAPH:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Multi-hop query requiring traversal
|
||||
query = """
|
||||
What is the connection between the uncertainty principle and Princeton?
|
||||
(Hint: trace through people and their relationships)
|
||||
"""
|
||||
|
||||
print(f"\n📝 Multi-hop Query: {query.strip()}")
|
||||
print("\n🔗 Expected path: Uncertainty Principle → Heisenberg → Bohr → Einstein → Princeton")
|
||||
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Answer:\n{response}")
|
||||
|
||||
|
||||
def entity_type_filtering():
|
||||
"""Show filtering by entity type."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ENTITY TYPE FILTERING")
|
||||
print("=" * 60)
|
||||
|
||||
# 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=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=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."
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
print("\n👤 People-focused view:")
|
||||
response1 = people_agent.chat(query)
|
||||
print(f" {response1[:250]}...")
|
||||
|
||||
print("\n💡 Concept-focused view:")
|
||||
response2 = concept_agent.chat(query)
|
||||
print(f" {response2[:250]}...")
|
||||
|
||||
|
||||
def graph_rag_patterns():
|
||||
"""Explain graph RAG patterns."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("GRAPH RAG PATTERNS")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📊 Graph-Assisted RAG Patterns:
|
||||
|
||||
1. **Entity-Centric Retrieval**
|
||||
- Store entities with their relationships
|
||||
- Query retrieves entity + connected entities
|
||||
- Good for: "Tell me about X and its connections"
|
||||
|
||||
2. **Path-Based Retrieval**
|
||||
- Find paths between two entities
|
||||
- Retrieve all nodes along the path
|
||||
- Good for: "How is X related to Y?"
|
||||
|
||||
3. **Neighborhood Expansion**
|
||||
- Start with query-matched entities
|
||||
- Expand to include 1-hop or 2-hop neighbors
|
||||
- Good for: Comprehensive context
|
||||
|
||||
4. **Subgraph Extraction**
|
||||
- Extract relevant subgraph for query
|
||||
- Include nodes and edges as context
|
||||
- Good for: Complex multi-entity questions
|
||||
|
||||
Implementation in PraisonAI:
|
||||
```python
|
||||
# Structure knowledge as entities with relationships
|
||||
entities = [
|
||||
{
|
||||
"id": "entity_1",
|
||||
"type": "person",
|
||||
"content": "...",
|
||||
"relationships": ["entity_2", "entity_3"]
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
# Agent retrieves related entities automatically
|
||||
agent = Agent(
|
||||
knowledge=entities,
|
||||
instructions="Trace relationships between entities..."
|
||||
)
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all graph-assisted RAG examples."""
|
||||
print("\n🚀 PraisonAI Graph-Assisted RAG Examples\n")
|
||||
|
||||
# Example 1: Graph-aware retrieval
|
||||
graph_aware_rag()
|
||||
|
||||
# Example 2: Multi-hop reasoning
|
||||
multi_hop_reasoning()
|
||||
|
||||
# Example 3: Entity type filtering
|
||||
entity_type_filtering()
|
||||
|
||||
# Example 4: Graph RAG patterns
|
||||
graph_rag_patterns()
|
||||
|
||||
print("\n✅ Graph-assisted RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,280 @@
|
||||
"""
|
||||
Multi-Document RAG: Synthesizing Information Across Sources
|
||||
|
||||
This example demonstrates retrieving and synthesizing information from
|
||||
multiple documents to answer complex questions.
|
||||
|
||||
RAG Concept: Real-world questions often require combining information
|
||||
from multiple sources. Multi-document RAG retrieves relevant chunks
|
||||
from different documents and synthesizes a coherent answer.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Multiple documents on different topics
|
||||
CLIMATE_REPORT = {
|
||||
"id": "climate_2024",
|
||||
"source": "Global Climate Report 2024",
|
||||
"content": """
|
||||
Global Climate Report 2024 - Executive Summary
|
||||
|
||||
Global temperatures in 2024 reached 1.45°C above pre-industrial levels,
|
||||
making it the warmest year on record. Key findings:
|
||||
|
||||
- Arctic sea ice extent hit a new minimum in September
|
||||
- Ocean heat content reached unprecedented levels
|
||||
- Extreme weather events increased by 23% compared to the decade average
|
||||
- CO2 concentrations reached 422 ppm, up from 417 ppm in 2023
|
||||
|
||||
Regional impacts varied significantly. Europe experienced its hottest
|
||||
summer, while parts of South America faced severe drought conditions.
|
||||
The economic cost of climate-related disasters exceeded $380 billion.
|
||||
"""
|
||||
}
|
||||
|
||||
ENERGY_REPORT = {
|
||||
"id": "energy_2024",
|
||||
"source": "World Energy Outlook 2024",
|
||||
"content": """
|
||||
World Energy Outlook 2024 - Key Highlights
|
||||
|
||||
Renewable energy capacity grew by 35% in 2024, led by solar and wind.
|
||||
Major developments:
|
||||
|
||||
- Solar installations reached 1.5 TW cumulative capacity globally
|
||||
- Wind power generated 12% of global electricity
|
||||
- Battery storage capacity doubled to 500 GWh
|
||||
- Electric vehicle sales reached 20 million units
|
||||
|
||||
Despite progress, fossil fuels still account for 75% of primary energy.
|
||||
Investment in clean energy reached $1.8 trillion, but $4 trillion annually
|
||||
is needed to meet net-zero targets by 2050.
|
||||
"""
|
||||
}
|
||||
|
||||
POLICY_REPORT = {
|
||||
"id": "policy_2024",
|
||||
"source": "Climate Policy Tracker 2024",
|
||||
"content": """
|
||||
Climate Policy Tracker 2024 - Global Progress
|
||||
|
||||
145 countries have now committed to net-zero emissions targets.
|
||||
Policy developments:
|
||||
|
||||
- EU Carbon Border Adjustment Mechanism fully implemented
|
||||
- US Inflation Reduction Act drove $300B in clean energy investment
|
||||
- China pledged to peak emissions before 2030
|
||||
- India launched National Green Hydrogen Mission
|
||||
|
||||
Carbon pricing now covers 23% of global emissions, up from 20% in 2023.
|
||||
Average carbon price increased to $45/ton. However, only 4% of emissions
|
||||
are priced at levels consistent with Paris Agreement goals ($100+/ton).
|
||||
"""
|
||||
}
|
||||
|
||||
TECH_REPORT = {
|
||||
"id": "tech_2024",
|
||||
"source": "Climate Technology Review 2024",
|
||||
"content": """
|
||||
Climate Technology Review 2024 - Breakthrough Innovations
|
||||
|
||||
Several climate technologies reached commercial viability in 2024:
|
||||
|
||||
- Direct Air Capture: Costs fell to $400/ton CO2, down from $600 in 2022
|
||||
- Green Hydrogen: Production costs reached $3/kg in optimal locations
|
||||
- Solid-state Batteries: Energy density improved 40%, enabling longer EV range
|
||||
- Perovskite Solar Cells: Efficiency reached 33%, approaching silicon limits
|
||||
|
||||
Emerging technologies to watch:
|
||||
- Enhanced geothermal systems showing promise in non-volcanic regions
|
||||
- Ocean-based carbon removal gaining investment interest
|
||||
- AI-optimized grid management reducing curtailment by 15%
|
||||
"""
|
||||
}
|
||||
|
||||
# Combine all documents
|
||||
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=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.
|
||||
|
||||
REPORTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Questions that require multi-document synthesis
|
||||
queries = [
|
||||
"What is the current state of global climate and what are we doing about it?",
|
||||
"How is renewable energy progressing and what policies support it?",
|
||||
"What technologies are emerging to address climate change?",
|
||||
"Compare the economic investment in clean energy with climate damage costs."
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print("MULTI-DOCUMENT RAG: Climate Analysis")
|
||||
print("=" * 60)
|
||||
print(f"\nKnowledge base: {len(ALL_DOCUMENTS)} documents")
|
||||
print(f"Sources: {[d['source'] for d in ALL_DOCUMENTS]}")
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response[:400]}..." if len(str(response)) > 400 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def cross_reference_example():
|
||||
"""Show how agents can cross-reference information."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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.
|
||||
|
||||
REPORTS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = """
|
||||
Analyze the relationship between renewable energy growth (from Energy Report),
|
||||
policy support (from Policy Report), and technology costs (from Tech Report).
|
||||
How do these factors reinforce each other?
|
||||
"""
|
||||
|
||||
print(f"\n📝 Complex Query: {query.strip()}")
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Synthesized Answer:\n{response}")
|
||||
|
||||
|
||||
def document_filtering():
|
||||
"""Demonstrate filtering documents by metadata."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("DOCUMENT FILTERING BY SOURCE")
|
||||
print("=" * 60)
|
||||
|
||||
# Create agents focused on specific document types
|
||||
policy_agent = Agent(
|
||||
name="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=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?"
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
|
||||
print("\n🏛️ Policy Expert's View:")
|
||||
policy_response = policy_agent.chat(query)
|
||||
print(f" {policy_response[:250]}...")
|
||||
|
||||
print("\n🔬 Technology Expert's View:")
|
||||
tech_response = tech_agent.chat(query)
|
||||
print(f" {tech_response[:250]}...")
|
||||
|
||||
print("\n💡 Note: Different document subsets yield different perspectives!")
|
||||
|
||||
|
||||
def multi_document_best_practices():
|
||||
"""Share best practices for multi-document RAG."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("MULTI-DOCUMENT RAG BEST PRACTICES")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📚 Best Practices for Multi-Document RAG:
|
||||
|
||||
1. **Document Metadata**
|
||||
- Include source, date, author in each document
|
||||
- Enables filtering and citation
|
||||
|
||||
2. **Consistent Chunking**
|
||||
- Use similar chunk sizes across documents
|
||||
- Maintains balanced retrieval
|
||||
|
||||
3. **Source Diversity**
|
||||
- Retrieve from multiple sources when possible
|
||||
- Reduces single-source bias
|
||||
|
||||
4. **Conflict Resolution**
|
||||
- Instruct agent how to handle contradictions
|
||||
- Prefer newer sources or authoritative ones
|
||||
|
||||
5. **Citation Requirements**
|
||||
- Ask agent to cite sources in responses
|
||||
- Enables verification and trust
|
||||
|
||||
Example configuration:
|
||||
```python
|
||||
documents = [
|
||||
{"id": "doc1", "source": "Report A", "date": "2024-01", "content": "..."},
|
||||
{"id": "doc2", "source": "Report B", "date": "2024-06", "content": "..."},
|
||||
]
|
||||
|
||||
agent = Agent(
|
||||
knowledge=documents,
|
||||
instructions="Always cite which report your information comes from."
|
||||
)
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all multi-document RAG examples."""
|
||||
print("\n🚀 PraisonAI Multi-Document RAG Examples\n")
|
||||
|
||||
# Example 1: Basic multi-document RAG
|
||||
multi_document_rag()
|
||||
|
||||
# Example 2: Cross-referencing
|
||||
cross_reference_example()
|
||||
|
||||
# Example 3: Document filtering
|
||||
document_filtering()
|
||||
|
||||
# Example 4: Best practices
|
||||
multi_document_best_practices()
|
||||
|
||||
print("\n✅ Multi-document RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
RAG Evaluation Example
|
||||
|
||||
This example demonstrates how to evaluate RAG quality by testing
|
||||
agent responses against expected answers.
|
||||
|
||||
Usage:
|
||||
python rag_evaluation.py
|
||||
"""
|
||||
|
||||
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 CloudManager Pro?",
|
||||
"expected_contains": ["monitor", "cloud", "infrastructure"],
|
||||
},
|
||||
{
|
||||
"query": "What are the key features?",
|
||||
"expected_contains": ["monitoring", "cost", "security"],
|
||||
},
|
||||
{
|
||||
"query": "How do I get started?",
|
||||
"expected_contains": ["sign up", "connect", "configure"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def evaluate_rag_agent(agent: Agent, test_queries: list) -> dict:
|
||||
"""
|
||||
Evaluate RAG agent performance on test queries.
|
||||
|
||||
Returns metrics including answer relevance accuracy.
|
||||
"""
|
||||
results = {
|
||||
"total": len(test_queries),
|
||||
"passed": 0,
|
||||
"details": [],
|
||||
}
|
||||
|
||||
for test in test_queries:
|
||||
query = test["query"]
|
||||
expected_terms = test.get("expected_contains", [])
|
||||
|
||||
# Get agent response
|
||||
response = agent.chat(query)
|
||||
response_lower = response.lower()
|
||||
|
||||
# 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
|
||||
|
||||
if passed:
|
||||
results["passed"] += 1
|
||||
|
||||
results["details"].append({
|
||||
"query": query,
|
||||
"passed": passed,
|
||||
"terms_found": terms_found,
|
||||
"total_terms": len(expected_terms),
|
||||
"pass_rate": pass_rate,
|
||||
"answer_preview": response[:150],
|
||||
})
|
||||
|
||||
results["accuracy"] = results["passed"] / results["total"]
|
||||
return results
|
||||
|
||||
|
||||
def print_results(results: dict):
|
||||
"""Print evaluation results in a formatted way."""
|
||||
print("\n" + "=" * 60)
|
||||
print("RAG Evaluation Results")
|
||||
print("=" * 60)
|
||||
|
||||
print(f"\nTotal Queries: {results['total']}")
|
||||
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["passed"] else "❌ FAIL"
|
||||
print(f"\n[{i}] {status}: {detail['query']}")
|
||||
print(f" Terms Found: {detail['terms_found']}/{detail['total_terms']}")
|
||||
print(f" Answer: {detail['answer_preview']}...")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("RAG Evaluation Example")
|
||||
print("=" * 60)
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
# Get test queries
|
||||
test_queries = create_test_queries()
|
||||
|
||||
# Run evaluation
|
||||
print(f"\nEvaluating {len(test_queries)} queries...")
|
||||
results = evaluate_rag_agent(agent, test_queries)
|
||||
|
||||
# Print results
|
||||
print_results(results)
|
||||
|
||||
# Return status
|
||||
if results["accuracy"] < 0.8:
|
||||
print("\n⚠️ WARNING: Accuracy below 80% threshold!")
|
||||
return 1
|
||||
|
||||
print("\n✅ Evaluation PASSED!")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
RAG Multi-Agent Example
|
||||
|
||||
This example demonstrates how multiple agents can work together
|
||||
on a shared knowledge base for collaborative RAG.
|
||||
|
||||
Usage:
|
||||
python rag_multi_agent.py
|
||||
"""
|
||||
|
||||
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: Collaborative Analysis")
|
||||
print("=" * 60)
|
||||
|
||||
# Create specialized agents with shared knowledge
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Research Analyst",
|
||||
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",
|
||||
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 BASE:
|
||||
{SHARED_KNOWLEDGE}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
role="Report Writer",
|
||||
goal="Create well-structured reports",
|
||||
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 key statistics
|
||||
|
||||
KNOWLEDGE BASE:
|
||||
{SHARED_KNOWLEDGE}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Define tasks
|
||||
research_task = Task(
|
||||
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 5 key bullet points",
|
||||
expected_output="Bullet-point summary of main findings",
|
||||
agent=summarizer,
|
||||
)
|
||||
|
||||
report_task = Task(
|
||||
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()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Final Report")
|
||||
print("=" * 60)
|
||||
print(result)
|
||||
|
||||
print("\n✅ Multi-agent RAG example completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
RAG Document Q&A Example
|
||||
|
||||
This example demonstrates how to use RAG to answer questions
|
||||
about documents with proper context injection.
|
||||
|
||||
Usage:
|
||||
python rag_pdf_qa.py
|
||||
"""
|
||||
|
||||
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 Context
|
||||
print("=" * 60)
|
||||
print("Method 1: Agent with Document Context")
|
||||
print("=" * 60)
|
||||
|
||||
agent = Agent(
|
||||
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
|
||||
)
|
||||
|
||||
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: Q&A with Citations
|
||||
print("=" * 60)
|
||||
print("Method 2: Q&A with Citations")
|
||||
print("=" * 60)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
queries = [
|
||||
"What methodology was used in this research?",
|
||||
"What were the key findings about productivity?",
|
||||
"What are the recommendations?"
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = citation_agent.chat(query)
|
||||
print(f"💡 Answer: {response[:300]}...")
|
||||
|
||||
# Method 3: Comparative Analysis
|
||||
print("\n" + "=" * 60)
|
||||
print("Method 3: Analytical Questions")
|
||||
print("=" * 60)
|
||||
|
||||
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__":
|
||||
main()
|
||||
@@ -0,0 +1,249 @@
|
||||
"""
|
||||
Reranking: Improving Retrieval Precision
|
||||
|
||||
This example demonstrates reranking - a second-pass scoring mechanism
|
||||
that improves the quality of retrieved documents.
|
||||
|
||||
RAG Concept: Initial retrieval (first stage) prioritizes recall - finding
|
||||
all potentially relevant documents. Reranking (second stage) prioritizes
|
||||
precision - ordering results by true relevance to the query.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Research paper abstracts
|
||||
RESEARCH_PAPERS = [
|
||||
{
|
||||
"id": "paper_001",
|
||||
"content": """
|
||||
Title: Attention Mechanisms in Neural Machine Translation
|
||||
|
||||
Abstract: This paper introduces a novel attention mechanism for sequence-to-sequence
|
||||
models in machine translation. Unlike previous approaches that use a fixed-length
|
||||
context vector, our method allows the model to automatically search for parts of
|
||||
the source sentence relevant to predicting the target word. Experiments on
|
||||
English-to-French translation show significant improvements over baseline systems.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "paper_002",
|
||||
"content": """
|
||||
Title: Deep Residual Learning for Image Recognition
|
||||
|
||||
Abstract: We present a residual learning framework to ease the training of very deep
|
||||
neural networks. By reformulating layers as learning residual functions with
|
||||
reference to layer inputs, we show that these networks are easier to optimize
|
||||
and can gain accuracy from considerably increased depth. Our 152-layer residual
|
||||
network achieves state-of-the-art results on ImageNet classification.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "paper_003",
|
||||
"content": """
|
||||
Title: BERT: Pre-training of Deep Bidirectional Transformers
|
||||
|
||||
Abstract: We introduce BERT, a new language representation model designed to
|
||||
pre-train deep bidirectional representations from unlabeled text. Unlike previous
|
||||
models, BERT is designed to jointly condition on both left and right context.
|
||||
The pre-trained BERT model can be fine-tuned with just one additional output
|
||||
layer for a wide range of NLP tasks.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "paper_004",
|
||||
"content": """
|
||||
Title: Generative Adversarial Networks
|
||||
|
||||
Abstract: We propose a new framework for estimating generative models via an
|
||||
adversarial process. We simultaneously train two models: a generative model G
|
||||
that captures the data distribution, and a discriminative model D that estimates
|
||||
the probability that a sample came from the training data rather than G.
|
||||
This framework can generate realistic images from random noise.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "paper_005",
|
||||
"content": """
|
||||
Title: Transformer Architecture for Language Understanding
|
||||
|
||||
Abstract: We propose the Transformer, a model architecture eschewing recurrence
|
||||
and instead relying entirely on an attention mechanism to draw global dependencies
|
||||
between input and output. The Transformer allows for significantly more
|
||||
parallelization and achieves new state of the art in translation quality.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "paper_006",
|
||||
"content": """
|
||||
Title: Word Embeddings and Semantic Similarity
|
||||
|
||||
Abstract: This paper explores methods for learning distributed representations
|
||||
of words that capture semantic relationships. We show that word vectors trained
|
||||
on large corpora encode meaningful syntactic and semantic regularities. These
|
||||
representations enable analogical reasoning: king - man + woman = queen.
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def explain_reranking():
|
||||
"""Explain the reranking concept."""
|
||||
|
||||
print("=" * 60)
|
||||
print("RERANKING EXPLAINED")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🔄 Two-Stage Retrieval Pipeline:
|
||||
|
||||
Stage 1: Initial Retrieval (Bi-encoder)
|
||||
├── Fast approximate matching
|
||||
├── Retrieves top-K candidates (e.g., 20-100)
|
||||
├── Uses pre-computed embeddings
|
||||
└── Optimizes for RECALL (find all relevant docs)
|
||||
|
||||
Stage 2: Reranking (Cross-encoder)
|
||||
├── Slower but more accurate scoring
|
||||
├── Reorders the top-K candidates
|
||||
├── Computes query-document interaction
|
||||
└── Optimizes for PRECISION (best docs first)
|
||||
|
||||
Why Reranking Matters:
|
||||
- Bi-encoders encode query and docs separately (fast but approximate)
|
||||
- Cross-encoders process query+doc together (slow but accurate)
|
||||
- Reranking gives you the best of both worlds
|
||||
""")
|
||||
|
||||
|
||||
def rag_with_reranking():
|
||||
"""Demonstrate RAG with reranking enabled."""
|
||||
|
||||
# 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=f"""You are a research paper expert.
|
||||
Answer questions about machine learning research.
|
||||
Reference specific papers when relevant.
|
||||
Be precise about technical details.
|
||||
|
||||
RESEARCH PAPERS:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
queries = [
|
||||
"Which paper introduced the attention mechanism for translation?",
|
||||
"Tell me about pre-training language models",
|
||||
"What architecture avoids recurrence entirely?"
|
||||
]
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RAG WITH RERANKING")
|
||||
print("=" * 60)
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 Answer: {response[:300]}..." if len(str(response)) > 300 else f"💡 Answer: {response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def compare_with_without_reranking():
|
||||
"""Compare retrieval quality with and without reranking."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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?"
|
||||
|
||||
# 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🔍 RAG Response:")
|
||||
response = agent.chat(query)
|
||||
print(f" {response[:250]}...")
|
||||
|
||||
print("\n💡 Note: Reranking uses cross-encoders to improve precision after initial retrieval.")
|
||||
|
||||
|
||||
def reranking_configuration():
|
||||
"""Show reranking configuration options."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RERANKING CONFIGURATION")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
Configuration options for reranking in PraisonAI:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
name="My Agent",
|
||||
knowledge=documents,
|
||||
knowledge_config={
|
||||
# Enable reranking
|
||||
"rerank": True,
|
||||
|
||||
# Number of candidates for initial retrieval
|
||||
"top_k": 10,
|
||||
|
||||
# Number of results after reranking
|
||||
"rerank_top_k": 3,
|
||||
|
||||
# Reranker model (optional)
|
||||
"rerank_model": "cross-encoder/ms-marco-MiniLM-L-6-v2"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Performance Considerations:
|
||||
┌─────────────────┬──────────────┬─────────────┐
|
||||
│ Configuration │ Latency │ Quality │
|
||||
├─────────────────┼──────────────┼─────────────┤
|
||||
│ No reranking │ ~50ms │ Good │
|
||||
│ Light reranker │ ~100ms │ Better │
|
||||
│ Heavy reranker │ ~200ms │ Best │
|
||||
└─────────────────┴──────────────┴─────────────┘
|
||||
|
||||
Tip: Use reranking for precision-critical applications
|
||||
where quality matters more than latency.
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all reranking examples."""
|
||||
print("\n🚀 PraisonAI Reranking Examples\n")
|
||||
|
||||
# Example 1: Explain reranking
|
||||
explain_reranking()
|
||||
|
||||
# Example 2: RAG with reranking
|
||||
rag_with_reranking()
|
||||
|
||||
# Example 3: Compare with/without reranking
|
||||
compare_with_without_reranking()
|
||||
|
||||
# Example 4: Configuration options
|
||||
reranking_configuration()
|
||||
|
||||
print("\n✅ Reranking examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,396 @@
|
||||
"""
|
||||
Retrieval Policies: Agent-Decided Retrieval Strategies
|
||||
|
||||
This example demonstrates how agents can dynamically decide retrieval
|
||||
strategies based on query characteristics.
|
||||
|
||||
RAG Concept: Not all queries are equal. Some need deep retrieval, others
|
||||
need broad coverage, and some don't need retrieval at all. Smart policies
|
||||
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 = [
|
||||
{
|
||||
"id": "password_reset",
|
||||
"category": "account",
|
||||
"priority": "high",
|
||||
"content": """
|
||||
Password Reset Procedure:
|
||||
1. Go to the self-service portal at https://portal.company.com
|
||||
2. Click "Forgot Password"
|
||||
3. Enter your employee ID and email
|
||||
4. Check your email for the reset link (valid for 1 hour)
|
||||
5. Create a new password meeting complexity requirements
|
||||
|
||||
If you don't receive the email, check spam or contact IT helpdesk.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "vpn_setup",
|
||||
"category": "network",
|
||||
"priority": "medium",
|
||||
"content": """
|
||||
VPN Setup Guide:
|
||||
1. Download the VPN client from the software center
|
||||
2. Install with default settings
|
||||
3. Launch and enter your credentials
|
||||
4. Select the nearest server location
|
||||
5. Click Connect
|
||||
|
||||
Troubleshooting: If connection fails, check firewall settings
|
||||
and ensure port 443 is open.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "email_config",
|
||||
"category": "communication",
|
||||
"priority": "medium",
|
||||
"content": """
|
||||
Email Configuration:
|
||||
- Server: mail.company.com
|
||||
- Port: 993 (IMAP) / 587 (SMTP)
|
||||
- Security: SSL/TLS required
|
||||
- Username: your full email address
|
||||
|
||||
Mobile setup: Use the Company Portal app for automatic configuration.
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "printer_setup",
|
||||
"category": "hardware",
|
||||
"priority": "low",
|
||||
"content": """
|
||||
Printer Setup:
|
||||
1. Open Settings > Printers & Scanners
|
||||
2. Click Add Printer
|
||||
3. Select your floor's printer from the list
|
||||
4. Install drivers if prompted
|
||||
|
||||
Printer naming convention: FLOOR-WING-NUMBER (e.g., 3-EAST-01)
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "software_request",
|
||||
"category": "software",
|
||||
"priority": "medium",
|
||||
"content": """
|
||||
Software Request Process:
|
||||
1. Submit request through ServiceNow
|
||||
2. Manager approval required for licensed software
|
||||
3. IT reviews for security compliance
|
||||
4. Approved software deployed within 48 hours
|
||||
|
||||
Pre-approved software can be installed directly from Software Center.
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def query_classification():
|
||||
"""Demonstrate query classification for retrieval decisions."""
|
||||
|
||||
print("=" * 60)
|
||||
print("QUERY CLASSIFICATION FOR RETRIEVAL")
|
||||
print("=" * 60)
|
||||
|
||||
# Different query types
|
||||
queries = [
|
||||
("How do I reset my password?", "procedural", True),
|
||||
("Hello!", "greeting", False),
|
||||
("What is 2 + 2?", "general_knowledge", False),
|
||||
("VPN connection keeps failing", "troubleshooting", True),
|
||||
("Thanks for your help", "closing", False),
|
||||
("How do I request new software?", "procedural", True),
|
||||
]
|
||||
|
||||
print("\n📊 Query Classification Examples:\n")
|
||||
print(f"{'Query':<40} {'Type':<20} {'Needs RAG':<10}")
|
||||
print("-" * 70)
|
||||
|
||||
for query, query_type, needs_rag in queries:
|
||||
rag_indicator = "✅ Yes" if needs_rag else "❌ No"
|
||||
print(f"{query:<40} {query_type:<20} {rag_indicator:<10}")
|
||||
|
||||
print("\n💡 Smart agents classify queries to decide retrieval strategy.")
|
||||
|
||||
|
||||
def policy_based_retrieval():
|
||||
"""Demonstrate different retrieval policies."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=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 = [
|
||||
(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(retrieval_policy=policy)
|
||||
auto_agent = AutoRagAgent(agent=base_agent, config=config)
|
||||
|
||||
print(f"🔄 Policy: {policy.value.upper()}")
|
||||
print(f" Description: {description}")
|
||||
|
||||
response = auto_agent.chat(query)
|
||||
print(f" Response: {response[:150]}...")
|
||||
print()
|
||||
|
||||
|
||||
def adaptive_top_k():
|
||||
"""Demonstrate adaptive top-k based on query complexity."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ADAPTIVE TOP-K RETRIEVAL")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📊 Adaptive Top-K Strategy:
|
||||
|
||||
Query Complexity → Top-K Value
|
||||
─────────────────────────────────
|
||||
Simple (single topic) → k=1-2
|
||||
Moderate (related topics) → k=3-5
|
||||
Complex (multi-topic) → k=5-10
|
||||
|
||||
Examples:
|
||||
""")
|
||||
|
||||
queries = [
|
||||
("Reset password", 2, "Simple - single procedure"),
|
||||
("VPN not working, also need email setup", 5, "Moderate - two topics"),
|
||||
("New employee setup: email, VPN, printer, software", 8, "Complex - multiple topics"),
|
||||
]
|
||||
|
||||
for query, suggested_k, complexity in queries:
|
||||
print(f" Query: \"{query}\"")
|
||||
print(f" Complexity: {complexity}")
|
||||
print(f" Suggested top_k: {suggested_k}")
|
||||
print()
|
||||
|
||||
|
||||
def priority_based_retrieval():
|
||||
"""Demonstrate priority-based document retrieval."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("PRIORITY-BASED RETRIEVAL")
|
||||
print("=" * 60)
|
||||
|
||||
# Group documents by priority
|
||||
high_priority = [d for d in IT_SUPPORT_DOCS if d.get('priority') == 'high']
|
||||
all_docs = IT_SUPPORT_DOCS
|
||||
|
||||
print("\n📊 Document Priorities in Knowledge Base:")
|
||||
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=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=f"""Handle all IT support requests.
|
||||
|
||||
IT SUPPORT DOCS:
|
||||
{full_context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "I'm locked out of my account!"
|
||||
|
||||
print(f"\n📝 Urgent Query: {query}")
|
||||
|
||||
print("\n🚨 Urgent Support Agent (high-priority docs only):")
|
||||
response1 = urgent_agent.chat(query)
|
||||
print(f" {response1[:200]}...")
|
||||
|
||||
print("\n📚 Full Support Agent (all docs):")
|
||||
response2 = full_agent.chat(query)
|
||||
print(f" {response2[:200]}...")
|
||||
|
||||
|
||||
def context_aware_retrieval():
|
||||
"""Demonstrate context-aware retrieval strategies."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("CONTEXT-AWARE RETRIEVAL")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🎯 Context-Aware Retrieval Strategies:
|
||||
|
||||
1. **User Role Context**
|
||||
- New employee → Onboarding docs first
|
||||
- IT admin → Technical docs
|
||||
- Executive → Summary docs
|
||||
|
||||
2. **Time Context**
|
||||
- Business hours → Standard support
|
||||
- After hours → Emergency procedures only
|
||||
|
||||
3. **Conversation Context**
|
||||
- First message → Broad retrieval
|
||||
- Follow-up → Narrow to topic
|
||||
|
||||
4. **Device Context**
|
||||
- Mobile → Mobile-specific guides
|
||||
- Desktop → Full documentation
|
||||
|
||||
Implementation:
|
||||
```python
|
||||
def get_retrieval_config(context: dict) -> dict:
|
||||
if context.get("user_role") == "new_employee":
|
||||
return {"filter": {"category": "onboarding"}, "top_k": 5}
|
||||
elif context.get("is_followup"):
|
||||
return {"top_k": 2} # Narrow focus
|
||||
else:
|
||||
return {"top_k": 3} # Default
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def fallback_strategies():
|
||||
"""Demonstrate fallback strategies when retrieval fails."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RETRIEVAL FALLBACK STRATEGIES")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🔄 Fallback Strategy Chain:
|
||||
|
||||
1. Primary: Vector search in knowledge base
|
||||
↓ (if no relevant results)
|
||||
2. Secondary: Expand search with synonyms
|
||||
↓ (if still no results)
|
||||
3. Tertiary: Search external sources
|
||||
↓ (if still no results)
|
||||
4. Final: Acknowledge limitation, offer alternatives
|
||||
|
||||
Example Implementation:
|
||||
```python
|
||||
def retrieve_with_fallback(query: str, knowledge) -> str:
|
||||
# Primary retrieval
|
||||
results = knowledge.search(query, top_k=3)
|
||||
|
||||
if results and results[0].score > 0.7:
|
||||
return format_context(results)
|
||||
|
||||
# Fallback: Expand query
|
||||
expanded = expand_query(query) # Add synonyms
|
||||
results = knowledge.search(expanded, top_k=5)
|
||||
|
||||
if results and results[0].score > 0.5:
|
||||
return format_context(results)
|
||||
|
||||
# Final fallback
|
||||
return "I don't have specific information about that. " \\
|
||||
"Please contact IT helpdesk at ext. 1234."
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def retrieval_metrics():
|
||||
"""Explain retrieval quality metrics."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("RETRIEVAL QUALITY METRICS")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
📈 Key Metrics for Retrieval Policy Tuning:
|
||||
|
||||
┌─────────────────┬────────────────────────────────────────┐
|
||||
│ Metric │ Description │
|
||||
├─────────────────┼────────────────────────────────────────┤
|
||||
│ Precision@K │ % of retrieved docs that are relevant │
|
||||
│ Recall@K │ % of relevant docs that are retrieved │
|
||||
│ MRR │ Mean Reciprocal Rank of first hit │
|
||||
│ NDCG │ Normalized Discounted Cumulative Gain │
|
||||
│ Latency │ Time to retrieve (ms) │
|
||||
│ Token Usage │ Context tokens consumed │
|
||||
└─────────────────┴────────────────────────────────────────┘
|
||||
|
||||
Policy Optimization Goals:
|
||||
|
||||
- High Precision: Use smaller top_k, stricter thresholds
|
||||
- High Recall: Use larger top_k, relaxed thresholds
|
||||
- Low Latency: Use smaller top_k, skip reranking
|
||||
- Low Cost: Use smaller top_k, aggressive filtering
|
||||
|
||||
Balanced Default:
|
||||
```python
|
||||
config = {
|
||||
"top_k": 3,
|
||||
"score_threshold": 0.5,
|
||||
"rerank": True,
|
||||
"max_tokens": 2000
|
||||
}
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all retrieval policy examples."""
|
||||
print("\n🚀 PraisonAI Retrieval Policies Examples\n")
|
||||
|
||||
# Example 1: Query classification
|
||||
query_classification()
|
||||
|
||||
# Example 2: Policy comparison
|
||||
policy_based_retrieval()
|
||||
|
||||
# Example 3: Adaptive top-k
|
||||
adaptive_top_k()
|
||||
|
||||
# Example 4: Priority-based
|
||||
priority_based_retrieval()
|
||||
|
||||
# Example 5: Context-aware
|
||||
context_aware_retrieval()
|
||||
|
||||
# Example 6: Fallback strategies
|
||||
fallback_strategies()
|
||||
|
||||
# Example 7: Metrics
|
||||
retrieval_metrics()
|
||||
|
||||
print("\n✅ Retrieval policies examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,359 @@
|
||||
"""
|
||||
Structured Output RAG: Guided and Constrained Generation
|
||||
|
||||
This example demonstrates how to guide RAG output into specific formats
|
||||
and structures using Pydantic models and output constraints.
|
||||
|
||||
RAG Concept: Sometimes you need RAG answers in a specific format - JSON,
|
||||
tables, or structured data. Guided generation ensures the LLM output
|
||||
conforms to your schema.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Sample knowledge base: Product catalog
|
||||
PRODUCT_CATALOG = [
|
||||
{
|
||||
"id": "prod_001",
|
||||
"content": """
|
||||
Product: UltraWidget Pro
|
||||
Category: Electronics
|
||||
Price: $299.99
|
||||
Rating: 4.5/5 (1,250 reviews)
|
||||
Features: Wireless connectivity, 12-hour battery, water-resistant
|
||||
Availability: In stock
|
||||
SKU: UW-PRO-2024
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "prod_002",
|
||||
"content": """
|
||||
Product: SmartHome Hub
|
||||
Category: Home Automation
|
||||
Price: $149.99
|
||||
Rating: 4.2/5 (890 reviews)
|
||||
Features: Voice control, 100+ device compatibility, energy monitoring
|
||||
Availability: In stock
|
||||
SKU: SH-HUB-2024
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "prod_003",
|
||||
"content": """
|
||||
Product: ErgoDesk Standing Desk
|
||||
Category: Furniture
|
||||
Price: $599.99
|
||||
Rating: 4.8/5 (2,100 reviews)
|
||||
Features: Electric height adjustment, memory presets, cable management
|
||||
Availability: Ships in 3-5 days
|
||||
SKU: ED-STAND-2024
|
||||
"""
|
||||
},
|
||||
{
|
||||
"id": "prod_004",
|
||||
"content": """
|
||||
Product: CloudSync Backup Drive
|
||||
Category: Storage
|
||||
Price: $199.99
|
||||
Rating: 4.6/5 (1,800 reviews)
|
||||
Features: 4TB capacity, automatic backup, encrypted storage
|
||||
Availability: In stock
|
||||
SKU: CS-4TB-2024
|
||||
"""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
# Pydantic models for structured output
|
||||
class ProductInfo(BaseModel):
|
||||
"""Structured product information."""
|
||||
name: str = Field(description="Product name")
|
||||
price: float = Field(description="Price in USD")
|
||||
rating: float = Field(description="Rating out of 5")
|
||||
in_stock: bool = Field(description="Whether product is in stock")
|
||||
key_features: List[str] = Field(description="Top 3 features")
|
||||
|
||||
|
||||
class ProductComparison(BaseModel):
|
||||
"""Structured comparison of products."""
|
||||
products: List[str] = Field(description="Names of compared products")
|
||||
winner: str = Field(description="Recommended product")
|
||||
reason: str = Field(description="Why this product is recommended")
|
||||
price_range: str = Field(description="Price range of compared products")
|
||||
|
||||
|
||||
class SearchResult(BaseModel):
|
||||
"""Structured search result."""
|
||||
query: str = Field(description="Original search query")
|
||||
matches: List[str] = Field(description="Matching product names")
|
||||
total_found: int = Field(description="Number of matches")
|
||||
summary: str = Field(description="Brief summary of results")
|
||||
|
||||
|
||||
def json_output_rag():
|
||||
"""Demonstrate RAG with JSON-structured output."""
|
||||
|
||||
print("=" * 60)
|
||||
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=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.
|
||||
|
||||
PRODUCT CATALOG:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
# Request structured product info
|
||||
query = """
|
||||
Tell me about the UltraWidget Pro. Respond in this JSON format:
|
||||
{
|
||||
"name": "product name",
|
||||
"price": 0.00,
|
||||
"rating": 0.0,
|
||||
"in_stock": true/false,
|
||||
"key_features": ["feature1", "feature2", "feature3"]
|
||||
}
|
||||
"""
|
||||
|
||||
print("\n📝 Query: Get structured product info")
|
||||
response = agent.chat(query)
|
||||
print("💡 Structured Response:\n" + str(response))
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def pydantic_guided_rag():
|
||||
"""Demonstrate RAG with Pydantic model guidance."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("PYDANTIC-GUIDED RAG")
|
||||
print("=" * 60)
|
||||
|
||||
# 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.
|
||||
|
||||
PRODUCT CATALOG:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
query = "Compare the UltraWidget Pro and SmartHome Hub. Which should I buy?"
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
print("📋 Expected Schema: ProductComparison")
|
||||
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Structured Comparison:\n{response}")
|
||||
|
||||
|
||||
def table_format_rag():
|
||||
"""Demonstrate RAG with table-formatted output."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=f"""You present product information in clean table format.
|
||||
Use markdown tables for structured data.
|
||||
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."
|
||||
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"\n💡 Table Output:\n{response}")
|
||||
|
||||
|
||||
def list_format_rag():
|
||||
"""Demonstrate RAG with list-formatted output."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
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=f"""You extract and present information as organized lists.
|
||||
Use bullet points and numbered lists appropriately.
|
||||
Group related items together.
|
||||
|
||||
PRODUCT CATALOG:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
queries = [
|
||||
"List all product features across the catalog",
|
||||
"What are the top-rated products? List them in order."
|
||||
]
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
response = agent.chat(query)
|
||||
print(f"💡 List Output:\n{response[:400]}..." if len(str(response)) > 400 else f"💡 List Output:\n{response}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def constrained_generation():
|
||||
"""Demonstrate constrained/guided generation patterns."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("CONSTRAINED GENERATION PATTERNS")
|
||||
print("=" * 60)
|
||||
|
||||
print("""
|
||||
🎯 Constrained Generation Techniques:
|
||||
|
||||
1. **Schema Enforcement**
|
||||
- Provide JSON schema in instructions
|
||||
- Agent outputs valid JSON matching schema
|
||||
```python
|
||||
instructions = f"Respond with JSON matching: {schema}"
|
||||
```
|
||||
|
||||
2. **Format Templates**
|
||||
- Give explicit output templates
|
||||
- Agent fills in the blanks
|
||||
```python
|
||||
instructions = '''
|
||||
Respond in this format:
|
||||
PRODUCT: [name]
|
||||
PRICE: $[amount]
|
||||
VERDICT: [recommendation]
|
||||
'''
|
||||
```
|
||||
|
||||
3. **Pydantic Validation**
|
||||
- Parse agent output with Pydantic
|
||||
- Retry if validation fails
|
||||
```python
|
||||
response = agent.chat(query)
|
||||
try:
|
||||
result = ProductInfo.model_validate_json(response)
|
||||
except ValidationError:
|
||||
# Retry with clarification
|
||||
```
|
||||
|
||||
4. **Output Parsers**
|
||||
- Post-process agent output
|
||||
- Extract structured data from text
|
||||
```python
|
||||
def parse_product(text: str) -> dict:
|
||||
# Extract price, rating, etc.
|
||||
return structured_data
|
||||
```
|
||||
|
||||
5. **Few-Shot Examples**
|
||||
- Show examples of desired output format
|
||||
- Agent learns pattern from examples
|
||||
```python
|
||||
instructions = '''
|
||||
Example:
|
||||
Q: Tell me about Product X
|
||||
A: {"name": "Product X", "price": 99.99}
|
||||
|
||||
Now answer the user's question in the same format.
|
||||
'''
|
||||
```
|
||||
""")
|
||||
|
||||
|
||||
def multi_format_rag():
|
||||
"""Demonstrate switching between output formats."""
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("MULTI-FORMAT RAG")
|
||||
print("=" * 60)
|
||||
|
||||
base_knowledge = PRODUCT_CATALOG
|
||||
query = "Tell me about the ErgoDesk Standing Desk"
|
||||
|
||||
formats = [
|
||||
("JSON", "Respond with a JSON object containing name, price, and features."),
|
||||
("Markdown", "Respond with a markdown-formatted product card with headers."),
|
||||
("Plain Text", "Respond with a brief, conversational product description."),
|
||||
("Bullet Points", "Respond with bullet points covering key product details.")
|
||||
]
|
||||
|
||||
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}
|
||||
|
||||
PRODUCT CATALOG:
|
||||
{context}""",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
response = agent.chat(query)
|
||||
print(f"📋 {format_name} Format:")
|
||||
print(f"{response[:250]}..." if len(str(response)) > 250 else response)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all structured output RAG examples."""
|
||||
print("\n🚀 PraisonAI Structured Output RAG Examples\n")
|
||||
|
||||
# Example 1: JSON output
|
||||
json_output_rag()
|
||||
|
||||
# Example 2: Pydantic-guided
|
||||
pydantic_guided_rag()
|
||||
|
||||
# Example 3: Table format
|
||||
table_format_rag()
|
||||
|
||||
# Example 4: List format
|
||||
list_format_rag()
|
||||
|
||||
# Example 5: Constrained generation patterns
|
||||
constrained_generation()
|
||||
|
||||
# Example 6: Multi-format
|
||||
multi_format_rag()
|
||||
|
||||
print("\n✅ Structured output RAG examples completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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)
|
||||
@@ -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!")
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic TUI Usage Example for PraisonAI.
|
||||
|
||||
This example demonstrates how to launch the interactive TUI.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Ensure API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Please set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""Launch the TUI."""
|
||||
try:
|
||||
from praisonai.cli.features.tui import run_tui
|
||||
|
||||
# Launch with default settings
|
||||
run_tui()
|
||||
|
||||
except ImportError:
|
||||
print("Textual is required for TUI. Install with:")
|
||||
print(" pip install praisonai[tui]")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
Headless TUI Simulation Example for PraisonAI.
|
||||
|
||||
This example demonstrates how to run TUI simulations without
|
||||
launching the interactive UI - useful for testing and CI.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonai.cli.features.tui import (
|
||||
TuiOrchestrator,
|
||||
SimulationRunner,
|
||||
MockProvider,
|
||||
)
|
||||
from praisonai.cli.features.tui.orchestrator import OutputMode
|
||||
from praisonai.cli.features.queue import QueueConfig
|
||||
|
||||
|
||||
async def run_simulation():
|
||||
"""Run a headless simulation."""
|
||||
|
||||
# Create orchestrator with mock provider (no real API calls)
|
||||
config = QueueConfig(enable_persistence=False)
|
||||
orchestrator = TuiOrchestrator(
|
||||
queue_config=config,
|
||||
output_mode=OutputMode.PRETTY,
|
||||
debug=True,
|
||||
)
|
||||
|
||||
# Create simulation runner
|
||||
runner = SimulationRunner(orchestrator, assert_mode=True)
|
||||
|
||||
# Define simulation script
|
||||
script = {
|
||||
"session_id": "sim-example",
|
||||
"model": "gpt-4o-mini",
|
||||
"steps": [
|
||||
# Navigate to different screens
|
||||
{"action": "navigate", "args": {"screen": "main"}},
|
||||
{"action": "focus", "args": {"widget": "composer"}},
|
||||
|
||||
# Change model
|
||||
{
|
||||
"action": "model",
|
||||
"args": {"model": "gpt-4"},
|
||||
"expected": {"model": "gpt-4"}
|
||||
},
|
||||
|
||||
# Navigate to queue screen
|
||||
{"action": "navigate", "args": {"screen": "queue"}},
|
||||
|
||||
# Take a snapshot
|
||||
{"action": "snapshot"},
|
||||
|
||||
# Navigate back
|
||||
{"action": "navigate", "args": {"screen": "main"}},
|
||||
|
||||
# Small delay
|
||||
{"action": "sleep", "args": {"seconds": 0.5}},
|
||||
]
|
||||
}
|
||||
|
||||
# Run the simulation
|
||||
print("Running headless TUI simulation...")
|
||||
print("=" * 50)
|
||||
|
||||
success = await runner.run_script(script)
|
||||
|
||||
# Print results
|
||||
print("=" * 50)
|
||||
summary = runner.get_summary()
|
||||
|
||||
print(f"\nSimulation {'PASSED' if success else 'FAILED'}")
|
||||
print(f" Assertions passed: {summary['assertions_passed']}")
|
||||
print(f" Assertions failed: {summary['assertions_failed']}")
|
||||
|
||||
if summary['errors']:
|
||||
print("\nErrors:")
|
||||
for error in summary['errors']:
|
||||
print(f" - {error}")
|
||||
|
||||
return success
|
||||
|
||||
|
||||
async def run_with_events():
|
||||
"""Run simulation and capture all events."""
|
||||
|
||||
config = QueueConfig(enable_persistence=False)
|
||||
orchestrator = TuiOrchestrator(
|
||||
queue_config=config,
|
||||
output_mode=OutputMode.SILENT,
|
||||
)
|
||||
|
||||
# Capture events
|
||||
events = []
|
||||
orchestrator.add_event_callback(lambda e: events.append(e))
|
||||
|
||||
await orchestrator.start(session_id="event-capture")
|
||||
|
||||
# Perform some actions
|
||||
orchestrator.set_model("gpt-4")
|
||||
orchestrator.navigate_screen("queue")
|
||||
orchestrator.set_focus("queue-panel")
|
||||
orchestrator.navigate_screen("main")
|
||||
|
||||
await orchestrator.stop()
|
||||
|
||||
# Print captured events
|
||||
print("\nCaptured Events:")
|
||||
print("-" * 40)
|
||||
for event in events:
|
||||
print(f" {event.event_type.value}")
|
||||
|
||||
return events
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
print("PraisonAI TUI Headless Simulation Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Run simulation
|
||||
success = asyncio.run(run_simulation())
|
||||
|
||||
# Run event capture
|
||||
print("\n" + "=" * 50)
|
||||
events = asyncio.run(run_with_events())
|
||||
|
||||
print(f"\nTotal events captured: {len(events)}")
|
||||
|
||||
return 0 if success else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Queue Management Example for PraisonAI.
|
||||
|
||||
This example demonstrates how to use the queue system programmatically.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonai.cli.features.queue import (
|
||||
QueueManager,
|
||||
QueueConfig,
|
||||
QueuedRun,
|
||||
RunState,
|
||||
RunPriority,
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Demonstrate queue management."""
|
||||
|
||||
print("PraisonAI Queue Management Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Create queue configuration
|
||||
config = QueueConfig(
|
||||
max_concurrent_global=2,
|
||||
max_concurrent_per_agent=1,
|
||||
max_queue_size=10,
|
||||
enable_persistence=False, # Use in-memory for demo
|
||||
)
|
||||
|
||||
# Track outputs
|
||||
outputs = {}
|
||||
|
||||
async def on_output(run_id: str, chunk: str):
|
||||
if run_id not in outputs:
|
||||
outputs[run_id] = ""
|
||||
outputs[run_id] += chunk
|
||||
print(f" [{run_id[:8]}] {chunk}", end="", flush=True)
|
||||
|
||||
async def on_complete(run_id: str, run: QueuedRun):
|
||||
print(f"\n [{run_id[:8]}] Completed: {run.state.value}")
|
||||
|
||||
async def on_error(run_id: str, error: Exception):
|
||||
print(f"\n [{run_id[:8]}] Error: {error}")
|
||||
|
||||
# Create queue manager
|
||||
manager = QueueManager(
|
||||
config=config,
|
||||
on_output=on_output,
|
||||
on_complete=on_complete,
|
||||
on_error=on_error,
|
||||
)
|
||||
|
||||
# Start the manager
|
||||
await manager.start(recover=False)
|
||||
print("Queue manager started")
|
||||
|
||||
# Submit some runs with different priorities
|
||||
print("\nSubmitting runs...")
|
||||
|
||||
run1 = await manager.submit(
|
||||
input_content="Low priority task",
|
||||
agent_name="Agent1",
|
||||
priority=RunPriority.LOW,
|
||||
)
|
||||
print(f" Submitted run1: {run1} (LOW priority)")
|
||||
|
||||
run2 = await manager.submit(
|
||||
input_content="High priority task",
|
||||
agent_name="Agent2",
|
||||
priority=RunPriority.HIGH,
|
||||
)
|
||||
print(f" Submitted run2: {run2} (HIGH priority)")
|
||||
|
||||
run3 = await manager.submit(
|
||||
input_content="Normal priority task",
|
||||
agent_name="Agent1",
|
||||
priority=RunPriority.NORMAL,
|
||||
)
|
||||
print(f" Submitted run3: {run3} (NORMAL priority)")
|
||||
|
||||
# Check queue status
|
||||
print(f"\nQueue status:")
|
||||
print(f" Queued: {manager.queued_count}")
|
||||
print(f" Running: {manager.running_count}")
|
||||
|
||||
# List runs
|
||||
runs = manager.list_runs()
|
||||
print(f"\nAll runs ({len(runs)}):")
|
||||
for run in runs:
|
||||
print(f" {run.run_id[:8]}: {run.agent_name} - {run.state.value} ({run.priority.name})")
|
||||
|
||||
# Wait a bit for processing
|
||||
print("\nWaiting for processing...")
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# Cancel a run
|
||||
if runs:
|
||||
cancel_id = runs[-1].run_id
|
||||
print(f"\nCancelling run: {cancel_id[:8]}")
|
||||
await manager.cancel(cancel_id)
|
||||
|
||||
# Get stats
|
||||
stats = manager.get_stats()
|
||||
print(f"\nQueue statistics:")
|
||||
print(f" Total runs: {stats.total_runs}")
|
||||
print(f" Queued: {stats.queued_count}")
|
||||
print(f" Running: {stats.running_count}")
|
||||
print(f" Succeeded: {stats.succeeded_count}")
|
||||
print(f" Failed: {stats.failed_count}")
|
||||
print(f" Cancelled: {stats.cancelled_count}")
|
||||
|
||||
# Stop the manager
|
||||
await manager.stop()
|
||||
print("\nQueue manager stopped")
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("Example complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Exa Web Search Tool Example
|
||||
*
|
||||
* Demonstrates semantic web search with Exa AI.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai-ts @exalabs/ai-sdk
|
||||
* export EXA_API_KEY=your-api-key
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*/
|
||||
|
||||
// For local development, use relative imports
|
||||
// For production, use: import { Agent, exaSearch } from 'praisonai-ts'
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
import { exaSearch } from '../../../src/praisonai-ts/src/tools/builtins';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Exa Web Search Tool Example ===\n');
|
||||
|
||||
// Example 1: Basic Semantic Search
|
||||
console.log('1. Basic Semantic Search');
|
||||
console.log('------------------------');
|
||||
|
||||
const searchAgent = new Agent({
|
||||
name: 'SemanticSearcher',
|
||||
instructions: 'You search the web using semantic search and provide detailed summaries.',
|
||||
tools: [exaSearch({ numResults: 5, type: 'auto' })],
|
||||
});
|
||||
|
||||
const searchResult = await searchAgent.chat('Find the latest AI research breakthroughs');
|
||||
console.log('Search Result:', searchResult);
|
||||
console.log();
|
||||
|
||||
// Example 2: Company Research
|
||||
console.log('2. Company Research');
|
||||
console.log('-------------------');
|
||||
|
||||
const companyAgent = new Agent({
|
||||
name: 'CompanyResearcher',
|
||||
instructions: 'You research companies and provide analysis.',
|
||||
tools: [
|
||||
exaSearch({
|
||||
type: 'auto',
|
||||
numResults: 5,
|
||||
category: 'company',
|
||||
contents: {
|
||||
text: { maxCharacters: 1000 },
|
||||
summary: true,
|
||||
livecrawl: 'preferred',
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const companyResult = await companyAgent.chat(
|
||||
'Find AI startups in Europe founded after 2020'
|
||||
);
|
||||
console.log('Company Result:', companyResult);
|
||||
console.log();
|
||||
|
||||
// Example 3: Academic Paper Search
|
||||
console.log('3. Academic Paper Search');
|
||||
console.log('------------------------');
|
||||
|
||||
const academicAgent = new Agent({
|
||||
name: 'AcademicResearcher',
|
||||
instructions: 'You search for academic papers and summarize findings.',
|
||||
tools: [
|
||||
exaSearch({
|
||||
type: 'neural',
|
||||
category: 'research paper',
|
||||
numResults: 5,
|
||||
includeDomains: ['arxiv.org', 'nature.com', 'science.org'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const academicResult = await academicAgent.chat(
|
||||
'Find recent papers on transformer architectures and attention mechanisms'
|
||||
);
|
||||
console.log('Academic Result:', academicResult);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Tavily Search Tool Example
|
||||
*
|
||||
* Demonstrates web search, content extraction, and crawling with Tavily.
|
||||
*
|
||||
* Prerequisites:
|
||||
* npm install praisonai @tavily/ai-sdk
|
||||
* export TAVILY_API_KEY=your-api-key
|
||||
* export OPENAI_API_KEY=your-api-key
|
||||
*/
|
||||
|
||||
// For local development, use relative imports
|
||||
// For production, use: import { Agent, tavilySearch } from 'praisonai-ts'
|
||||
import { Agent } from '../../../src/praisonai-ts/src';
|
||||
import { tavilySearch, tavilyExtract, tavilyCrawl } from '../../../src/praisonai-ts/src/tools/builtins';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Tavily Search Tool Example ===\n');
|
||||
|
||||
// Example 1: Basic Web Search
|
||||
console.log('1. Basic Web Search');
|
||||
console.log('-------------------');
|
||||
|
||||
const searchAgent = new Agent({
|
||||
name: 'WebSearcher',
|
||||
instructions: 'You search the web for information and provide concise summaries with sources.',
|
||||
tools: [tavilySearch({ maxResults: 5, includeAnswer: true })],
|
||||
});
|
||||
|
||||
const searchResult = await searchAgent.chat('What are the latest developments in AI agents?');
|
||||
console.log('Search Result:', searchResult);
|
||||
console.log();
|
||||
|
||||
// Example 2: News Search with Time Filter
|
||||
console.log('2. News Search with Time Filter');
|
||||
console.log('-------------------------------');
|
||||
|
||||
const newsAgent = new Agent({
|
||||
name: 'NewsSearcher',
|
||||
instructions: 'You search for recent news and summarize key points.',
|
||||
tools: [
|
||||
tavilySearch({
|
||||
topic: 'news',
|
||||
timeRange: 'week',
|
||||
maxResults: 5,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const newsResult = await newsAgent.chat('Latest news about OpenAI');
|
||||
console.log('News Result:', newsResult);
|
||||
console.log();
|
||||
|
||||
// Example 3: Content Extraction
|
||||
console.log('3. Content Extraction');
|
||||
console.log('--------------------');
|
||||
|
||||
const extractAgent = new Agent({
|
||||
name: 'ContentExtractor',
|
||||
instructions: 'You extract and summarize content from web pages.',
|
||||
tools: [tavilyExtract()],
|
||||
});
|
||||
|
||||
const extractResult = await extractAgent.chat(
|
||||
'Extract the main content from https://docs.tavily.com and summarize it'
|
||||
);
|
||||
console.log('Extract Result:', extractResult);
|
||||
console.log();
|
||||
|
||||
// Example 4: Website Crawling
|
||||
console.log('4. Website Crawling');
|
||||
console.log('------------------');
|
||||
|
||||
const crawlAgent = new Agent({
|
||||
name: 'WebCrawler',
|
||||
instructions: 'You crawl websites and analyze their structure and content.',
|
||||
tools: [tavilyCrawl({ maxDepth: 2 })],
|
||||
});
|
||||
|
||||
const crawlResult = await crawlAgent.chat(
|
||||
'Crawl https://docs.tavily.com and list the main documentation sections'
|
||||
);
|
||||
console.log('Crawl Result:', crawlResult);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/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()
|
||||
Loaded 100 of 562 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user