# Commit the changes and close the connection
conn.commit()
conn.close()
```

## Creating a function to query a SQL database

Next, we'll define a function called `sql_table_query` that allows us to execute SQL queries on our evaluation\_results database.

This function will enable us to retrieve and analyze data from our evaluation\_results table, allowing for dynamic querying based on our specific needs.

```python PYTHON
def sql_table_query(query: str) -> dict:
    """
    Execute an SQL query on the evaluation_results table and return the result as a dictionary.

    Args:
    query (str): SQL query to execute on the evaluation_results table

    Returns:
    dict: Result of the SQL query
    """
    try:
        # Connect to the SQLite database
        conn = sqlite3.connect("evaluation_results.db")

        # Execute the query and fetch the results into a DataFrame
        df = pd.read_sql_query(query, conn)

        # Close the connection
        conn.close()

        # Convert DataFrame to dictionary
        result_dict = df.to_dict(orient="records")

        return result_dict

    except sqlite3.Error as e:
        print(f"An error occurred: {e}")
        return str(e)
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return str(e)


functions_map = {"sql_table_query": sql_table_query}
```

We can test the function by running a simple query:

```python PYTHON
result = sql_table_query(
    "SELECT * FROM evaluation_results WHERE usecase = 'extract_names'"
)
print(result)
```

```mdx

[{'usecase': 'extract_names', 'run': 'A', 'score': 0.5, 'temperature': 0.3, 'tokens': 103, 'latency': 1.12}, {'usecase': 'extract_names', 'run': 'B', 'score': 0.2, 'temperature': 0.3, 'tokens': 101, 'latency': 2.85}, {'usecase': 'extract_names', 'run': 'C', 'score': 0.7, 'temperature': 0.3, 'tokens': 101, 'latency': 2.22}, {'usecase': 'extract_names', 'run': 'D', 'score': 0.7, 'temperature': 0.5, 'tokens': 120, 'latency': 3.2}]
```

## Setting up a tool to interact with the database

Next, we'll create a tool that will allow the agent to interact with the SQLite database containing our evaluation results.

```python PYTHON
sql_table_query_tool = {
    "type": "function",
    "function": {
        "name": "sql_table_query",
        "description": "Execute an SQL query on the evaluation_results table in the SQLite database. The table has columns 'usecase', 'run', 'score', 'temperature', 'tokens', and 'latency'.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "SQL query to execute on the evaluation_results table",
                }
            },
            "required": ["query"],
        },
    },
}

tools = [sql_table_query_tool]
```

## Building an agent for querying SQL data

Next, let's create a `run_agent` function to run the agentic RAG workflow,  just as we did in Part 1.

The only change we are making here is to make the system message more specific and describe the database schema to the agent.

```python PYTHON
system_message = """## Task and Context
You are an assistant who helps developers analyze LLM application evaluation results from a SQLite database. The database contains a table named 'evaluation_results' with the following schema:

- usecase (TEXT): The type of task being evaluated
- run (TEXT): The identifier for a specific evaluation run
- score (REAL): The performance score of the run
- temperature (REAL): The temperature setting used for the LLM
- tokens (INTEGER): The number of tokens used in the run
- latency (REAL): The time taken for the run in seconds

You can use SQL queries to analyze this data and provide insights to the developers."""
```

```python PYTHON
model = "command-a-03-2025"


def run_agent(query, messages=None):
    if messages is None:
        messages = []

    if "system" not in {m.get("role") for m in messages}:
        messages.append({"role": "system", "content": system_message})

    # Step 1: get user message
    print(f"Question:\n{query}")
    print("=" * 50)

    messages.append({"role": "user", "content": query})

    # Step 2: Generate tool calls (if any)
    response = co.chat(
        model=model, messages=messages, tools=tools, temperature=0.3
    )

    while response.message.tool_calls:

        print("Tool plan:")
        print(response.message.tool_plan, "\n")
        print("Tool calls:")
        for tc in response.message.tool_calls:
            # print(f"Tool name: {tc.function.name} | Parameters: {tc.function.arguments}")
            if tc.function.name == "analyze_evaluation_results":
                print(f"Tool name: {tc.function.name}")
                tool_call_prettified = print(
                    "\n".join(
                        f"  {line}"
                        for line_num, line in enumerate(
                            json.loads(tc.function.arguments)[
                                "code"
                            ].splitlines()
                        )
                    )
                )
                print(tool_call_prettified)
            else:
                print(
                    f"Tool name: {tc.function.name} | Parameters: {tc.function.arguments}"
                )
        print("=" * 50)

        messages.append(response.message)

        # Step 3: Get tool results
        for tc in response.message.tool_calls:
            tool_result = functions_map[tc.function.name](
                **json.loads(tc.function.arguments)
            )
            tool_content = [
                {
                    "type": "document",
                    "document": {"data": json.dumps(tool_result)},
                }
            ]

            messages.append(
                {
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": tool_content,
                }
            )

        # Step 4: Generate response and citations
        response = co.chat(
            model=model,
            messages=messages,
            tools=tools,
            temperature=0.3,
        )

    messages.append(
        {
            "role": "assistant",
            "content": response.message.content[0].text,
        }
    )

    # Print final response
    print("Response:")
    print(response.message.content[0].text)
    print("=" * 50)

    # Print citations (if any)
    verbose_source = (
        False  # Change to True to display the contents of a source
    )
    if response.message.citations:
        print("CITATIONS:\n")
        for citation in response.message.citations:
            print(
                f"Start: {citation.start}| End:{citation.end}| Text:'{citation.text}' "
            )
            print("Sources:")
            for idx, source in enumerate(citation.sources):
                print(f"{idx+1}. {source.id}")
                if verbose_source:
                    print(f"{source.tool_output}")
            print("\n")

    return messages
```

## Running the agent

Let's now ask the agent the same set of questions we asked in the previous chapter. While the previous chapter translates the questions into pandas Python code, this time the agent will be using SQL queries.

```python PYTHON
messages = run_agent("What's the average evaluation score in run A")