1+ #!/usr/bin/env python3
2+ """
3+ Fixed example demonstrating proper guardrail usage with PraisonAI Agents.
4+ This addresses the issues reported in issue #875.
5+ """
6+
7+ from praisonaiagents import Agent , Task , GuardrailResult , PraisonAIAgents
8+ from typing import Tuple , Any
9+ import trafilatura
10+
11+ # Example 1: Using GuardrailResult return type (now supported!)
12+ def validate_length_guardrailresult (output ) -> GuardrailResult :
13+ """Ensure output is between 100-500 characters using GuardrailResult"""
14+ # Extract the raw text from the TaskOutput object
15+ text = output .raw if hasattr (output , 'raw' ) else str (output )
16+ length = len (text )
17+
18+ if 100 <= length <= 500 :
19+ return GuardrailResult (
20+ success = True ,
21+ result = output , # Pass through the original output
22+ error = ""
23+ )
24+ else :
25+ return GuardrailResult (
26+ success = False ,
27+ result = None ,
28+ error = f"Output must be 100-500 chars, got { length } "
29+ )
30+
31+ # Example 2: Using Tuple[bool, Any] return type (original method)
32+ def validate_length_tuple (output ) -> Tuple [bool , Any ]:
33+ """Ensure output is between 100-500 characters using tuple"""
34+ text = output .raw if hasattr (output , 'raw' ) else str (output )
35+ length = len (text )
36+
37+ if 100 <= length <= 500 :
38+ return True , output
39+ else :
40+ return False , f"Output must be 100-500 chars, got { length } "
41+
42+ # Tool function
43+ def get_url_context (url ):
44+ """Fetch and extract content from a URL"""
45+ downloaded = trafilatura .fetch_url (url )
46+ if not downloaded :
47+ return "Sorry, I couldn't fetch the content from that URL."
48+
49+ extracted = trafilatura .extract (
50+ downloaded ,
51+ include_comments = False ,
52+ include_links = True ,
53+ output_format = 'json' ,
54+ with_metadata = True ,
55+ url = url
56+ )
57+
58+ if not extracted :
59+ return "Sorry, I couldn't extract readable content from that page."
60+
61+ return extracted # returns JSON string
62+
63+ # Create agent with FIXED tools parameter (must be a list!)
64+ agent = Agent (
65+ name = "Content Summarizer" ,
66+ role = "Content Analysis Expert" ,
67+ goal = "Summarize web content concisely" ,
68+ instructions = "You are a helpful assistant that summarizes web content" ,
69+ llm = "gemini/gemini-2.5-flash-lite-preview-06-17" ,
70+ self_reflect = False ,
71+ verbose = True ,
72+ tools = [get_url_context ] # FIX: tools must be a list, not a single function
73+ )
74+
75+ # Create task with GuardrailResult guardrail
76+ task_with_guardrailresult = Task (
77+ name = "summarise article with GuardrailResult" ,
78+ description = "get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary below 500 characters" ,
79+ agent = agent ,
80+ guardrail = validate_length_guardrailresult , # Using GuardrailResult
81+ expected_output = "summary of the article below 500 characters" ,
82+ max_retries = 3 # Will retry up to 3 times if guardrail fails
83+ )
84+
85+ # Alternative: Create task with tuple guardrail
86+ task_with_tuple = Task (
87+ name = "summarise article with tuple" ,
88+ description = "get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary below 500 characters" ,
89+ agent = agent ,
90+ guardrail = validate_length_tuple , # Using Tuple[bool, Any]
91+ expected_output = "summary of the article below 500 characters" ,
92+ max_retries = 3
93+ )
94+
95+ # Example with string-based LLM guardrail
96+ task_with_llm_guardrail = Task (
97+ name = "summarise with LLM guardrail" ,
98+ description = "get the context of this url: https://blog.google/technology/ai/dolphingemma/ and produce a summary" ,
99+ agent = agent ,
100+ guardrail = "Ensure the summary is professional, factual, and between 100-500 characters" ,
101+ expected_output = "professional summary of the article"
102+ )
103+
104+ # Run with GuardrailResult example
105+ print ("=== Running with GuardrailResult guardrail ===" )
106+ agents_gr = PraisonAIAgents (
107+ agents = [agent ],
108+ tasks = [task_with_guardrailresult ]
109+ )
110+
111+ # Uncomment to run:
112+ # result_gr = agents_gr.start()
113+
114+ # Run with Tuple example
115+ print ("\n === Running with Tuple[bool, Any] guardrail ===" )
116+ agents_tuple = PraisonAIAgents (
117+ agents = [agent ],
118+ tasks = [task_with_tuple ]
119+ )
120+
121+ # Uncomment to run:
122+ # result_tuple = agents_tuple.start()
123+
124+ # Run with LLM guardrail example
125+ print ("\n === Running with LLM-based guardrail ===" )
126+ agents_llm = PraisonAIAgents (
127+ agents = [agent ],
128+ tasks = [task_with_llm_guardrail ]
129+ )
130+
131+ # Uncomment to run:
132+ # result_llm = agents_llm.start()
133+
134+ print ("""
135+ Key fixes applied:
136+ 1. GuardrailResult is now accepted as a valid return type annotation
137+ 2. tools parameter must be a list: tools=[get_url_context] not tools=get_url_context
138+ 3. Both GuardrailResult and Tuple[bool, Any] return types are supported
139+ 4. String-based LLM guardrails are also supported
140+
141+ The guardrail will automatically retry (up to max_retries times) if validation fails.
142+ """ )
0 commit comments