Spaces:
Sleeping
Add interactive demo scripts for exploring persona responses
Browse filesNew Features:
- demo_interactive.py: Full-featured interactive demo with multiple modes
* Quick demo (1 question, all personas)
* Full demo (4 preset questions)
* Custom questions (user input)
* Compare mode (2 personas side-by-side)
- quick_demo.py: Simple script for rapid testing with any question
Both scripts demonstrate the system's ability to generate diverse
stakeholder perspectives on urban planning issues.
Usage:
python demo_interactive.py # Interactive menu-driven experience
python quick_demo.py # Simple question prompt
Tested successfully with questions about food trucks, community gardens,
and other urban planning scenarios. Shows authentic persona responses
with clear value differences across stakeholders.
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- demo_interactive.py +221 -0
- quick_demo.py +49 -0
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Interactive Demo: AI Personas for Urban Planning
|
| 3 |
+
|
| 4 |
+
This script demonstrates the system with interesting questions
|
| 5 |
+
and shows how different stakeholders respond.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import sys
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
sys.path.insert(0, str(Path(__file__).parent))
|
| 11 |
+
|
| 12 |
+
from src.pipeline.query_engine import QueryEngine
|
| 13 |
+
from rich.console import Console
|
| 14 |
+
from rich.panel import Panel
|
| 15 |
+
from rich.prompt import Prompt, Confirm
|
| 16 |
+
from rich.table import Table
|
| 17 |
+
|
| 18 |
+
console = Console()
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def show_personas(engine):
|
| 22 |
+
"""Display available personas in a nice table"""
|
| 23 |
+
personas = engine.list_available_personas()
|
| 24 |
+
|
| 25 |
+
table = Table(title="ποΈ Your Urban Planning Stakeholders", show_header=True, header_style="bold cyan")
|
| 26 |
+
table.add_column("#", style="dim", width=3)
|
| 27 |
+
table.add_column("Name", style="green", width=20)
|
| 28 |
+
table.add_column("Role", style="yellow", width=25)
|
| 29 |
+
table.add_column("Perspective", style="white")
|
| 30 |
+
|
| 31 |
+
descriptions = [
|
| 32 |
+
"Progressive, sustainability-focused",
|
| 33 |
+
"Pragmatic, economy-focused",
|
| 34 |
+
"Data-driven, safety-first",
|
| 35 |
+
"Traditional, community-protective",
|
| 36 |
+
"Activist, equity-focused",
|
| 37 |
+
"Market-driven, growth-oriented"
|
| 38 |
+
]
|
| 39 |
+
|
| 40 |
+
for i, (persona_id, name, role) in enumerate(personas, 1):
|
| 41 |
+
table.add_row(str(i), name, role, descriptions[i-1])
|
| 42 |
+
|
| 43 |
+
console.print("\n")
|
| 44 |
+
console.print(table)
|
| 45 |
+
console.print("\n")
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def ask_question(engine, question, persona_ids=None):
|
| 49 |
+
"""Ask a question to one or more personas"""
|
| 50 |
+
console.print(Panel(
|
| 51 |
+
f"[bold cyan]Question:[/bold cyan]\n{question}",
|
| 52 |
+
border_style="cyan"
|
| 53 |
+
))
|
| 54 |
+
console.print()
|
| 55 |
+
|
| 56 |
+
if persona_ids is None:
|
| 57 |
+
# Ask all personas
|
| 58 |
+
persona_ids = [p[0] for p in engine.list_available_personas()]
|
| 59 |
+
|
| 60 |
+
console.print(f"[dim]Querying {len(persona_ids)} persona(s)...[/dim]\n")
|
| 61 |
+
|
| 62 |
+
responses = engine.query_multiple(
|
| 63 |
+
persona_ids=persona_ids,
|
| 64 |
+
question=question,
|
| 65 |
+
context_id="downtown_district"
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
for i, response in enumerate(responses, 1):
|
| 69 |
+
console.print(f"[bold green]βββ {i}. {response.persona_name}[/bold green] [dim]({response.persona_role})[/dim] [bold green]βββ[/bold green]")
|
| 70 |
+
console.print()
|
| 71 |
+
console.print(response.response)
|
| 72 |
+
console.print("\n")
|
| 73 |
+
|
| 74 |
+
return responses
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def main():
|
| 78 |
+
console.print("\n")
|
| 79 |
+
console.print("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ", style="bold cyan")
|
| 80 |
+
console.print("β ποΈ AI Personas for Urban Planning - Demo Session β", style="bold cyan")
|
| 81 |
+
console.print("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ", style="bold cyan")
|
| 82 |
+
console.print()
|
| 83 |
+
|
| 84 |
+
# Initialize
|
| 85 |
+
console.print("[dim]Initializing system...[/dim]")
|
| 86 |
+
engine = QueryEngine()
|
| 87 |
+
|
| 88 |
+
if not engine.test_system():
|
| 89 |
+
console.print("[red]System initialization failed![/red]")
|
| 90 |
+
return
|
| 91 |
+
|
| 92 |
+
console.print("[green]β System ready![/green]")
|
| 93 |
+
|
| 94 |
+
# Show personas
|
| 95 |
+
show_personas(engine)
|
| 96 |
+
|
| 97 |
+
# Demo questions
|
| 98 |
+
questions = [
|
| 99 |
+
{
|
| 100 |
+
"q": "Should we allow food trucks in the downtown plaza?",
|
| 101 |
+
"desc": "A simple yes/no question with complex stakeholder implications"
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"q": "The city is considering a $100M affordable housing bond. What's your opinion?",
|
| 105 |
+
"desc": "A major policy decision with fiscal and social dimensions"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"q": "What's the most important issue facing our downtown right now?",
|
| 109 |
+
"desc": "An open-ended question showing different priorities"
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"q": "How would you improve public transit in the city?",
|
| 113 |
+
"desc": "Solution-oriented question revealing different approaches"
|
| 114 |
+
},
|
| 115 |
+
]
|
| 116 |
+
|
| 117 |
+
console.print("[bold]Choose your adventure:[/bold]")
|
| 118 |
+
console.print()
|
| 119 |
+
console.print(" [cyan]1[/cyan] - Quick demo (1 question, all stakeholders)")
|
| 120 |
+
console.print(" [cyan]2[/cyan] - Full demo (4 questions, showing diverse perspectives)")
|
| 121 |
+
console.print(" [cyan]3[/cyan] - Custom question (you choose)")
|
| 122 |
+
console.print(" [cyan]4[/cyan] - Compare two personas (see contrasting views)")
|
| 123 |
+
console.print()
|
| 124 |
+
|
| 125 |
+
choice = Prompt.ask("Your choice", choices=["1", "2", "3", "4"], default="1")
|
| 126 |
+
console.print()
|
| 127 |
+
|
| 128 |
+
if choice == "1":
|
| 129 |
+
# Quick demo
|
| 130 |
+
console.print("[bold cyan]βββ QUICK DEMO βββ[/bold cyan]\n")
|
| 131 |
+
ask_question(engine, questions[0]["q"])
|
| 132 |
+
|
| 133 |
+
elif choice == "2":
|
| 134 |
+
# Full demo
|
| 135 |
+
console.print("[bold cyan]βββ FULL DEMO βββ[/bold cyan]\n")
|
| 136 |
+
for i, q_info in enumerate(questions, 1):
|
| 137 |
+
console.print(f"\n[bold yellow]Question {i}/4:[/bold yellow] [dim]{q_info['desc']}[/dim]\n")
|
| 138 |
+
ask_question(engine, q_info["q"])
|
| 139 |
+
|
| 140 |
+
if i < len(questions):
|
| 141 |
+
if not Confirm.ask("\n[cyan]Continue to next question?[/cyan]", default=True):
|
| 142 |
+
break
|
| 143 |
+
console.print("\n" + "β" * 70 + "\n")
|
| 144 |
+
|
| 145 |
+
elif choice == "3":
|
| 146 |
+
# Custom question
|
| 147 |
+
console.print("[bold cyan]βββ CUSTOM QUESTION βββ[/bold cyan]\n")
|
| 148 |
+
console.print("[dim]Ask the personas anything about urban planning![/dim]")
|
| 149 |
+
console.print("[dim]Examples: parking policies, green spaces, housing, transit, development, etc.[/dim]\n")
|
| 150 |
+
|
| 151 |
+
question = Prompt.ask("[cyan]Your question[/cyan]")
|
| 152 |
+
console.print()
|
| 153 |
+
|
| 154 |
+
ask_all = Confirm.ask("Ask all 6 personas?", default=True)
|
| 155 |
+
|
| 156 |
+
if ask_all:
|
| 157 |
+
ask_question(engine, question)
|
| 158 |
+
else:
|
| 159 |
+
show_personas(engine)
|
| 160 |
+
persona_choice = Prompt.ask("Which persona? (1-6)", choices=["1","2","3","4","5","6"])
|
| 161 |
+
persona_map = {
|
| 162 |
+
"1": "sarah_chen",
|
| 163 |
+
"2": "marcus_thompson",
|
| 164 |
+
"3": "elena_rodriguez",
|
| 165 |
+
"4": "james_obrien",
|
| 166 |
+
"5": "priya_patel",
|
| 167 |
+
"6": "david_kim"
|
| 168 |
+
}
|
| 169 |
+
ask_question(engine, question, [persona_map[persona_choice]])
|
| 170 |
+
|
| 171 |
+
elif choice == "4":
|
| 172 |
+
# Compare two personas
|
| 173 |
+
console.print("[bold cyan]βββ COMPARE TWO PERSPECTIVES βββ[/bold cyan]\n")
|
| 174 |
+
console.print("[dim]See how two different stakeholders view the same issue[/dim]\n")
|
| 175 |
+
|
| 176 |
+
question = Prompt.ask(
|
| 177 |
+
"[cyan]Question[/cyan]",
|
| 178 |
+
default="Should we prioritize affordable housing or economic development?"
|
| 179 |
+
)
|
| 180 |
+
console.print()
|
| 181 |
+
|
| 182 |
+
console.print("[yellow]Pick first persona:[/yellow]")
|
| 183 |
+
show_personas(engine)
|
| 184 |
+
p1 = Prompt.ask("Persona 1 (1-6)", choices=["1","2","3","4","5","6"])
|
| 185 |
+
|
| 186 |
+
console.print("\n[yellow]Pick second persona:[/yellow]")
|
| 187 |
+
p2 = Prompt.ask("Persona 2 (1-6)", choices=["1","2","3","4","5","6"])
|
| 188 |
+
|
| 189 |
+
persona_map = {
|
| 190 |
+
"1": "sarah_chen",
|
| 191 |
+
"2": "marcus_thompson",
|
| 192 |
+
"3": "elena_rodriguez",
|
| 193 |
+
"4": "james_obrien",
|
| 194 |
+
"5": "priya_patel",
|
| 195 |
+
"6": "david_kim"
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
console.print()
|
| 199 |
+
ask_question(engine, question, [persona_map[p1], persona_map[p2]])
|
| 200 |
+
|
| 201 |
+
# Wrap up
|
| 202 |
+
console.print("\n" + "β" * 70)
|
| 203 |
+
console.print("\n[bold green]β Demo complete![/bold green]")
|
| 204 |
+
console.print("\n[dim]This demonstrates how the AI Personas system helps explore")
|
| 205 |
+
console.print("diverse stakeholder perspectives on urban planning issues.[/dim]\n")
|
| 206 |
+
console.print("[cyan]Next steps:[/cyan]")
|
| 207 |
+
console.print(" β’ Customize personas in [yellow]data/personas/[/yellow]")
|
| 208 |
+
console.print(" β’ Add your own contexts in [yellow]data/contexts/[/yellow]")
|
| 209 |
+
console.print(" β’ Use [yellow]python -m src.cli[/yellow] for full interactive mode")
|
| 210 |
+
console.print(" β’ Build Phase 2 for population distributions\n")
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
if __name__ == "__main__":
|
| 214 |
+
try:
|
| 215 |
+
main()
|
| 216 |
+
except KeyboardInterrupt:
|
| 217 |
+
console.print("\n\n[yellow]Demo interrupted. Goodbye![/yellow]\n")
|
| 218 |
+
except Exception as e:
|
| 219 |
+
console.print(f"\n[red]Error: {e}[/red]\n")
|
| 220 |
+
import traceback
|
| 221 |
+
traceback.print_exc()
|
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Quick Demo - Ask the personas anything!
|
| 3 |
+
|
| 4 |
+
Usage: python quick_demo.py
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
sys.path.insert(0, str(Path(__file__).parent))
|
| 10 |
+
|
| 11 |
+
from src.pipeline.query_engine import QueryEngine
|
| 12 |
+
|
| 13 |
+
print("\n" + "="*70)
|
| 14 |
+
print(" AI Personas for Urban Planning - Quick Demo")
|
| 15 |
+
print("="*70 + "\n")
|
| 16 |
+
|
| 17 |
+
# Initialize
|
| 18 |
+
print("Initializing...")
|
| 19 |
+
engine = QueryEngine()
|
| 20 |
+
engine.test_system()
|
| 21 |
+
|
| 22 |
+
print("\n" + "="*70)
|
| 23 |
+
print("\nAvailable Personas:")
|
| 24 |
+
for i, (pid, name, role) in enumerate(engine.list_available_personas(), 1):
|
| 25 |
+
print(f" {i}. {name} - {role}")
|
| 26 |
+
|
| 27 |
+
print("\n" + "="*70)
|
| 28 |
+
|
| 29 |
+
# Ask your question here!
|
| 30 |
+
question = input("\nYour question: ")
|
| 31 |
+
|
| 32 |
+
print(f"\nAsking all 6 personas: '{question}'\n")
|
| 33 |
+
print("="*70 + "\n")
|
| 34 |
+
|
| 35 |
+
responses = engine.query_multiple(
|
| 36 |
+
persona_ids=[p[0] for p in engine.list_available_personas()],
|
| 37 |
+
question=question,
|
| 38 |
+
context_id="downtown_district"
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
for i, response in enumerate(responses, 1):
|
| 42 |
+
print(f"\n{'='*70}")
|
| 43 |
+
print(f"{i}. {response.persona_name} ({response.persona_role})")
|
| 44 |
+
print('='*70)
|
| 45 |
+
print(f"\n{response.response}\n")
|
| 46 |
+
|
| 47 |
+
print("\n" + "="*70)
|
| 48 |
+
print(f"β Got {len(responses)} responses!")
|
| 49 |
+
print("="*70 + "\n")
|