Aggiunta cartella per i modelli, agenti e team
This commit is contained in:
111
src/app/agents/__init__.py
Normal file
111
src/app/agents/__init__.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import os
|
||||
import requests
|
||||
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
|
||||
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.
|
||||
"""
|
||||
ollama_host = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
||||
result = requests.get(f"{ollama_host}/api/tags")
|
||||
if result.status_code != 200:
|
||||
log_warning(f"Ollama is not running or not reachable {result}")
|
||||
return []
|
||||
|
||||
availables = []
|
||||
result = result.text
|
||||
for model in [model for model in AppModels if model.name.startswith("OLLAMA")]:
|
||||
if model.value in result:
|
||||
availables.append(model)
|
||||
return availables
|
||||
|
||||
@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: BaseModel | None = None, tools: list[Toolkit] = []) -> 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)
|
||||
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 # se si usa uno schema di output, lo si passa qui
|
||||
# TODO Eventuali altri parametri da mettere all'agente anche se si possono comunque assegnare dopo la creazione
|
||||
)
|
||||
52
src/app/agents/predictor.py
Normal file
52
src/app/agents/predictor.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field
|
||||
from app.markets.base import ProductInfo
|
||||
|
||||
|
||||
class PredictorStyle(Enum):
|
||||
CONSERVATIVE = "Conservativo"
|
||||
AGGRESSIVE = "Aggressivo"
|
||||
|
||||
class PredictorInput(BaseModel):
|
||||
data: list[ProductInfo] = Field(..., description="Market data as a list of ProductInfo")
|
||||
style: PredictorStyle = Field(..., description="Prediction style")
|
||||
sentiment: str = Field(..., description="Aggregated sentiment from news and social analysis")
|
||||
|
||||
class ItemPortfolio(BaseModel):
|
||||
asset: str = Field(..., description="Name of the asset")
|
||||
percentage: float = Field(..., description="Percentage allocation to the asset")
|
||||
motivation: str = Field(..., description="Motivation for the allocation")
|
||||
|
||||
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")**.
|
||||
"""
|
||||
35
src/app/agents/team.py
Normal file
35
src/app/agents/team.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from agno.team import Team
|
||||
from app.agents import AppModels
|
||||
from app.markets import MARKET_INSTRUCTIONS, MarketAPIsTool
|
||||
from app.news import NEWS_INSTRUCTIONS, NewsAPIsTool
|
||||
from app.social import SOCIAL_INSTRUCTIONS, SocialAPIsTool
|
||||
|
||||
|
||||
def create_team_with(models: AppModels, coordinator: AppModels | None = None) -> Team:
|
||||
market_agent = models.get_agent(
|
||||
instructions=MARKET_INSTRUCTIONS,
|
||||
name="MarketAgent",
|
||||
tools=[MarketAPIsTool()]
|
||||
)
|
||||
news_agent = models.get_agent(
|
||||
instructions=NEWS_INSTRUCTIONS,
|
||||
name="NewsAgent",
|
||||
tools=[NewsAPIsTool()]
|
||||
)
|
||||
social_agent = models.get_agent(
|
||||
instructions=SOCIAL_INSTRUCTIONS,
|
||||
name="SocialAgent",
|
||||
tools=[SocialAPIsTool()]
|
||||
)
|
||||
|
||||
coordinator = coordinator or models
|
||||
return Team(
|
||||
model=coordinator.get_model(COORDINATOR_INSTRUCTIONS),
|
||||
name="CryptoAnalysisTeam",
|
||||
members=[market_agent, news_agent, social_agent],
|
||||
)
|
||||
|
||||
# TODO: migliorare le istruzioni del team
|
||||
COORDINATOR_INSTRUCTIONS = """
|
||||
Agisci come coordinatore: smista le richieste tra MarketAgent, NewsAgent e SocialAgent.
|
||||
"""
|
||||
Reference in New Issue
Block a user