Skip to content
Learn Agentic AI9 min read0 views

AI-Powered Onboarding Flows: Guiding New Users with Intelligent Agents

Build an AI onboarding agent that adapts to each user's role, experience level, and goals to guide them through your SaaS product with personalized walkthroughs and recommendations.

The Problem with Static Onboarding

Most SaaS products have a fixed onboarding flow: five steps, same for everyone. A CEO sees the same tutorial as an analyst. A power user who has used three competing products gets the same walkthrough as someone who has never seen software in this category. Static onboarding leads to two failure modes — experienced users skip everything and miss important differences, while new users feel overwhelmed by irrelevant features.

An AI-powered onboarding agent solves this by adapting the flow based on who the user is and what they need.

Capturing User Context at Signup

The onboarding agent starts by gathering context through a brief conversational intake. Instead of a static form, the AI asks follow-up questions based on previous answers.

from pydantic import BaseModel
from enum import Enum

class ExperienceLevel(str, Enum):
    BEGINNER = "beginner"
    INTERMEDIATE = "intermediate"
    EXPERT = "expert"

class UserProfile(BaseModel):
    role: str
    experience_level: ExperienceLevel
    goals: list[str]
    team_size: int | None = None
    previous_tools: list[str] = []
    industry: str | None = None

INTAKE_SYSTEM_PROMPT = """You are an onboarding assistant for a project management SaaS.
Your job is to learn about the new user in 3-5 questions so you can personalize their setup.

Ask about:
1. Their role (manager, individual contributor, executive)
2. Their experience with similar tools
3. Their primary goal for using this product
4. Their team size

Be conversational and concise. After gathering enough info, respond with a JSON
block containing the UserProfile fields.

Do NOT ask all questions at once. Ask one at a time and adapt based on answers."""


class OnboardingAgent:
    def __init__(self, llm_client):
        self.llm_client = llm_client
        self.conversations: dict[str, list[dict]] = {}

    async def process_message(self, user_id: str, message: str) -> dict:
        if user_id not in self.conversations:
            self.conversations[user_id] = []

        self.conversations[user_id].append({"role": "user", "content": message})

        response = await self.llm_client.chat(
            system=INTAKE_SYSTEM_PROMPT,
            messages=self.conversations[user_id],
        )

        reply = response.content
        self.conversations[user_id].append({"role": "assistant", "content": reply})

        # Check if the AI has gathered enough info
        profile = self.try_extract_profile(reply)
        if profile:
            return {"type": "profile_complete", "profile": profile, "reply": reply}
        return {"type": "question", "reply": reply}

    def try_extract_profile(self, reply: str) -> UserProfile | None:
        import json
        import re
        match = re.search(r'{[^}]+}', reply, re.DOTALL)
        if match:
            try:
                data = json.loads(match.group())
                return UserProfile(**data)
            except (json.JSONDecodeError, ValueError):
                return None
        return None

Generating Personalized Tour Steps

Once the user profile is captured, the agent generates a custom sequence of feature walkthroughs.

from dataclasses import dataclass

@dataclass
class TourStep:
    feature_key: str
    title: str
    description: str
    target_selector: str  # CSS selector for the UI element to highlight
    action_url: str       # Page to navigate to for this step
    priority: int

FEATURE_CATALOG = [
    {"key": "dashboard", "name": "Dashboard", "roles": ["all"],
     "complexity": "beginner"},
    {"key": "kanban", "name": "Kanban Board", "roles": ["ic", "manager"],
     "complexity": "beginner"},
    {"key": "gantt", "name": "Gantt Charts", "roles": ["manager", "executive"],
     "complexity": "intermediate"},
    {"key": "time_tracking", "name": "Time Tracking", "roles": ["ic"],
     "complexity": "beginner"},
    {"key": "reports", "name": "Reports & Analytics", "roles": ["manager", "executive"],
     "complexity": "beginner"},
    {"key": "automations", "name": "Workflow Automations", "roles": ["manager"],
     "complexity": "expert"},
    {"key": "api_access", "name": "API & Integrations", "roles": ["ic"],
     "complexity": "expert"},
]


async def generate_tour(profile: UserProfile, llm_client) -> list[TourStep]:
    # Filter features relevant to this user
    role_map = {"manager": "manager", "individual contributor": "ic",
                "executive": "executive"}
    user_role = role_map.get(profile.role.lower(), "ic")

    relevant_features = [
        f for f in FEATURE_CATALOG
        if "all" in f["roles"] or user_role in f["roles"]
    ]

    # Further filter by experience level
    complexity_order = {"beginner": 0, "intermediate": 1, "expert": 2}
    user_level = complexity_order.get(profile.experience_level.value, 0)
    filtered = [
        f for f in relevant_features
        if complexity_order.get(f["complexity"], 0) <= user_level + 1
    ]

    prompt = f"""Generate an onboarding tour for a {profile.role} with
{profile.experience_level.value} experience. Their goals: {', '.join(profile.goals)}.
Previous tools: {', '.join(profile.previous_tools) or 'None'}.

Available features to highlight:
{[f['name'] for f in filtered]}

Return a JSON array of tour steps ordered by relevance to the user's goals.
Each step: {{"feature_key": "...", "title": "...", "description": "...",
"priority": 1-5}}. Limit to 5-7 steps."""

    response = await llm_client.chat(
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    return parse_tour_steps(response.content, filtered)

In-App Question Answering

During onboarding, users have questions that do not fit neatly into tour steps. The agent handles free-form questions using product documentation as context.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

class OnboardingQAAgent:
    def __init__(self, llm_client, doc_retriever):
        self.llm_client = llm_client
        self.doc_retriever = doc_retriever

    async def answer_question(self, question: str,
                               user_profile: UserProfile,
                               current_page: str) -> str:
        # Retrieve relevant documentation chunks
        docs = await self.doc_retriever.search(
            query=question, limit=5
        )
        doc_context = "\n\n".join([d.content for d in docs])

        system = f"""You are an onboarding assistant. The user is a
{user_profile.experience_level.value}-level {user_profile.role}.
They are currently on the {current_page} page.

Answer their question using ONLY the documentation below.
If the answer is not in the documentation, say so and suggest
contacting support.

Documentation:
{doc_context}"""

        response = await self.llm_client.chat(
            system=system,
            messages=[{"role": "user", "content": question}],
        )
        return response.content

Feature Recommendation Engine

As users complete onboarding steps, the agent suggests next features based on adoption patterns from similar users.

async def recommend_next_features(db, user_profile: UserProfile,
                                   completed_features: list[str]) -> list[dict]:
    # Find users with similar profiles who completed onboarding
    similar_users = await db.fetch("""
        SELECT u.id, array_agg(fa.feature_key ORDER BY fa.adopted_at) as adoption_order
        FROM users u
        JOIN feature_adoption fa ON fa.user_id = u.id
        WHERE u.role = $1
          AND u.experience_level = $2
          AND fa.feature_key = ANY($3)
        GROUP BY u.id
        HAVING count(fa.feature_key) >= $4
        LIMIT 100;
    """, user_profile.role, user_profile.experience_level.value,
         completed_features, len(completed_features))

    # Count which features these similar users adopted next
    from collections import Counter
    next_features = Counter()
    for user in similar_users:
        order = user["adoption_order"]
        for feature in order:
            if feature not in completed_features:
                next_features[feature] += 1
                break  # Only count the immediate next feature

    return [
        {"feature": feat, "adopted_by_similar_users": count}
        for feat, count in next_features.most_common(3)
    ]

FAQ

How do I handle users who skip the onboarding intake?

Provide a "Skip" button that sets sensible defaults (role: individual contributor, experience: intermediate, goals: general). Track which features they use in the first session and retroactively adjust recommendations. Offer to revisit personalization after their first week.

Should the onboarding AI have access to the user's data?

During onboarding, the user typically has no data yet. The AI should have access to sample data and documentation only. If the user imported data before onboarding (e.g., via CSV), the agent can reference that to make the tour more concrete — "I see you imported 47 contacts. Let me show you how to organize them."

How do I measure onboarding AI effectiveness?

Compare three cohorts: users who completed AI onboarding, users who completed static onboarding, and users who skipped onboarding. Track activation rate (percentage reaching their first meaningful action), time-to-first-value, and 30-day retention. The AI cohort should outperform static by at least 15-20% on activation to justify the added complexity.


#AIOnboarding #SaaS #UserGuidance #FeatureRecommendation #Python #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.