the first part of my journey constructing Fernão, my private AI agent. Now it’s time to proceed the story and let’s dive into the second half!
On this publish, I’ll stroll you thru the most recent enhancements to Fernão, refining current options and including new capabilities to the agent. Let’s begin with what modified.
Keep in mind the perform that fetched the calendar by ICS (the common calendar format) and extracted my calendar duties?
That perform was a multitude and it mirrored a poor architectural choice. ICS calendars don’t assist native filtering, which implies each request required pulling all occasions from the calendar and filtering them afterward. In apply, Fernão was downloading my whole schedule simply to extract just a few related conferences.
This was like bringing dwelling a complete library simply to lookup a single sentence in a single e book.
The extra I attempted to optimize the perform, the much less I might get wherever, as a result of I wanted a brand new system answer. I wanted to alter the best way the system was getting the calendar and I might NEVER get round this bottleneck with ICS.
So I dug deeper and located that Google offers calendar entry by an API that helps native filtering. Fernão now solely retrieves the occasions it really wants. This improved the schedule technology, that dropped from almost 5 minutes to about twenty seconds.
With this new pipeline in place, I additionally refactored the encompassing logic. The whole perform is now considerably cleaner and quicker. We now have a lovely option to fetch the calendar occasions by way of API:
def get_events_for_date(target_date=None, use_api=True):
"""
Fetches occasions for a particular date from Google Calendar.
Tries API first (if use_api=True), falls again to ICS if API fails.
Args:
target_date: datetime.date object for the goal day. If None, makes use of at the moment.
use_api: If True, attempt Google Calendar API first. If False, use ICS solely.
Returns:
Record of occasion dictionaries.
"""
if use_api and GCAL_API_AVAILABLE:
print("[GCal] Trying to make use of Google Calendar API...")
attempt:
occasions = get_events_for_date_api(target_date)
if occasions just isn't None:
print(f"[GCal] Efficiently fetched {len(occasions)} occasions by way of API")
return occasions
else:
print("[GCal] API returned None. Falling again to ICS...")
besides Exception as e:
print(f"[GCal] API failed with error: {e}")
print("[GCal] Falling again to ICS...")
# Fallback to ICS
print("[GCal] Utilizing ICS feed methodology...")
return get_events_for_date_ics(target_date)
.. and right here is our API fetch:
def get_events_for_date_api(target_date=None):
"""
Fetches occasions for a particular date from Google Calendar utilizing the Calendar API.
Args:
target_date: datetime.date object for the goal day. If None, makes use of at the moment.
Returns:
Record of occasion dictionaries, or None if API name fails.
"""
service = get_calendar_service()
if not service:
return None
day_start, day_end, target_date, tz_name, native = _get_local_time_range(target_date)
print(f"n[GCal API] Fetching occasions for {target_date.strftime('%Y-%m-%d')}")
print(f" Timezone: {tz_name}")
# Get calendar IDs from surroundings, or use major
calendar_ids_str = os.getenv('GCAL_CALENDAR_IDS', '')
if calendar_ids_str:
calendar_ids = [cid.strip() for cid in calendar_ids_str.split(',')]
else:
calendar_ids = ['primary']
all_events = []
# Fetch from every calendar
for calendar_id in calendar_ids:
attempt:
print(f"[GCal API] Fetching from calendar: {calendar_id}")
# Name the Calendar API with timeoutlobally
old_timeout = socket.getdefaulttimeout()
socket.setdefaulttimeout(10)
attempt:
events_result = service.occasions().record(
calendarId=calendar_id,
timeMin=day_start.isoformat(),
timeMax=day_end.isoformat(),
singleEvents=True,
orderBy='startTime'
).execute()
lastly:
socket.setdefaulttimeout(old_timeout)
occasions = events_result.get('objects', [])
print(f"[GCal API] Discovered {len(occasions)} occasion(s) in {calendar_id}")
# Parse occasions
for occasion in occasions:
# Get begin time
begin = occasion['start'].get('dateTime', occasion['start'].get('date'))
finish = occasion['end'].get('dateTime', occasion['end'].get('date'))
# Parse datetime
if 'T' in begin: # DateTime
start_dt = datetime.fromisoformat(begin.substitute('Z', '+00:00'))
end_dt = datetime.fromisoformat(finish.substitute('Z', '+00:00'))
# Convert to native timezone
start_local = start_dt.astimezone(native)
end_local = end_dt.astimezone(native)
start_str = start_local.strftime("%H:%M")
end_str = end_local.strftime("%H:%M")
else: # All-day occasion
start_str = "00:00"
end_str = "23:59"
all_events.append({
"title": occasion.get('abstract', 'Untitled Occasion'),
"begin": start_str,
"finish": end_str,
"location": occasion.get('location', ''),
"description": occasion.get('description', '')
})
besides Exception as e:
print(f"[GCal API] Error fetching from {calendar_id}: {e}")
proceed
# Type by begin time
all_events.type(key=lambda x: x["start"])
print(f"[GCal API] Complete occasions: {len(all_events)}")
return all_events
Past the back-end enhancements, I’ve additionally added new options to the assistant.
Within the schedule view, I can now mark duties as accomplished. The cool factor is that and once I do it, they’re robotically synced with Microsoft To-Do app.

Marvelous!
It makes me assume that, over time, many people will find yourself constructing our personal “private working techniques”, assembling workflows from our most well-liked instruments, integrating what we want, and changing elements every time higher choices seem. If options turn out to be simple to copy, loyalty to particular platforms will weaken.
This additionally raises an attention-grabbing query: will firms finally attempt to lock down their techniques by proscribing APIs and exterior integrations? Presumably. However isolating themselves is unlikely to work in the long term. If customers can’t join instruments to their very own workflows, they’ll merely transfer elsewhere (… and can this be a conduct solely performed by energy customers?)
With this new functionality within the Schedule Maker, I went on to implement one other function for Fernão, instructed by one of many readers.
Introducing: the Process Breaker.

The Process Breaker follows a easy workflow:
- Begin with a big, generic job in Microsoft To Do;
- Add context on how the duty ought to be damaged down;
- Fernão decomposes the duty and builds a plan;
- The ensuing duties are saved again to To-Do with due dates, and later seem within the each day assistant;

Right here’s how the Process Breaker seems within the sidebar:

And right here’s the present (nonetheless tough) entrance finish for the Process Breaker:

Let’s take an actual instance. One of many bigger duties sitting in my To Do record is “Mission Documentation at DareData.” This isn’t a small operational job, it’s a structural undertaking.
The objective is to consolidate and formalize the inner data of the corporate in Notion, ensuring the principle questions in regards to the enterprise are clearly answered in our Data Hub. Which means reviewing each division, figuring out gaps, creating or refining pages, structuring data correctly, and assigning clear possession for every part.
In apply, this requires auditing, writing, and governance choices. It’s not one thing you “simply do” in a single sitting and I need to full this inside three weeks. Realistically, I can dedicate between half-hour to 1 hour per day.
That is precisely the sort of job that advantages from decomposition, so I’ll use some context data within the Process Breaker and switch this giga-task into an actionable plan:
I’m presently constructing our Data Hub in Notion. Right here’s the present construction of Notion and Departments:
# DareData Hub
## Handbook for Community Members
[DareData Network Member Handbook ]
## Groups
### Gross sales & Partnerships
### Advertising and marketing (Model and Gen-OS)
### Finance
### Supply
[Tech Specialists]
[Principals]
[Account Managers]
I’ll have to:
- Go By way of Each division and create the pages that make sense (search the net for extra context on what DareData is should you want concepts of the pages I have to create on each division or use your data on what undoubtedly must be documented in a 130 individuals B2B firm).
- Make it possible for each web page has an Proprietor
- Revisit the Strategies recorded by my group within the recommendations, I can in all probability decide one or two recommendations day by day
- Add some departments that aren't in there: Admin, Core Members, Finance, Product
I've round half-hour to 1 hour to work day by day and need to full this undertaking by 7 March 2026.
After hitting “breakdown”, Fernão will begin to forge:

Marvelous x2!

We will now evaluation the duties and plan generated and submit them to my Microsoft To-Do. I additionally have to assign which record on Microsoft To-Do they are going to be assigned to:

Cool! Let’s see the way it seems in Microsoft app:

Perfecto.
Whereas testing this, I realised I need to alter two issues.
- First, Fernão is supposed to be a medieval chronicler, not a fantasy warrior, so I’ll change the forging design. His clothes ought to really feel medieval.
- Second, on a extra sensible word, I would like a “Submit All” button. Submitting duties one after the other is tedious, particularly when breaking down bigger tasks.
Let’s break one other job after making these modifications:

Fernão now forges in a correct medieval vest:

And now we are able to additionally use the good “Submit All” button:

I’ll possible take away the emoji. That sort of “emojification” may be very typical of LLM-generated code within the front-end, and it actually annoys me.
Anyway, constructing this new function for Fernão was genuinely satisfying. If in case you have concepts for added options, I’m open to recommendations!
Under is the present immediate I’m utilizing for job breakdown. I’ll proceed refining it as I experiment and observe the way it performs in actual use circumstances.
title: task_breakdown
description: Break down a job into 20-minute actionable subtasks
max_tokens: 4096
variables:
- task_name
- task_context
- current_date
template: |
You're a productiveness skilled serving to break down complicated duties into manageable 20-minute chunks.
**CURRENT DATE:** {current_date}
**TASK TO BREAK DOWN:**
{task_name}
**CONTEXT PROVIDED BY USER:**
{task_context}
**YOUR JOB:**
Break this job into particular, actionable subtasks that may every be accomplished in roughly 20 minutes.
**RULES:**
1. Every subtask ought to be concrete and actionable (begins with a verb)
2. Every subtask ought to take ~20 minutes (could be 15-25 min, however intention for 20)
3. Subtasks ought to comply with a logical order
4. Be particular - keep away from obscure duties like “work on X”
5. If the duty is already sufficiently small, you may create 1-3 subtasks
6. If it’s massive, create > 5 subtasks
7. Take into account the context supplied - use it to make subtasks related and particular
8. **SCHEDULING:** Primarily based on the consumer’s context (e.g., “each different day”, “weekends solely”), counsel a particular Due Date for every job ranging from the Present Date.
**OUTPUT FORMAT:**
Return ONLY a markdown record of subtasks on this format:
- {task_name} - [Subtask description] (20 min) [Due: YYYY-MM-DD]
Instance:
- {task_name} - Create undertaking repository (20 min) [Due: 2026-02-13]
- {task_name} - Configure CI/CD pipeline (20 min) [Due: 2026-02-15]
Do NOT embody another textual content or explanations. Simply the record.
In parallel, I’m already engaged on a number of new modules:
- A Dividend Analyzer to undertaking earnings from my shares and ETFs
- A Writing Assistant to create an editorial plan for my writings.
- A Reductions module to test related promotions once I’m planning a purchase order
- A Guitar Organizer to construction and schedule apply classes every time I decide up my guitar
Keep tuned for the following modules and hope this conjures up you to your private tasks as nicely!

