Skip to content

Chat & Agents Guide

Learn how to use Satori's AI agents for advanced interactions with your documents and knowledge bases.

Overview

Satori provides multiple ways to interact with your documents via AI. Choose the right approach based on your needs:

Feature Simple Chat Agent Chat Query
Endpoint /enclaves/{id}/chat/ /chat/agent /enclaves/{id}/query
Response Streaming text Streaming JSON events JSON
History Session-based Thread-based None
Use case Basic Q&A Advanced tools/conversation One-shot questions

When to Use Each

  • Simple Chat: Quick questions about documents with streaming responses
  • Agent Chat: Complex multi-turn conversations with tool use capabilities
  • Query: Programmatic one-shot questions returning structured JSON

Knowledge Base Chat

The simplest way to interact with your documents is through the enclave chat endpoint.

Basic Chat

curl -X POST "__API_HOST__/api/tenants/{tenant_id}/enclaves/{enclave_id}/chat/" \
  -H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What are the key findings?",
    "user_id": "user-123",
    "session_id": "session-abc"
  }'

Response: Streaming text via Server-Sent Events

Python Example

import requests

def chat_with_enclave(enclave_id, query, session_id=None, user_id=None):
    url = f"{BASE_URL}/api/tenants/{tenant_id}/enclaves/{enclave_id}/chat/"
    headers = {
        "Authorization": f"Bearer {JWT_TOKEN}",
        "Content-Type": "application/json"
    }
    data = {
        "query": query,
        "session_id": session_id or f"session-{uuid.uuid4()}",
        "user_id": user_id
    }

    response = requests.post(url, headers=headers, json=data, stream=True)

    full_response = ""
    for line in response.iter_lines():
        if line:
            text = line.decode('utf-8')
            if text.startswith('data: '):
                content = text[6:]
                if content == '[DONE]':
                    break
                full_response += content
                print(content, end='', flush=True)

    return full_response

Session Management

Sessions maintain conversation history for context:

session_id = "my-session-123"

# First message
response1 = chat_with_enclave(enclave_id, "What is this document about?", session_id)

# Follow-up (uses previous context)
response2 = chat_with_enclave(enclave_id, "Tell me more about that", session_id)

Retrieving Session History

curl -X GET "__API_HOST__/api/tenants/{tenant_id}/enclaves/{enclave_id}/chat/{session_id}/messages" \
  -H "Authorization: Bearer <YOUR_JWT_TOKEN>"

Response:

{
  "messages": [
    {
      "role": "user",
      "content": "What are the primary endpoints?",
      "timestamp": "2025-01-15T10:30:00Z"
    },
    {
      "role": "assistant",
      "content": "The primary endpoints are...",
      "timestamp": "2025-01-15T10:30:05Z",
      "sources": [
        {
          "file_name": "protocol.pdf",
          "chunk_id": "node_abc123",
          "page": 12
        }
      ]
    }
  ],
  "session_id": "my-session-123",
  "message_count": 2
}

Advanced Agents

Satori supports advanced AI agents with tool calling capabilities.

Agent Chat Endpoint

curl -X POST "__API_HOST__/api/tenants/{tenant_id}/chat/agent" \
  -H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What are the key findings?"}],
    "thread_id": "user-123",
    "run_id": "session-456",
    "forwarded_props": {
      "enclave_id": "750e8400-e29b-41d4-a716-446655440000",
      "file_id": "850e8400-e29b-41d4-a716-446655440000"
    }
  }'

Key Fields:

  • messages: Conversation history (required)
  • thread_id: User identifier for maintaining conversation state
  • run_id: Session identifier for grouping related queries
  • forwarded_props: Metadata passed to agent
  • enclave_id: Target enclave (required for document queries)
  • file_id: Optional specific file to query

Agent Architecture

Agents use a message-based system:

  • User Messages: Questions and instructions
  • System Messages: Configuration and context
  • Assistant Messages: Agent responses
  • Tool Messages: Results from tool executions
  • Developer Messages: System instructions

Example Agent Interaction

import requests
from typing import List, Dict, Any

def create_agent_request(
    query: str,
    thread_id: str,
    run_id: str,
    enclave_id: str = None,
    file_id: str = None
) -> Dict[str, Any]:
    request = {
        "thread_id": thread_id,
        "run_id": run_id,
        "messages": [
            {"role": "user", "content": query}
        ]
    }

    if enclave_id:
        request["forwarded_props"] = {"enclave_id": enclave_id}
        if file_id:
            request["forwarded_props"]["file_id"] = file_id

    return request

# Query documents in an enclave
request = create_agent_request(
    query="What are the key findings in the research papers?",
    thread_id="user-123",
    run_id="session-456",
    enclave_id="750e8400-e29b-41d4-a716-446655440000"
)

response = requests.post(
    f"{BASE_URL}/api/tenants/{tenant_id}/chat/agent",
    headers={"Authorization": f"Bearer {JWT_TOKEN}"},
    json=request,
    stream=True
)

Clinical Research Agents

Clinical research agents have access to medical literature and research databases.

Clinical Agent Features

  • Access to medical research databases
  • Clinical trial information
  • Medical literature search
  • Evidence-based answers

Using Clinical Agents

import uuid

def query_clinical_agent(query: str, session_id: str = None):
    """Query the clinical research agent (without specifying an enclave)."""
    url = f"{BASE_URL}/api/tenants/{tenant_id}/chat/agent"

    request = {
        "thread_id": session_id or f"clinical-{uuid.uuid4()}",
        "run_id": f"run-{uuid.uuid4()}",
        "messages": [
            {"role": "user", "content": query}
        ]
        # No forwarded_props means agent uses clinical research capabilities
    }

    response = requests.post(
        url,
        headers={"Authorization": f"Bearer {JWT_TOKEN}"},
        json=request,
        stream=True
    )

    return response

Tool Calling

Agents can use tools to perform actions:

Available Tools

  • Document Search: Search within enclave documents
  • Web Search: Search the web for information
  • Database Queries: Query structured data
  • Custom Tools: Define your own tools

Tool Definition

tool = {
    "name": "search_documents",
    "description": "Search for information in uploaded documents",
    "parameters": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Search query"
            },
            "limit": {
                "type": "integer",
                "description": "Maximum number of results"
            }
        },
        "required": ["query"]
    }
}

Agent with Tools

def agent_with_tools(query: str, enclave_id: str):
    tools = [
        {
            "name": "search_enclave",
            "description": f"Search documents in enclave {enclave_id}",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "limit": {"type": "integer", "default": 5}
                }
            }
        }
    ]

    request = create_agent_request(
        query=query,
        thread_id=f"thread-{uuid.uuid4()}",
        run_id=f"run-{uuid.uuid4()}",
        tools=tools
    )

    response = requests.post(
        f"{BASE_URL}/api/tenants/{tenant_id}/chat/agent",
        headers={"Authorization": f"Bearer {JWT_TOKEN}"},
        json=request,
        stream=True
    )

    return response

Streaming Responses

All chat endpoints support streaming for real-time responses.

Handling Streams

def process_stream(response):
    """Process streaming response from agent."""
    full_text = ""

    for line in response.iter_lines():
        if line:
            text = line.decode('utf-8')

            # Handle Server-Sent Events format
            if text.startswith('data: '):
                content = text[6:]
                if content == '[DONE]':
                    break
                full_text += content
                yield content

    return full_text

JavaScript Streaming

async function* streamAgentResponse(response: Response) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split("\n");

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const text = line.slice(6);
        if (text === "[DONE]") return;
        yield text;
      }
    }
  }
}

Best Practices

✅ DO:

  • Use sessions for conversational context
  • Provide clear, specific queries
  • Handle streaming responses properly
  • Review source citations
  • Use appropriate agent types for your use case

❌ DON'T:

  • Mix different conversation topics in the same session
  • Ignore source citations
  • Make too many concurrent agent requests
  • Use agents for simple queries (use direct query endpoint instead)

Error Handling

Common Errors

404 Not Found

  • Enclave doesn't exist
  • Session not found
  • Solution: Verify IDs and create resources first

500 Internal Server Error

  • Agent execution error
  • Tool execution failed
  • Solution: Check query format and tool definitions

Error Handling Example

def safe_agent_query(query: str, enclave_id: str):
    try:
        response = agent_with_tools(query, enclave_id)
        return process_stream(response)
    except requests.HTTPError as e:
        if e.response.status_code == 404:
            raise ValueError("Enclave or session not found")
        elif e.response.status_code == 500:
            raise ValueError("Agent execution error")
        else:
            raise

Next Steps