Appearance
Agent Creation Flow
The gnosari_* tools use atomic decomposition — instead of one monolithic call with 20+ parameters, each configuration concern is handled by a dedicated tool.
Philosophy
Before (19-tool era):
python
# One massive call, all-or-nothing
create_agent(
name="...", instructions="...", model="...", temperature=0.7,
access_level="PUBLIC", uri="...", domain_id=1,
knowledge_sources=[...], trait_ids=[...],
chat_theme={...}, data_collection={...},
empty_state_title="...", image_url="..."
)Now (gnosari_ tools):*
python
# Each concern has its own tool
gnosari_create(name="...", instructions="...") # identity
gnosari_manage_instructions(action="replace", ...) # instructions
gnosari_manage_knowledge(action="create", ...) # knowledge
gnosari_manage_traits(action="assign", ...) # personality
gnosari_manage_appearance(greeting="...", ...) # visuals
gnosari_manage_access(access_level="PUBLIC", ...) # publishingBenefits:
- Each step is independently verifiable
- Partial failures don't cascade
- Retry individual steps without redoing everything
- Hints guide the next step after each call
Step 1: Create the Bare Agent
Call gnosari_create with name and instructions. The agent starts PRIVATE.
python
result = gnosari_create(
name="Product Support Bot",
instructions="You are a helpful support agent for Acme Corp products."
)
gnosari_id = result.agent.gnosari_id
print(f"Created agent {gnosari_id}")
print(result.hints)
# ["Set instructions with gnosari_manage_instructions...",
# "Make public with gnosari_manage_access...",
# "Add knowledge with gnosari_manage_knowledge...", ...]What happens:
- Agent record written to PostgreSQL via
core/services/agent_service.create_agent() - Defaults applied: model =
GNOSARI_DEFAULT_MODEL, temperature = 0.7, reasoning_effort = "medium", access_level = PRIVATE - Maximum hints returned (bare agent needs full configuration)
Step 2: Refine Instructions (optional)
If you need to build instructions iteratively:
python
# Start with a draft
gnosari_create(name="Bot", instructions="Draft: support agent.")
# Replace with final version
gnosari_manage_instructions(
gnosari_id=gnosari_id,
action="replace",
content="""You are a helpful customer support agent for Acme Corp.
Responsibilities:
- Answer product questions accurately
- Escalate billing issues to support@acme.com
- Never make promises about refunds without checking policy
Tone: professional, patient, clear."""
)
# Append a rule later
gnosari_manage_instructions(
gnosari_id=gnosari_id,
action="append",
content="\n\nAlways confirm the user's issue is resolved before ending the conversation."
)Step 3: Add Knowledge
Use gnosari_manage_knowledge to add domain expertise. type is optional — omit it to auto-resolve sitemap vs. discovery from the URL (see the Manage Resources reference).
python
# Create a source and assign it in one call — type omitted, auto-resolves
gnosari_manage_knowledge(
action="create",
name="Product Documentation",
url="https://docs.acme.com",
gnosari_id=gnosari_id # assign immediately
)
# Or create separately, then assign
gnosari_manage_knowledge(
action="create",
name="FAQ",
url="https://acme.com/faq",
type="website"
)
# returns KnowledgeSourceRead with id=45
gnosari_manage_knowledge(
action="assign",
source_ids=[45],
gnosari_id=gnosari_id
)What happens:
core/services/knowledge_service.create_source()creates the record- Background crawler starts indexing (async)
- Agent can be used immediately; knowledge becomes available as indexing completes
Loading states: unloaded → loading → loaded (or failed)
Step 4: Add Traits (optional)
Traits shape how the agent communicates:
python
# List available traits
traits_result = gnosari_manage_traits(action="list")
# Find trait IDs
friendly_id = next(t.id for t in traits_result.data if t.name == "Friendly")
professional_id = next(t.id for t in traits_result.data if t.name == "Professional")
# Assign
gnosari_manage_traits(
action="assign",
trait_ids=[friendly_id, professional_id],
gnosari_id=gnosari_id
)Step 5: Configure Appearance (optional)
python
gnosari_manage_appearance(
gnosari_id=gnosari_id,
greeting="Hi! I'm here to help with Acme products. What can I assist you with?",
empty_state_title="Acme Product Support",
empty_state_description="Get answers to product questions and troubleshooting help.",
suggested_prompts=[
{"title": "How do I get started?", "prompt": "Walk me through getting started with Acme"},
{"title": "Troubleshooting", "prompt": "I'm having an issue, can you help troubleshoot?"}
],
style_preset="clean-dots"
)Step 6: Configure Data Collection (optional)
python
# Create a template and assign it
gnosari_manage_data_collection(
action="create",
name="Support Ticket",
description="Capture issue details for support tracking",
fields=[
{
"name": "issue_type",
"field_type": "text",
"description": "Category: billing, technical, account, other",
"required": True
},
{
"name": "severity",
"field_type": "text",
"description": "Impact level: low, medium, high, critical",
"required": False
}
],
gnosari_id=gnosari_id, # assign immediately
collection_mode="opportunistic" # capture naturally during conversation
)Collection modes:
| Mode | Behavior |
|---|---|
passive | Extract silently, never ask |
opportunistic | Capture when mentioned + brief follow-ups |
active | Proactively ask for required fields |
guided | Follow a custom_prompt script |
Step 7: Publish
Check URI availability, then publish:
python
# Check before committing
check = gnosari_check_uri(uri="product-support")
if not check.available:
print(f"Taken by: {check.current_gnosari_name}")
print(check.hints) # alternatives suggested
# Try a variation
check = gnosari_check_uri(uri="product-support-bot")
# Publish
gnosari_manage_access(
gnosari_id=gnosari_id,
access_level="PUBLIC",
uri="product-support"
)What happens:
access_levelis updated to PUBLIC- Public URL generated:
https://joina.chat/{domain-slug}/product-support - joina.chat link becomes active immediately
Step 8: Verify
python
overview = gnosari_get(gnosari_id=gnosari_id)
print(f"Agent: {overview.name}")
print(f"Public URL: {overview.public_url}")
print(f"Knowledge sources: {len(overview.sources)}")
for source in overview.sources:
print(f" - {source.name}: {source.loading_status}")
print(f"Traits: {[t.name for t in overview.traits]}")
print(f"Data templates: {[t.name for t in overview.templates]}")
print(f"Hints: {overview.hints}") # empty when fully configuredFlow Diagram
gnosari_create(name, instructions)
↓
gnosari_id returned + hints
↓
gnosari_manage_instructions(replace/append)
↓
gnosari_manage_knowledge(create+assign) ← async loading starts
↓
gnosari_manage_traits(assign)
↓
gnosari_manage_data_collection(create+assign)
↓
gnosari_manage_appearance(greeting, prompts, theme)
↓
gnosari_check_uri(uri) → available?
↓
gnosari_manage_access(PUBLIC, uri)
↓
gnosari_get(gnosari_id) → hints=[] → fully configuredMonitoring Knowledge Loading
Knowledge sources load asynchronously. Check status via gnosari_get:
python
overview = gnosari_get(gnosari_id=gnosari_id)
for source in overview.sources:
print(f"{source.name}: {source.loading_status}")
# loading_status: unloaded | loading | loaded | failedTypical loading times:
- Small site (< 50 pages): 1–2 minutes
- Medium site (< 500 pages): 5–10 minutes
- Large site (> 1000 pages): 15–30 minutes
If a source fails, recreate it via gnosari_manage_knowledge(action="delete", source_id=...) then gnosari_manage_knowledge(action="create", ...).
Error Recovery
Each step is independent. If a step fails, fix and retry without repeating earlier steps.
| Failure | Recovery |
|---|---|
| Knowledge source creation fails | Retry gnosari_manage_knowledge(action="create", ...) |
| URI conflict on publish | Use gnosari_check_uri to find available alternative |
| Appearance update fails | Retry gnosari_manage_appearance — earlier steps unaffected |
| Agent creation fails | No partial state — start with a new gnosari_create |
Related
- Agent Creation Guide - Step-by-step with examples
- Data Collection Flow - What happens during conversations
- Knowledge Loading Flow - Knowledge source lifecycle