每个人如何在几小时内构建个人AI代理

我对个人开发者现在能够多快地发布真实且有用的原型感到非常惊讶。

像Claude Code、Google AntiGravity以及围绕它们成长的生态系统已经跨越了一个门槛:你可以在线查看其他人正在构建的东西,并意识到今天你能多快地构建。

在过去的几周里,我开始每天专门预留一到两个小时,使用我的AI优先技术栈进行构建:

  • Google AntiGravity
  • Google Gemini Pro
  • 通过AntiGravity访问的Claude模型

这种设置从根本上改变了我对原型设计、迭代速度以及"个人AI代理"在今天实际能做什么的看法。更重要的是,它把我拉回到了动手编码和构建的工作中,这是我在DareData的角色转向管理和协调而非执行后个人牺牲的东西。

就个人而言,这场革命对一个总是会倾向于管理角色的人来说是一种福音。它消除了我曾经接受的权衡:发展公司意味着完全放弃构建。我不再需要在构建和管理之间做出选择,它们实际上相互强化。

但是,对于"只是"开发的人来说,这里有一个更广泛的含义。如果AI代理越来越多地处理执行,那么纯粹的实现就不再足够。开发者将被推向(无论他们是否愿意)协调、决策和…管理——这是个人贡献者从心底讨厌的东西。换句话说,管理技能成为技术栈的一部分,而AI代理是被管理的上下文的一部分。

最让我惊讶的是,我现有的管理技能竟然如此可转移:

  • 引导代理而不是微观管理它
  • 要求结果而不是指令
  • 映射、优先排序和指出灰色区域

在实践中,我正在管理和协调一个虚拟员工。我可以深刻影响其工作的某些部分,同时对其他部分几乎完全无知——这不是一个缺陷,而是一个大特性。例如,在我的个人AI助手中,我可以清楚地推理后端,而对前端大多一无所知。系统仍然工作,因为我的角色不再是了解一切,而是引导系统朝着正确的方向前进。

这直接类似于我在公司内部协调人员的方式。随着DareData的成长,我们不会雇佣创始人的复制品。我们有意雇佣能够做我们不能做的事情的人,并且随着时间的推移,做我们永远不会学得足够深入以至于做得好的事情。

按Enter或点击查看完整大小的图片

当前构建栈 - Google AntiGravity

足够的管理自我反思。让我们看看我在构建什么,因为这是你在这里的原因:

  • 一个围绕我实际日常工作设计的个人AI助手,而不是通用的生产力模板。它适应我的工作、思考和决策方式。
  • 一个每周推荐一张音乐专辑的移动应用,没有传统的推荐系统。没有舒适区强化,这有助于我扩大我的聆听范围。
  • 一个围绕单个角色通过分层地牢前进的移动游戏,主要作为创意游乐场而非商业产品开发。

有趣的是,虽然我对编码大部分后端感到舒适,但前端开发不是我拥有的技能——如果我被迫自己做,这些项目会从几小时减慢到几天,或者根本不会发布。

这个约束现在在很大程度上无关紧要。有了这个新的技术栈,想象力成为真正的瓶颈。"不知道"整个层的成本已经崩溃。

所以,在这篇文章的其余部分,我将介绍我的个人AI助手:它做什么,它如何构建,以及为什么它对我有效。我的目标是在它稳定后开源它,以便其他人可以将其适应自己的工作流程。它目前非常特定于我的生活,但这种特异性是有意的,使其更通用是实验的一部分。

认识Fernão

Fernão Lopes是葡萄牙君主制的编年史家。我故意选择了一位葡萄牙历史人物——葡萄牙人有一个习惯,几乎给任何东西都附上历史名称。如果这听起来像是典型的葡萄牙人,那是有意的。

Fernão是葡萄牙人,但实际上说英语,称他为21世纪的现代编年史家。

Fernão Lopes,葡萄牙王国的官方编年史家——他看起来像一个合适的中世纪学者。

在这一点上,我正在做两件我通常避免的事情:将AI拟人化并倾向于非常葡萄牙式的命名本能。把它看作是我变老的无害标志。

除此之外,Fernão实际上为我做了什么?最好的起点是他的首页。

按Enter或点击查看完整大小的图片

Fernão的首页

Fernão是一个看起来很酷的家伙,目前处理五项任务:

  • 日程安排:通过整合日历、待办事项和目标来计划我的一天,然后将它们变成我可以遵循的东西。
  • 写作助手:帮助我审查和清理博客文章和其他文本的草稿。
  • 投资组合助手:根据重新平衡需求和宏观世界发生的事情建议添加的公司或ETF(不假装是水晶球)。
  • 财务组织者:从我的银行对账单中提取支出并将所有内容上传到Cashew应用程序,为我节省了每月约3到4小时的任务。
  • 订阅和折扣:跟踪我所有的订阅并显示我可能拥有但从未记得使用的折扣或福利。

在这篇文章中,我将专注于日程安排应用程序。

目前,Fernão的日程安排做三件简单的事情:

  • 获取我的日历,包括所有预定的会议
  • 从Microsoft To Do中提取我的待办事项
  • 从Notion中检索我的个人关键结果

所有这些都通过API连接。这个想法很简单:每天,Fernão查看我的约束、优先级和承诺,然后为我生成最好的可能日程安排。

按Enter或点击查看完整大小的图片

Fernão的当前能力

在前端生成日程安排非常容易(所有前端都是氛围编码的)。这是生成日程安排按钮:

按Enter或点击查看完整大小的图片

为特定日期生成日程安排

一旦我点击生成日程安排,Fernão就开始在后台工作:

按Enter或点击查看完整大小的图片

Fernão正在生成日程安排

然后按顺序执行步骤:获取我的日历、任务和Notion数据。

下一点也是基本编码素养真正开始重要的地方,不是因为以下代码做的所有事情都不起作用,而是因为你需要理解正在发生什么以及事情最终可能在哪里崩溃或需要改进。

让我们从日历获取开始。目前,这由Claude创建的一个巨大函数处理,该函数完全未优化。

def get_events_for_date(target_date=None):
    """
    Fetches events for a specific date from Google Calendar via ICS feeds.
    
    Args:
        target_date: datetime.date object for the target day. If None, uses today.
    
    Returns a list of event dictionaries.
    """
    # Hardcoded calendar URLs (not using env var to avoid placeholder issues)
    CALENDAR_URLS = [
        'cal1url',
        'call2url'
    ]
    
    LOCAL_TZ = os.getenv('TIMEZONE', 'Europe/Lisbon')
    
    # Get timezone
    local = tz.gettz(LOCAL_TZ)
    
    # If no target date provided, use today
    if target_date is None:
        target_date = datetime.now(local).date()
    
    # Create datetime for the target day boundaries
    day_start = datetime.combine(target_date, datetime.min.time()).replace(tzinfo=local)
    day_end = day_start + timedelta(days=1)
    
    # Debug: Print the date range we're checking
    print(f"\n[Debug] Checking calendars for date: {target_date.strftime('%Y-%m-%d')}")
    print(f"  Start: {day_start.strftime('%Y-%m-%d %H:%M %Z')}")
    print(f"  End: {day_end.strftime('%Y-%m-%d %H:%M %Z')}")
    print(f"  Timezone: {LOCAL_TZ}")
    
    all_events = []
    
    # Fetch from each calendar
    for idx, cal_url in enumerate(CALENDAR_URLS, 1):
        calendar_name = f"Calendar {idx}"
        print(f"\n[Debug] Fetching {calendar_name}...")
        
        try:
            # Load calendar from ICS URL with adequate timeout
            r = requests.get(cal_url, timeout=30)
            r.raise_for_status()
            cal = Calendar(r.text)
            
            events_found_this_cal = 0
            total_events_in_cal = len(list(cal.events))
            print(f"  Total events in {calendar_name}: {total_events_in_cal}")
            
            # Use timeline to efficiently filter events for target day's date range
            # Convert local times to UTC for timeline filtering
            day_start_utc = day_start.astimezone(timezone.utc)
            day_end_utc = day_end.astimezone(timezone.utc)
            
            # Get events in target day's range using timeline
            days_timeline = cal.timeline.overlapping(day_start_utc, day_end_utc)
            
            for e in days_timeline:
                if not e.begin:
                    continue
                    
                # Get event start time
                start = e.begin.datetime
                if start.tzinfo is None:
                    start = start.replace(tzinfo=timezone.utc)
                
                # Convert to local timezone
                start_local = start.astimezone(local)
                
                # Debug: Print first few events to see dates
                if events_found_this_cal < 3:
                    print(f"  Event: '{e.name}' at {start_local.strftime('%Y-%m-%d %H:%M')}")
                
                # Get end time
                end = e.end.datetime if e.end else None
                end_local = end.astimezone(local) if end else None
                
                all_events.append({
                    "title": e.name,
                    "start": start_local.strftime("%H:%M"),
                    "end": end_local.strftime("%H:%M") if end_local else None,
                    "location": e.location or "",
                    "description": e.description or ""
                })
                events_found_this_cal += 1
            
            print(f"  [OK] Found {events_found_this_cal} event(s) for target day in {calendar_name}")
            
        except requests.exceptions.RequestException as e:
            print(f"  [X] Network error fetching {calendar_name}: {str(e)}")
            continue
        except Exception as e:
            print(f"  [X] Error processing {calendar_name}: {type(e).__name__}: {str(e)}")
            continue
    
    # Sort by start time
    all_events.sort(key=lambda x: x["start"])
    
    # Print all events in detail
    if all_events:
        print(f"\n[Google Calendar] Found {len(all_events)} event(s) for {target_date.strftime('%Y-%m-%d')}:")
        print("-" * 60)
        for event in all_events:
            time_str = f"{event['start']}-{event['end']}" if event['end'] else event['start']
            location_str = f" @ {event['location']}" if event['location'] else ""
            print(f"  {time_str} | {event['title']}{location_str}")
        print("-" * 60)
    else:
        print(f"\n[Google Calendar] No events for {target_date.strftime('%Y-%m-%d')}")
    
    return all_events

作为一名Python开发者,所有的print语句都让我感到不舒服。但这是Fernão下一阶段的问题:一旦产品逻辑稳固,就重构和优化代码。

这也是我真正看到人类+AI动态工作的地方。我可以立即发现几种改进此函数的方法(减少冗长性、减少不必要的延迟、清理流程),但做好这一点仍然需要时间、判断和意图。AI帮助我快速行动;它不会取代知道什么是卓越的需要。

目前,我没有花太多时间优化它,这是一个有意识的选择。尽管有粗糙的边缘,该函数完全按照它需要做的事情:从我的日历中提取数据并将会议信息馈送到Fernão,启用接下来的一切。

接下来,Fernão从Microsoft To Do中提取我的任务。这是我日常待办事项所在的地方(需要完成的小而具体的事情,为特定的一天提供结构)。所有这些都直接在Microsoft To Do应用程序中配置,这是我日常工作流程的核心部分。

然后,Fernão从Notion中获取我的个人关键结果,这让它了解我的长期目标,以便在安排任务时优先考虑重要的事情。

按Enter或点击查看完整大小的图片

个人目标示例

顺便说一句,在写这篇文章时,我最终在应用程序中添加了一个小部件。如果我们正在构建一个个人助手,它也应该有一些个性。

所以我问Gemini:

“你能在应用程序检索日历事件、检查待办事项和提取Notion数据时添加一个有趣的动画吗?也许是Fernão搅拌锅的小部件,标题为’Fernão is cooking’。”

Fernão is cooking

一旦这一轮完成,任务和日历事件就会成功收集并显示在Fernão的前端(下面显示了我一天的任务和会议样本)。

Fernão获取的任务和日历事件

现在是有趣的部分:有了日历、任务和目标,Fernão将我的整个一天组成一个单一的、神奇的计划:

07:30-09:30 | 健身房
09:30-09:40 | 在Odoo上检查工时表 (截止日期: 2026-02-04)
09:40-09:55 | 审查待办事项中的任务 - [组织任务] (截止日期: 2026-02-04)
09:55-10:15 | 阅读Feedly内容 - [新闻追赶] (截止日期: 2026-02-04)
10:15-10:30 | 撰写文化文档,灵感: https://pt.slideshare.net/slideshow/culture-1798664/1798664 (截止日期: 2026-02-04)
10:30-10:45 | 回答LinkedIns - [组织任务] (截止日期: 2026-02-04)
10:45-11:00 | 检查Looker (截止日期: 2026-02-04)
11:00-11:30 | 本周AI帖子 (截止日期: 2026-02-04)
11:30-12:00 | 准备播客解码AI
12:00-13:00 | 播客解码AI - Ivo Bernardo, DareData (事件)
13:00-14:00 | 午餐休息
14:00-14:30 | 候选人1 (姓名隐藏) 和 Ivo Bernardo @ Google Meet (事件)
14:30-18:00 | 准备DareData国情咨文
18:00-18:30 | 候选人2 (姓名隐藏) 和 Ivo Bernardo @ Google Meet (事件)
18:30-19:00 | 候选人3 (姓名隐藏) 和 Ivo Bernardo @ Google Meet (事件)
19:00-19:30 | 候选人4 (姓名隐藏) 和 Ivo Bernardo @ Google Meet (事件)
19:30-20:00 | 候选人5 (姓名隐藏) 和 Ivo Bernardo @ Google Meet (事件)
20:00-20:15 | 检查内幕交易信号以获取股票想法 https://finviz.com/insidertrading?tc=1 (逾期)
20:15-20:30 | 营销时间表 (逾期)
20:30-21:00 | 晚餐休息
21:00-22:00 | 阅读
22:00-22:15 | 一天结束 - 审查并为明天做准备

这是那些真正让人感觉有点神奇的时刻之一。不是因为技术是不透明的,而是因为结果是如此干净。混乱的会议、任务和长期目标的混合变成了我实际上可以执行的一天。

让它更有趣的是最后一步是多么简单。在后台完成所有繁重的工作(日历事件、待办事项、目标)后,我不会编排复杂的管道或提示链。我使用一个单一的提示。

这个提示接受Fernão关于我的约束和优先级的所有知识,并将其转化为你即将看到的一天。

name: daily_schedule
description: Generate a daily schedule based on calendar events and tasks
model: gemini-2.5-flash-lite
temperature: 0.3
max_tokens: 8192

variables:
  - date_context
  - events_str
  - tasks_str
  - context
  - todo_context
  - auto_context
  - currently_reading
  - notion_context
  - is_todaytemplate: |
    You are my personal AI scheduling assistant. Help me plan my day!
    **TARGET DATE:** Planning for {date_context}
    **EVENTS (Fixed):**
    {events_str}
    **TASKS TO SCHEDULE:**
    {tasks_str}
    **MY CONTEXT:**
    {context}
    **TASK CONTEXT (how long tasks typically take):**
    {todo_context}
    **AUTO-LEARNED CONTEXT:**
    {auto_context}
    **CURRENTLY READING:**
    {currently_reading}  **RESULTS & OBJECTIVES (from Notion):**
    {notion_context}  **SCHEDULING RULES:**
  1. **Cannot schedule tasks during calendar events** - events are fixed
  2. **Mandatory breaks:**
     - Lunch: 12:30-13:30 (reserve when possible)
     - Dinner: 20:30-21:00
     - Playing with my cat: 45-60 minutes somewhere scattered around the day
  3. **Task fitting:** Intelligently fit tasks between events based on available time
  4. **Time estimates:** Use the task context to estimate how long each task will take
  5. **Working hours:** 09:30 to 22:00
  6. **Start the schedule:** {is_today}  **YOUR JOB:**
  1. Create a complete hourly schedule for {date_context}
  2. Fit all tasks between events and breaks
  3. **Prioritize tasks based on my Results & Objectives from Notion** - focus on what matters most
  4. Be conversational - if you need more info about a task, ask me!
  5. **Save learnings:** If I give you context about tasks, acknowledge it and say you'll remember it  **FORMAT:**
    Start with a friendly greeting, then provide the schedule in this format:
09:30-10:00 | Task/Event
10:00-11:00 | Task/Event
...
```
After the schedule, ask if I need any adjustments or if you need clarification on any tasks.
Generate my day's schedule now, thanks!
有了这个,Fernão肯定在工作:

按Enter或点击查看完整大小的图片

![](https://raw.githubusercontent.com/errolyan/tuchuang/master/uPic/0*tSxXBiBYtaU__sNm.png)

Fernão又在工作了

这是一个真正有趣的系统来构建。我将继续发展Fernão,给他新的责任,打破东西,修复它们,并在这里分享我一路上学到的东西。

随着时间的推移,我还计划撰写关于如何自己构建和部署类似应用程序的实用教程。目前,Fernão只存在于我的机器上,这可能会保持这种状态。不过,我确实打算开源它。不是因为它在当前形式下普遍有用(它深深定制于我的生活),而是因为潜在的想法可能有用。

为了使这成为可能,我需要抽象工具,模块化功能,并允许打开和关闭功能,以便其他人可以围绕自己的工作流程塑造助手,而不是我的。

我本可以仅使用Claude Code构建类似的东西。我没有。我想要完全控制:交换模型、混合提供商的自由,并最终在本地LLM上运行Fernão,而不是依赖外部API。在这里,所有权和灵活性比便利性更重要。

如果你正在构建个人AI助手,你会给它什么任务?我真的想听听你的想法,并尝试在Fernão内部构建它们。留下评论,因为这个项目仍在发展,外部视角通常是使其变得更好的最快方式。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐