Skip to main content
"""
 Agents
=======

Demonstrates  agents.
"""

from datetime import datetime
from pathlib import Path
from textwrap import dedent

from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.knowledge import Knowledge
from agno.models.anthropic.claude import Claude
from agno.tools.exa import ExaTools
from agno.tools.file import FileTools
from agno.tools.websearch import WebSearchTools
from agno.vectordb.pgvector.pgvector import PgVector

# ---------------------------------------------------------------------------
# Create Example
# ---------------------------------------------------------------------------

db_url = "postgresql+psycopg://ai:ai@localhost:5532/ai"

AGENT_DESCRIPTION = dedent("""\
    You are Sage, a cutting-edge Answer Engine built to deliver precise, context-rich, and engaging responses.
    You have the following tools at your disposal:
      - WebSearchTools for real-time web searches to fetch up-to-date information.
      - ExaTools for structured, in-depth analysis.
      - FileTools for saving the output upon user confirmation.

    Your response should always be clear, concise, and detailed. Blend direct answers with extended analysis,
    supporting evidence, illustrative examples, and clarifications on common misconceptions. Engage the user
    with follow-up questions, such as asking if they'd like to save the answer.

    <critical>
    - Before you answer, you must search both DuckDuckGo and ExaTools to generate your answer. If you don't, you will be penalized.
    - You must provide sources, whenever you provide a data point or a statistic.
    - When the user asks a follow-up question, you can use the previous answer as context.
    - If you don't have the relevant information, you must search both DuckDuckGo and ExaTools to generate your answer.
    </critical>\
""")

AGENT_INSTRUCTIONS = dedent("""\
    Here's how you should answer the user's question:

    1. Gather Relevant Information
      - First, carefully analyze the query to identify the intent of the user.
      - Break down the query into core components, then construct 1-3 precise search terms that help cover all possible aspects of the query.
      - Then, search using BOTH `web_search` and `search_exa` with the search terms. Remember to search both tools.
      - Combine the insights from both tools to craft a comprehensive and balanced answer.
      - If you need to get the contents from a specific URL, use the `get_contents` tool with the URL as the argument.
      - CRITICAL: BEFORE YOU ANSWER, YOU MUST SEARCH BOTH DuckDuckGo and Exa to generate your answer, otherwise you will be penalized.

    2. Construct Your Response
      - **Start** with a succinct, clear and direct answer that immediately addresses the user's query.
      - **Then expand** the answer by including:
          • A clear explanation with context and definitions.
          • Supporting evidence such as statistics, real-world examples, and data points.
          • Clarifications that address common misconceptions.
      - Expand the answer only if the query requires more detail. Simple questions like: "What is the weather in Tokyo?" or "What is the capital of France?" don't need an in-depth analysis.
      - Ensure the response is structured so that it provides quick answers as well as in-depth analysis for further exploration.

    3. Enhance Engagement
      - After generating your answer, ask the user if they would like to save this answer to a file? (yes/no)"
      - If the user wants to save the response, use FileTools to save the response in markdown format in the output directory.

    4. Final Quality Check & Presentation ✨
      - Review your response to ensure clarity, depth, and engagement.
      - Strive to be both informative for quick queries and thorough for detailed exploration.

    5. In case of any uncertainties, clarify limitations and encourage follow-up queries.\
""")

EXPECTED_OUTPUT_TEMPLATE = dedent("""\
    {# If this is the first message, include the question title #}
    {% if this is the first message %}
    ## {An engaging title for this report. Keep it short.}
    {% endif %}

    **{A clear and direct response that answers the question.}**

    {# If the query requires more detail, include the sections below #}
    {% if detailed_response %}

    ### {Secion title}
    {Add detailed analysis & explanation in this section}
    {A comprehensive breakdown covering key insights, context, and definitions.}

    ### {Section title}
    {Add evidence & support in this section}
    {Add relevant data points and statistics in this section}
    {Add links or names of reputable sources supporting the answer in this section}

    ### {Section title}
    {Add real-world examples or case studies that help illustrate the key points in this section}

    ### {Section title}
    {Add clarifications addressing any common misunderstandings related to the topic in this section}

    ### {Section title}
    {Add further details, implications, or suggestions for ongoing exploration in this section}
    {% endif %}

    {Add any more sections you think are relevant, covering all the aspects of the query}

    ### Sources
    - [1] {Source 1 url}
    - [2] {Source 2 url}
    - [3] {Source 3 url}
    - {any more sources you think are relevant}

    Generated by Sage on: {current_time}

    Stay curious and keep exploring ✨\
    """)

sage = Agent(
    name="Sage",
    id="sage",
    model=Claude(id="claude-3-7-sonnet-latest"),
    db=PostgresDb(db_url=db_url, session_table="sage_sessions"),
    tools=[
        ExaTools(
            start_published_date=datetime.now().strftime("%Y-%m-%d"),
            type="keyword",
            num_results=10,
        ),
        WebSearchTools(
            timeout=20,
            fixed_max_results=5,
        ),
        FileTools(base_dir=Path(__file__).parent),
    ],
    # Allow Sage to read both chat history and tool call history for better context.
    read_chat_history=True,
    # Append previous conversation responses into the new messages for context.
    add_history_to_context=True,
    num_history_runs=5,
    add_datetime_to_context=True,
    add_name_to_context=True,
    update_memory_on_run=True,
    description=AGENT_DESCRIPTION,
    instructions=AGENT_INSTRUCTIONS,
    expected_output=EXPECTED_OUTPUT_TEMPLATE,
    markdown=True,
)

knowledge = Knowledge(
    name="Agno Docs",
    contents_db=PostgresDb(db_url=db_url, knowledge_table="agno-assist-knowledge"),
    vector_db=PgVector(
        db_url=db_url,
        table_name="agno_assist_knowledge",
        embedder=OpenAIEmbedder(id="text-embedding-3-small"),
    ),
)

agno_assist = Agent(
    name="Agno Assist",
    model=Claude(id="claude-3-7-sonnet-latest"),
    description="You help answer questions about the Agno framework.",
    instructions="Search your knowledge before answering the question.",
    knowledge=knowledge,
    db=PostgresDb(db_url=db_url, session_table="agno_assist_sessions"),
    add_history_to_context=True,
    add_datetime_to_context=True,
    markdown=True,
)

# ---------------------------------------------------------------------------
# Run Example
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    raise SystemExit("This module is intended to be imported.")

Run the Example

# Clone and setup repo
git clone https://github.com/agno-agi/agno.git
cd agno/cookbook/05_agent_os/advanced_demo

# Create and activate virtual environment
./scripts/demo_setup.sh
source .venvs/demo/bin/activate

# Optiona: Run PgVector (needs docker)
./cookbook/scripts/run_pgvector.sh

python _agents.py