Spaces:
Runtime error
Runtime error
Upload 3 files
Browse files- analytics_agent_new.py +155 -0
- coordinator_agent.py +59 -0
- rag_agent.py +72 -0
analytics_agent_new.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# ✅ Import Libraries
|
| 3 |
+
import os
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
from prophet import Prophet
|
| 8 |
+
from statsmodels.tsa.arima.model import ARIMA
|
| 9 |
+
import gradio as gr
|
| 10 |
+
import re
|
| 11 |
+
from dotenv import load_dotenv
|
| 12 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 13 |
+
from langchain.agents import Tool, initialize_agent
|
| 14 |
+
from langchain.agents.agent_types import AgentType
|
| 15 |
+
import zipfile
|
| 16 |
+
import chromadb
|
| 17 |
+
from langchain.vectorstores import Chroma
|
| 18 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
| 19 |
+
from langgraph.prebuilt import create_react_agent
|
| 20 |
+
|
| 21 |
+
# ✅ Load Environment Variables from .env or set securely
|
| 22 |
+
load_dotenv()
|
| 23 |
+
|
| 24 |
+
# ✅ Setup Gemini Model
|
| 25 |
+
llm = ChatGoogleGenerativeAI(model="models/gemini-1.5-flash", google_api_key=os.getenv("GOOGLE_API_KEY"))
|
| 26 |
+
|
| 27 |
+
# ✅ Upload and Load Chroma Vector Store
|
| 28 |
+
# uploaded = files.upload()
|
| 29 |
+
# for filename in uploaded.keys():
|
| 30 |
+
# if filename.endswith(".zip"):
|
| 31 |
+
# with zipfile.ZipFile(filename, 'r') as zip_ref:
|
| 32 |
+
# zip_ref.extractall("/content/chroma_db")
|
| 33 |
+
# print(f"✅ Extracted: {filename} to /content/chroma_db")
|
| 34 |
+
|
| 35 |
+
persistent_client = chromadb.PersistentClient(path="chroma/")
|
| 36 |
+
vectorstore = Chroma(
|
| 37 |
+
client=persistent_client,
|
| 38 |
+
collection_name="financial_reports",
|
| 39 |
+
embedding_function=HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
# ✅ Forecasting utility functions
|
| 43 |
+
def parse_yearly_data(text):
|
| 44 |
+
pattern = r"(\d{4})[^0-9]{1,10}?([\d.,]+)[\s]?[BbMm]?"
|
| 45 |
+
matches = re.findall(pattern, text)
|
| 46 |
+
data = {}
|
| 47 |
+
for year, value in matches:
|
| 48 |
+
try:
|
| 49 |
+
value = float(value.replace(',', ''))
|
| 50 |
+
data[int(year)] = value
|
| 51 |
+
except ValueError:
|
| 52 |
+
continue
|
| 53 |
+
return dict(sorted(data.items()))
|
| 54 |
+
|
| 55 |
+
def forecast_next_value(data: dict):
|
| 56 |
+
years = list(data.keys())
|
| 57 |
+
values = list(data.values())
|
| 58 |
+
if len(years) < 2:
|
| 59 |
+
raise ValueError("❌ Error: Need at least 2 years of numerical data in your query to generate a forecast.\n\n👉 Try rephrasing and include examples like 'Revenue was 100B in 2022 and 120B in 2023'.")
|
| 60 |
+
delta = values[-1] - values[-2]
|
| 61 |
+
next_year = years[-1] + 1
|
| 62 |
+
next_value = values[-1] + delta
|
| 63 |
+
data[next_year] = next_value
|
| 64 |
+
return data
|
| 65 |
+
|
| 66 |
+
def forecast_with_prophet(data):
|
| 67 |
+
try:
|
| 68 |
+
df = pd.DataFrame({"ds": pd.to_datetime([f"{year}-01-01" for year in data.keys()], errors='coerce'), "y": list(data.values())})
|
| 69 |
+
df = df.dropna()
|
| 70 |
+
if len(df) < 2:
|
| 71 |
+
return "⚠️ Not enough valid data for Prophet."
|
| 72 |
+
model = Prophet()
|
| 73 |
+
model.fit(df)
|
| 74 |
+
future = model.make_future_dataframe(periods=1, freq='Y')
|
| 75 |
+
forecast = model.predict(future)
|
| 76 |
+
return forecast.iloc[-1]['yhat']
|
| 77 |
+
except Exception as e:
|
| 78 |
+
return f"⚠️ Prophet forecast failed: {str(e)}"
|
| 79 |
+
|
| 80 |
+
def forecast_with_arima(data):
|
| 81 |
+
try:
|
| 82 |
+
values = list(data.values())
|
| 83 |
+
if len(values) < 3:
|
| 84 |
+
return "⚠️ Not enough data for ARIMA."
|
| 85 |
+
model = ARIMA(values, order=(1, 1, 1))
|
| 86 |
+
model_fit = model.fit()
|
| 87 |
+
return model_fit.forecast()[0]
|
| 88 |
+
except Exception as e:
|
| 89 |
+
return f"⚠️ ARIMA forecast failed: {str(e)}"
|
| 90 |
+
|
| 91 |
+
def plot_forecast(data: dict, title="Financial Forecast"):
|
| 92 |
+
years = list(data.keys())
|
| 93 |
+
values = list(data.values())
|
| 94 |
+
plt.figure(figsize=(8, 5))
|
| 95 |
+
plt.plot(years, values, marker='o', linestyle='-')
|
| 96 |
+
plt.title(title)
|
| 97 |
+
plt.xlabel("Year")
|
| 98 |
+
plt.ylabel("Value")
|
| 99 |
+
plt.grid(True)
|
| 100 |
+
plt.tight_layout()
|
| 101 |
+
plt.savefig("forecast.png")
|
| 102 |
+
plt.close()
|
| 103 |
+
|
| 104 |
+
# ✅ Forecasting Tool Function
|
| 105 |
+
last_data_source = ""
|
| 106 |
+
|
| 107 |
+
def forecast_tool_func(query: str) -> str:
|
| 108 |
+
global last_data_source
|
| 109 |
+
data = parse_yearly_data(query)
|
| 110 |
+
if not data or len(data) < 2:
|
| 111 |
+
raise ValueError("❌ Error: Need at least 2 years of numerical data in your query to generate a forecast.\n\n👉 Try rephrasing and include examples like 'Revenue was 100B in 2022 and 120B in 2023'.")
|
| 112 |
+
|
| 113 |
+
extrapolated = forecast_next_value(data.copy())
|
| 114 |
+
prophet_result = forecast_with_prophet(data)
|
| 115 |
+
arima_result = forecast_with_arima(data)
|
| 116 |
+
plot_forecast(extrapolated, title="Forecast based on financial data")
|
| 117 |
+
last_data_source = query
|
| 118 |
+
|
| 119 |
+
result_text = (
|
| 120 |
+
f"📈 Forecast complete using Prophet and ARIMA.\n"
|
| 121 |
+
f"🔮 Prophet prediction for {max(data.keys()) + 1}: {prophet_result}\n"
|
| 122 |
+
f"📊 ARIMA prediction for {max(data.keys()) + 1}: {arima_result}\n"
|
| 123 |
+
f"🖼️ Plot saved as forecast.png.\n"
|
| 124 |
+
f"\n📌 Source data extracted from your query: '{query}'"
|
| 125 |
+
f"\n🔢 Parsed numeric data: {data}"
|
| 126 |
+
)
|
| 127 |
+
return result_text
|
| 128 |
+
|
| 129 |
+
# ✅ LangChain Tool and Agent
|
| 130 |
+
forecast_tool = Tool(
|
| 131 |
+
name="FinancialForecaster",
|
| 132 |
+
func=forecast_tool_func,
|
| 133 |
+
description="Use this tool to forecast financial metrics based on yearly values given in the user's query. It parses numerical data and extrapolates one year forward using Prophet and ARIMA, also saving a plot."
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
# analytics_agent = initialize_agent(
|
| 137 |
+
# tools=[forecast_tool],
|
| 138 |
+
# llm=llm,
|
| 139 |
+
# agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
|
| 140 |
+
# verbose=True
|
| 141 |
+
# )
|
| 142 |
+
|
| 143 |
+
analytics_agent = create_react_agent(
|
| 144 |
+
model=llm,
|
| 145 |
+
tools=[forecast_tool],
|
| 146 |
+
name="analytics_agent",
|
| 147 |
+
prompt=(
|
| 148 |
+
"You are a financial forecasting assistant. Based on the user's query, "
|
| 149 |
+
"extract yearly data and forecast the next year's value using Prophet and ARIMA.\n"
|
| 150 |
+
"Your job is to answer user questions involving trends, predictions, market dynamics, and revenue projections.\n"
|
| 151 |
+
"Respond clearly with citations where appropriate.\n"
|
| 152 |
+
"If you cannot find any relevant data in the user's query, respond with 'No relevant data found.'\n"
|
| 153 |
+
"If you find relevant data but cannot make a prediction, respond with 'Prediction not possible.'\n"
|
| 154 |
+
)
|
| 155 |
+
)
|
coordinator_agent.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# agents/coordinator_agent.py
|
| 2 |
+
# coordinator_agent.py
|
| 3 |
+
|
| 4 |
+
from langchain.chat_models import init_chat_model
|
| 5 |
+
from langgraph_supervisor import create_supervisor
|
| 6 |
+
from web_agent import web_agent # <-- импортируй deinen Web-Agent mit Tool
|
| 7 |
+
import os
|
| 8 |
+
from langchain_core.tools import Tool
|
| 9 |
+
from langgraph.prebuilt import create_react_agent
|
| 10 |
+
from rag_agent import financial_rag_agent
|
| 11 |
+
from analytics_agent_new import analytics_agent
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
gemini_model = init_chat_model("gemini-2.0-flash", model_provider="google_genai")
|
| 15 |
+
# 🧠 Koordinator-Agent mit Gemini 2.0 Flash
|
| 16 |
+
coordinator_agent = create_supervisor(
|
| 17 |
+
agents=[web_agent, analytics_agent, financial_rag_agent],
|
| 18 |
+
model=gemini_model,
|
| 19 |
+
prompt=(
|
| 20 |
+
# "Du bist ein Koordinator-Agent, der zwei spezialisierte Agenten verwaltet:\n"
|
| 21 |
+
# "- web_agent für Nachrichten und aktuelle Informationen und stock prices and financial news, schlusskurs\n"
|
| 22 |
+
# "- analytics_agent für Analyse, Prognosen und Finanzbewertung\n"
|
| 23 |
+
# "\n"
|
| 24 |
+
# "Wähle den passenden Agenten basierend auf der Nutzerfrage.\n"
|
| 25 |
+
# "Gib die Antworten und Ergebnisse von Agenten zurück und zeight mir die Antworten."
|
| 26 |
+
# "Du bist ein intelligenter Koordinator-Agent, der drei spezialisierte Agenten verwaltet:\n"
|
| 27 |
+
# "1. 📰 web_agent\n"
|
| 28 |
+
# " - Recherchiert aktuelle Nachrichten, Aktienkurse, Schlusskurse und wirtschaftliche Entwicklungen im Internet.\n"
|
| 29 |
+
# " - Verwenden, wenn die Nutzerfrage aktuelle Daten oder Marktgeschehen betrifft.\n\n"
|
| 30 |
+
# "2. 📊 analytics_agent\n"
|
| 31 |
+
# " - Führt Marktanalysen, statistische Bewertungen, Prognosen oder Wirtschaftstrend-Analysen durch.\n"
|
| 32 |
+
# " - Verwenden, wenn analytische oder bewertende Aufgaben verlangt sind.\n\n"
|
| 33 |
+
# "3. 📁 financial_rag_agent\n"
|
| 34 |
+
# " - Beantwortet Fragen zu historischen Finanzberichten (10-K, 10-Q, Annual Reports) der Unternehmen Apple, Microsoft, Google, NVIDIA und Meta aus den letzten fünf Jahren.\n"
|
| 35 |
+
# " - Gibt präzise Antworten mit Angabe der Quelle (Firma, Jahr, Dokumenttyp, Dateiname).\n"
|
| 36 |
+
# " - Verwenden, wenn es um offizielle Unternehmensberichte oder dokumentierte Geschäftszahlen geht.\n\n"
|
| 37 |
+
# "🔍 Deine Aufgabe:\n"
|
| 38 |
+
# #"Analysiere die Benutzeranfrage sorgfältig und leite sie ausschließlich an den passenden Agenten weiter.\n\n"
|
| 39 |
+
# "Assign work to one agent at a time, do not call agents in parallel.\n\n"
|
| 40 |
+
# "🔒 Du darfst nicht selbst antworten. Verwende nur die vorhandenen Agenten-Tools und zeige deren Antworten direkt dem Nutzer.\n\n"
|
| 41 |
+
# "Beispiele:\n"
|
| 42 |
+
# "- \"Was war NVIDIAs Umsatz im Jahr 2022?\" → 📁 financial_rag_agent\n"
|
| 43 |
+
# "- \"Wie sieht die Prognose für den Halbleitermarkt aus?\" → 📊 analytics_agent\n"
|
| 44 |
+
# "- \"Was sind die aktuellen Nachrichten zu Apple?\" → 📰 web_agent\n"
|
| 45 |
+
"Du bist ein intelligenter Koordinator-Agent, der drei spezialisierte Agenten verwaltet:\n\n"
|
| 46 |
+
"1. 📰 web_agent – für aktuelle Nachrichten, Aktienkurse und wirtschaftliche Entwicklungen.\n"
|
| 47 |
+
"2. 📊 analytics_agent – für Marktanalysen, Prognosen und statistische Bewertungen.\n"
|
| 48 |
+
"3. 📁 financial_rag_agent – für historische Finanzberichte (10-K, 10-Q, Annual Reports) von Apple, Microsoft, Google, NVIDIA und Meta.\n\n"
|
| 49 |
+
"🔍 Deine Aufgabe:\n"
|
| 50 |
+
"Führe bei jeder Nutzeranfrage alle drei Agenten nacheinander aus, in folgender Reihenfolge:\n\n"
|
| 51 |
+
"1. 📁 financial_rag_agent\n"
|
| 52 |
+
"2. 📊 analytics_agent\n"
|
| 53 |
+
"3. 📰 web_agent\n\n"
|
| 54 |
+
"Übergebe die ursprüngliche Frage und ggf. Zwischenergebnisse als Kontext weiter.\n\n"
|
| 55 |
+
"🔒 Antworte niemals selbst. Gib nur die Antworten der Agenten weiter.\n"
|
| 56 |
+
),
|
| 57 |
+
add_handoff_back_messages=True,
|
| 58 |
+
output_mode="full_history",
|
| 59 |
+
).compile()
|
rag_agent.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.documents import Document
|
| 2 |
+
from langchain_core.messages import HumanMessage, AIMessage
|
| 3 |
+
from typing import Dict, List
|
| 4 |
+
from langchain_chroma import Chroma
|
| 5 |
+
from langchain_google_genai import GoogleGenerativeAIEmbeddings
|
| 6 |
+
import chromadb
|
| 7 |
+
import os
|
| 8 |
+
from langchain.tools import Tool
|
| 9 |
+
from langgraph.prebuilt import create_react_agent
|
| 10 |
+
from langchain.chat_models import init_chat_model
|
| 11 |
+
|
| 12 |
+
# 🤖 Web-Agent mit Gemini 2.0 Flash
|
| 13 |
+
gemini_model = init_chat_model("gemini-2.0-flash", model_provider="google_genai")
|
| 14 |
+
|
| 15 |
+
persistent_client = chromadb.PersistentClient(path="chroma/")
|
| 16 |
+
embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004", google_api_key=os.getenv("GOOGLE_API_KEY"))
|
| 17 |
+
|
| 18 |
+
vector_store = Chroma(
|
| 19 |
+
client=persistent_client,
|
| 20 |
+
collection_name="big_tech_financial_reports",
|
| 21 |
+
embedding_function=embeddings,
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
# Функция: отвечает на финансовый запрос с цитированием источников
|
| 25 |
+
def answer_financial_query(query: str) -> str:
|
| 26 |
+
# Используем глобальные vector_store и llm
|
| 27 |
+
global vector_store, gemini_model
|
| 28 |
+
|
| 29 |
+
query_embedding = embeddings.embed_query(query)
|
| 30 |
+
retrieved_docs = vector_store.similarity_search_by_vector(query_embedding, k=5)
|
| 31 |
+
|
| 32 |
+
context = "\n\n".join([
|
| 33 |
+
f"[{doc.metadata['company']}, {doc.metadata['year']}, {doc.metadata['type']}, {doc.metadata['source']}]:\n{doc.page_content}"
|
| 34 |
+
for doc in retrieved_docs
|
| 35 |
+
])
|
| 36 |
+
|
| 37 |
+
prompt = f"""
|
| 38 |
+
You are a financial assistant. Based only on the following financial report excerpts, answer the user's query.
|
| 39 |
+
Use a clear and concise tone and cite the company, year, document type, and source for any fact.
|
| 40 |
+
|
| 41 |
+
User Query: {query}
|
| 42 |
+
|
| 43 |
+
Documents:
|
| 44 |
+
{context}
|
| 45 |
+
|
| 46 |
+
Answer:
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
response = gemini_model([HumanMessage(content=prompt)])
|
| 50 |
+
return response.content
|
| 51 |
+
|
| 52 |
+
financial_rag_tool = Tool(
|
| 53 |
+
name="analyze_financial_report",
|
| 54 |
+
func=answer_financial_query,
|
| 55 |
+
description=(
|
| 56 |
+
"Beantworte Fragen zu Finanzberichten, Bilanzen, Quartalszahlen und Jahresabschlüssen "
|
| 57 |
+
"von Apple, Microsoft, Google, NVIDIA und Meta in den letzten fünf Jahren. "
|
| 58 |
+
"Die Antworten enthalten genaue Quellenangaben zum Bericht."
|
| 59 |
+
)
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
financial_rag_agent = create_react_agent(
|
| 63 |
+
model=gemini_model,
|
| 64 |
+
tools=[financial_rag_tool],
|
| 65 |
+
name="financial_rag_agent",
|
| 66 |
+
prompt=(
|
| 67 |
+
"Du bist ein spezialisierter Finanzassistent.\n"
|
| 68 |
+
"Du beantwortest ausschließlich Fragen zu den Finanzberichten von Apple, Microsoft, Google, NVIDIA und Meta.\n"
|
| 69 |
+
"Nutze ausschließlich das Tool 'analyze_financial_report', um Informationen aus diesen Quellen zu beziehen.\n"
|
| 70 |
+
"Gib stets eine präzise Antwort mit Angabe der Quelle (Unternehmen, Jahr, Berichtstyp, Dateiname)."
|
| 71 |
+
)
|
| 72 |
+
)
|