Squadbase

Sales Coach AI Chat

Streamlit Streamlit

Overview

This is a coaching chatbot that answers salespeople’s questions by referencing the contents of markdown files stored in the project’s codebase alongside meeting notes imported from a CRM (Attio). The app is built with Streamlit.

By providing the correct context, you can train the chatbot to suit your specific needs.

Customization

Managing Markdown Files

The markdown files in the /knowledge directory are loaded as follows:

@st.cache_resource(show_spinner=False)
def load_all_markdown() -> str:
    """Recursively read all *.md files under the knowledge directory and concatenate them."""
    md_files = sorted(KNOWLEDGE_DIR.rglob("*.md"))
    if not md_files:
        return "# Knowledge base is empty!"

    contents = []
    for path in md_files:
        contents.append(f"<!-- {path.relative_to(KNOWLEDGE_DIR)} -->\n")
        contents.append(path.read_text(encoding="utf-8"))
        contents.append("\n\n")
    return "".join(contents)

SYSTEM_PROMPT = load_all_markdown()

Edit or add markdown files to customize the context! In a production environment, you can tailor the overall chat behavior by updating these markdown files on a regular basis.

Connecting to Your CRM

In this example, we use Attio’s API to fetch Note data, which ensures that recent deal discussions are taken into account when formulating responses.

@st.cache_data(ttl=300)   # 5-minute cache
def fetch_notes(page_size: int = 20) -> pd.DataFrame:
    url = f"{ATTIO_API_BASE}/notes"
    params = {"limit": page_size}
    all_rows = []

    while True:
        response = requests.get(
            url,
            headers={
                "Authorization": f"Bearer {attio_api_key}",
                "Content-Type": "application/json"
            },
            params=params,
            timeout=30
        )
        response.raise_for_status()  # Raise exception for 4xx/5xx responses
        payload = response.json()    # {"data":[…], "next_cursor": "...", "has_more": true}
        all_rows.extend(payload["data"])

        # --- Pagination ---
        if payload.get("has_more") and payload.get("next_cursor"):
            params["page[after]"] = payload["next_cursor"]
        else:
            break

You can integrate any data accessible from Python code—simply replace this section to connect to other CRMs such as Salesforce or Zendesk.