Added Prompt for tools (#68)

* Create detailed markdown instructions for all toolkits
* Update all toolkit classes to load instructions from external .md files
* Add query examples for cryptocurrency report generation
This commit was merged in pull request #68.
This commit is contained in:
Simo
2025-10-31 00:13:02 +01:00
committed by GitHub
parent df14ae5bc6
commit c501a58bf4
17 changed files with 1115 additions and 59 deletions

View File

@@ -0,0 +1,21 @@
from pathlib import Path
__INSTRUCTIONS_PATH = Path(__file__).parent
def __load_tool_instruction(file_name: str) -> str:
file_path = __INSTRUCTIONS_PATH / file_name
return file_path.read_text(encoding='utf-8').strip()
MARKET_TOOL_INSTRUCTIONS = __load_tool_instruction("market_instructions.md")
NEWS_TOOL_INSTRUCTIONS = __load_tool_instruction("news_instructions.md")
SOCIAL_TOOL_INSTRUCTIONS = __load_tool_instruction("social_instructions.md")
PLAN_MEMORY_TOOL_INSTRUCTIONS = __load_tool_instruction("plan_memory_instructions.md")
SYMBOLS_TOOL_INSTRUCTIONS = __load_tool_instruction("symbols_instructions.md")
__all__ = [
"MARKET_TOOL_INSTRUCTIONS",
"NEWS_TOOL_INSTRUCTIONS",
"SOCIAL_TOOL_INSTRUCTIONS",
"PLAN_MEMORY_TOOL_INSTRUCTIONS",
"SYMBOLS_TOOL_INSTRUCTIONS",
]

View File

@@ -0,0 +1,26 @@
# Market APIs - Instructions
## Tools (6)
**Single-source (fast):** First available provider
1. `get_product(asset_id)` - Current price, 1 asset
2. `get_products(asset_ids)` - Current prices, multiple assets
3. `get_historical_prices(asset_id, limit=100)` - Price history, 1 asset
**Aggregated (accurate):** All providers, VWAP calculation
4. `get_product_aggregated(asset_id)` - Accurate price, 1 asset (4x API calls)
5. `get_products_aggregated(asset_ids)` - Accurate prices, multiple (4x per asset)
6. `get_historical_prices_aggregated(asset_id, limit=100)` - Historical, all sources (4x calls)
## Selection Strategy
- Quick check → single-source (tools 1-3)
- Keywords "accurate", "reliable", "comprehensive" → aggregated (tools 4-6)
## Key Mappings
**Assets:** Bitcoin→BTC, Ethereum→ETH, Solana→SOL, Cardano→ADA, Ripple→XRP, Polkadot→DOT, Dogecoin→DOGE
**Time:** "7 days"→limit=7, "30 days"→limit=30, "24h"→limit=24, "3 months"→limit=90
## Critical Rules
- Never fabricate data - only report actual tool outputs
- Include: ticker, price+currency, timestamp, provider source
- Failure handling: Report explicit error, no placeholder data
- Be concise to save tokens

View File

@@ -0,0 +1,32 @@
# News APIs - Instructions
## Tools (4)
**Single-source (fast):** First available provider
1. `get_top_headlines(limit=100)` - Top crypto headlines
2. `get_latest_news(query, limit=100)` - Search specific topic
**Aggregated (comprehensive):** All providers (4x API calls)
3. `get_top_headlines_aggregated(limit=100)` - Headlines from all sources
4. `get_latest_news_aggregated(query, limit=100)` - Topic search, all sources
## Selection Strategy
- Quick overview → single-source (tools 1-2)
- Keywords "comprehensive", "all sources", "complete" → aggregated (tools 3-4)
## Query Formulation
- "Bitcoin regulation" → query="Bitcoin regulation"
- "ETH price surge" → query="Ethereum price increase"
- Use full crypto names (Bitcoin not BTC), specific keywords for focus
## Article Structure
Contains: title, source, url, published_at, description (optional), author (optional)
## Limits
- Quick: 5-10 | Standard: 20-30 | Deep: 50-100
## Critical Rules
- Never fabricate articles - only report actual tool outputs
- Always include: title, source, URL, publication date
- Failure handling: Report explicit error, suggest broader terms
- Deduplicate same stories across sources
- Be concise to save tokens

View File

@@ -0,0 +1,64 @@
# Plan Memory Tool - Instructions
## Purpose
Stateful task management for Team Leader: create, track, and record execution plans with persistent state.
## Tools (4)
### 1. `add_tasks(task_names: list[str])` → str
Adds tasks with 'pending' status. Prevents duplicates. Returns: "Added N new tasks."
**Best Practices:**
- Clear, descriptive names (e.g., "Fetch BTC price for last 7 days" not "Get data")
- Order logically (dependencies first)
- Include specific details in names
### 2. `get_next_pending_task()` → Task | None
Returns FIRST pending task (FIFO order) or None if no pending tasks.
**Task Object:** `{name: str, status: "pending", result: None}`
### 3. `update_task_status(task_name, status, result)` → str
Updates task after execution. Status: "completed" or "failed". Result: optional outcome/error.
**Example:**
```python
update_task_status("Fetch BTC price", "completed", "BTC=$67,543 at 14:23:00")
update_task_status("Analyze sentiment", "failed", "API rate limit exceeded")
```
**Best Practices:**
- Update immediately after execution
- Include key data in result (prices, counts, timestamps)
- For failures, include error details
### 4. `list_all_tasks()` → list[str]
Lists all tasks with status and results. Format: "- {name}: {status} (Result: {result})"
## Workflow Pattern
```python
add_tasks(["Task A", "Task B", "Task C"])
while task := get_next_pending_task():
result = execute(task['name'])
update_task_status(task['name'], "completed", result)
all_tasks = list_all_tasks()
```
## Critical Rules
1. Task names must be unique (exact match for updates)
2. Always update status after execution
3. Execute sequentially using get_next_pending_task()
4. Store meaningful results, not just "Done"
5. Handle failures: update status="failed" and continue
6. Review with list_all_tasks() before finishing
## Good vs Poor Examples
**Good Task Names:** "Fetch BTC price from Binance for 7 days" | "Analyze Ethereum news sentiment"
**Poor Task Names:** "Get data" | "Step 1" | "Do analysis"
**Good Results:** "BTC: $67,543 (Binance, 2025-10-30 14:23)" | "15 articles, Bullish sentiment"
**Poor Results:** "Done" | "Success" | "OK"
## State Persistence
- Persists within single session only (not across restarts)
- Call list_all_tasks() periodically to preserve context

View File

@@ -0,0 +1,46 @@
# Social Media APIs - Instructions
## Tools (2)
**Single-source (fast):** First available platform
1. `get_top_crypto_posts(limit=5)` - Top crypto posts, first platform
**Aggregated (comprehensive):** All platforms (3x API calls: Reddit, X, 4chan)
2. `get_top_crypto_posts_aggregated(limit_per_wrapper=5)` - Posts from all platforms
## Selection Strategy
- Quick snapshot → single-source (tool 1)
- Keywords "all platforms", "comprehensive", "compare" → aggregated (tool 2)
## Post Structure
Contains: content, author, platform, url, created_at, score/upvotes, comments_count, subreddit/board
## Limits (posts are verbose)
- Quick: 5 (default) | Standard: 10-15 | Deep: 20-30 | Max: 50
## Platform Notes
- **Reddit:** r/cryptocurrency, r/bitcoin, r/ethereum (upvotes metric)
- **X (Twitter):** High engagement crypto tweets (likes metric)
- **4chan:** /biz/ board (replies metric, may contain inappropriate language)
## Critical Rules
- Never fabricate posts - only report actual tool outputs
- Include: platform, author, URL, engagement metrics, timestamp
- Truncate content to max 280 chars
- Summarize sentiment trends, don't list all posts verbatim
- Frame as opinions, not facts - add disclaimers for unverified info
- Be VERY concise to save tokens
## Sentiment Analysis
- Identify recurring topics, positive/negative patterns, trending coins
- Compare sentiment across platforms, highlight high engagement
- Flag potential FUD or shilling
- Do not treat social media posts as factual evidence
- Encourage users to verify information from official sources
BEST PRACTICES:
- Use aggregated tool for sentiment comparison across platforms
- Combine with news data for context
- Focus on high-engagement posts for quality
- Summarize trends rather than listing every post
- Be selective - quality over quantity
- Respect character limits to avoid token overflow

View File

@@ -0,0 +1,97 @@
# Crypto Symbols Tool - Instructions
## Purpose
Cryptocurrency symbol lookup and name-based search using cached Yahoo Finance database.
## Tools (2)
### 1. `get_all_symbols()` → list[str]
Returns all available cryptocurrency symbols from cache. No API calls, instant response.
**Returns:** List like `["BTC-USD", "ETH-USD", "SOL-USD", ...]` (~1,500+ symbols)
**Use Cases:**
- Verify symbol availability before API call
- List all supported cryptocurrencies
- Validate user input against known symbols
### 2. `get_symbols_by_name(query: str)` → list[tuple[str, str]]
Searches cryptocurrency names (case-insensitive, substring match). Returns list of (symbol, name) tuples.
**Examples:**
```python
get_symbols_by_name("bitcoin") # [("BTC-USD", "Bitcoin USD"), ("BCH-USD", "Bitcoin Cash USD"), ...]
get_symbols_by_name("ethereum") # [("ETH-USD", "Ethereum USD"), ("ETC-USD", "Ethereum Classic USD")]
get_symbols_by_name("doge") # [("DOGE-USD", "Dogecoin USD")]
```
**Use Cases:**
- Convert user-friendly names to symbols
- Handle ambiguous input (multiple matches)
- Discover cryptocurrencies by partial name
## Workflow Patterns
### Pattern 1: Symbol Validation
```python
matches = get_symbols_by_name(user_query)
if not matches:
return "Cryptocurrency not found"
elif len(matches) == 1:
symbol, name = matches[0]
# Use with market API
else:
# Multiple matches - ask user to clarify
return f"Multiple matches: {[name for _, name in matches]}"
```
### Pattern 2: Batch Resolution
```python
names = ["Bitcoin", "Ethereum", "UnknownCoin"]
resolved = []
failed = []
for name in names:
matches = get_symbols_by_name(name.lower())
if matches:
resolved.append(matches[0][0])
else:
failed.append(name)
# Use resolved with market_tool.get_products(resolved)
```
## Integration with Market Tools
1. User provides name (e.g., "Bitcoin")
2. Search: `get_symbols_by_name("bitcoin")``("BTC-USD", "Bitcoin USD")`
3. Fetch price: `market_tool.get_product("BTC-USD")`
4. Return result
## Symbol Format
- Yahoo Finance format: `BTC-USD`, `ETH-USD` (includes `-USD` suffix)
- Some APIs need base only: strip suffix with `symbol.split('-')[0]``"BTC"`
## Common Mappings
Bitcoin→BTC-USD | Ethereum→ETH-USD | Solana→SOL-USD | Cardano→ADA-USD | Dogecoin→DOGE-USD
## Critical Rules
1. Always search before using names - never assume direct conversion
2. Handle multiple matches (e.g., "Bitcoin" matches BTC and BCH)
3. Case-insensitive: always use `.lower()` for queries
4. Check empty results before accessing
5. Remember `-USD` suffix in Yahoo symbols
## Search Best Practices
- ✅ Full names: "ethereum", "bitcoin", "solana"
- ✅ Partial OK: "doge" finds "Dogecoin"
- ❌ Avoid: ticker symbols ("BTC"), too generic ("coin")
## Cache Notes
- Cache file: `resources/cryptos.csv` (~1,500+ symbols)
- No API calls during queries (instant response)
- Loaded automatically on initialization
- Static snapshot, not real-time
## Error Handling
- Empty cache → Ensure `resources/cryptos.csv` exists
- No results → Try broader terms, check spelling
- Multiple matches → Show all, ask user to clarify
- Symbol format mismatch → Strip `-USD` suffix if needed

View File

@@ -1,4 +1,5 @@
from agno.tools import Toolkit
from app.api.tools.instructions import MARKET_TOOL_INSTRUCTIONS
from app.api.wrapper_handler import WrapperHandler
from app.api.core.markets import MarketWrapper, Price, ProductInfo
from app.api.markets import BinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper, YFinanceWrapper
@@ -29,6 +30,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit):
Toolkit.__init__( # type: ignore
self,
name="Market APIs Toolkit",
instructions=MARKET_TOOL_INSTRUCTIONS,
tools=[
self.get_product,
self.get_products,

View File

@@ -1,4 +1,5 @@
from agno.tools import Toolkit
from app.api.tools.instructions import NEWS_TOOL_INSTRUCTIONS
from app.api.wrapper_handler import WrapperHandler
from app.api.core.news import NewsWrapper, Article
from app.api.news import NewsApiWrapper, GoogleNewsWrapper, CryptoPanicWrapper, DuckDuckGoWrapper
@@ -32,6 +33,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit):
Toolkit.__init__( # type: ignore
self,
name="News APIs Toolkit",
instructions=NEWS_TOOL_INSTRUCTIONS,
tools=[
self.get_top_headlines,
self.get_latest_news,

View File

@@ -0,0 +1,94 @@
from agno.tools.toolkit import Toolkit
from typing import TypedDict, Literal
from app.api.tools.instructions import PLAN_MEMORY_TOOL_INSTRUCTIONS
class Task(TypedDict):
name: str
status: Literal["pending", "completed", "failed"]
result: str | None
class PlanMemoryTool(Toolkit):
def __init__(self):
self.tasks: list[Task] = []
Toolkit.__init__(self, # type: ignore[call-arg]
name="Plan Memory Toolkit",
instructions=PLAN_MEMORY_TOOL_INSTRUCTIONS,
tools=[
self.add_tasks,
self.get_next_pending_task,
self.update_task_status,
self.list_all_tasks,
]
)
def add_tasks(self, task_names: list[str]) -> str:
"""
Adds one or more new tasks to the execution plan with a 'pending' status.
If a task with the same name already exists, it will not be added again.
Args:
task_names (list[str]): A list of descriptive names for the tasks to be added.
Returns:
str: A confirmation message, e.g., "Added 3 new tasks."
"""
count = 0
for name in task_names:
if not any(t['name'] == name for t in self.tasks):
self.tasks.append({"name": name, "status": "pending", "result": None})
count += 1
return f"Added {count} new tasks."
def get_next_pending_task(self) -> Task | None:
"""
Retrieves the *first* task from the plan that is currently in 'pending' status.
This is used to fetch the next step in the execution plan.
Returns:
Task | None: A Task object (dict) with 'name', 'status', and 'result' keys,
or None if no tasks are pending.
"""
for task in self.tasks:
if task["status"] == "pending":
return task
return None
def update_task_status(self, task_name: str, status: Literal["completed", "failed"], result: str | None = None) -> str:
"""
Updates the status and result of a specific task, identified by its unique name.
This is crucial for tracking the plan's progress after a step is executed.
Args:
task_name (str): The exact name of the task to update (must match one from add_tasks).
status (Literal["completed", "failed"]): The new status for the task.
result (str | None, optional): An optional string describing the outcome or result
of the task (e.g., a summary, an error message).
Returns:
str: A confirmation message (e.g., "Task 'Task Name' updated to completed.")
or an error message if the task is not found.
"""
for task in self.tasks:
if task["name"] == task_name:
task["status"] = status
if result is not None:
task["result"] = result
return f"Task '{task_name}' updated to {status}."
return f"Error: Task '{task_name}' not found."
def list_all_tasks(self) -> list[str]:
"""
Lists all tasks currently in the execution plan, along with their status and result.
Useful for reviewing the overall plan and progress.
Returns:
list[str]: A list of formatted strings, where each string describes a task
(e.g., "- TaskName: completed (Result: Done.)").
"""
if not self.tasks:
return ["No tasks in the plan."]
return [f"- {t['name']}: {t['status']} (Result: {t.get('result', 'N/A')})" for t in self.tasks]

View File

@@ -1,4 +1,5 @@
from agno.tools import Toolkit
from app.api.tools.instructions import SOCIAL_TOOL_INSTRUCTIONS
from app.api.wrapper_handler import WrapperHandler
from app.api.core.social import SocialPost, SocialWrapper
from app.api.social import *
@@ -32,7 +33,8 @@ class SocialAPIsTool(SocialWrapper, Toolkit):
Toolkit.__init__( # type: ignore
self,
name="Socials Toolkit",
name="Socials APIs Toolkit",
instructions=SOCIAL_TOOL_INSTRUCTIONS,
tools=[
self.get_top_crypto_posts,
self.get_top_crypto_posts_aggregated,

View File

@@ -5,6 +5,7 @@ import logging
import pandas as pd
from io import StringIO
from agno.tools.toolkit import Toolkit
from app.api.tools.instructions import SYMBOLS_TOOL_INSTRUCTIONS
logging.basicConfig(level=logging.INFO)
logging = logging.getLogger("crypto_symbols")
@@ -23,7 +24,7 @@ class CryptoSymbolsTools(Toolkit):
self.final_table = pd.read_csv(self.cache_file) if os.path.exists(self.cache_file) else pd.DataFrame() # type: ignore
Toolkit.__init__(self, # type: ignore
name="Crypto Symbols Tool",
instructions="Tool to get cryptocurrency symbols and search them by name.",
instructions=SYMBOLS_TOOL_INSTRUCTIONS,
tools=[
self.get_all_symbols,
self.get_symbols_by_name,