Website Agent¶
The Website Agent is Rose's flagship AI agent that powers conversational experiences on B2B websites. It answers visitor questions 24/7, keeps them engaged, and guides them toward conversion.
Overview¶
The Website Agent is deployed as an embeddable chat widget that integrates with any website. It combines:
- Retrieval-Augmented Generation (RAG) for knowledge-based responses
- Multi-agent router architecture for specialized intent handling
- Visitor enrichment for personalized conversations
- Lead qualification with interest signal detection
- Form collection through natural conversation
Architecture¶
Component Locations¶
| Component | Location | Purpose |
|---|---|---|
| Frontend Widget | frontend/widget/ |
Embeddable React chat interface |
| Search API | backend/apps/api/search/ |
FastAPI service handling chat requests |
| ixchat Package | backend/packages/ixchat/ |
LangGraph-based chatbot core |
Core Capabilities¶
Intelligent Conversations¶
The Website Agent uses LangGraph to orchestrate a complex workflow:
- Intent Classification - Classifies visitor intent (LEARN, CONTEXT, SUPPORT, OFFTOPIC, OTHER)
- Action Routing - Routes to specialized handlers based on intent and signals
- RAG Retrieval - Fetches relevant context from the knowledge base
- Response Generation - Produces contextual, brand-aligned answers
- Suggestion Generation - Creates follow-up questions or answer options
Visitor Enrichment¶
Multi-source enrichment pipeline identifies visitors:
| Priority | Source | Description |
|---|---|---|
| 1 | Redis Cache | Fast, short-lived cache |
| 2 | Supabase Lookup | IP hash lookup for returning visitors |
| 3 | Browser Reveal | Client-side company identification |
| 4 | Snitcher Radar | Session UUID identification |
| 5 | Enrich.so | Server-side API fallback |
Browser Reveal: The loader auto-captures window.reveal data if present on the page.
Snitcher Radar: The widget loads the Snitcher Radar SDK (cdn.snitcher.com) to capture session UUIDs for backend enrichment. The SDK is initialized with:
- Namespace: "Rose"
- All tracking features disabled (only need session UUID)
- Session ID available via
getSnitcherSessionId()and sent with API requests
Lead Qualification¶
The agent tracks interest signals throughout the conversation:
- Engagement signals - Message count, time on page
- Buying signals - Pricing questions, timeline mentions
- Context signals - Company size, use case details
When signals exceed the site-specific threshold, the agent proposes a demo or next step.
Form Collection¶
Extracts information naturally during conversation:
- Email, phone, company name
- Dynamic CTA URLs with extracted values
- Confidence thresholds for accuracy
Features¶
The Website Agent includes several specialized features:
- AI Sections - Embeddable question prompts that trigger the widget
- Form Field Extraction - AI-powered extraction from conversation for dynamic CTAs
- Dynamic Questions - Per-page contextual prompts based on URL patterns
- Preprod Testing - Internal testing interface for QA
- Analytics Tracking - Interaction storage and form attribution
Backend Package: ixchat¶
The ixchat package is the core backend component of the Website Agent. It provides:
- LangGraph orchestration - Complex conversation workflow management
- Multi-agent router - Intent-based routing to specialized handlers
- Session memory - Redis-based conversation persistence
- Streaming responses - Real-time token streaming for fast UX
See IXChat Package Documentation for detailed architecture.
Key Modules¶
| Module | Purpose |
|---|---|
ixchat/chatbot.py |
Main chatbot with LangGraph orchestration |
ixchat/service.py |
Singleton service manager |
ixchat/nodes/ |
LangGraph node implementations |
ixchat/enrichment/ |
Visitor enrichment pipeline |
ixchat/memory.py |
Session persistence with Redis |
Frontend Widget¶
The widget is built with React and deployed as a UMD bundle:
Key Features¶
- Shadow DOM isolation - CSS isolation from host website
- Streaming responses - Real-time token display
- Mobile responsive - Adapts to all device sizes
- Hidden zone mode - Click-to-reveal for non-intrusive UX
- Multi-language support - Automatic language detection
Deployment Targets¶
| Target | Description |
|---|---|
| widget | Production website integration |
| chrome-plugin | Browser extension version |
| preprod-ui | Testing interface |
Integration¶
<script src="https://cdn.userose.ai/loader/rose-loader.js"></script>
<script>
window.InboundXLoader.init({
api_key: "YOUR_API_KEY",
api_host: "https://api.userose.ai/rose",
cdn_url: "https://cdn.userose.ai/widget/your-domain"
});
</script>
API Endpoints¶
| Endpoint | Method | Description |
|---|---|---|
/api/version |
GET | Health check and environment detection |
/api/lightrag/query |
POST | Single-turn query with response |
/api/lightrag/query/stream |
POST | Server-Sent Events streaming |
/api/lightrag/reset_memory |
POST | Clear conversation history |
/api/lightrag/sites |
GET | List supported sites |
/api/sites/check |
POST | Verify domain is configured (Chrome extension) |
Streaming Response Format¶
{"type": "thinking", "content": "Thinking..."}
{"type": "token", "content": "The"}
{"type": "token", "content": " answer"}
{"type": "complete", "content": "The answer is...", "metadata": {...}}
Configuration¶
Site Configuration (Supabase)¶
Per-site settings in the site_configs table:
| Setting | Type | Description |
|---|---|---|
domain |
string | Site domain identifier (primary key) |
name |
string | Human-readable site name for analytics |
color |
hex | Brand color for theming |
widget_display_mode |
string | 'hidden_zone_click' or normal (default) |
traffic_allocated |
0-100 | Gradual rollout percentage |
api_endpoint |
URL | Site-specific API override |
legacy_api_endpoint |
URL | Switches to legacy N8N mode if set |
display_only_on_urls |
string[] | URL inclusion patterns (whitelist) |
session_utm_name |
string | UTM parameter name added to CTA URLs |
custom_config Fields:
| Field | Type | Description |
|---|---|---|
enable_mobile |
boolean | Show widget on mobile (default: false) |
z_index |
number | Widget stacking order (default: 9999) |
exclude_url_patterns |
string[] | URL exclusion patterns |
booking_workflow |
boolean | Enable email collection before redirect |
always_show_disclaimer |
boolean | Show disclaimer in collapsed view |
dynamic_questions.number_of_questions |
number | How many questions to display (default: 2) |
Global feature flags in global_client_config table (single row, id=1):
| Setting | Description |
|---|---|
features.followup_suggestions |
Global default for AI follow-ups |
Agent Configuration (Supabase)¶
Behavior settings in the agent_config table:
| Column Group | Purpose |
|---|---|
| Identity | company_name, website_url, client_description |
| Prompts | Pricing responses, case studies, calculators |
| Behavior | Interest thresholds, suggested answers rules |
Traffic Control (Gradual Rollout)¶
The traffic_allocated field controls what percentage of visitors see the widget:
| Value | Behavior |
|---|---|
| 0 | Widget disabled for all visitors |
| 1-99 | Widget shown to that percentage of visitors |
| 100 | Widget enabled for all visitors |
How it works:
- Widget generates a stable client ID (stored in localStorage)
- Client ID is hashed to produce a bucket number (0-99)
- If bucket <
traffic_allocated, widget displays
This ensures consistent experience per visitor across page loads.
Hidden Zone Mode¶
When widget_display_mode: 'hidden_zone_click' is set, the widget displays as an invisible 16x16px click target in the bottom-left corner instead of showing immediately.
Use case: This mode enables launching the widget in production on customer websites without showing it explicitly to their visitors. It's used for:
- Client testing in production - Clients can click on the hidden zone to test the widget
- Soft launches - Deploy to production without committing to full visibility
- Internal QA - Team members can test on the live site without affecting user experience
Technical detail: The hidden zone button is rendered outside the Shadow DOM in its own container to ensure fixed positioning works correctly across all browsers.
Mobile Support¶
By default, the widget is hidden on mobile devices. To enable mobile display:
- Set
custom_config.enable_mobile: truein the site's Supabase configuration - The widget uses a 1236px breakpoint for responsive layout adjustments (not for display decisions)
Important: Even when mobile is enabled, dynamic questions are disabled on mobile by code - only the search bar is shown (no rotating suggestions). This is a hardcoded behavior in dynamicQuestionManager.ts.
Mobile detection uses User Agent string matching only (iOS, Android, etc.). The 1236px breakpoint is used for responsive layout, not display decisions.
See Widget Display Logic for the full display decision flow.
Analytics¶
All events tracked with rw_ prefix via PostHog:
| Event | Description |
|---|---|
rw_widget_impression |
Widget displayed |
rw_query_sent |
User sends message |
rw_response_received |
Response completed |
rw_demo_cta_clicked |
CTA button clicked |
rw_message_evaluated |
Quality rating (like/dislike) |
Development¶
Local Development¶
Testing¶
# Backend tests
cd backend
poetry run pytest packages/ixchat/
# Frontend tests
cd frontend
just test
Related Documentation¶
- IXChat Package - Detailed backend architecture
- Architecture - System overview
- Backend Setup - Development environment
- Frontend Setup - Widget development