Team Workflow aggiornato #37

Merged
Berack96 merged 16 commits from 19-team-instructions into main 2025-10-20 22:05:58 +02:00
8 changed files with 155 additions and 149 deletions
Showing only changes of commit 0bc8e23303 - Show all commits

View File

@@ -4,28 +4,42 @@ import logging
import random import random
from typing import Any, Callable from typing import Any, Callable
from agno.agent import RunEvent from agno.agent import RunEvent
from agno.team import Team, TeamRunEvent from agno.team import Team
from agno.tools.reasoning import ReasoningTools from agno.tools.reasoning import ReasoningTools
from agno.run.workflow import WorkflowRunEvent from agno.run.workflow import WorkflowRunEvent
from agno.workflow.types import StepInput, StepOutput
from agno.workflow.step import Step from agno.workflow.step import Step
from agno.workflow.workflow import Workflow from agno.workflow.workflow import Workflow
from app.api.tools import * from app.api.tools import *
from app.agents.prompts import * from app.agents.prompts import *
from app.agents.core import PipelineInputs from app.agents.core import *
logging = logging.getLogger("pipeline") logging = logging.getLogger("pipeline")
class PipelineEvent(str, Enum): class PipelineEvent(str, Enum):
PLANNER = "Planner" QUERY_CHECK = "Query Check"
QUERY_ANALYZER = "Query Analyzer"
INFO_RECOVERY = "Info Recovery" INFO_RECOVERY = "Info Recovery"
REPORT_GENERATION = "Report Generation" REPORT_GENERATION = "Report Generation"
REPORT_TRANSLATION = "Report Translation" REPORT_TRANSLATION = "Report Translation"
RUN_FINISHED = WorkflowRunEvent.workflow_completed
TOOL_USED = RunEvent.tool_call_completed TOOL_USED = RunEvent.tool_call_completed
def check_event(self, event: str, step_name: str) -> bool: def check_event(self, event: str, step_name: str) -> bool:
return event == self.value or (WorkflowRunEvent.step_completed and step_name == self.value) return event == self.value or (WorkflowRunEvent.step_completed == event and step_name == self.value)
@classmethod
def get_log_events(cls, run_id: int) -> list[tuple['PipelineEvent', Callable[[Any], None]]]:
return [
(PipelineEvent.QUERY_CHECK, lambda _: logging.info(f"[{run_id}] Query Check completed.")),
(PipelineEvent.QUERY_ANALYZER, lambda _: logging.info(f"[{run_id}] Query Analyzer completed.")),
(PipelineEvent.INFO_RECOVERY, lambda _: logging.info(f"[{run_id}] Info Recovery completed.")),
(PipelineEvent.REPORT_GENERATION, lambda _: logging.info(f"[{run_id}] Report Generation completed.")),
(PipelineEvent.TOOL_USED, lambda e: logging.info(f"[{run_id}] Tool used: {getattr(e, 'tool_name', 'unknown')}")),
(PipelineEvent.RUN_FINISHED, lambda _: logging.info(f"[{run_id}] Run completed.")),
]
class Pipeline: class Pipeline:
@@ -38,10 +52,7 @@ class Pipeline:
def __init__(self, inputs: PipelineInputs): def __init__(self, inputs: PipelineInputs):
self.inputs = inputs self.inputs = inputs
# ====================== def interact(self, listeners: list[tuple[PipelineEvent, Callable[[Any], None]]] = []) -> str:
# Core interaction
# ======================
def interact(self, listeners: dict[RunEvent | TeamRunEvent, Callable[[PipelineEvent], None]] = {}) -> str:
""" """
Esegue la pipeline di agenti per rispondere alla query dell'utente. Esegue la pipeline di agenti per rispondere alla query dell'utente.
Args: Args:
@@ -51,7 +62,7 @@ class Pipeline:
""" """
return asyncio.run(self.interact_async(listeners)) return asyncio.run(self.interact_async(listeners))
async def interact_async(self, listeners: dict[RunEvent | TeamRunEvent, Callable[[PipelineEvent], None]] = {}) -> str: async def interact_async(self, listeners: list[tuple[PipelineEvent, Callable[[Any], None]]] = []) -> str:
""" """
Versione asincrona che esegue la pipeline di agenti per rispondere alla query dell'utente. Versione asincrona che esegue la pipeline di agenti per rispondere alla query dell'utente.
Args: Args:
@@ -62,12 +73,31 @@ class Pipeline:
run_id = random.randint(1000, 9999) # Per tracciare i log run_id = random.randint(1000, 9999) # Per tracciare i log
logging.info(f"[{run_id}] Pipeline query: {self.inputs.user_query}") logging.info(f"[{run_id}] Pipeline query: {self.inputs.user_query}")
events = [*PipelineEvent.get_log_events(run_id), *listeners]
query = QueryInputs(
user_query=self.inputs.user_query,
strategy=self.inputs.strategy.description
)
workflow = self.build_workflow()
result = await self.run(workflow, query, events=events)
return result
def build_workflow(self) -> Workflow:
"""
Costruisce il workflow della pipeline di agenti.
Returns:
L'istanza di Workflow costruita.
"""
# Step 1: Crea gli agenti e il team # Step 1: Crea gli agenti e il team
q_check_agent = self.inputs.query_analyzer_model.get_agent(instructions=QUERY_CHECK_INSTRUCTIONS, name="QueryCheckAgent", output_schema=QueryOutputs)
report_agent = self.inputs.report_generation_model.get_agent(instructions=REPORT_GENERATION_INSTRUCTIONS, name="ReportGeneratorAgent")
market_tool, news_tool, social_tool = self.get_tools() market_tool, news_tool, social_tool = self.get_tools()
market_agent = self.inputs.team_model.get_agent(instructions=MARKET_INSTRUCTIONS, name="MarketAgent", tools=[market_tool]) market_agent = self.inputs.team_model.get_agent(instructions=MARKET_INSTRUCTIONS, name="MarketAgent", tools=[market_tool])
news_agent = self.inputs.team_model.get_agent(instructions=NEWS_INSTRUCTIONS, name="NewsAgent", tools=[news_tool]) news_agent = self.inputs.team_model.get_agent(instructions=NEWS_INSTRUCTIONS, name="NewsAgent", tools=[news_tool])
social_agent = self.inputs.team_model.get_agent(instructions=SOCIAL_INSTRUCTIONS, name="SocialAgent", tools=[social_tool]) social_agent = self.inputs.team_model.get_agent(instructions=SOCIAL_INSTRUCTIONS, name="SocialAgent", tools=[social_tool])
team = Team( team = Team(
model=self.inputs.team_leader_model.get_model(COORDINATOR_INSTRUCTIONS), model=self.inputs.team_leader_model.get_model(COORDINATOR_INSTRUCTIONS),
name="CryptoAnalysisTeam", name="CryptoAnalysisTeam",
@@ -75,31 +105,24 @@ class Pipeline:
members=[market_agent, news_agent, social_agent], members=[market_agent, news_agent, social_agent],
) )
# Step 3: Crea il workflow # Step 2: Crea gli steps
#query_planner = Step(name=PipelineEvent.PLANNER, agent=Agent()) def condition_query_ok(step_input: StepInput) -> StepOutput:
val = step_input.previous_step_content
return StepOutput(stop=not val.is_ok) if isinstance(val, QueryOutputs) else StepOutput(stop=True)
query_check = Step(name=PipelineEvent.QUERY_CHECK, agent=q_check_agent)
info_recovery = Step(name=PipelineEvent.INFO_RECOVERY, team=team) info_recovery = Step(name=PipelineEvent.INFO_RECOVERY, team=team)
#report_generation = Step(name=PipelineEvent.REPORT_GENERATION, agent=Agent()) report_generation = Step(name=PipelineEvent.REPORT_GENERATION, agent=report_agent)
#report_translate = Step(name=AppEvent.REPORT_TRANSLATION, agent=Agent())
workflow = Workflow( # Step 3: Ritorna il workflow completo
name="App Workflow", return Workflow(name="App Workflow", steps=[
steps=[ query_check,
#query_planner, condition_query_ok,
info_recovery, info_recovery,
#report_generation, report_generation
#report_translate ])
]
)
# Step 4: Fai partire il workflow e prendi l'output
query = f"The user query is: {self.inputs.user_query}\n\n They requested a {self.inputs.strategy.label} investment strategy."
result = await self.run(workflow, query, events={})
logging.info(f"[{run_id}] Run finished")
return result
# ======================
# Helpers
# =====================
def get_tools(self) -> tuple[MarketAPIsTool, NewsAPIsTool, SocialAPIsTool]: def get_tools(self) -> tuple[MarketAPIsTool, NewsAPIsTool, SocialAPIsTool]:
""" """
Restituisce la lista di tools disponibili per gli agenti. Restituisce la lista di tools disponibili per gli agenti.
@@ -116,7 +139,7 @@ class Pipeline:
return (market_tool, news_tool, social_tool) return (market_tool, news_tool, social_tool)
@classmethod @classmethod
async def run(cls, workflow: Workflow, query: str, events: dict[PipelineEvent, Callable[[Any], None]]) -> str: async def run(cls, workflow: Workflow, query: QueryInputs, events: list[tuple[PipelineEvent, Callable[[Any], None]]]) -> str:
""" """
Esegue il workflow e gestisce gli eventi tramite le callback fornite. Esegue il workflow e gestisce gli eventi tramite le callback fornite.
Args: Args:
@@ -131,16 +154,18 @@ class Pipeline:
content = None content = None
async for event in iterator: async for event in iterator:
step_name = getattr(event, 'step_name', '') step_name = getattr(event, 'step_name', '')
for app_event, listener in events:
for app_event, listener in events.items():
if app_event.check_event(event.event, step_name): if app_event.check_event(event.event, step_name):
listener(event) listener(event)
if event.event == WorkflowRunEvent.step_completed:
if event.event == WorkflowRunEvent.workflow_completed:
content = getattr(event, 'content', '') content = getattr(event, 'content', '')
if isinstance(content, str):
if content and isinstance(content, str):
think_str = "</think>" think_str = "</think>"
think = content.rfind(think_str) think = content.rfind(think_str)
content = content[(think + len(think_str)):] if think != -1 else content return content[(think + len(think_str)):] if think != -1 else content
if content and isinstance(content, QueryOutputs):
return content.response
return content if content else "No output from workflow, something went wrong." logging.error(f"No output from workflow: {content}")
return "No output from workflow, something went wrong."

View File

@@ -10,7 +10,7 @@ COORDINATOR_INSTRUCTIONS = __load_prompt("team_leader.txt")
MARKET_INSTRUCTIONS = __load_prompt("team_market.txt") MARKET_INSTRUCTIONS = __load_prompt("team_market.txt")
NEWS_INSTRUCTIONS = __load_prompt("team_news.txt") NEWS_INSTRUCTIONS = __load_prompt("team_news.txt")
SOCIAL_INSTRUCTIONS = __load_prompt("team_social.txt") SOCIAL_INSTRUCTIONS = __load_prompt("team_social.txt")
QUERY_ANALYZER_INSTRUCTIONS = __load_prompt("query_analyzer.txt") QUERY_CHECK_INSTRUCTIONS = __load_prompt("query_check.txt")
REPORT_GENERATION_INSTRUCTIONS = __load_prompt("report_generation.txt") REPORT_GENERATION_INSTRUCTIONS = __load_prompt("report_generation.txt")
__all__ = [ __all__ = [
@@ -18,6 +18,6 @@ __all__ = [
"MARKET_INSTRUCTIONS", "MARKET_INSTRUCTIONS",
"NEWS_INSTRUCTIONS", "NEWS_INSTRUCTIONS",
"SOCIAL_INSTRUCTIONS", "SOCIAL_INSTRUCTIONS",
"QUERY_ANALYZER_INSTRUCTIONS", "QUERY_CHECK_INSTRUCTIONS",
"REPORT_GENERATION_INSTRUCTIONS", "REPORT_GENERATION_INSTRUCTIONS",
] ]

View File

@@ -1,27 +0,0 @@
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")**.

View File

@@ -1,40 +0,0 @@
0) Determine the language of the query:
- This will help you understand better the intention of the user
- Focus on the query of the user
1) Determine if the query is crypto or investment-related:
- Crypto-related if it mentions cryptocurrencies, tokens, NFTs, blockchain, exchanges, wallets, DeFi, oracles, smart contracts, on-chain, off-chain, staking, yield, liquidity, tokenomics, coins, ticker symbols, etc.
- Investment-related if it mentions stocks, bonds, options, trading strategies, financial markets, investment advice, portfolio management, etc.
- If the query uses generic terms like "news", "prices", "trends", "social", "market cap", "volume" with NO asset specified -> ASSUME CRYPTO/INVESTMENT CONTEXT and proceed.
- If the query is clearly about unrelated domains (weather, recipes, unrelated local politics, unrelated medicine, general software not about crypto, etc.) -> return NOT_CRYPTO error.
- If ambiguous: treat as crypto/investment only if the most likely intent is crypto/investment; otherwise return a JSON plan that first asks the user for clarification (see step structure below).
2) REQUIRED OUTPUT (only JSON; no extra content):
- If crypto/investment is ok, write the query steps.
- If not crypto/investment: is not ok and write why is not.
3) Constraints & clarifications for plan content:
- Each step must be concrete and actionable (examples: "fetch current price from CoinGecko API", "normalize tickers to uppercase and resolve aliases (e.g. XBT->BTC)", "compute 24h percent change").
- List explicitly which inputs are required from the user (e.g. asset ticker(s), timeframe, exchange, fiat currency).
- If the plan can proceed without additional inputs, steps should include default assumptions and list them under "assumptions".
- Always tell to use Tools if are availables.
- Use clear formats for data: tickers uppercase, timeframe examples "1h, 24h, 7d", fiat "USD, EUR".
- "estimated_time" should be a short human-readable estimate (e.g. "30s", "2m") or ISO 8601 duration (e.g. "PT30S").
- "confidence" must be a float between 0 and 1 (prefer two decimals), indicating how confidently the query is crypto/investment-related and the plan coverage.
4) Error rules:
- the message must be short and clear.
- Provide a brief reason in the message clarifying why it's not crypto/investment.
5) Ambiguity handling:
- If essential info is missing (e.g. which asset), include a first step that asks the user for it:
Step example: { "id":1, "description":"Request missing inputs from user", "inputs_needed":["asset_ticker"], "expected_output":"User provides ticker(s) or confirms defaults"}
- If user likely meant crypto/investment but unspecified assets (e.g. "prices and news"), explicitly state assumed default assets or that you will retrieve market-wide top assets.
6) Examples:
- Input: "Dammi prezzi e notizie" -> ASSUME crypto/investment -> plan that uses defaults or asks which assets.
- Input: "How do i cook 'carbonara'?" -> error "The query is not about CRYPTO or INVESTMENTS, it is about cooking".
- Input: "BTC and ETH prices" -> plan to fetch prices for BTC and ETH with Tools.
- Input: "What are good stocks to invest in?" -> plan to fetch stock data and provide investment analysis.
7) Output must be concise, only the JSON response described. No additional commentary.

View File

@@ -0,0 +1,17 @@
GOAL: check if the query is crypto-related
copilot-pull-request-reviewer[bot] commented 2025-10-20 21:52:00 +02:00 (Migrated from github.com)
Review

Corrected spelling of 'releated' to 'related'.

    - if is not crypto related, then output why is not related in a brief message
Corrected spelling of 'releated' to 'related'. ```suggestion - if is not crypto related, then output why is not related in a brief message ```
1) Determine the language of the query:
- This will help you understand better the intention of the user
- Focus on the query of the user
2) Determine if the query is crypto or investment-related:
- Crypto-related if it mentions cryptocurrencies, tokens, NFTs, blockchain, exchanges, wallets, DeFi, oracles, smart contracts, on-chain, off-chain, staking, yield, liquidity, tokenomics, coins, ticker symbols, etc.
- Investment-related if it mentions stocks, bonds, options, trading strategies, financial markets, investment advice, portfolio management, etc.
- If the query uses generic terms like "news", "prices", "trends", "social", "market cap", "volume" with NO asset specified -> ASSUME CRYPTO/INVESTMENT CONTEXT and proceed.
- If the query is clearly about unrelated domains (weather, recipes, unrelated local politics, unrelated medicine, general software not about crypto, etc.) -> return NOT_CRYPTO error.
- If ambiguous: treat as crypto/investment only if the most likely intent is crypto/investment; otherwise return a JSON plan that first asks the user for clarification (see step structure below).
3) Ouput the result:
- if is crypto related then output the query
- if is not crypto related, then output why is not releated in a brief message

View File

@@ -1,31 +1,52 @@
You are an analytical assistant. You will receive, as input, a block of data and optional accompanying notes or pre-existing analyses. Your task is to produce a single, well-structured report in Markdown only. Do NOT output anything outside the Markdown report. You are a Reporting Assistant. Your task is to receive a block of input (which may include raw data, notes, or pre-existing analyses from other agents) and synthesize it into a single, cohesive, and well-structured report in Markdown.
Rules and structure: Your primary function is to **report and summarize**, not to perform novel or complex analysis.
1. Understand the input:
- Parse raw data, metadata (dates, units, sources), and any pre-existing analyses or notes.
- If pre-existing analyses are included, incorporate them as evidence and explain how they influence conclusions. If they conflict with raw data, explain the conflict and state assumptions used to resolve it.
- Do not invent facts. If key data is missing, state what is missing and any assumption(s) you make for the analysis.
2. Report format (required headings and order): **Core Rules**
- Title: concise and descriptive.
- Executive summary: 35 bullet points summarizing the main conclusions and recommended actions.
- Key findings: numbered list of the most important observations, each with supporting evidence (numbers, percent changes, references to input fields/rows).
- Data & Methods: brief description of the dataset (period, scope, sample size), preprocessing steps you inferred or performed, and analytical methods used.
- Use of pre-existing analyses: list which provided analyses you used and how they affected the report.
- Detailed analysis: subsections for major topics, with tables or code blocks for key calculations, and clear references to the source lines/fields in the input.
- Recommendations: prioritized, actionable recommendations tied to specific findings, with estimated impact where possible.
- Limitations & uncertainties: concise list of what reduces confidence (data quality, missing fields, assumptions).
- Appendix (if applicable): full summary tables, formulas, and any raw data snippets necessary to reproduce calculations.
3. Presentation rules: 1. **Synthesize, Don't Invent:**
- Output must be valid Markdown. Use headings (##, ###), bullet lists, numbered lists, and Markdown tables as appropriate. - If the input includes **pre-existing analyses**, your report MUST prioritize summarizing these findings.
- Include inline numeric evidence (values and percent changes). When showing calculations, use fenced code blocks with brief comments. - If the input is **only raw data**, your "analysis" is limited to basic summarization (e.g., calculating totals, averages, identifying key figures, or spotting obvious trends) necessary to write the report.
- Keep language concise, formal, and non-speculative. Avoid jargon; explain technical terms when used. - **Do NOT** invent new hypotheses, metrics, or conclusions that are not directly supported by the input.
- If any metrics require interpretation (e.g., growth rate), show the formula and the computed result. - **Do NOT** print the agents or tools used to get the datas.
- If you need clarification to improve the report, list the specific missing items at the end under a "Clarifying questions" heading.
4. Output constraints: 2. **Adaptive Report Structure (Crucial):**
- Return only the Markdown report (no extra commentary, no metadata). - You MUST adjust the report's length and complexity based on the input.
- Prefer concise, stakeholder-oriented writing; keep the executive summary and recommendations prominent. - **Simple Input** (e.g., one small data table, a few notes) requires a **Brief Summary**.
- **Complex Input** (e.g., multiple datasets, detailed pre-existing analyses) requires a **Full Report**.
Start the report now once you receive the input data. 3. **No Extraneous Output:**
- Your entire response must be **only the Markdown report**.
- Do not include any pre-amble, post-amble, or commentary outside the report structure.
---
**Report Formats:**
**1. For Simple Input (Brief Summary)**
Use a concise format, such as:
- **Title:** Concise and descriptive title.
- **Key Conclusion:** [A 1-2 sentence summary of the main point from the data or analysis.]
- **Summary Points:** (if needed)
- [Bullet point with key metric or finding.]
- [Bullet point with key metric or finding.]
- **Source:** [Brief note on what data was provided, e.g., "Based on Q3 sales data table."]
**2. For Complex Input (Full Report)**
Use the following formal structure:
- **Title:** Concise and descriptive title.
- **Executive Summary:** 2-4 bullet points summarizing the most critical conclusions *found in the provided input*.
- **Summary of Findings:**
* A numbered list of the most important observations.
* Each point must be supported by evidence (numbers, percent changes) from the input.
* Clearly state if a finding is from a **pre-existing analysis** (e.g., "The 'Q1-Review' analysis states...") or a **direct data summary** (e.g., "Raw data shows a total of...").
- **Detailed Breakdown:**
* (Optional, use if needed for clarity) Subsections for major topics.
* Use Markdown tables or lists to present synthesized data.
- **Input Overview & Limitations:**
* Briefly describe the data provided (period, scope).
* List any limitations, conflicts, or assumptions *stated in the input* or necessary to create the report (e.g., "Note: Input 'Analysis A' and 'Analysis B' provided conflicting figures for user growth. This report uses the figures from 'Analysis B' as it was marked 'Final'").

View File

@@ -1,15 +1,25 @@
You are the expert coordinator of a financial analysis team specializing in cryptocurrencies. You are the expert coordinator of a financial analysis team specializing in cryptocurrencies.
Input: You will user query. Your role is to create and execute a plan by coordinating the team agents and utilizing their available tools, to validate and complete the plan where needed, and to deliver the final analysis exactly as specified by the plan.
Your team consists of three agents: Your team consists of three agents:
- **MarketAgent**: Provides quantitative market data, price analysis, and technical indicators. - MarketAgent: Provides quantitative market data, price analysis, and technical indicators.
- **NewsAgent**: Scans and analyzes the latest news, articles, and official announcements. - NewsAgent: Scans and analyzes the latest news, articles, and official announcements.
- **SocialAgent**: Gauges public sentiment, trends, and discussions on social media. - 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. Each agent has access to specific tools relevant to their function. You should leverage these tools as defined in the resolution plan.
Your workflow is as follows: Primary objective: Execute the received query by delegating tasks to agents, validating their outputs, iterating as needed, and synthesizing agent outputs until the plan's required outputs are met. Use the agents' tools to accomplish the plan.
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. Workflow:
3. **Analyze the information** returned by the agents. 1. Analize the query and create a task list to follow. Immediately validate the plan for completeness, feasibility, required output fields, data/time constraints, and explicit task assignments, considering the available tools of each agent.
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. 2. If the plan is incomplete, ambiguous, or infeasible, automatically fill gaps with reasonable assumptions but always record and report those assumptions. Ensure that the plan utilizes the appropriate tools.
5. **Synthesize all the gathered information** into a final, coherent, and complete analysis that fills all the required output fields. 3. Decompose the plan into concrete tasks and map each task to the most appropriate agent(s) and their tools. For every delegated task specify: the exact deliverable, required format (e.g., JSON with keys: agent, task, result, confidence, sources), time horizon, and acceptance criteria tied to the plan's outputs. Specify which tool should be used.
4. Dispatch tasks to agents, collect structured results, and validate each result against the task's acceptance criteria and the original plan requirements. Ensure the agents have used the correct tools.
5. Analyze aggregated agent outputs to determine whether the plan's outputs are satisfied. Identify inconsistencies, missing data, or low-confidence results.
6. Iteratively re-engage the relevant agents with specific follow-up tasks or requests for deeper analysis until all acceptance criteria are met or until a justified plan modification is agreed. Ensure that the correct tools are being utilized.
7. Synthesize a final, coherent analysis that fulfils all output fields specified by the plan. Include: a concise final answer to the user's query, the mapping of plan tasks to agent contributions (including tools used), any deviations or assumptions made, confidence levels, sources for data/claims, and the original query.
Behavioral rules:
- Always produce and expect structured agent responses so results can be validated and traced back to tasks and the tools used.
- Prioritize clarity and reproducibility: every output should show which agent produced which piece of information, which tool was used, and how it maps to the plan.

View File

@@ -97,12 +97,12 @@ class WrapperHandler(Generic[WrapperType]):
wrapper_name = wrapper.__class__.__name__ wrapper_name = wrapper.__class__.__name__
if not try_all: if not try_all:
logging.info(f"try_call {wrapper_name}") logging.debug(f"try_call {wrapper_name}")
for try_count in range(1, self.retry_per_wrapper + 1): for try_count in range(1, self.retry_per_wrapper + 1):
try: try:
result = func(wrapper) result = func(wrapper)
logging.info(f"{wrapper_name} succeeded") logging.debug(f"{wrapper_name} succeeded")
results[wrapper_name] = result results[wrapper_name] = result
break break