Skip to content

Skill development

This tutorial demonstrates how to develop and use custom skills through two examples: first creating a weather reporting skill from scratch, and then using that skill to generate weather comparisons and visualizations.

Prerequisites

It is recommended to complete the Basic Usage tutorial before proceeding with this one, as it covers fundamental concepts that are built upon here.

Interactive development

This section demonstrates the interactive development of a custom weather reporting skill through a conversation between a user and a freeact agent. You'll see how the agent assists with software engineering tasks, evolving a simple weather query into a Python package that can:

  • Fetch current weather conditions for any city
  • Resolve city names to geographical coordinates
  • Retrieve historical weather data
  • Return structured data for both current and historical weather

The development progresses through several iterations:

  1. Creating a code action to fetch Vienna's weather
  2. Converting it into a proper Python package
  3. Adding city name coordinate resolution
  4. Incorporating historical weather data
  5. Testing with various cities (New York, Vienna, Salzburg)

The interactively developed weather.weather_report (1) skill uses the Open-Meteo API for weather data and geocoding.

  1. """Module for getting weather reports for cities."""
    
    from datetime import datetime, timedelta
    from typing import Any, Dict
    
    import requests
    
    
    def get_weather_report(city_name: str, n_days: int = 7) -> Dict[str, Any]:
        """Get current and historical weather report for a given city.
    
        Args:
            city_name: Name of the city to get weather for
            n_days: Number of past days to get historical data for (excluding current day)
    
        Returns:
            Dictionary containing:
            - temperature: Current temperature in Celsius
            - humidity: Current relative humidity percentage
            - measurement_time: Timestamp of current measurement
            - coordinates: Dict with latitude and longitude
            - city: City name used for query
            - history: List of daily measurements for past n_days (excluding current day), each containing:
                - date: Date of measurement
                - temperature: Average daily temperature in Celsius
                - humidity: Average daily relative humidity percentage
        """
        # First get coordinates using geocoding API
        geocoding_url = f"https://geocoding-api.open-meteo.com/v1/search?name={city_name}&count=1&language=en&format=json"
        geo_response = requests.get(geocoding_url)
        geo_data = geo_response.json()
    
        if not geo_data.get("results"):
            raise ValueError(f"Could not find coordinates for city: {city_name}")
    
        location = geo_data["results"][0]
        lat = location["latitude"]
        lon = location["longitude"]
    
        # Calculate date range for historical data
        end_date = datetime.now().date() - timedelta(days=1)  # yesterday
        start_date = end_date - timedelta(days=n_days - 1)
    
        # Get current and historical weather data using coordinates
        weather_url = (
            f"https://api.open-meteo.com/v1/forecast?"
            f"latitude={lat}&longitude={lon}"
            f"&current=temperature_2m,relative_humidity_2m"
            f"&daily=temperature_2m_mean,relative_humidity_2m_mean"
            f"&timezone=auto"
            f"&start_date={start_date}&end_date={end_date}"
        )
        weather_response = requests.get(weather_url)
        weather_data = weather_response.json()
    
        current = weather_data["current"]
        daily = weather_data["daily"]
    
        # Process historical data
        history = []
        for i in range(len(daily["time"])):
            history.append(
                {
                    "date": datetime.fromisoformat(daily["time"][i]).date(),
                    "temperature": daily["temperature_2m_mean"][i],
                    "humidity": daily["relative_humidity_2m_mean"][i],
                }
            )
    
        return {
            "temperature": current["temperature_2m"],
            "humidity": current["relative_humidity_2m"],
            "measurement_time": datetime.fromisoformat(current["time"]),
            "coordinates": {"latitude": lat, "longitude": lon},
            "city": location["name"],
            "history": history,
        }
    

Note

Interactive skill development is currently only supported for Claude models.

The example conversation below was initiated with the following freeact.cli command1. The developed weather.weather_report skill is written to the workspace/skills/private/example directory.

python -m freeact.cli \
  --model-name=claude-3-5-sonnet-20241022 \
  --ipybox-tag=ghcr.io/gradion-ai/ipybox:example \
  --executor-key=example \
  --skill-modules=freeact_skills.search.google.stream.api \
  --skill-modules=freeact_skills.zotero.api \
  --skill-modules=freeact_skills.reader.api

Example conversation

output-dev

Skill usage

This section demonstrates how to use the previously developed weather reporting skill in a separate conversation. After loading weather.weather_report as additional skill2, we'll see how the agent can:

  • Create multi-city weather reports with much smaller code actions
  • Create data visualizations comparing weather patterns
  • Provide clear, natural language summaries of weather conditions

The example shows a request for weather data from Paris and Berlin, where the agent automatically:

  1. Retrieves current conditions and 4-day history for both cities using the weather.weather_report skill
  2. Creates comparative visualizations of temperature and humidity trends
  3. Presents the data in both graphical and text formats

The example conversation was initiated with the following freeact.cli command. The developed skill is is read from workspace/skills/private/example. If we moved it to workspace/skills/shared, it would be available to all executors regardless of their executor-key value.

python -m freeact.cli \
  --model-name=claude-3-5-sonnet-20241022 \
  --ipybox-tag=ghcr.io/gradion-ai/ipybox:example \
  --executor-key=example \
  --skill-modules=freeact_skills.search.google.stream.api \
  --skill-modules=freeact_skills.zotero.api \
  --skill-modules=freeact_skills.reader.api \
  --skill-modules=weather.weather_report

Example conversation

output-use

Produced images:

image_0


  1. The provided skill modules are not needed in this example but included to demonstrate that their usage is optional. 

  2. We may automate this process in the future e.g. to load skill modules based on the details of a user's query.