Build an Earnings Call ReAct Agent - Custom Tool Use

Build an Earnings Call ReAct Agent - Custom Tool Use

by EarningsCall Editor

6/16/2024

Overview

Large Language Models (LLMs) like ChatGPT excel at answering general questions but often struggle to incorporate domain-specific knowledge or data beyond their training cutoff date. One effective solution to this limitation is “Retrieval Augmented Generation” (RAG). RAG enhances in-context learning by allowing users to include information retrieved from documents within a prompt. This enables the language model to make inferences based on previously unseen information. However, traditional RAG operates under the assumption that every query requires additional context, even when it may not be necessary. A more refined approach involves enabling the LLM to determine when additional context is needed.

Enter ReAct : Reason + Act

ReAct is a method that integrates both thinking and doing into a single process for LLMs. It aids the model in not only devising a plan by thinking through the problem but also taking specific actions to gather more information or interact with external systems. This dual capability enhances the model’s effectiveness in solving tasks, such as answering questions or making decisions, by improving its ability to plan, adjust, and understand its actions. Additionally, a ReAct agent can discern when it is appropriate to request additional context and when it is not.

Researchers at Google pioneered this method in 2022, yielding notable results. Here’s an example of how using ReAct produces accurate answers in two different queries.

Image Source: https://arxiv.org/abs/2210.03629

Building the Agent

Let's get into the code.  Before we get started creating our agent, ensure you have at least Python 3.8 or greater installed.  Then, install the required dependencies:

pip install --upgrade earningscall langchain langchain-anthropic langchain-community langgraph pydantic

For an agent to be useful, it needs to have access to tools.  We will build some custom tools that allow the agent to gain access to earnings calls.  We will build three separate tools.

Developing Custom Tools

For the ReAct agent to function effectively in accessing earnings calls data, three main tools are developed:

    1.    CompanyMetaDataTool: Retrieves company metadata through stock ticker symbols.
    2.    CompanyEventsTool: Returns all earnings events associated with a particular company, including the year, quarter, and conference date.
    3.    EarningsEventTranscriptTool: Fetches the raw transcript text from earnings calls.

1. CompanyMetaDataTool

  The first is a simple tool used to look up companies by ticker symbol, such as AAPL for Apple, Inc.

from typing import Optional, Type

from earningscall import get_company
from langchain.pydantic_v1 import BaseModel
from langchain_core.callbacks import (
    CallbackManagerForToolRun,
)
from langchain_core.tools import BaseTool
from pydantic.v1 import Field


class CompanyMetaDataToolInput(BaseModel):
    ticker_symbol: str = Field(description="A ticker symbol is a unique series of letters assigned to a security or "
                                           "stock for trading purposes on a particular stock exchange. It acts as an"
                                           " abbreviation or code that represents the publicly traded company. For e"
                                           "xample, Apple’s ticker symbol is AAPL, and Microsoft’s is MSFT. Ticker s"
                                           "ymbols are used to identify the stock in trading and other financial tra"
                                           "nsactions. They can consist of letters, numbers, or a combination of bo"
                                           "th, depending on the exchange and the type of security.")


class CompanyMetaDataTool(BaseTool):
    name = "CompanyMetaDataTool"
    description = ("The Company Metadata Retriever is a tool designed to fetch comprehensive company "
                   "metadata using a stock ticker symbol. This tool is essential for investors, analysts, and "
                   "businesses seeking detailed information about a specific company in a streamlined and "
                   "efficient manner.")
    args_schema: Type[BaseModel] = CompanyMetaDataToolInput
    return_direct: bool = True

    def _run(
        self, ticker_symbol: str, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        """Use the tool."""
        company = get_company(ticker_symbol)
        return company.company_info.to_json()

When defining the tool, it's important to give a good description of how the tool should be used.  This description is used by the LLM to determine whether or not it's appropriate to invoke the tool.

2. CompanyEventsTool

Next, we'll define another tool used for fetching all the earnings events for a given company.

class CompanyEventsToolInput(BaseModel):
    ticker_symbol: str = Field(description="A ticker symbol is a unique series of letters assigned to a security or "
                                           "stock for trading purposes on a particular stock exchange. It acts as an"
                                           " abbreviation or code that represents the publicly traded company. For e"
                                           "xample, Apple’s ticker symbol is AAPL, and Microsoft’s is MSFT. Ticker s"
                                           "ymbols are used to identify the stock in trading and other financial tra"
                                           "nsactions. They can consist of letters, numbers, or a combination of bo"
                                           "th, depending on the exchange and the type of security.")


class CompanyEventsTool(BaseTool):
    name = "CompanyEventsTool"
    description = ("The Company Events Tool is a tool designed to fetch earnings events for a company."
                   " Each earnings event contains metadata including the conference date, the quarter, and the year.")
    args_schema: Type[BaseModel] = CompanyMetaDataToolInput
    return_direct: bool = True

    def _run(
        self, ticker_symbol: str, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> list:
        """Use the tool."""
        company = get_company(ticker_symbol)
        return [event.to_json() for event in company.events()]

This tool will return a list of all company events.  For example:

[
  {
    "year": 2024,
    "quarter": 2,
    "conference_date": "2024-05-02T17:00:00-04:00"
  },
  ...
]

3. EarningsEventTranscriptTool

Finally, we'll build a tool that is used for retrieving the raw transcript text from an earnings conference call, which is simply a very long string of characters representing the entire text of the earnings conference call.

class EarningsEventTranscriptToolInput(BaseModel):
    ticker_symbol: str = Field(description="A ticker symbol is a unique series of letters assigned to a security or "
                                           "stock for trading purposes on a particular stock exchange. It acts as an"
                                           " abbreviation or code that represents the publicly traded company. For e"
                                           "xample, Apple’s ticker symbol is AAPL, and Microsoft’s is MSFT. Ticker s"
                                           "ymbols are used to identify the stock in trading and other financial tra"
                                           "nsactions. They can consist of letters, numbers, or a combination of bo"
                                           "th, depending on the exchange and the type of security.")
    year: int = Field(description="The year of the conference.")
    quarter: int = Field(description="The quarter of the conference.")


class EarningsEventTranscriptTool(BaseTool):
    name = "EarningsEventTranscriptTool"
    description = ("The Earnings Event Transcript Tool Events Tool is a tool designed to fetch the entire transcript"
                   " text for a given earnings event. The transcript text contains the full transcript for the event"
                   " and includes speaker names, timestamps, and other relevant information.")
    args_schema: Type[BaseModel] = EarningsEventTranscriptToolInput
    return_direct: bool = True

    def _run(
        self, ticker_symbol: str, year: int, quarter: int, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        """Use the tool."""
        company = get_company(ticker_symbol)
        transcript = company.get_transcript(year=year, quarter=quarter)
        return transcript.text

Now that we have our three tools defined (CompanyMetaDataToolInput, CompanyEventsTool, and EarningsEventTranscriptTool) we can create our ReAct agent.  This code example will use Anthropic's model as the LLM, but because we are using LangChain, we can easily swap out the LLM with a number of supported LLMs.

import logging
import json
from time import time

from langchain_anthropic import ChatAnthropic
from langchain_core.messages import BaseMessage
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.prebuilt import create_react_agent


def get_anthropic_secret_api_key():
    with open(".anthropic-api-key", "r") as fd:
        return fd.read().strip()


# Price is per million tokens
model_costs = {
    # https://www.anthropic.com/api#pricing
    "claude-3-haiku-20240307": {
        "input_tokens": 0.25,
        "output_tokens": 1.25,
    },
    "claude-3-sonnet-20240229": {
        "input_tokens": 3.0,
        "output_tokens": 15.0,
    },
    "claude-3-opus-20240229": {
        "input_tokens": 15.0,
        "output_tokens": 75.0,
    },
}


def verbose_timedelta(seconds):
    days, rem = divmod(seconds, 86400)
    hours, rem = divmod(rem, 3600)
    minutes, seconds = divmod(rem, 60)
    if seconds < 1:
        seconds = 1
    locals_ = locals()
    magnitudes_str = ("{n} {magnitude}".format(n=int(locals_[magnitude]), magnitude=magnitude)
                      for magnitude in ("days", "hours", "minutes", "seconds") if locals_[magnitude])
    return ", ".join(magnitudes_str)


class Agent:

    def __init__(self, model_name):
        self.start_time = None
        self.end_time = None
        self.input_tokens = 0
        self.output_tokens = 0
        self.model_name = model_name

    def query(self, query: str):
        self.start_time = time()
        memory_checkpointer = SqliteSaver.from_conn_string(":memory:")
        model = ChatAnthropic(model_name=self.model_name, api_key=get_anthropic_secret_api_key())
        tools = [
            CompanyMetaDataTool(),
            CompanyEventsTool(),
            EarningsEventTranscriptTool(),
        ]
        agent_executor = create_react_agent(model, tools, checkpointer=memory_checkpointer)
        config = {
            "configurable": {
                "thread_id": "abc123",
            },
        }
        for chunk in agent_executor.stream(
            {"messages": [HumanMessage(content=query)]}, config
        ):
            if "agent" in chunk:
                for message in chunk["agent"]["messages"]:
                    self.input_tokens += message.usage_metadata["input_tokens"]
                    self.output_tokens += message.usage_metadata["output_tokens"]
            yield chunk
        self.end_time = time()

    def total_cost(self):
        input_cost = model_costs[self.model_name]["input_tokens"] * self.input_tokens / 1_000_000.0
        output_cost = model_costs[self.model_name]["output_tokens"] * self.output_tokens / 1_000_000.0
        return (input_cost + output_cost) * 100.0

    def log_cost(self):
        log.info(f"Total Cost of Query: ¢{round(self.total_cost(), 2)}")

    def log_query_time(self):
        elapsed_time = self.end_time - self.start_time
        log.info(f"Total Agent Runtime was {verbose_timedelta(elapsed_time)}.")

Finally, let's define a main function to invoke our Agent:

import argparse

from earningscall.utils import configure_sane_logging


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='')
    parser.add_argument('--debug',
                        action='store_true',
                        help='Enable debug logs')
    parser.add_argument('--model',
                        type=str,
                        default="claude-3-sonnet-20240229",
                        help='The Anthropic Model to use.  One of: '
                             '[claude-3-haiku-20240307, claude-3-sonnet-20240229 or claude-3-opus-20240229]')
    parser.add_argument('--query',
                        default="Summarize MSFT's most recent earnings call.",
                        type=str,
                        help='Your query')
    args = parser.parse_args()
    level = logging.INFO
    if args.debug:
        level = logging.DEBUG
    configure_sane_logging(level)

    agent = Agent(args.model)
    chunks = [chunk for chunk in agent.query(query=args.query)]
    for chunk in chunks:
        log.info(dump_ai_json(chunk))
        log.info("   ----------------------------   ")
    agent.log_cost()
    agent.log_query_time()

Testing the Agent

Now it's time to see our work in action.  Let's run our program with some queries.  For our first task, we'll ask the Agent to summarize Microsoft's latest earnings call: "Summarize the latest Earnings Call from Microsoft Corp."

python agent.py --query "Summarize the latest Earnings Call from Microsoft Corp."
[2024-06-16 11:22:52,193] [    INFO] --- HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK" (_client.py:1026)
[2024-06-16 11:22:55,633] [    INFO] --- HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK" (_client.py:1026)
[2024-06-16 11:23:04,296] [    INFO] --- HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK" (_client.py:1026)
{'agent': {'messages': [AIMessage(content=[{'text': 'OK, to summarize the latest Microsoft earnings call transcript, I will:', 'type': 'text'}, {'id': 'toolu_01H2K46NbJiy6VukkCUi2bRq', 'input': {'ticker_symbol': 'MSFT'}, 'name': 'CompanyEventsTool', 'type': 'tool_use'}], response_metadata={'id': 'msg_01KBrKsarY1vP6yDvmwxVKMr', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 853, 'output_tokens': 75}}, id='run-fde67d9b-9abd-44eb-8275-f3916938da95-0', tool_calls=[{'name': 'CompanyEventsTool', 'args': {'ticker_symbol': 'MSFT'}, 'id': 'toolu_01H2K46NbJiy6VukkCUi2bRq'}], usage_metadata={'input_tokens': 853, 'output_tokens': 75, 'total_tokens': 928})]}}
[2024-06-16 11:23:04,303] [    INFO] ---    ----------------------------    (agent.py:127)
{'tools': {'messages': [ToolMessage(content='["{\\"year\\": 2024, \\"quarter\\": 3, \\"conference_date\\": \\"2024-04-25T17:30:00-04:00\\"}", "{\\"year\\": 2024, \\"quarter\\": 2, \\"conference_date\\": \\"2024-01-30T17:30:00-05:00\\"}", "{\\"year\\": 2024, \\"quarter\\": 1, \\"conference_date\\": \\"2023-10-24T14:30:00-07:00\\"}", "{\\"year\\": 2023, \\"quarter\\": 4, \\"conference_date\\": \\"2023-07-25T14:30:00-07:00\\"}", "{\\"year\\": 2023, \\"quarter\\": 3, \\"conference_date\\": \\"2023-04-25T14:30:00-07:00\\"}", "{\\"year\\": 2023, \\"quarter\\": 2, \\"conference_date\\": \\"2023-01-24T14:30:00-08:00\\"}", "{\\"year\\": 2023, \\"quarter\\": 1, \\"conference_date\\": \\"2022-10-25T09:00:00-04:00\\"}", "{\\"year\\": 2022, \\"quarter\\": 4, \\"conference_date\\": \\"2022-07-26T09:00:00-04:00\\"}", "{\\"year\\": 2022, \\"quarter\\": 3, \\"conference_date\\": \\"2022-04-26T14:30:00-07:00\\"}", "{\\"year\\": 2022, \\"quarter\\": 2, \\"conference_date\\": \\"2022-01-25T14:30:00-08:00\\"}", "{\\"year\\": 2022, \\"quarter\\": 1, \\"conference_date\\": \\"2021-10-26T14:30:00-07:00\\"}", "{\\"year\\": 2021, \\"quarter\\": 4, \\"conference_date\\": \\"2021-07-27T14:30:00-07:00\\"}", "{\\"year\\": 2021, \\"quarter\\": 3, \\"conference_date\\": \\"2021-04-29T14:30:00-07:00\\"}", "{\\"year\\": 2021, \\"quarter\\": 2, \\"conference_date\\": \\"2021-01-26T14:30:00-08:00\\"}", "{\\"year\\": 2021, \\"quarter\\": 1, \\"conference_date\\": \\"2020-10-27T14:30:00-07:00\\"}", "{\\"year\\": 2020, \\"quarter\\": 4, \\"conference_date\\": \\"2020-07-22T14:30:00-07:00\\"}", "{\\"year\\": 2020, \\"quarter\\": 3, \\"conference_date\\": \\"2020-04-29T14:30:00-05:00\\"}", "{\\"year\\": 2020, \\"quarter\\": 2, \\"conference_date\\": \\"2020-01-29T14:30:00-08:00\\"}", "{\\"year\\": 2020, \\"quarter\\": 1, \\"conference_date\\": \\"2019-10-23T14:30:00-07:00\\"}", "{\\"year\\": 2019, \\"quarter\\": 4, \\"conference_date\\": \\"2019-07-18T14:30:00-07:00\\"}"]', name='CompanyEventsTool', id='f0814c32-dbfe-43c2-a24d-c4bfcfe38b21', tool_call_id='toolu_01H2K46NbJiy6VukkCUi2bRq')]}}
[2024-06-16 11:23:04,303] [    INFO] ---    ----------------------------    (agent.py:127)
{'agent': {'messages': [AIMessage(content=[{'text': 'The latest earnings event quarter and year is 2024 Q3. To get the transcript:', 'type': 'text'}, {'id': 'toolu_017fVXesaKeTh5vmP3xicpBH', 'input': {'ticker_symbol': 'MSFT', 'year': 2024, 'quarter': 3}, 'name': 'EarningsEventTranscriptTool', 'type': 'tool_use'}], response_metadata={'id': 'msg_014a2EVCPvqyM4bpML4RDnMK', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 1760, 'output_tokens': 120}}, id='run-dba415ee-5839-447f-bd9a-85cf453fc491-0', tool_calls=[{'name': 'EarningsEventTranscriptTool', 'args': {'ticker_symbol': 'MSFT', 'year': 2024, 'quarter': 3}, 'id': 'toolu_017fVXesaKeTh5vmP3xicpBH'}], usage_metadata={'input_tokens': 1760, 'output_tokens': 120, 'total_tokens': 1880})]}}
[2024-06-16 11:23:04,304] [    INFO] ---    ----------------------------    (agent.py:127)
{'tools': {'messages': [ToolMessage(content="Greetings and welcome to the Microsoft Fiscal Year 2024 Third Quarter Earnings Conference Call. At this time, all participants are in a listen-only mode. A question and answer session will follow ....<clipped>... You may now disconnect your lines at this time. Enjoy the rest of your day.", name='EarningsEventTranscriptTool', id='e79c9ced-86c8-4895-83b7-f2a71572a1a1', tool_call_id='toolu_017fVXesaKeTh5vmP3xicpBH')]}}
[2024-06-16 11:23:04,309] [    INFO] ---    ----------------------------    (agent.py:127)
{'agent': {'messages': [AIMessage(content='In summary, the key points from the latest Microsoft earnings call on AI:\n\n- Microsoft is leaning into AI and pursuing this technology aggressively\n- They see AI as a major opportunity across business sectors and uses\n- Benefit of AI will be most apparent with enterprise customers and commercial uses\n- Microsoft\'s focus is both on model training ("hard tech"), and application/integration to drive value \n- They see commercial/enterprise as a key area of focus for AI integration\n- There is a need for culture change and process redesign to properly integrate AI \n- Microsoft views this as a multi-year journey, not a short-term project\n\nThe tone seems ambitious, ambitious, but grounded in the reality that this will take time and proper planning/resourcing. The key points are their emphasis on enterprise/commercial use cases as an area of focus, and that change management and process redesign will be crucial for successful AI deployment.', response_metadata={'id': 'msg_01NHBLsNeHXKJKFTYj6UfW5b', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 13804, 'output_tokens': 199}}, id='run-34fa43d3-411d-49da-9746-6bb6dbb8e776-0', usage_metadata={'input_tokens': 13804, 'output_tokens': 199, 'total_tokens': 14003})]}}
[2024-06-16 11:23:04,310] [    INFO] ---    ----------------------------    (agent.py:127)
[2024-06-16 11:23:04,310] [    INFO] --- Total Cost of Query: ¢5.52 (agent.py:91)
[2024-06-16 11:23:04,310] [    INFO] --- Total Agent Runtime was 14 seconds. (agent.py:95)
  1. The Agent invoked the LLM, which returned a response, saying to invoke the CompanyEventsTool to return all events for ticker symbol MSFT.
  2. The Agent called the called the CompanyEventsTool, getting a list of all events.
  3. The Agent invoked the LLM again, this time with the list of events.  The LLM returned The latest earnings event quarter and year is 2024 Q3. and decided to invoke the EarningsEventTranscriptTool.
  4. The Agent invoked the EarningsEventTranscriptTool to get the transcript text.
  5. Finally, the Agent invoked the LLM again, telling it to summarize the transcript text.  The LLM returned the summary.

In total, the Agent made 3 calls to the LLM, with a total cost of roughly 5 cents.  To decrease costs you can use a cheaper LLM model at the expense of accuracy.  Invoking an LLM 3 separate times does add latency, and the whole query took 14 seconds to complete.

The final summary the Agent produced was:

In summary, the key points from the latest Microsoft earnings call on AI:

- Microsoft is leaning into AI and pursuing this technology aggressively
- They see AI as a major opportunity across business sectors and uses
- Benefit of AI will be most apparent with enterprise customers and commercial uses
- Microsoft's focus is both on model training ("hard tech"), and application/integration to drive value
- They see commercial/enterprise as a key area of focus for AI integration
- There is a need for culture change and process redesign to properly integrate AI
- Microsoft views this as a multi-year journey, not a short-term project

The tone seems ambitious, ambitious, but grounded in the reality that this will take time and proper planning/resourcing. The key points are their emphasis on enterprise/commercial use cases as an area of focus, and that change management and process redesign will be crucial for successful AI deployment.

Pretty, prettay, prettaaaaaay good!

Actually, we can get much better summaries if we use a more powerful model.  Let's try the same thing, but with Opus instead:

python agent.py --query "Summarize the latest Earnings Call from Microsoft Corp." --model claude-3-opus-20240229

<result>

Here are the key highlights from Microsoft's latest earnings call for Q3 2024:

Financial Performance:
- Revenue was $61.9 billion, up 17% year-over-year
- Earnings per share was $2.94, up 20%
- Microsoft Cloud revenue was $35.1 billion, up 23%
- Commercial bookings increased 29% driven by strong Azure commitments
- Operating income increased 23% and operating margins expanded about 2 points to 45%

Key Business Highlights:
- Over half of Fortune 500 uses Azure OpenAI Service. Number of Azure AI customers growing with increasing spend.
- GitHub Copilot has 1.8 million paid subscribers, growth accelerating to over 35% QoQ. Over 90% of Fortune 100 are GitHub customers.
- Nearly 60% of Fortune 500 now uses Copilot for Microsoft 365. Seeing increased usage intensity.
- Dynamics 365 took share with AI-powered apps across all categories. Co-pilots for sales, service, finance seeing adoption.
- Windows Co-pilot now available on nearly 225 million Windows 10/11 PCs, up 2x QoQ. OEMs launching AI PCs.
- Teams surpassed 1 million Rooms. Teams Phone is leader in cloud calling with over 20 million PSTN users.
- LinkedIn saw record engagement. AI-assisted features driving accelerated premium subscription growth of 29%.
- Bing reached over 140 million daily active users. Surge in mobile app downloads after Super Bowl ad.

Outlook:
- Expect continued solid growth in commercial bookings driven by strong sales execution 
- Azure revenue expected to grow 30-31% in constant currency in Q4
- Will continue to make significant capital investments to scale cloud and AI infrastructure to meet growing demand
- Projecting double-digit revenue and operating income growth again in FY25
- Expect FY25 operating margins to be down only about 1 point year-over-year even with significant cloud/AI investments

In summary, Microsoft delivered very strong Q3 results with accelerating growth in key businesses like Azure, GitHub, Dynamics and LinkedIn driven by early progress with its comprehensive AI platform and co-pilot experiences. The company is seeing strong customer demand and adoption of its AI innovations and expects that momentum to continue, though it is investing significantly to scale its cloud and AI infrastructure to capture the massive opportunity ahead.
</result>

Although the tradeoff here is time and money:

[2024-06-16 12:37:42,981] [    INFO] --- Total Cost of Query: ¢35.36 (agent.py:90)
[2024-06-16 12:37:42,982] [    INFO] --- Total Agent Runtime was 1 minutes, 7 seconds. (agent.py:94)

Let's try another query: Who are the main speakers in Q1 2023 MSFT earnings call?

python agent.py --query "Who are the main speakers in Q1 2023 MSFT earnings call?"
[2024-06-16 11:39:41,545] [    INFO] --- HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK" (_client.py:1026)
[2024-06-16 11:39:48,132] [    INFO] --- HTTP Request: POST https://api.anthropic.com/v1/messages "HTTP/1.1 200 OK" (_client.py:1026)
[2024-06-16 11:39:48,141] [    INFO] --- {"agent": {"messages": [{"content": [{"text": "To get the main speakers for the Microsoft Q1 2023 earnings call transcript, we can use the EarningsEventTranscriptTool like this:", "type": "text"}, {"id": "toolu_01HSQLsmaPCrSCkGuYuWLdsX", "input": {"ticker_symbol": "MSFT", "year": 2023, "quarter": 1}, "name": "EarningsEventTranscriptTool", "type": "tool_use"}], "additional_kwargs": {}, "response_metadata": {"id": "msg_0194yaUZcuVmsq21TnUEki5V", "model": "claude-3-sonnet-20240229", "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 859, "output_tokens": 133}}, "type": "ai", "name": null, "id": "run-fff26534-cbb1-45a0-b0e9-0a734acef7cc-0", "example": false, "tool_calls": [{"name": "EarningsEventTranscriptTool", "args": {"ticker_symbol": "MSFT", "year": 2023, "quarter": 1}, "id": "toolu_01HSQLsmaPCrSCkGuYuWLdsX"}], "invalid_tool_calls": [], "usage_metadata": {"input_tokens": 859, "output_tokens": 133, "total_tokens": 992}}]}} (agent.py:128)
[2024-06-16 11:39:48,142] [    INFO] ---    ----------------------------    (agent.py:129)
[2024-06-16 11:39:48,143] [    INFO] --- {"tools": {"messages": [{"content": "Greetings, and welcome to the Microsoft Fiscal Year 2023 First Quarter Earnings Conference Call. At this time, all participants are in a listen-only mode. A question and answer session will follow the formal presentation. If anyone should require operator assistance during the conference, please press star zero on your telephone keypad...<clipped>...We thank you for your participation, and we disconnect your lines at this time.", "additional_kwargs": {}, "response_metadata": {}, "type": "tool", "name": "EarningsEventTranscriptTool", "id": "c95a3419-e89b-40d8-a850-346e7b0f1b5e", "tool_call_id": "toolu_01HSQLsmaPCrSCkGuYuWLdsX"}]}} (agent.py:128)
[2024-06-16 11:39:48,153] [    INFO] ---    ----------------------------    (agent.py:129)
[2024-06-16 11:39:48,154] [    INFO] --- {"agent": {"messages": [{"content": "Based on the transcript, the main speakers were:\n\n- Satya Nadella, Chairman and CEO\n- Amy Hood, Chief Financial Officer \n- Brett Iverson, VP of Investor Relations\n- Analysts from various financial firms asking questions\n\nSatya Nadella and Amy Hood delivered the prepared remarks providing an overview of Microsoft's quarterly results and outlook. Brett Iverson moderated the Q&A session with financial analysts.", "additional_kwargs": {}, "response_metadata": {"id": "msg_01UPwRvV6ViozCmujTK2uFPY", "model": "claude-3-sonnet-20240229", "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 13149, "output_tokens": 97}}, "type": "ai", "name": null, "id": "run-56208b16-1296-4147-8e94-e0a7a4208a91-0", "example": false, "tool_calls": [], "invalid_tool_calls": [], "usage_metadata": {"input_tokens": 13149, "output_tokens": 97, "total_tokens": 13246}}]}} (agent.py:128)
[2024-06-16 11:39:48,154] [    INFO] ---    ----------------------------    (agent.py:129)
[2024-06-16 11:39:48,154] [    INFO] --- Total Cost of Query: ¢4.55 (agent.py:90)
[2024-06-16 11:39:48,154] [    INFO] --- Total Agent Runtime was 13 seconds. (agent.py:94)

The agent's final result was:

Based on the transcript, the main speakers were:

 

- Satya Nadella, Chairman and CEO
- Amy Hood, Chief Financial Officer
- Brett Iverson, VP of Investor Relations
- Analysts from various financial firms asking questions

 

Satya Nadella and Amy Hood delivered the prepared remarks providing an overview of Microsoft's quarterly results and outlook. Brett Iverson moderated the Q&A session with financial analysts.

Could have been better if it produced a list of all analysts on the call, but maybe we needed a better prompt for that.  Instead of asking for only the “main speakers”, I could have said: “List each and every speaker…”.

Summary

The ReAct agent represents a significant step forward in tailoring LLMs to specific domains such as financial analysis, where up-to-date and precise information is crucial. By integrating selective retrieval capabilities, the ReAct agent not only enhances response accuracy but also optimizes computational efficiency.  However, there is a compromise as agents using tools might initiate multiple calls to the LLM, potentially raising both latency and costs. Despite this, for specific scenarios, adopting this strategy could be advantageous.

For the full code please see our GitHub repository 👨‍💻.

About EarningsCall

EarningsCall is dedicated to simplifying the process of accessing public market earnings calls, providing essential tools for investors. Our offerings are designed to support informed investment decisions through user-friendly interfaces and comprehensive data access.

Related Articles