1. 项目缘起:当AI遇上副业探索

最近几年,AI Agent(智能体)的概念越来越火,从自动写代码到处理客服,似乎无所不能。作为一个常年混迹在数字世界、对搞点副业收入有点想法的人,我一直在琢磨:能不能让AI Agent来帮我干点“脏活累活”,比如自动寻找那些潜在的、可持续的在线收入机会?毕竟,手动去刷各种平台、论坛、项目发布站,既耗时又低效,还容易错过转瞬即逝的机会。

这个想法在我脑子里盘旋了很久。所谓的“在线收入”(Online Income),范围其实很广,它可以是接一个远程的编程外包单子,可以是运营一个利基(Niche)博客通过广告或联盟营销赚钱,也可以是参与一些众包任务平台,甚至是在二手市场发现价格差做点小生意。核心逻辑是,互联网上散落着大量未被充分发掘或需要特定技能匹配的“赚钱信号”,而人类的时间和注意力是有限的。于是,我决定启动一个实验项目:搭建一个AI Agent,让它充当我的“数字侦察兵”,7x24小时不间断地在指定的网络空间里巡逻、筛选、分析,并把最有价值的机会报告给我。这篇文章,就是第一周的实验记录、技术拆解和踩坑实录。

2. 整体设计与核心思路拆解

我的目标不是创造一个能直接替我赚钱的“印钞机”——那太不切实际了。我的目标是构建一个 信息过滤与初步评估系统 。它的核心工作流可以概括为: 感知 -> 理解 -> 筛选 -> 报告

2.1 核心需求与功能定义

首先,我需要明确这个AI Agent具体要做什么,不能做什么。

  1. 信息源监控 :Agent需要能自动访问一系列我预设的网站,比如特定领域的招聘板(如Upwork, Fiverr的特定类别)、论坛的热门板块(如Reddit的r/forhire, r/slavelabour)、众包平台(如Amazon Mechanical Turk的新任务流)、甚至是一些加密货币空投或测试项目的公告网站。它不能漫无目的地全网爬取,那样效率低且法律风险高。
  2. 内容理解与提取 :仅仅抓取网页标题和摘要是不够的。Agent需要理解一段文本描述的是一个“赚钱机会”。这涉及到自然语言处理(NLP),需要识别关键词(如“paid”, “remote”, “freelance”, “bounty”, “commission”)、判断任务类型(写作、设计、编程、测试)、提取关键约束(预算、截止日期、技能要求)。
  3. 机会评分与排序 :不是所有被识别出来的“机会”都值得我花时间。Agent需要根据一套我设定的规则进行初步评分。规则可能包括:预算是否明确且合理(拒绝“面议”或极低预算)、技能要求是否与我的技能栈匹配、发布时间是否新鲜、平台信誉如何等。
  4. 结构化报告与通知 :最终,Agent需要将筛选后的机会,以清晰、结构化的格式(比如一个简单的仪表板或每日摘要邮件)推送给我,让我能在5分钟内浏览完一天的所有“高潜信号”,并决定是否深入跟进。

2.2 技术栈选型与考量

基于以上需求,我选择了以下技术组合,并解释一下为什么这么选:

  • 核心“大脑”(LLM API) :我选择了OpenAI的GPT-4 API。虽然Claude或本地部署的模型如Llama 3在某些任务上可能表现更好,但GPT-4在指令遵循、复杂文本理解和格式输出方面非常稳定,且API调用方便。考虑到这个Agent需要处理多变的网页文本和进行逻辑判断,一个强大的通用模型是必要的。我没有选择微调,而是依赖精心设计的提示词(Prompt)来引导它。
  • “手脚”(自动化与爬取) :这里我用了 Playwright 。相比传统的Selenium或Requests+BeautifulSoup,Playwright对现代网页(尤其是大量使用JavaScript渲染的单页应用)的支持更好,能更可靠地模拟真实用户操作来获取动态加载的内容。它支持无头(Headless)模式,非常适合后台自动运行。
  • “调度中心”(任务编排) :我使用 Python 作为主语言,结合 APScheduler 库来管理定时任务。比如,设定每天在早上8点、下午1点、晚上8点各运行一次扫描任务,避免对目标网站造成访问压力,也符合我查看信息的习惯。
  • “记忆与判断”(数据存储与规则引擎) :使用轻量级的 SQLite 数据库来存储历史抓取到的机会、原始内容、分析结果和我的后续操作(如“已申请”、“忽略”)。规则引擎则直接写在Python代码里,是一系列 if-else 逻辑和基于GPT-4分析的评分函数组合。
  • “通信官”(通知系统) :最简单的方案是邮件。我使用了Gmail的SMTP服务,让Agent在发现高评分机会时,或每天固定时间,发送汇总报告到我的邮箱。未来可以考虑集成Telegram Bot或Slack Webhook,实现实时提醒。

注意 :在涉及网页抓取时, 必须严格遵守 robots.txt 协议 ,尊重网站的访问频率限制(设置合理的延时),并且只抓取公开可访问的信息。我的Agent设计为“只读”和“信息聚合”,不进行任何登录、提交表单或交互式操作,以最大限度降低法律和伦理风险。

3. 核心模块实现细节

3.1 信息抓取模块:用Playwright做聪明的“搬运工”

抓取模块的目标是稳定、准确、礼貌地获取目标网页的内容。我并没有做一个通用的爬虫,而是为每个目标网站编写了特定的“提取器”(Extractor)。

import asyncio
from playwright.async_api import async_playwright
from bs4 import BeautifulSoup
import re

class WebsiteScraper:
    def __init__(self, url, extractor_type):
        self.url = url
        self.extractor_type = extractor_type # 例如:'upwork_job_list', 'reddit_posts'

    async def fetch_content(self):
        async with async_playwright() as p:
            browser = await p.chromium.launch(headless=True) # 无头模式
            context = await browser.new_context(
                user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...' # 伪装成普通浏览器
            )
            page = await context.new_page()

            try:
                await page.goto(self.url, wait_until='networkidle', timeout=60000) # 等待网络空闲
                # 针对不同网站,可能需要滚动加载或点击“加载更多”
                if self.extractor_type == 'reddit_posts':
                    for _ in range(3): # 滚动3次以加载更多内容
                        await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
                        await page.wait_for_timeout(2000) # 等待2秒

                page_content = await page.content()
                await browser.close()
                return self._parse_content(page_content)
            except Exception as e:
                print(f"抓取 {self.url} 失败: {e}")
                await browser.close()
                return []

    def _parse_content(self, html):
        soup = BeautifulSoup(html, 'html.parser')
        opportunities = []

        if self.extractor_type == 'upwork_job_list':
            # 解析Upwork工作列表项
            job_cards = soup.select('section[data-test="JobTile"]') # 这是一个示例选择器,实际需调整
            for card in job_cards[:20]: # 限制前20个,避免过多
                title_elem = card.select_one('h2 a')
                budget_elem = card.select_one('strong[data-test="budget"]')
                desc_elem = card.select_one('div[data-test="description"]')
                # ... 提取其他字段
                if title_elem and desc_elem:
                    opportunities.append({
                        'title': title_elem.get_text(strip=True),
                        'budget': budget_elem.get_text(strip=True) if budget_elem else '未标明',
                        'snippet': desc_elem.get_text(strip=True)[:500], # 只取前500字符
                        'url': 'https://www.upwork.com' + title_elem['href'] if title_elem.get('href') else '',
                        'source': 'Upwork'
                    })
        # ... 其他网站类型的解析逻辑
        return opportunities

实操心得

  • 选择器稳定性 :网页结构经常变动,所以CSS选择器或XPath不能写得太死。尽量选择那些带有 data-testid data-cy 等测试属性的元素,它们相对稳定。如果没有,就选择结构上具有唯一性的父容器。
  • 等待策略 wait_until='networkidle' 并不总是可靠,有时页面会有持续的后台请求。结合 page.wait_for_selector() 等待特定关键元素出现,是更稳健的做法。
  • 速率限制 :在循环抓取多个页面或网站时,务必在请求间添加随机延时(如 await asyncio.sleep(random.uniform(2, 5)) ),模拟人类操作速度,避免IP被封锁。

3.2 机会分析与评分模块:让GPT-4充当“初级分析师”

这是整个Agent的“智能”核心。我将抓取到的原始信息(标题、片段、预算等)组合成一段清晰的提示词,发送给GPT-4 API,让它进行结构化分析和评分。

import openai
from typing import Dict, Any

class OpportunityAnalyzer:
    def __init__(self, api_key):
        openai.api_key = api_key
        self.scoring_prompt_template = """
        你是一个在线副业机会评估助手。请分析以下工作机会,并严格按照JSON格式输出你的分析结果。

        原始信息:
        标题:{title}
        来源:{source}
        预算:{budget}
        描述片段:{description}

        请分析:
        1. **机会类型**:从以下选项中选择:编程开发、写作翻译、设计创意、营销推广、数据录入、测试反馈、其他。
        2. **预算评估**:如果预算明确(如$50固定,或$20/小时),请解析为数值;如果模糊(如“面议”、“预算灵活”),标记为“模糊”。
        3. **技能匹配度**:根据我的技能(Python编程、技术写作、基础SEO),评估此机会与我的技能匹配度,给出0-10的分数(10为完全匹配)。
        4. **复杂度/时间评估**:根据描述,初步判断其预计耗时:短期(<1天)、中期(1-7天)、长期(>1周)或不确定。
        5. **可信度初判**:基于来源平台和描述专业性,给出高、中、低的初步判断。
        6. **综合推荐分**:结合以上所有因素,给出一个1-10的综合推荐分数(10为强烈推荐)。
        7. **简要理由**:用一句话说明综合推荐分的理由。

        请输出纯JSON,格式如下:
        {{
            "opportunity_type": "...",
            "budget_parsed": "...",
            "skill_match_score": ...,
            "estimated_duration": "...",
            "credibility": "...",
            "composite_score": ...,
            "reason": "..."
        }}
        """

    def analyze_and_score(self, opportunity: Dict[str, Any]) -> Dict[str, Any]:
        prompt = self.scoring_prompt_template.format(
            title=opportunity['title'],
            source=opportunity['source'],
            budget=opportunity['budget'],
            description=opportunity['snippet']
        )

        try:
            response = openai.ChatCompletion.create(
                model="gpt-4",
                messages=[{"role": "user", "content": prompt}],
                temperature=0.2, # 低温度,保证输出稳定、可重复
                max_tokens=500
            )
            analysis_text = response.choices[0].message.content.strip()

            # 清理响应,确保是纯JSON
            import json
            analysis_json = json.loads(analysis_text)
            # 将分析结果合并回原始机会数据
            opportunity.update(analysis_json)
            return opportunity
        except Exception as e:
            print(f"分析机会失败: {e}")
            # 返回一个默认的低分分析,避免流程中断
            default_analysis = {
                "opportunity_type": "其他",
                "budget_parsed": "解析失败",
                "skill_match_score": 0,
                "estimated_duration": "不确定",
                "credibility": "低",
                "composite_score": 1,
                "reason": "分析过程出错"
            }
            opportunity.update(default_analysis)
            return opportunity

核心考量

  • 提示词工程 :提示词的质量直接决定分析结果的可用性。我花了大量时间迭代提示词,确保GPT-4能理解我的技能背景、评分标准,并始终以稳定的JSON格式输出。明确的指令和示例至关重要。
  • 成本控制 :GPT-4 API调用不便宜。我通过以下方式控制成本:1) 只对抓取到的新机会进行分析;2) 在发送给GPT-4前,先用简单的关键词规则进行粗筛,过滤掉明显不相关的内容(如完全不符合我技能的关键词);3) 限制描述片段的长度(如前500字符)。
  • 错误处理 :API调用可能失败,返回内容可能不是合法JSON。必须要有健壮的错误处理逻辑,比如重试机制、JSON解析异常捕获,并为失败的分析提供一个默认的“低优先级”结果,保证整个流水线不会因为单次分析失败而崩溃。

3.3 数据存储与报告生成模块

所有经过分析的机会都会被存入SQLite数据库。数据库表设计很简单:

CREATE TABLE opportunities (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT,
    source TEXT,
    original_url TEXT UNIQUE, -- 用URL作为去重依据
    budget TEXT,
    description_snippet TEXT,
    opportunity_type TEXT,
    budget_parsed TEXT,
    skill_match_score INTEGER,
    estimated_duration TEXT,
    credibility TEXT,
    composite_score INTEGER,
    analysis_reason TEXT,
    first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status TEXT DEFAULT 'new' -- 'new', 'reviewed', 'applied', 'ignored'
);

报告生成模块会查询数据库中状态为 new composite_score 高于某个阈值(比如6分)的记录,生成一份HTML格式的每日摘要邮件。

实操心得

  • 去重是关键 :用 original_url 作为唯一约束,避免同一机会被重复抓取和分析,浪费API调用次数和造成干扰。
  • 状态管理 :设计 status 字段让我可以在查看报告后,手动更新机会的状态(例如标记为“已申请”或“忽略”),这样下次生成报告时就不会重复出现了。这个简单的反馈循环对优化Agent的“注意力”很有帮助。
  • 报告可读性 :HTML邮件模板里,我会用不同颜色高亮显示 composite_score (比如8-10分用绿色,5-7分用黄色,1-4分用灰色),并按照分数降序排列。这样我一打开邮箱,就能立刻抓住重点。

4. 第一周运行结果与数据分析

经过一周(7天)的持续运行,Agent扫描了预先设定的5个主要来源(2个自由职业平台、2个相关论坛、1个众包平台),共抓取了约1200条原始信息。经过初步关键词过滤和GPT-4深度分析,最终有87条记录进入了我的数据库,并生成了每日报告。

4.1 量化结果统计

指标 数量/比例 说明
总抓取条目 ~1200条 原始数据量
经分析入库条目 87条 通过初步筛选并调用GPT-4分析
高推荐分(≥7) 23条 综合推荐分7分及以上的机会
中推荐分(4-6) 41条 可保持关注的机会
低推荐分(≤3) 23条 基本不匹配或质量差的机会
预算明确率 65% 约57条机会有明确预算数值
平均技能匹配分 5.8 满分10分,说明多数机会只是部分匹配

从这87条机会中,我手动筛选并实际跟进申请了 4个 。截至本周末,收到了 1个 的明确回复并进入了洽谈阶段(一个关于API文档编写的短期项目),另外3个暂无回音或已关闭。

4.2 质量分析与发现

  1. 信息过载与信号噪声 :即使经过Agent的两层过滤(规则+AI),最终进入我视野的“高潜机会”(23条)仍然需要我花费平均每条3-5分钟去仔细阅读完整描述和评估。这说明 AI的筛选极大地提升了信息质量,但尚未完全替代人工判断 。很多机会描述模糊,或者预算与工作量严重不匹配,AI只能基于文本给出初步警示。
  2. 预算透明度问题 :高达35%的机会没有明确预算,标注为“面议”或“根据经验”。GPT-4会将其标记为“模糊”,并在综合评分中给予扣分。这类机会通常需要额外的时间成本去沟通询价,对于追求效率的副业模式来说,优先级应放低。
  3. 技能匹配的局限性 :我预设的技能是“Python编程、技术写作、基础SEO”。AI能很好地识别出明确需要“Python”或“Technical Writer”的机会。但对于一些描述宽泛的需求,如“需要一个网站开发者”,AI难以判断其具体技术栈是否与Python(如Django/Flask)匹配,有时会给出虚高的匹配分。这提示我需要进一步细化我的技能标签。
  4. “新鲜度”的价值 :第一周的数据清晰显示,发布后2小时内被Agent捕获并推送给我的机会,获得回复的概率远高于发布超过24小时的机会。这验证了 速度在在线机会竞争中的关键作用 。我的Agent的定时抓取频率(每天3次)可能还需要在高峰时段加密。

4.3 成本核算

  • 计算成本 :运行Agent的服务器成本(低配VPS)忽略不计。
  • API成本 :这是主要成本。一周内调用GPT-4 API分析约100次(部分抓取项被规则过滤),每次分析消耗的Token数约在800-1500之间(包含Prompt和Completion)。总成本大约在 3-5美元
  • 时间成本 :我每天花费约10-15分钟浏览Agent发送的日报,并跟进感兴趣的机会。相比之前自己手动刷多个网站, 每周节省了至少5-7小时 的盲目搜索时间。

结论 :第一周的投入产出比(ROI)从时间节省上看是 非常正面 的。虽然直接经济收益(一个进入洽谈的项目)尚未落袋,但获取高质量机会线索的效率得到了数量级的提升。金钱成本(API费用)在可接受范围内。

5. 遇到的问题、调试与优化

第一周绝非一帆风顺,遇到了不少技术性和逻辑性的问题。

5.1 技术性问题与解决

  1. 问题:网页结构变动导致抓取失败

    • 现象 :针对某论坛的抓取脚本在第三天突然返回空数据。
    • 排查 :手动访问页面,发现网站进行了一次前端UI微调,之前使用的CSS选择器失效了。
    • 解决 :我改用了更健壮的定位方式,例如通过XPath定位包含特定文本的父级容器,或者使用Playwright的 get_by_role() get_by_text() 等语义化定位器。 关键教训:为每个网站的抓取脚本编写一个简单的“健康检查” ,每天首次运行时,检查是否能提取到至少一条数据,如果失败则发送警报邮件给我,而不是静默失效。
  2. 问题:GPT-4分析结果格式不稳定

    • 现象 :偶尔,GPT-4的回复会在JSON前后加上markdown代码块标记( json ... )或额外的解释文字,导致 json.loads() 解析失败。
    • 解决 :在解析前增加一个文本清洗步骤,用正则表达式提取第一个出现的由 { } 包裹的JSON对象。
      import re
      def extract_json_from_text(text):
          json_match = re.search(r'\{.*\}', text, re.DOTALL)
          if json_match:
              return json_match.group(0)
          return None
      
  3. 问题:重复抓取与数据库锁

    • 现象 :当定时任务密集执行时,偶尔会出现数据库写入冲突(SQLite在并发写入时可能锁库)。
    • 解决 :将数据库操作封装为函数,并使用连接池和适当的重试机制。更简单的方案是,将抓取、分析、存储设计为顺序执行的流水线,而不是并行任务,虽然总时间变长,但稳定性极大提高。

5.2 策略性问题与优化

  1. 优化:动态调整抓取频率

    • 初始策略 :所有网站每天固定抓取3次。
    • 问题 :有些网站更新频率很低,有些则很高。固定频率要么浪费资源,要么错过信息。
    • 优化 :为每个网站源添加一个“活跃度”元数据。根据历史数据,计算该网站过去几天新机会出现的平均频率。对于高活跃度网站(如自由职业平台),提高抓取频率(如每4小时一次);对于低活跃度网站(如某些专业论坛),降低频率(如每天一次)。这需要在抓取效率和资源消耗间取得平衡。
  2. 优化:评分模型的反馈循环

    • 初始策略 :GPT-4的评分完全基于我预设的提示词。
    • 问题 :我发现AI对某些“看起来很好但实际是陷阱”(如要求先做测试题但预算极低)的机会识别不足。
    • 优化 :我在数据库中增加了“我的评分”字段。当我手动查看一个AI评分7分的机会后,如果我认为它只值3分,我会更新这个字段。计划未来用这些“纠正数据”来微调提示词,或者训练一个简单的本地分类模型作为GPT-4评分的前置过滤器。
  3. 优化:机会类型的细化

    • 初始策略 :机会类型分类较粗。
    • 问题 :“编程开发”这个大类下,Python后端、前端、数据分析的机会价值对我截然不同。
    • 优化 :我修改了提示词,要求GPT-4进行二级分类。例如,在“编程开发”下,进一步识别是“Web后端(Python)”、“数据爬虫”、“自动化脚本”还是“其他”。这让我在浏览报告时能更快地聚焦到我最擅长的领域。

6. 第一周总结与后续计划

第一周的实验证明,用AI Agent辅助寻找在线收入机会在 技术上是完全可行的 ,并且在 提升信息获取效率方面效果显著 。它就像一个不知疲倦的初级助理,帮我完成了从海量噪音中挖掘潜在信号的重度劳动。

最大的收获不是那一个进入洽谈的项目,而是验证了这个工作流的有效性 。我建立了一个自动化的、可迭代的“感知-分析-推送”系统。这个系统的核心价值在于将我的时间从“寻找”环节解放出来,投入到更高效的“评估与执行”环节。

对于也想尝试类似项目的朋友,我的核心建议是

  1. 从小处着手 :不要一开始就想监控几十个网站。先从1-2个你最熟悉、机会质量最高的平台开始,把整个流水线(抓取->分析->报告)跑通。
  2. 提示词是关键 :你给AI的指令越清晰,它的表现就越好。花时间精心设计并迭代你的分析提示词,这比换用更强大的模型可能更有效。
  3. 成本意识 :密切关注API调用费用。通过规则进行粗筛、压缩发送给AI的文本长度、合理设置调用频率,都是控制成本的必要手段。
  4. 保持人工最终裁决 :至少在可预见的未来,AI只是一个强大的过滤器,而不是决策者。最终是否值得投入时间,必须由你基于更全面的信息(如平台信誉、客户历史评价等)来判断。

接下来的计划

  • 扩展来源 :在现有流水线稳定的基础上,谨慎地增加1-2个新的机会来源,例如特定行业的远程工作板或 newsletters。
  • 丰富分析维度 :尝试让AI从机会描述中提取更多元的信息,例如判断项目的紧急程度、客户沟通风格(从文字描述中感知)等。
  • 探索本地模型 :为了进一步降低成本,我将尝试用性能较好的开源模型(如Llama 3 8B的量化版)来处理一部分分析任务,比如初步的分类和情感判断,只把最复杂的综合评分交给GPT-4。
  • 建立知识库 :将我发现的高质量机会、成功的申请话术、常见的“坑”整理起来,让AI在分析新机会时能参考这些历史经验,实现持续学习。

这个项目我会继续运行下去,并计划在第二周结束时,再分享一次迭代后的进展和新的发现。这条路看起来很有搞头,至少,它让寻找副业这件事,从一件枯燥的体力活,变成了一场有趣的人机协作实验。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐