By Sagar Shankaran, Founder of CallSphere
Design a self-service platform where customers create, test, and deploy AI agents without writing code. Covers no-code builder architecture, template wizards, testing sandboxes, and one-click deployment pipelines.
Key takeaways
Every support ticket asking "can you set up an agent for me" is a scaling bottleneck. If deploying an agent requires your engineering team's involvement, your growth is capped by engineering headcount. A self-service platform lets customers go from sign-up to deployed agent without ever talking to your team.
The key insight is that most agent configurations follow patterns. A customer support agent needs a knowledge base, tone settings, and escalation rules. A sales agent needs product information, pricing data, and CRM integration. By building guided workflows for these patterns, you eliminate the need for engineering involvement in 90% of deployments.
The builder is a wizard-style interface backed by a configuration engine. Each step collects configuration values that feed into the agent deployment pipeline:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
SIGNUP(["New signup"])
AGENT["AI onboarding agent"]
GOAL["Detect goal and<br/>persona"]
PATH{"Personalized path"}
ACT1["Activation step 1<br/>configure profile"]
ACT2["Activation step 2<br/>connect data"]
ACT3["Activation step 3<br/>first value moment"]
NUDGE["In-app and email<br/>nudges"]
CSM(["CSM handoff if<br/>account flagged"])
DONE(["Activated"])
SIGNUP --> AGENT --> GOAL --> PATH
PATH --> ACT1 --> ACT2 --> ACT3 --> DONE
AGENT --> NUDGE
AGENT --> CSM
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style DONE fill:#059669,stroke:#047857,color:#fff
style CSM fill:#f59e0b,stroke:#d97706,color:#1f2937
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Optional
class WizardStep(Enum):
USE_CASE = "use_case"
IDENTITY = "identity"
KNOWLEDGE = "knowledge"
BEHAVIOR = "behavior"
INTEGRATIONS = "integrations"
TESTING = "testing"
DEPLOY = "deploy"
@dataclass
class StepConfig:
step: WizardStep
title: str
description: str
fields: list[dict]
validation_rules: list[dict] = field(
default_factory=list
)
help_text: str = ""
@dataclass
class AgentDraft:
id: str
tenant_id: str
current_step: WizardStep = WizardStep.USE_CASE
use_case: str = ""
template_id: Optional[str] = None
config: dict = field(default_factory=dict)
knowledge_sources: list[dict] = field(
default_factory=list
)
test_results: list[dict] = field(
default_factory=list
)
created_at: str = ""
updated_at: str = ""
class AgentBuilderService:
def __init__(
self, template_store, knowledge_processor,
draft_store,
):
self.templates = template_store
self.knowledge = knowledge_processor
self.drafts = draft_store
async def create_draft(
self, tenant_id: str, use_case: str
) -> AgentDraft:
# Find matching template
template = await self.templates.find_best_match(
use_case
)
draft = AgentDraft(
id=str(__import__("uuid").uuid4()),
tenant_id=tenant_id,
use_case=use_case,
template_id=template.id if template else None,
config=(
self._extract_defaults(template)
if template
else {}
),
created_at=__import__(
"datetime"
).datetime.now().isoformat(),
)
await self.drafts.save(draft)
return draft
async def update_step(
self, draft_id: str, step: WizardStep,
values: dict,
) -> AgentDraft:
draft = await self.drafts.get(draft_id)
if not draft:
raise ValueError("Draft not found")
# Validate step values
errors = self._validate_step(step, values)
if errors:
raise ValueError(
f"Validation failed: {'; '.join(errors)}"
)
# Merge values into config
draft.config.update(values)
draft.current_step = step
draft.updated_at = __import__(
"datetime"
).datetime.now().isoformat()
await self.drafts.save(draft)
return draft
def _extract_defaults(self, template) -> dict:
defaults = {}
for field_def in template.customization_fields:
if field_def.default_value is not None:
defaults[field_def.key] = (
field_def.default_value
)
return defaults
def _validate_step(
self, step: WizardStep, values: dict
) -> list[str]:
errors = []
if step == WizardStep.IDENTITY:
if not values.get("agent_name"):
errors.append("Agent name is required")
if not values.get("company_name"):
errors.append("Company name is required")
elif step == WizardStep.KNOWLEDGE:
sources = values.get("knowledge_sources", [])
for src in sources:
if src["type"] == "url" and not src.get("url"):
errors.append("URL is required")
return errors
Non-technical users cannot write vector database queries. The platform must ingest documents, URLs, and FAQs into a searchable knowledge base with zero configuration:
from dataclasses import dataclass
from typing import Optional
import hashlib
@dataclass
class KnowledgeSource:
id: str
draft_id: str
source_type: str # "file", "url", "faq", "text"
name: str
status: str = "pending" # pending, processing, ready, error
chunk_count: int = 0
error_message: Optional[str] = None
class KnowledgeIngestionService:
def __init__(
self, chunker, embedding_client, vector_store,
web_scraper,
):
self.chunker = chunker
self.embedder = embedding_client
self.vectors = vector_store
self.scraper = web_scraper
async def ingest_file(
self, draft_id: str, file_path: str, file_name: str
) -> KnowledgeSource:
source = KnowledgeSource(
id=hashlib.md5(
f"{draft_id}:{file_name}".encode()
).hexdigest(),
draft_id=draft_id,
source_type="file",
name=file_name,
status="processing",
)
try:
text = await self._extract_text(file_path)
chunks = self.chunker.chunk(
text, max_tokens=500, overlap=50
)
embeddings = await self.embedder.embed_batch(
[c.text for c in chunks]
)
for chunk, embedding in zip(chunks, embeddings):
await self.vectors.upsert(
id=f"{source.id}:{chunk.index}",
vector=embedding,
metadata={
"draft_id": draft_id,
"source_id": source.id,
"text": chunk.text,
"source_name": file_name,
},
namespace=draft_id,
)
source.status = "ready"
source.chunk_count = len(chunks)
except Exception as e:
source.status = "error"
source.error_message = str(e)
return source
async def ingest_url(
self, draft_id: str, url: str
) -> KnowledgeSource:
source = KnowledgeSource(
id=hashlib.md5(
f"{draft_id}:{url}".encode()
).hexdigest(),
draft_id=draft_id,
source_type="url",
name=url,
status="processing",
)
try:
pages = await self.scraper.crawl(
url, max_pages=20
)
total_chunks = 0
for page in pages:
chunks = self.chunker.chunk(
page.text, max_tokens=500, overlap=50
)
embeddings = await self.embedder.embed_batch(
[c.text for c in chunks]
)
for chunk, embedding in zip(
chunks, embeddings
):
await self.vectors.upsert(
id=f"{source.id}:{total_chunks}",
vector=embedding,
metadata={
"draft_id": draft_id,
"source_id": source.id,
"text": chunk.text,
"source_url": page.url,
},
namespace=draft_id,
)
total_chunks += 1
source.status = "ready"
source.chunk_count = total_chunks
except Exception as e:
source.status = "error"
source.error_message = str(e)
return source
async def _extract_text(self, file_path: str) -> str:
if file_path.endswith(".pdf"):
return await self._extract_pdf(file_path)
elif file_path.endswith((".txt", ".md")):
with open(file_path) as f:
return f.read()
elif file_path.endswith((".csv",)):
return await self._extract_csv(file_path)
else:
raise ValueError(
f"Unsupported file type: {file_path}"
)
Before deploying, users must test their agent in a sandbox. The sandbox provides a chat interface connected to the draft agent configuration:
class TestingSandbox:
def __init__(
self, agent_factory, knowledge_service
):
self.factory = agent_factory
self.knowledge = knowledge_service
async def create_test_session(
self, draft: AgentDraft
) -> dict:
# Build agent from draft config
agent_config = await self._build_config(draft)
session_id = str(__import__("uuid").uuid4())
agent_instance = await self.factory.create(
agent_config
)
return {
"session_id": session_id,
"agent_id": agent_instance.id,
"status": "ready",
"suggested_test_messages": [
"Hello, what can you help me with?",
"I have a problem with my order",
"Can you explain your return policy?",
],
}
async def send_test_message(
self, session_id: str, message: str
) -> dict:
response = await self.factory.invoke(
session_id, message
)
return {
"response": response.output,
"tools_used": response.tool_calls,
"tokens_used": response.usage.total_tokens,
"estimated_cost": response.usage.cost_usd,
"latency_ms": response.duration_ms,
}
async def _build_config(
self, draft: AgentDraft
) -> dict:
config = dict(draft.config)
config["knowledge_namespace"] = draft.id
config["model"] = config.get(
"model", "gpt-4o-mini"
)
return config
After testing, deployment should be a single action that provisions infrastructure, sets up monitoring, and returns a live endpoint:
class OneClickDeployer:
def __init__(
self, runtime_manager, dns_manager,
monitoring_service, draft_store,
):
self.runtime = runtime_manager
self.dns = dns_manager
self.monitoring = monitoring_service
self.drafts = draft_store
async def deploy(
self, draft_id: str, tenant_id: str
) -> dict:
draft = await self.drafts.get(draft_id)
# Provision runtime
runtime = await self.runtime.provision(
tenant_id=tenant_id,
config=draft.config,
knowledge_namespace=draft.id,
)
# Set up custom subdomain
subdomain = self._generate_subdomain(
draft.config.get("agent_name", "agent"),
tenant_id,
)
await self.dns.create_record(
subdomain, runtime.endpoint
)
# Enable monitoring
await self.monitoring.create_alerts(
agent_id=runtime.agent_id,
tenant_id=tenant_id,
error_rate_threshold=0.05,
latency_threshold_ms=5000,
)
# Mark draft as deployed
draft.config["deployed"] = True
await self.drafts.save(draft)
return {
"agent_id": runtime.agent_id,
"endpoint": f"https://{subdomain}.agents.example.com",
"widget_embed_code": self._generate_embed(
subdomain
),
"api_key": runtime.api_key,
"status": "live",
}
def _generate_subdomain(
self, agent_name: str, tenant_id: str
) -> str:
slug = agent_name.lower().replace(" ", "-")[:20]
short_id = tenant_id[:8]
return f"{slug}-{short_id}"
def _generate_embed(self, subdomain: str) -> str:
return (
'<script src="https://' + subdomain
+ '.agents.example.com/widget.js"></script>'
)
Provide an export path. Let customers download their agent configuration as code (a Python project with the system prompt, tool definitions, and knowledge base references). This graduated path means customers start no-code, and when they need custom logic, they can continue development in code without rebuilding from scratch.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Knowledge base quality. Customers upload poorly structured documents or provide URLs with thin content, then blame the agent when it gives bad answers. Mitigate this by showing a knowledge base quality score during the wizard — check document coverage, identify gaps, and suggest improvements before deployment.
Implement usage limits per tier, rate limiting on the testing sandbox, content moderation on system prompts, and automated scanning for agents that attempt to generate harmful content. Require email verification and payment method on file before allowing production deployments.
#SelfServicePlatform #NoCodeAI #AgentBuilder #CustomerOnboarding #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
The 2026 desktop AI agent landscape — ServiceNow Project Arc, Anthropic Claude offerings, OpenAI agents, and Google Mariner. A buyer's map.
Workspace Studio puts a Gemini-powered AI agent builder inside Google Workspace. A walkthrough of what it does, who it is for, and where it fits in 2026.
An agentic-AI perspective on Anthropic Skills system, covering orchestration patterns, tool use, and how agent tooling fits production agent stacks.
Enterprise CIO Guide perspective on Comet's general-availability launch put an agentic browser in front of millions of consumers, and it works better than the demos suggested.
Enterprise CIO Guide perspective on Harvey AI's enterprise rollout numbers show legal agents have moved past the pilot stage at AmLaw 100 firms.
Enterprise CIO Guide perspective on Hippocratic AI's deployment numbers show healthcare voice agents are moving from pilot to production across major US health systems.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco