Merge branch 'main' into 21-team-monitoring
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from app.agents.models import AppModels
|
||||
from app.agents.predictor import PredictorInput, PredictorOutput, PredictorStyle, PREDICTOR_INSTRUCTIONS
|
||||
from app.agents.predictor import PredictorInput, PredictorOutput
|
||||
from app.agents.team import create_team_with
|
||||
from app.agents.pipeline import Pipeline
|
||||
|
||||
__all__ = ["AppModels", "PredictorInput", "PredictorOutput", "PredictorStyle", "PREDICTOR_INSTRUCTIONS", "create_team_with", "Pipeline"]
|
||||
__all__ = ["PredictorInput", "PredictorOutput", "create_team_with", "Pipeline"]
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import os
|
||||
import ollama
|
||||
from enum import Enum
|
||||
from agno.agent import Agent
|
||||
from agno.models.base import Model
|
||||
from agno.models.google import Gemini
|
||||
from agno.models.ollama import Ollama
|
||||
from agno.tools import Toolkit
|
||||
from agno.utils.log import log_warning #type: ignore
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AppModels(Enum):
|
||||
"""
|
||||
Enum per i modelli supportati.
|
||||
Aggiungere nuovi modelli qui se necessario.
|
||||
Per quanto riguarda Ollama, i modelli dovranno essere scaricati e installati
|
||||
localmente seguendo le istruzioni di https://ollama.com/docs/guide/install-models
|
||||
"""
|
||||
GEMINI = "gemini-2.0-flash" # API online
|
||||
GEMINI_PRO = "gemini-2.0-pro" # API online, più costoso ma migliore
|
||||
OLLAMA_GPT = "gpt-oss:latest" # + good - slow (13b)
|
||||
OLLAMA_QWEN = "qwen3:latest" # + good + fast (8b)
|
||||
OLLAMA_QWEN_4B = "qwen3:4b" # + fast + decent (4b)
|
||||
OLLAMA_QWEN_1B = "qwen3:1.7b" # + very fast + decent (1.7b)
|
||||
|
||||
@staticmethod
|
||||
def availables_local() -> list['AppModels']:
|
||||
"""
|
||||
Controlla quali provider di modelli LLM locali sono disponibili.
|
||||
Ritorna una lista di provider disponibili.
|
||||
"""
|
||||
try:
|
||||
models_list = ollama.list()
|
||||
availables = [model['model'] for model in models_list['models']]
|
||||
app_models = [model for model in AppModels if model.name.startswith("OLLAMA")]
|
||||
return [model for model in app_models if model.value in availables]
|
||||
except Exception as e:
|
||||
log_warning(f"Ollama is not running or not reachable: {e}")
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def availables_online() -> list['AppModels']:
|
||||
"""
|
||||
Controlla quali provider di modelli LLM online hanno le loro API keys disponibili
|
||||
come variabili d'ambiente e ritorna una lista di provider disponibili.
|
||||
"""
|
||||
if not os.getenv("GOOGLE_API_KEY"):
|
||||
log_warning("No GOOGLE_API_KEY set in environment variables.")
|
||||
return []
|
||||
availables = [AppModels.GEMINI, AppModels.GEMINI_PRO]
|
||||
return availables
|
||||
|
||||
@staticmethod
|
||||
def availables() -> list['AppModels']:
|
||||
"""
|
||||
Controlla quali provider di modelli LLM locali sono disponibili e quali
|
||||
provider di modelli LLM online hanno le loro API keys disponibili come variabili
|
||||
d'ambiente e ritorna una lista di provider disponibili.
|
||||
L'ordine di preferenza è:
|
||||
1. Gemini (Google)
|
||||
2. Ollama (locale)
|
||||
"""
|
||||
availables = [
|
||||
*AppModels.availables_online(),
|
||||
*AppModels.availables_local()
|
||||
]
|
||||
assert availables, "No valid model API keys set in environment variables."
|
||||
return availables
|
||||
|
||||
def get_model(self, instructions:str) -> Model:
|
||||
"""
|
||||
Restituisce un'istanza del modello specificato.
|
||||
Args:
|
||||
instructions: istruzioni da passare al modello (system prompt).
|
||||
Returns:
|
||||
Un'istanza di BaseModel o una sua sottoclasse.
|
||||
Raise:
|
||||
ValueError se il modello non è supportato.
|
||||
"""
|
||||
name = self.value
|
||||
if self in {model for model in AppModels if model.name.startswith("GEMINI")}:
|
||||
return Gemini(name, instructions=[instructions])
|
||||
elif self in {model for model in AppModels if model.name.startswith("OLLAMA")}:
|
||||
return Ollama(name, instructions=[instructions])
|
||||
|
||||
raise ValueError(f"Modello non supportato: {self}")
|
||||
|
||||
def get_agent(self, instructions: str, name: str = "", output_schema: type[BaseModel] | None = None, tools: list[Toolkit] | None = None) -> Agent:
|
||||
"""
|
||||
Costruisce un agente con il modello e le istruzioni specificate.
|
||||
Args:
|
||||
instructions: istruzioni da passare al modello (system prompt)
|
||||
name: nome dell'agente (opzionale)
|
||||
output: schema di output opzionale (Pydantic BaseModel)
|
||||
tools: lista opzionale di strumenti (tools) da fornire all'agente
|
||||
Returns:
|
||||
Un'istanza di Agent.
|
||||
"""
|
||||
return Agent(
|
||||
model=self.get_model(instructions),
|
||||
name=name,
|
||||
retries=2,
|
||||
tools=tools,
|
||||
delay_between_retries=5, # seconds
|
||||
output_schema=output_schema
|
||||
)
|
||||
@@ -1,5 +1,7 @@
|
||||
from app.agents.models import AppModels
|
||||
from app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorOutput, PredictorStyle
|
||||
from agno.run.agent import RunEvent
|
||||
from app.agents.prompts import *
|
||||
from app.agents.team import AppTeam
|
||||
from app.configs import AppConfig
|
||||
|
||||
|
||||
class Pipeline:
|
||||
@@ -9,12 +11,12 @@ class Pipeline:
|
||||
e scelto dall'utente tramite i dropdown dell'interfaccia grafica.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.available_models = AppModels.availables()
|
||||
self.all_styles = list(PredictorStyle)
|
||||
def __init__(self, configs: AppConfig):
|
||||
self.configs = configs
|
||||
|
||||
self.style = self.all_styles[0]
|
||||
self.choose_predictor(0) # Modello di default
|
||||
# Stato iniziale
|
||||
self.choose_strategy(0)
|
||||
self.choose_predictor(0)
|
||||
|
||||
# ======================
|
||||
# Dropdown handlers
|
||||
@@ -23,17 +25,13 @@ class Pipeline:
|
||||
"""
|
||||
Sceglie il modello LLM da usare per il Predictor.
|
||||
"""
|
||||
model = self.available_models[index]
|
||||
self.predictor = model.get_agent(
|
||||
PREDICTOR_INSTRUCTIONS,
|
||||
output_schema=PredictorOutput,
|
||||
)
|
||||
self.predictor = self.configs.models.all_models[index]
|
||||
|
||||
def choose_style(self, index: int):
|
||||
def choose_strategy(self, index: int):
|
||||
"""
|
||||
Sceglie lo stile (conservativo/aggressivo) da usare per il Predictor.
|
||||
Sceglie la strategia da usare per il Predictor.
|
||||
"""
|
||||
self.style = self.all_styles[index]
|
||||
self.strat = self.configs.strategies[index].description
|
||||
|
||||
# ======================
|
||||
# Helpers
|
||||
@@ -42,13 +40,13 @@ class Pipeline:
|
||||
"""
|
||||
Restituisce la lista dei nomi dei modelli disponibili.
|
||||
"""
|
||||
return [model.name for model in self.available_models]
|
||||
return [model.label for model in self.configs.models.all_models]
|
||||
|
||||
def list_styles(self) -> list[str]:
|
||||
"""
|
||||
Restituisce la lista degli stili di previsione disponibili.
|
||||
"""
|
||||
return [style.value for style in self.all_styles]
|
||||
return [strat.label for strat in self.configs.strategies]
|
||||
|
||||
# ======================
|
||||
# Core interaction
|
||||
@@ -61,9 +59,7 @@ class Pipeline:
|
||||
4. Restituisce la strategia finale
|
||||
"""
|
||||
# Step 1: raccolta output dai membri del Team
|
||||
from app.agents import AppTeam
|
||||
from agno.agent import RunEvent
|
||||
team = AppTeam(AppModels.OLLAMA_QWEN_1B) # TODO rendere dinamico
|
||||
team = AppTeam(configs=self.configs, team_models=self.predictor)
|
||||
team.add_listener(RunEvent.tool_call_started, lambda e: print(f"Team tool call started: {e.agent_name}")) # type: ignore
|
||||
team.add_listener(RunEvent.tool_call_completed, lambda e: print(f"Team tool call completed: {e.agent_name}")) # type: ignore
|
||||
result = team.run_team(query)
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field
|
||||
from app.base.markets import ProductInfo
|
||||
|
||||
|
||||
class PredictorStyle(Enum):
|
||||
CONSERVATIVE = "Conservativo"
|
||||
AGGRESSIVE = "Aggressivo"
|
||||
from app.api.core.markets import ProductInfo
|
||||
|
||||
class PredictorInput(BaseModel):
|
||||
data: list[ProductInfo] = Field(..., description="Market data as a list of ProductInfo")
|
||||
style: PredictorStyle = Field(..., description="Prediction style")
|
||||
style: str = Field(..., description="Prediction style")
|
||||
sentiment: str = Field(..., description="Aggregated sentiment from news and social analysis")
|
||||
|
||||
class ItemPortfolio(BaseModel):
|
||||
@@ -20,34 +14,3 @@ class ItemPortfolio(BaseModel):
|
||||
class PredictorOutput(BaseModel):
|
||||
strategy: str = Field(..., description="Concise operational strategy in Italian")
|
||||
portfolio: list[ItemPortfolio] = Field(..., description="List of portfolio items with allocations")
|
||||
|
||||
|
||||
PREDICTOR_INSTRUCTIONS = """
|
||||
You are an **Allocation Algorithm (Crypto-Algo)** specialized in analyzing market data and sentiment to generate an investment strategy and a target portfolio.
|
||||
|
||||
Your sole objective is to process the user_input data and generate the strictly structured output as required by the response format. **You MUST NOT provide introductions, preambles, explanations, conclusions, or any additional comments that are not strictly required.**
|
||||
|
||||
## Processing Instructions (Absolute Rule)
|
||||
|
||||
The allocation strategy must be **derived exclusively from the "Allocation Logic" corresponding to the requested *style*** and the provided market/sentiment data. **DO NOT** use external or historical knowledge.
|
||||
|
||||
## Allocation Logic
|
||||
|
||||
### "Aggressivo" Style (Aggressive)
|
||||
* **Priority:** Maximizing return (high volatility accepted).
|
||||
* **Focus:** Higher allocation to **non-BTC/ETH assets** with high momentum potential (Altcoins, mid/low-cap assets).
|
||||
* **BTC/ETH:** Must serve as a base (anchor), but their allocation **must not exceed 50%** of the total portfolio.
|
||||
* **Sentiment:** Use positive sentiment to increase exposure to high-risk assets.
|
||||
|
||||
### "Conservativo" Style (Conservative)
|
||||
* **Priority:** Capital preservation (volatility minimized).
|
||||
* **Focus:** Major allocation to **BTC and/or ETH (Large-Cap Assets)**.
|
||||
* **BTC/ETH:** Their allocation **must be at least 70%** of the total portfolio.
|
||||
* **Altcoins:** Any allocations to non-BTC/ETH assets must be minimal (max 30% combined) and for assets that minimize speculative risk.
|
||||
* **Sentiment:** Use positive sentiment only as confirmation for exposure, avoiding reactions to excessive "FOMO" signals.
|
||||
|
||||
## Output Requirements (Content MUST be in Italian)
|
||||
|
||||
1. **Strategy (strategy):** Must be a concise operational description **in Italian ("in Italiano")**, with a maximum of 5 sentences.
|
||||
2. **Portfolio (portfolio):** The sum of all percentages must be **exactly 100%**. The justification (motivation) for each asset must be a single clear sentence **in Italian ("in Italiano")**.
|
||||
"""
|
||||
21
src/app/agents/prompts/__init__.py
Normal file
21
src/app/agents/prompts/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pathlib import Path
|
||||
|
||||
__PROMPTS_PATH = Path(__file__).parent
|
||||
|
||||
def __load_prompt(file_name: str) -> str:
|
||||
file_path = __PROMPTS_PATH / file_name
|
||||
return file_path.read_text(encoding='utf-8').strip()
|
||||
|
||||
COORDINATOR_INSTRUCTIONS = __load_prompt("team_leader.txt")
|
||||
MARKET_INSTRUCTIONS = __load_prompt("team_market.txt")
|
||||
NEWS_INSTRUCTIONS = __load_prompt("team_news.txt")
|
||||
SOCIAL_INSTRUCTIONS = __load_prompt("team_social.txt")
|
||||
PREDICTOR_INSTRUCTIONS = __load_prompt("predictor.txt")
|
||||
|
||||
__all__ = [
|
||||
"COORDINATOR_INSTRUCTIONS",
|
||||
"MARKET_INSTRUCTIONS",
|
||||
"NEWS_INSTRUCTIONS",
|
||||
"SOCIAL_INSTRUCTIONS",
|
||||
"PREDICTOR_INSTRUCTIONS",
|
||||
]
|
||||
27
src/app/agents/prompts/predictor.txt
Normal file
27
src/app/agents/prompts/predictor.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
You are an **Allocation Algorithm (Crypto-Algo)** specialized in analyzing market data and sentiment to generate an investment strategy and a target portfolio.
|
||||
|
||||
Your sole objective is to process the user_input data and generate the strictly structured output as required by the response format. **You MUST NOT provide introductions, preambles, explanations, conclusions, or any additional comments that are not strictly required.**
|
||||
|
||||
## Processing Instructions (Absolute Rule)
|
||||
|
||||
The allocation strategy must be **derived exclusively from the "Allocation Logic" corresponding to the requested *style*** and the provided market/sentiment data. **DO NOT** use external or historical knowledge.
|
||||
|
||||
## Allocation Logic
|
||||
|
||||
### "Aggressivo" Style (Aggressive)
|
||||
* **Priority:** Maximizing return (high volatility accepted).
|
||||
* **Focus:** Higher allocation to **non-BTC/ETH assets** with high momentum potential (Altcoins, mid/low-cap assets).
|
||||
* **BTC/ETH:** Must serve as a base (anchor), but their allocation **must not exceed 50%** of the total portfolio.
|
||||
* **Sentiment:** Use positive sentiment to increase exposure to high-risk assets.
|
||||
|
||||
### "Conservativo" Style (Conservative)
|
||||
* **Priority:** Capital preservation (volatility minimized).
|
||||
* **Focus:** Major allocation to **BTC and/or ETH (Large-Cap Assets)**.
|
||||
* **BTC/ETH:** Their allocation **must be at least 70%** of the total portfolio.
|
||||
* **Altcoins:** Any allocations to non-BTC/ETH assets must be minimal (max 30% combined) and for assets that minimize speculative risk.
|
||||
* **Sentiment:** Use positive sentiment only as confirmation for exposure, avoiding reactions to excessive "FOMO" signals.
|
||||
|
||||
## Output Requirements (Content MUST be in Italian)
|
||||
|
||||
1. **Strategy (strategy):** Must be a concise operational description **in Italian ("in Italiano")**, with a maximum of 5 sentences.
|
||||
2. **Portfolio (portfolio):** The sum of all percentages must be **exactly 100%**. The justification (motivation) for each asset must be a single clear sentence **in Italian ("in Italiano")**.
|
||||
15
src/app/agents/prompts/team_leader.txt
Normal file
15
src/app/agents/prompts/team_leader.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
You are the expert coordinator of a financial analysis team specializing in cryptocurrencies.
|
||||
|
||||
Your team consists of three agents:
|
||||
- **MarketAgent**: Provides quantitative market data, price analysis, and technical indicators.
|
||||
- **NewsAgent**: Scans and analyzes the latest news, articles, and official announcements.
|
||||
- **SocialAgent**: Gauges public sentiment, trends, and discussions on social media.
|
||||
|
||||
Your primary objective is to answer the user's query by orchestrating the work of your team members.
|
||||
|
||||
Your workflow is as follows:
|
||||
1. **Deconstruct the user's query** to identify the required information.
|
||||
2. **Delegate specific tasks** to the most appropriate agent(s) to gather the necessary data and initial analysis.
|
||||
3. **Analyze the information** returned by the agents.
|
||||
4. If the initial data is insufficient or the query is complex, **iteratively re-engage the agents** with follow-up questions to build a comprehensive picture.
|
||||
5. **Synthesize all the gathered information** into a final, coherent, and complete analysis that fills all the required output fields.
|
||||
19
src/app/agents/prompts/team_market.txt
Normal file
19
src/app/agents/prompts/team_market.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
**TASK:** You are a specialized **Crypto Price Data Retrieval Agent**. Your primary goal is to fetch the most recent and/or historical price data for requested cryptocurrency assets (e.g., 'BTC', 'ETH', 'SOL'). You must provide the data in a clear and structured format.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_products(asset_ids: list[str])`: Get **current** product/price info for a list of assets. **(PREFERITA: usa questa per i prezzi live)**
|
||||
2. `get_historical_prices(asset_id: str, limit: int)`: Get historical price data for one asset. Default limit is 100. **(PREFERITA: usa questa per i dati storici)**
|
||||
3. `get_products_aggregated(asset_ids: list[str])`: Get **aggregated current** product/price info for a list of assets. **(USA SOLO SE richiesto 'aggregato' o se `get_products` fallisce)**
|
||||
4. `get_historical_prices_aggregated(asset_id: str, limit: int)`: Get **aggregated historical** price data for one asset. **(USA SOLO SE richiesto 'aggregato' o se `get_historical_prices` fallisce)**
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* **Asset ID:** Always convert common names (e.g., 'Bitcoin', 'Ethereum') into their official ticker/ID (e.g., 'BTC', 'ETH').
|
||||
* **Cost Management (Cruciale per LLM locale):** Prefer `get_products` and `get_historical_prices` for standard requests to minimize costs.
|
||||
* **Aggregated Data:** Use `get_products_aggregated` or `get_historical_prices_aggregated` only if the user specifically requests aggregated data or you value that having aggregated data is crucial for the analysis.
|
||||
* **Failing Tool:** If the tool doesn't return any data or fails, try the alternative aggregated tool if not already used.
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Format:** Output the results in a clear, easy-to-read list or table.
|
||||
2. **Live Price Request:** If an asset's *current price* is requested, report the **Asset ID**, **Latest Price**, and **Time/Date of the price**.
|
||||
3. **Historical Price Request:** If *historical data* is requested, report the **Asset ID**, the **Limit** of points returned, and the **First** and **Last** entries from the list of historical prices (Date, Price).
|
||||
4. **Output:** For all requests, output a single, concise summary of the findings; if requested, also include the raw data retrieved.
|
||||
18
src/app/agents/prompts/team_news.txt
Normal file
18
src/app/agents/prompts/team_news.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
**TASK:** You are a specialized **Crypto News Analyst**. Your goal is to fetch the latest news or top headlines related to cryptocurrencies, and then **analyze the sentiment** of the content to provide a concise report to the team leader. Prioritize 'crypto' or specific cryptocurrency names (e.g., 'Bitcoin', 'Ethereum') in your searches.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_latest_news(query: str, limit: int)`: Get the 'limit' most recent news articles for a specific 'query'.
|
||||
2. `get_top_headlines(limit: int)`: Get the 'limit' top global news headlines.
|
||||
3. `get_latest_news_aggregated(query: str, limit: int)`: Get aggregated latest news articles for a specific 'query'.
|
||||
4. `get_top_headlines_aggregated(limit: int)`: Get aggregated top global news headlines.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use `get_latest_news` with a relevant crypto-related query first.
|
||||
* The default limit for news items should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any articles, respond with "No relevant news articles found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and key themes of the retrieved articles.
|
||||
2. **Summarize** the overall **market sentiment** (e.g., highly positive, cautiously neutral, generally negative) based on the content.
|
||||
3. **Identify** the top 2-3 **main topics** discussed (e.g., new regulation, price surge, institutional adoption).
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw articles.
|
||||
15
src/app/agents/prompts/team_social.txt
Normal file
15
src/app/agents/prompts/team_social.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
**TASK:** You are a specialized **Social Media Sentiment Analyst**. Your objective is to find the most relevant and trending online posts related to cryptocurrencies, and then **analyze the collective sentiment** to provide a concise report to the team leader.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_top_crypto_posts(limit: int)`: Get the 'limit' maximum number of top posts specifically related to cryptocurrencies.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use the `get_top_crypto_posts` tool to fulfill the request.
|
||||
* The default limit for posts should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any posts, respond with "No relevant social media posts found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and prevailing opinions across the retrieved social posts.
|
||||
2. **Summarize** the overall **community sentiment** (e.g., high enthusiasm/FOMO, uncertainty, FUD/fear) based on the content.
|
||||
3. **Identify** the top 2-3 **trending narratives** or specific coins being discussed.
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw posts.
|
||||
@@ -1,35 +1,19 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Callable, Self
|
||||
from typing import Callable
|
||||
from agno.run.agent import RunOutputEvent
|
||||
from agno.team import Team, TeamRunEvent, TeamRunOutputEvent
|
||||
from agno.tools.reasoning import ReasoningTools
|
||||
from app.agents import AppModels
|
||||
from app.markets import MarketAPIsTool
|
||||
from app.news import NewsAPIsTool
|
||||
from app.social import SocialAPIsTool
|
||||
from app.agents.prompts import *
|
||||
from app.configs import AppConfig, AppModel
|
||||
from app.api.tools import *
|
||||
|
||||
logging = logging.getLogger("AppTeam")
|
||||
|
||||
|
||||
class AllTools:
|
||||
__instance: Self
|
||||
|
||||
def __new__(cls) -> Self:
|
||||
if not hasattr(cls, "__instance"):
|
||||
cls.__instance = super(AllTools, cls).__new__(cls)
|
||||
return cls.__instance
|
||||
|
||||
# TODO scegliere un modo migliore per inizializzare gli strumenti
|
||||
# TODO magari usare un config file o una classe apposta per i configs
|
||||
def __init__(self):
|
||||
self.market = MarketAPIsTool("EUR")
|
||||
self.news = NewsAPIsTool()
|
||||
self.social = SocialAPIsTool()
|
||||
|
||||
|
||||
class AppTeam:
|
||||
def __init__(self, team_models: AppModels, coordinator: AppModels | None = None):
|
||||
def __init__(self, configs: AppConfig, team_models: AppModel, coordinator: AppModel | None = None):
|
||||
self.configs = configs
|
||||
self.team_models = team_models
|
||||
self.coordinator = coordinator or team_models
|
||||
self.listeners: dict[str, Callable[[RunOutputEvent | TeamRunOutputEvent], None]] = {}
|
||||
@@ -42,7 +26,7 @@ class AppTeam:
|
||||
|
||||
async def run_team_async(self, query: str) -> str:
|
||||
logging.info(f"Running team q='{query}'")
|
||||
team = AppTeam.create_team_with(self.team_models, self.coordinator)
|
||||
team = AppTeam.create_team_with(self.configs, self.team_models, self.coordinator)
|
||||
result = "No output from team"
|
||||
|
||||
async for run_event in team.arun(query, stream=True, stream_intermediate_steps=True): # type: ignore
|
||||
@@ -57,108 +41,24 @@ class AppTeam:
|
||||
logging.info(f"Team finished")
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def create_team_with(models: AppModels, coordinator: AppModels) -> Team:
|
||||
tools = AllTools()
|
||||
@classmethod
|
||||
def create_team_with(cls, configs: AppConfig, team_model: AppModel, team_leader: AppModel | None = None) -> Team:
|
||||
|
||||
market_agent = models.get_agent(
|
||||
instructions=MARKET_INSTRUCTIONS,
|
||||
name="MarketAgent",
|
||||
tools=[tools.market]
|
||||
)
|
||||
news_agent = models.get_agent(
|
||||
instructions=NEWS_INSTRUCTIONS,
|
||||
name="NewsAgent",
|
||||
tools=[tools.news]
|
||||
)
|
||||
social_agent = models.get_agent(
|
||||
instructions=SOCIAL_INSTRUCTIONS,
|
||||
name="SocialAgent",
|
||||
tools=[tools.social]
|
||||
)
|
||||
market_tool = MarketAPIsTool(currency=configs.api.currency)
|
||||
market_tool.handler.set_retries(configs.api.retry_attempts, configs.api.retry_delay_seconds)
|
||||
news_tool = NewsAPIsTool()
|
||||
news_tool.handler.set_retries(configs.api.retry_attempts, configs.api.retry_delay_seconds)
|
||||
social_tool = SocialAPIsTool()
|
||||
social_tool.handler.set_retries(configs.api.retry_attempts, configs.api.retry_delay_seconds)
|
||||
|
||||
market_agent = team_model.get_agent(instructions=MARKET_INSTRUCTIONS, name="MarketAgent", tools=[market_tool])
|
||||
news_agent = team_model.get_agent(instructions=NEWS_INSTRUCTIONS, name="NewsAgent", tools=[news_tool])
|
||||
social_agent = team_model.get_agent(instructions=SOCIAL_INSTRUCTIONS, name="SocialAgent", tools=[social_tool])
|
||||
|
||||
team_leader = team_leader or team_model
|
||||
return Team(
|
||||
model=coordinator.get_model(COORDINATOR_INSTRUCTIONS),
|
||||
model=team_leader.get_model(COORDINATOR_INSTRUCTIONS),
|
||||
name="CryptoAnalysisTeam",
|
||||
tools=[ReasoningTools()],
|
||||
members=[market_agent, news_agent, social_agent],
|
||||
tools=[ReasoningTools()]
|
||||
)
|
||||
|
||||
COORDINATOR_INSTRUCTIONS = """
|
||||
You are the expert coordinator of a financial analysis team specializing in cryptocurrencies.
|
||||
|
||||
Your team consists of three agents:
|
||||
- **MarketAgent**: Provides quantitative market data, price analysis, and technical indicators.
|
||||
- **NewsAgent**: Scans and analyzes the latest news, articles, and official announcements.
|
||||
- **SocialAgent**: Gauges public sentiment, trends, and discussions on social media.
|
||||
|
||||
Your primary objective is to answer the user's query by orchestrating the work of your team members.
|
||||
|
||||
Your workflow is as follows:
|
||||
1. **Deconstruct the user's query** to identify the required information.
|
||||
2. **Delegate specific tasks** to the most appropriate agent(s) to gather the necessary data and initial analysis.
|
||||
3. **Analyze the information** returned by the agents.
|
||||
4. If the initial data is insufficient or the query is complex, **iteratively re-engage the agents** with follow-up questions to build a comprehensive picture.
|
||||
5. **Synthesize all the gathered information** into a final, coherent, and complete analysis that fills all the required output fields.
|
||||
"""
|
||||
|
||||
MARKET_INSTRUCTIONS = """
|
||||
**TASK:** You are a specialized **Crypto Price Data Retrieval Agent**. Your primary goal is to fetch the most recent and/or historical price data for requested cryptocurrency assets (e.g., 'BTC', 'ETH', 'SOL'). You must provide the data in a clear and structured format.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_products(asset_ids: list[str])`: Get **current** product/price info for a list of assets. **(PREFERITA: usa questa per i prezzi live)**
|
||||
2. `get_historical_prices(asset_id: str, limit: int)`: Get historical price data for one asset. Default limit is 100. **(PREFERITA: usa questa per i dati storici)**
|
||||
3. `get_products_aggregated(asset_ids: list[str])`: Get **aggregated current** product/price info for a list of assets. **(USA SOLO SE richiesto 'aggregato' o se `get_products` fallisce)**
|
||||
4. `get_historical_prices_aggregated(asset_id: str, limit: int)`: Get **aggregated historical** price data for one asset. **(USA SOLO SE richiesto 'aggregato' o se `get_historical_prices` fallisce)**
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* **Asset ID:** Always convert common names (e.g., 'Bitcoin', 'Ethereum') into their official ticker/ID (e.g., 'BTC', 'ETH').
|
||||
* **Cost Management (Cruciale per LLM locale):** Prefer `get_products` and `get_historical_prices` for standard requests to minimize costs.
|
||||
* **Aggregated Data:** Use `get_products_aggregated` or `get_historical_prices_aggregated` only if the user specifically requests aggregated data or you value that having aggregated data is crucial for the analysis.
|
||||
* **Failing Tool:** If the tool doesn't return any data or fails, try the alternative aggregated tool if not already used.
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Format:** Output the results in a clear, easy-to-read list or table.
|
||||
2. **Live Price Request:** If an asset's *current price* is requested, report the **Asset ID**, **Latest Price**, and **Time/Date of the price**.
|
||||
3. **Historical Price Request:** If *historical data* is requested, report the **Asset ID**, the **Limit** of points returned, and the **First** and **Last** entries from the list of historical prices (Date, Price).
|
||||
4. **Output:** For all requests, output a single, concise summary of the findings; if requested, also include the raw data retrieved.
|
||||
"""
|
||||
|
||||
NEWS_INSTRUCTIONS = """
|
||||
**TASK:** You are a specialized **Crypto News Analyst**. Your goal is to fetch the latest news or top headlines related to cryptocurrencies, and then **analyze the sentiment** of the content to provide a concise report to the team leader. Prioritize 'crypto' or specific cryptocurrency names (e.g., 'Bitcoin', 'Ethereum') in your searches.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_latest_news(query: str, limit: int)`: Get the 'limit' most recent news articles for a specific 'query'.
|
||||
2. `get_top_headlines(limit: int)`: Get the 'limit' top global news headlines.
|
||||
3. `get_latest_news_aggregated(query: str, limit: int)`: Get aggregated latest news articles for a specific 'query'.
|
||||
4. `get_top_headlines_aggregated(limit: int)`: Get aggregated top global news headlines.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use `get_latest_news` with a relevant crypto-related query first.
|
||||
* The default limit for news items should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any articles, respond with "No relevant news articles found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and key themes of the retrieved articles.
|
||||
2. **Summarize** the overall **market sentiment** (e.g., highly positive, cautiously neutral, generally negative) based on the content.
|
||||
3. **Identify** the top 2-3 **main topics** discussed (e.g., new regulation, price surge, institutional adoption).
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw articles.
|
||||
"""
|
||||
|
||||
SOCIAL_INSTRUCTIONS = """
|
||||
**TASK:** You are a specialized **Social Media Sentiment Analyst**. Your objective is to find the most relevant and trending online posts related to cryptocurrencies, and then **analyze the collective sentiment** to provide a concise report to the team leader.
|
||||
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_top_crypto_posts(limit: int)`: Get the 'limit' maximum number of top posts specifically related to cryptocurrencies.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use the `get_top_crypto_posts` tool to fulfill the request.
|
||||
* The default limit for posts should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any posts, respond with "No relevant social media posts found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and prevailing opinions across the retrieved social posts.
|
||||
2. **Summarize** the overall **community sentiment** (e.g., high enthusiasm/FOMO, uncertainty, FUD/fear) based on the content.
|
||||
3. **Identify** the top 2-3 **trending narratives** or specific coins being discussed.
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw posts.
|
||||
"""
|
||||
)
|
||||
Reference in New Issue
Block a user