首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >第1期:AI Agent 的核心架构,其实就是一个文件夹

第1期:AI Agent 的核心架构,其实就是一个文件夹

作者头像
前端达人
发布2026-05-25 12:33:35
发布2026-05-25 12:33:35
50
举报
文章被收录于专栏:前端达人前端达人

很多人问我:搭一个 AI Agent 需要学多少东西?

框架要选哪个?向量数据库怎么接?记忆系统怎么设计?编排层用 LangChain 还是自己写?

我想说一件事——这些问题在你搭第一个 Agent 之前,一个都不需要想

因为 AI Agent 的核心架构,说到底就是一个文件夹。

这不是简化问题,是字面意思。

我们对 Agent 的误解从哪来

大部分人接触 Agent 的路径:

看文章 → 里面提到"工具调用、记忆系统、编排层、多 Agent 协作" → 脑子里建起一套复杂的技术印象 → 不知道从哪下手 → 放弃。

这条路我走过。当时以为搭一个 Agent 需要先弄懂一整套分布式系统,像搭微服务一样,要有独立的记忆服务、工具注册表、调度器……

结果有一天打开一个开源 Agent 项目的源码,翻了半天,发现里面就是一堆 markdown 文件和几个 Python 脚本。

那一刻我有点懵:就这?

对,就这。

文件夹即架构,这句话是什么意思

把那些听起来很厉害的概念,逐个还原成它们实际的样子:

代码语言:javascript
复制
my-agent/
├── CLAUDE.md                       ← "指令系统"的真面目
├── memory/
│   ├── notes.md                    ← "记忆模块"的真面目
│   └── weekly-digest.md            ← Agent 写给你看的周报
├── projects/
│   └── tech-newsletter/
│       ├── fetch_newsletters.py    ← "工具"的真面目
│       ├── analyze.py              ← "AI 调用"的真面目
│       ├── run.py                  ← "编排层"的真面目
│       ├── prompt.md               ← "提示工程"的真面目
│       └── inbox.txt               ← 中间数据(脚本自动生成)
├── config/
│   └── newsletters.json            ← 订阅源白名单
├── secrets/
│   └── .env                        ← API 密钥(不提交 Git)
└── requirements.txt                ← Python 依赖

逐行翻译:

  • 有人说"Agent 有指令系统" → 一个叫 CLAUDE.md 的文本文件
  • 有人说"Agent 有记忆模块" → memory/ 里的 markdown 文件
  • 有人说"Agent 有工具调用" → projects/ 里的几个 Python 脚本
  • 有人说"Agent 有任务编排" → run.py,十几行代码串起所有步骤
  • 有人说"Agent 有知识库" → config/ 里的几个 JSON 配置

没有隐藏的魔法层。一切都是文件。

看到这个文件夹结构,再去看市面上任何 Agent 项目,你都能一眼看出它的骨架。

我搭这个 Agent 是为了解决什么问题

先讲背景,下面的代码你才能看懂为什么这样写。

作为前端开发者 + 内容创作者,我订阅了 50+ 个技术类邮件:JavaScript Weekly、CSS Weekly、ByteByteGo、Frontend Focus、Node Weekly、ThisWeb……每周收到 100+ 封。

问题来了:

  • 邮件太多,没时间一封一封看
  • 看了也记不住,过两周想找某个东西完全找不到
  • 知道里面有「前端达人」公众号能用的素材,但挖出来太麻烦
  • 想系统学习,但不知道这周该学什么

简单的邮件摘要解决不了这些。我要的不是"今天哪封邮件重要",而是:

代码语言:javascript
复制
本周 100+ 封 newsletter → Agent 自动处理 → 我看一份 5 分钟的报告

报告里包含:
  1. 本周技术热点(按主题聚合)
  2. 高价值内容 TOP 5(按价值排序,附原文链接)
  3. 公众号选题建议(3-5 个,带写作切入点)
  4. 值得深入学习的知识点

把"读邮件 + 整理 + 找选题"这三个动作,从每周 4-5 小时压缩到 30 分钟以内。

下面是完整的实现。所有代码我都跑通过,复制就能用。

动手:搭一个真实跑通的技术邮件分析 Agent

第一步:建文件夹骨架

打开终端(Mac 用 Terminal,Windows 用 PowerShell),执行:

代码语言:javascript
复制
mkdir tech-agent
cd tech-agent
mkdir -p memory projects/tech-newsletter config secrets
touch CLAUDE.md memory/notes.md

mkdir -p 的意思是"如果父目录不存在,一起创建"。Windows PowerShell 把 touch 换成 New-Item -ItemType File 文件名 就行。

跑完之后,你的 tech-agent 文件夹长这样:

代码语言:javascript
复制
tech-agent/
├── CLAUDE.md         ← 空文件,等会儿填
├── memory/
│   └── notes.md      ← 空文件
├── projects/tech-newsletter/
├── config/
└── secrets/

第二步:安装 Python 依赖

tech-agent/ 根目录创建一个 requirements.txt

代码语言:javascript
复制
anthropic>=0.40.0
python-dotenv>=1.0.0

然后安装:

代码语言:javascript
复制
pip install -r requirements.txt

如果你 Mac 提示 "externally-managed-environment",加上 --break-system-packages 参数。

就这两个包:

  • anthropic:Anthropic 官方 SDK,用来调用 Claude API
  • python-dotenv:从 .env 文件读取密钥用的(这样不用把密钥写死在代码里)

第三步:申请 API Key 和邮箱授权码

需要两样东西,提前准备好:

1. Claude API Key

去 https://console.anthropic.com 注册账号,左边菜单 → API Keys → Create Key。新账号有 $5 免费额度,跑这个 Agent 能用一个多月。

2. 邮箱 IMAP 授权码

这不是你登录邮箱的密码,是邮箱专门给"第三方程序"用的临时密码。每个邮箱的获取方法不一样:

  • QQ 邮箱:网页版 → 设置 → 账户 → 开启 IMAP/SMTP → 生成授权码(16 位)
  • 163 邮箱:网页版 → 设置 → POP3/SMTP/IMAP → 开启 IMAP → 设置客户端授权密码
  • Gmail:开启两步验证 → Google 账号 → 安全 → 应用专用密码

把授权码复制下来,下一步要用。

第四步:配置密钥(.env 文件)

secrets/ 下新建 .env 文件,填入你的密钥:

代码语言:javascript
复制
# secrets/.env

# Claude API Key(第 3 步申请的)
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxx

# 邮箱配置(按你的邮箱选一段,删掉其他段)

# ── QQ 邮箱 ──────────────────────────────
IMAP_HOST=imap.qq.com
IMAP_PORT=993
EMAIL_USER=your@qq.com
EMAIL_PASS=刚才生成的16位授权码

# ── 163 邮箱 ──────────────────────────────
# IMAP_HOST=imap.163.com
# IMAP_PORT=993
# EMAIL_USER=your@163.com
# EMAIL_PASS=你的授权密码

# ── Gmail ────────────────────────────────
# IMAP_HOST=imap.gmail.com
# IMAP_PORT=993
# EMAIL_USER=your@gmail.com
# EMAIL_PASS=应用专用密码(16 位字母)

然后把 secrets/ 加进 .gitignore——这是底线,密钥绝对不能进 Git:

代码语言:javascript
复制
echo "secrets/" >> .gitignore
echo "*.env" >> .gitignore

第五步:配置订阅源白名单

这是这个 Agent 的关键设计:只处理白名单里的邮件,工作邮件、私人邮件完全不动。安全且高效。

config/newsletters.json 里列出你订阅的 newsletter 发件人邮箱:

代码语言:javascript
复制
{
  "newsletters": [
    {
      "name": "JavaScript Weekly",
      "from": "javascriptweekly@cooperpress.com",
      "category": "前端"
    },
    {
      "name": "CSS Weekly",
      "from": "zoran@cssweekly.com",
      "category": "前端"
    },
    {
      "name": "Frontend Focus",
      "from": "frontendfocus@cooperpress.com",
      "category": "前端"
    },
    {
      "name": "ByteByteGo",
      "from": "alex@bytebytego.com",
      "category": "架构"
    },
    {
      "name": "ThisWeb 请网这边走",
      "from": "kun@thisweb.io",
      "category": "前端"
    }
  ]
}

怎么知道发件人邮箱是啥? 打开任意一封 newsletter,看发件人字段,复制 <> 里那串邮箱地址,粘过来即可。

第一次配置只需要 10 分钟。后面想加新订阅源,就来这里加一行 JSON。

第六步:写取件脚本(这是干活的代码)

projects/tech-newsletter/ 下创建 fetch_newsletters.py

代码语言:javascript
复制
# projects/tech-newsletter/fetch_newsletters.py
# 干一件事:把订阅的 newsletter 邮件下载下来,存成一个文本文件

import imaplib                              # 标准库:连接邮箱
import email                                # 标准库:解析邮件
import json
from email.header import decode_header      # 解析邮件头里的中文/特殊字符
from datetime import datetime, timedelta
from pathlib import Path
import os
import sys

# ───── 读取 secrets/.env 里的密钥 ─────
from dotenv import load_dotenv
load_dotenv(dotenv_path=Path(__file__).parent.parent.parent / "secrets" / ".env")

IMAP_HOST  = os.getenv("IMAP_HOST")
IMAP_PORT  = int(os.getenv("IMAP_PORT", 993))
EMAIL_USER = os.getenv("EMAIL_USER")
EMAIL_PASS = os.getenv("EMAIL_PASS")

# ───── 读取白名单 ─────
config_path = Path(__file__).parent.parent.parent / "config" / "newsletters.json"
with open(config_path, encoding="utf-8") as f:
    newsletters_config = json.load(f)

# 把白名单转成 dict,方便后面"按邮箱地址查找"
# 形如:{"javascriptweekly@cooperpress.com": {"name": "...", "category": "..."}}
SENDER_MAP = {
    nl["from"].lower(): nl
    for nl in newsletters_config["newsletters"]
}


def decode_str(s):
    """解码邮件头。邮件头里的中文/日文等非英语字符是经过特殊编码的,
       直接读取会变成乱码(比如 =?UTF-8?B?...?=),这个函数把它解回正常字符。"""
    ifnot s:
        return""
    parts = decode_header(s)
    result = []
    for part, charset in parts:
        if isinstance(part, bytes):
            # bytes 类型:需要按指定编码解码(默认按 UTF-8 试,失败的字符用 ? 替代)
            result.append(part.decode(charset or"utf-8", errors="replace"))
        else:
            result.append(str(part))
    return"".join(result)


def extract_sender_email(from_header):
    """从 'JavaScript Weekly <javascriptweekly@cooperpress.com>' 中
       提取出 'javascriptweekly@cooperpress.com'"""
    decoded = decode_str(from_header).lower()
    if"<"in decoded and">"in decoded:
        return decoded.split("<")[1].split(">")[0].strip()
    return decoded.strip()


def get_body(msg):
    """提取邮件正文。邮件可能是单部分(纯文本)或多部分(文本+HTML+附件),
       这里只取 text/plain 部分,忽略 HTML 和附件。"""
    if msg.is_multipart():
        for part in msg.walk():
            if part.get_content_type() == "text/plain":
                charset = part.get_content_charset() or"utf-8"
                try:
                    return part.get_payload(decode=True).decode(charset, errors="replace").strip()
                except Exception:
                    return""
    else:
        charset = msg.get_content_charset() or"utf-8"
        try:
            return msg.get_payload(decode=True).decode(charset, errors="replace").strip()
        except Exception:
            return""
    return""


def fetch_newsletters(days=7, max_per_source=2):
    """主函数:抓取过去 N 天的 newsletter。
       max_per_source: 每个订阅源最多取最近几期,避免某个源刷屏。"""
    # 检查配置
    ifnot all([IMAP_HOST, EMAIL_USER, EMAIL_PASS]):
        print("❌ 请先在 secrets/.env 中配置邮箱信息")
        sys.exit(1)

    # IMAP 协议要求的日期格式:'24-May-2024'
    since_date = (datetime.now() - timedelta(days=days)).strftime("%d-%b-%Y")

    # ───── 连接邮箱 ─────
    print(f"  正在连接 {IMAP_HOST}...")
    mail = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
    mail.login(EMAIL_USER, EMAIL_PASS)
    mail.select("INBOX")    # 选择收件箱

    # ───── 搜索过去 N 天的所有邮件 ─────
    # SINCE 是 IMAP 关键字,表示"该日期之后"
    _, data = mail.search(None, f'(SINCE "{since_date}")')
    email_ids = data[0].split()    # 返回的是 [b'1', b'2', b'3', ...] 这种邮件 ID 列表
    print(f"  找到 {len(email_ids)} 封邮件,开始按白名单过滤...")

    # ───── 按订阅源收集邮件 ─────
    matched_emails = []     # 最终匹配上的 newsletter
    source_count = {}       # 每个源已经收了几封,用于限流

    # reversed 是从最新邮件开始处理(IMAP 默认返回的是从旧到新的 ID)
    for eid in reversed(email_ids):
        # RFC822 是邮件的标准格式,这里告诉 IMAP "把整封邮件原文给我"
        _, msg_data = mail.fetch(eid, "(RFC822)")
        msg = email.message_from_bytes(msg_data[0][1])

        sender_email = extract_sender_email(msg.get("From", ""))

        # 不在白名单里的,跳过
        if sender_email notin SENDER_MAP:
            continue

        source_info = SENDER_MAP[sender_email]
        source_name = source_info["name"]

        # 每个源已经收够了,跳过
        if source_count.get(source_name, 0) >= max_per_source:
            continue

        # ───── 提取信息 ─────
        subject = decode_str(msg.get("Subject", "(无主题)"))
        date    = msg.get("Date", "")
        body    = get_body(msg)

        # Newsletter 普遍很长(几千上万字),截断防止 token 爆炸
        body_truncated = body[:2000] if body else""

        source_count[source_name] = source_count.get(source_name, 0) + 1

        matched_emails.append({
            "source":   source_name,
            "category": source_info["category"],
            "subject":  subject,
            "date":     date,
            "content":  body_truncated,
        })

    mail.logout()
    return matched_emails


# ───── 命令行入口 ─────
if __name__ == "__main__":
    emails = fetch_newsletters(days=7, max_per_source=2)

    ifnot emails:
        result = "过去 7 天没有订阅 newsletter 邮件。\n请检查 config/newsletters.json 的发件人邮箱是否正确。"
    else:
        # 拼成一段文本,方便给 Claude 看
        lines = [f"# 本周技术 Newsletter 汇总(共 {len(emails)} 期)\n"]
        for i, e in enumerate(emails, 1):
            lines.append(
                f"\n--- 第 {i} 期 | {e['source']}({e['category']})---\n"
                f"主题:{e['subject']}\n"
                f"时间:{e['date']}\n"
                f"内容:\n{e['content']}\n"
            )
        result = "\n".join(lines)

    # 写入文件
    out_path = Path(__file__).parent / "inbox.txt"
    out_path.write_text(result, encoding="utf-8")
    print(f"  ✅ 已写入 inbox.txt({len(emails)} 期 newsletter)")

这个脚本只做一件事:按白名单过滤,把订阅的 newsletter 下载下来存成文本。没有任何 AI 调用,全是 Python 标准库。

注意几个关键设计:

  1. 白名单过滤:不处理工作邮件、私人邮件,只处理 newsletters.json 里的发件人——这是安全底线
  2. 每个源限量:每个 newsletter 最多取最近 2 期,避免某个源把其他源都挤掉
  3. 内容截断:每封最多 2000 字。Newsletter 普遍很长,不截断 token 会爆

第七步:写提示词(告诉 Claude 怎么分析)

projects/tech-newsletter/ 下新建 prompt.md

代码语言:javascript
复制
你是阿森的技术内容分析师。阿森是一名前端开发者,运营着「前端达人」公众号,每周需要从订阅的技术 newsletter 中:

1. 了解本周技术动向
2. 找到 3-5 个可写成公众号文章的选题
3. 标记值得深入学习的知识点

以下是本周的 newsletter 汇总:

---
{inbox_content}
---

请按以下结构输出一份周报(Markdown 格式):

## 📊 本周热点主题
按"前端框架 / AI & LLM / 架构设计 / 开发工具 / 性能优化"分类,列出本周出现的关键话题。每个主题 1-2 句话点评。

## 🚀 高价值内容 TOP 5
从所有 newsletter 中挑出最有价值的 5 条,按价值排序。每条包含:
- 标题
- 来源 newsletter
- 一句话亮点
- 价值评分(1-5 星)

## 💡 公众号选题建议
给出 3 个适合「前端达人」公众号的文章选题,每个包含:
- **选题**:标题方向
- **来源**:哪封 newsletter 启发的
- **写作切入点**:怎么写比较有特色
- **预计字数**:1500 / 3000 / 5000 字
- **推荐指数**:⭐⭐⭐⭐⭐

## 📚 值得深入学习的知识点
列出 3-5 个本周出现的、对前端开发者有用的概念或工具。每条 1-2 句话说明为什么值得学。

要求:
- 不要复述邮件原文,提取精华即可
- 选题要具体可写,不要泛泛而谈
- 全程使用中文输出(newsletter 原文是英文)
- 不输出任何"以下是周报内容"之类的开场白,直接输出报告

把提示词放在单独的 markdown 文件里有个好处:改提示词不用动代码,对非技术同学也友好。

第八步:写分析脚本(调用 Claude 生成周报)

projects/tech-newsletter/ 下创建 analyze.py

代码语言:javascript
复制
# projects/tech-newsletter/analyze.py
# 干一件事:读取 inbox.txt,让 Claude 分析,把结果写到 memory/weekly-digest.md

import os
import anthropic
from pathlib import Path
from datetime import datetime

from dotenv import load_dotenv
load_dotenv(dotenv_path=Path(__file__).parent.parent.parent / "secrets" / ".env")


def analyze_newsletters():
    project_dir = Path(__file__).parent
    memory_dir  = Path(__file__).parent.parent.parent / "memory"
    memory_dir.mkdir(parents=True, exist_ok=True)

    # ───── 读取上一步生成的 inbox.txt ─────
    inbox_path = project_dir / "inbox.txt"
    ifnot inbox_path.exists():
        print("❌ inbox.txt 不存在,请先运行 fetch_newsletters.py")
        return

    inbox_content = inbox_path.read_text(encoding="utf-8")

    if"没有订阅 newsletter"in inbox_content:
        print("  📭 本周没有 newsletter,跳过分析")
        return

    # ───── 拼接提示词 ─────
    prompt_template = (project_dir / "prompt.md").read_text(encoding="utf-8")
    prompt = prompt_template.replace("{inbox_content}", inbox_content)

    # 估算输入长度(1 个 token 约等于 3-4 个英文字符或 1-2 个汉字)
    print("  正在调用 Claude 生成周报...")
    print(f"  输入 token 估算:约 {len(prompt) // 3} tokens")

    # ───── 调用 Claude API(整个流程只在这一步消耗 token)─────
    client  = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
    message = client.messages.create(
        model="claude-sonnet-4-20250514",   # 平衡性价比的模型
        max_tokens=4000,                     # 输出最多 4000 token(周报比较长)
        messages=[{"role": "user", "content": prompt}],
    )
    report = message.content[0].text

    # ───── 保存周报到 memory/ ─────
    today = datetime.now().strftime("%Y-%m-%d")
    week_num = datetime.now().isocalendar()[1]    # 当前是一年中第几周

    report_with_meta = (
        f"# 第 {week_num} 周技术周报({today})\n\n"
        f"_由 Agent 自动生成 | 输入 {message.usage.input_tokens} tokens | "
        f"输出 {message.usage.output_tokens} tokens_\n\n"
        f"---\n\n{report}\n"
    )

    weekly_path = memory_dir / "weekly-digest.md"

    # 追加到周报文件(新的放最前面,历史保留在下面)
    if weekly_path.exists():
        existing = weekly_path.read_text(encoding="utf-8")
        weekly_path.write_text(report_with_meta + "\n\n---\n\n" + existing, encoding="utf-8")
    else:
        weekly_path.write_text(report_with_meta, encoding="utf-8")

    # ───── 打印结果 ─────
    print("\n  ✅ 周报生成完成:")
    print("=" * 60)
    print(report)
    print("=" * 60)
    print(f"\n  已保存至 memory/weekly-digest.md")
    print(f"  本次消耗:输入 {message.usage.input_tokens} + 输出 {message.usage.output_tokens} tokens")


if __name__ == "__main__":
    analyze_newsletters()

这里的流水线设计很重要,画出来给你看:

代码语言:javascript
复制
fetch_newsletters.py    →    analyze.py
(普通 Python 取数据)       (唯一一次 AI 调用)
        ↓                          ↓
    inbox.txt              weekly-digest.md
    (中间文件)              (最终输出)

整个流程只调用一次 Claude API。这是省钱的关键——以前我习惯每一步都用 AI,token 蹭蹭涨。后来发现"取数据"用普通代码、"理解+决策"才用 AI,成本降 80% 以上。

第九步:写主入口(一条命令跑完所有步骤)

projects/tech-newsletter/ 下创建 run.py

代码语言:javascript
复制
# projects/tech-newsletter/run.py
# 干一件事:把两个脚本串起来,一条命令搞定

import subprocess
import sys
from pathlib import Path

# 把所有要执行的步骤按顺序列出来
STEPS = [
    ("fetch_newsletters.py", "第一步:抓取本周订阅 newsletter"),
    ("analyze.py",            "第二步:生成技术周报"),
]


def run_step(script, description):
    """跑一个脚本,失败就停下"""
    print(f"\n▶ {description}")
    result = subprocess.run(
        [sys.executable, script],
        cwd=Path(__file__).parent,    # 当前脚本所在目录
        capture_output=False,          # 实时打印输出,不要捕获
    )
    if result.returncode != 0:
        print(f"\n❌ {description} 失败,已停止")
        sys.exit(1)


if __name__ == "__main__":
    print("📬 技术 Newsletter 分析 Agent 启动")
    for script, desc in STEPS:
        run_step(script, desc)
    print("\n✅ 全部完成!查看 memory/weekly-digest.md 看本周技术周报")

第十步:写 CLAUDE.md(Agent 的"宪法")

在项目根目录的 CLAUDE.md 里写:

代码语言:javascript
复制
# CLAUDE.md

## 我是谁
我是阿森的技术内容分析助手。阿森是「前端达人」公众号作者,订阅了 50+ 个技术 newsletter,每周需要:
- 了解技术热点动态
- 寻找公众号选题
- 标记值得深入学习的知识点

## 工作原则
- 任务完成前不中断,遇到不确定情况先告知再继续
- 只读取 config/newsletters.json 白名单内的邮件
- 每周分析结果追加写入 memory/weekly-digest.md(保留历史)

## 当前能力
- 抓取并分析本周 newsletter(运行 projects/tech-newsletter/run.py)

## 禁止事项
- 不修改本文件(CLAUDE.md)
- 不向任何第三方服务发送邮件原文
- 不自动删除或标记已读邮件(只读,不写)
- 不读取白名单之外的邮件(保护工作邮件、私人邮件隐私)

第十一步:跑起来

回到项目根目录 tech-agent/,执行:

代码语言:javascript
复制
python projects/tech-newsletter/run.py

顺利的话你会看到:

代码语言:javascript
复制
📬 技术 Newsletter 分析 Agent 启动

▶ 第一步:抓取本周订阅 newsletter
  正在连接 imap.qq.com...
  找到 217 封邮件,开始按白名单过滤...
  ✅ 已写入 inbox.txt(11 期 newsletter)

▶ 第二步:生成技术周报
  正在调用 Claude 生成周报...
  输入 token 估算:约 8240 tokens

  ✅ 周报生成完成:
============================================================
## 📊 本周热点主题

### 前端框架(占比 35%)
Svelte 5 的 runes 响应式系统讨论度最高,多个 newsletter 都
做了深度分析。Vue 3.5 发布预告,重点是性能优化。

### AI & LLM(占比 25%)
Claude API 推出了 prompt caching 功能,可大幅降低重复调用
成本。本地推理方面,Llama 3 的优化方案有新进展。

## 🚀 高价值内容 TOP 5

1. **Svelte 5 Runes 完整指南**
   来源:JavaScript Weekly
   亮点:用普通 JS 就能写响应式,告别 `$:` 语法
   评分:⭐⭐⭐⭐⭐

[...省略余下内容...]

## 💡 公众号选题建议

### 选题 1:从 Svelte 5 看响应式系统的演进
- **来源**:JavaScript Weekly + 你之前写过的 Vue/React 对比
- **写作切入点**:横向对比三大框架的响应式实现,配可视化图
- **预计字数**:3000 字
- **推荐指数**:⭐⭐⭐⭐⭐
============================================================

  已保存至 memory/weekly-digest.md
  本次消耗:输入 8240 + 输出 1856 tokens

每周一次,5 分钟内你就能拿到这份报告。

常见报错和处理方法

IMAP 连接失败

代码语言:javascript
复制
imaplib.IMAP4.error: [AUTHENTICATIONFAILED]

99% 的原因EMAIL_PASS 填的是邮箱登录密码,不是授权码

QQ / 163 / Gmail 都需要单独生成一个授权码(参见第三步)。普通的登录密码是不能用于 IMAP 的。

API Key 无效

代码语言:javascript
复制
anthropic.AuthenticationError: 401

检查 secrets/.env 里的 ANTHROPIC_API_KEY 是否正确,注意:

  • 别多了空格或换行
  • sk-ant- 开头,不是 sk-(那是 OpenAI 的)

白名单匹配不到任何邮件

代码语言:javascript
复制
✅ 已写入 inbox.txt(0 期 newsletter)

最常见的原因:newsletters.json 里的发件人邮箱写错了。

正确做法:打开一封真实的 newsletter,复制完整的发件人地址,粘到 JSON 里。注意大小写不敏感(代码里都 .lower() 了),但拼写要一致。

Token 超限

代码语言:javascript
复制
anthropic.BadRequestError: prompt is too long

Newsletter 普遍很长,加起来容易爆。三个解法(在 fetch_newsletters.py 里改):

  1. 减小 max_per_source:从 2 改成 1(每个源只取最新一期)
  2. 减小 body_truncated 的长度:从 2000 改成 1000
  3. 减小 days:从 7 天改成 3 天

中文乱码

正文输出乱码通常是编码问题。fetch_newsletters.py 里的 decode_str()get_body() 已经处理了大多数情况(UTF-8、GBK、Base64 编码的邮件头都能解)。

如果还有问题,在 get_body() 里加一行 print(charset),看看邮件用的是什么编码,大概率是 GBK,改一行即可。

让它自动跑起来:cron 定时任务

手动跑一次是验证,每周自动跑才是真的 Agent。

macOS / Linux,用系统内置的 cron:

代码语言:javascript
复制
crontab -e

添加一行(每周一早上 8:00 自动运行):

代码语言:javascript
复制
0 8 * * 1 /usr/bin/python3 /Users/你的用户名/tech-agent/projects/tech-newsletter/run.py >> /Users/你的用户名/tech-agent/memory/cron.log 2>&1

0 8 * * 1 是 cron 的时间表达式,含义:

  • 第 1 位 0:第 0 分钟
  • 第 2 位 8:8 点
  • 第 3 位 *:每月每一天都可以
  • 第 4 位 *:每个月
  • 第 5 位 1:星期一(0 是周日)

>> cron.log 2>&1 的意思是把所有输出(包括错误)都追加到日志文件,出问题时可以查。

Windows,用任务计划程序:

代码语言:javascript
复制
操作 → 创建任务 → 触发器:每周一 08:00
程序:C:\Python311\python.exe
参数:C:\tech-agent\projects\tech-newsletter\run.py

设置完成后,每周一早上电脑自动跑一次,周报自动出现在 memory/weekly-digest.md 里。

一定会出问题,这才是真正的学习

Agent 第一次跑,大概率会出问题。可能是鉴权失败、周报质量不对、白名单没匹配到……

不要慌。 处理路径:

代码语言:javascript
复制
报错出现
  │
  ▼
完整复制报错粘贴给 Claude
"帮我解释这个报错,并告诉我下一步怎么改"
  │
  ├─ 周报质量差 → 改 prompt.md(不用动代码)
  ├─ 没匹配到邮件 → 检查 newsletters.json 的发件人邮箱
  ├─ 数据没进来 → 看 inbox.txt 是否生成、内容对不对
  └─ 行为诡异 → 99% 是 CLAUDE.md 的指令有问题

定位问题的最快路径:倒着查。先看最后一步(周报)拿到了什么,再往上追数据断在哪。

我踩过的坑,挑最重要的说

坑 1:让 Agent 自己改 CLAUDE.md

我曾经做了一个"自我优化"层,让 Agent 根据行为记录自动更新指令文件。几天后,它悄悄改掉了我明确写入的规则,Agent 开始往预期外的方向漂移。

现在的做法CLAUDE.md 受保护,禁止任何自动化写入。每个 project 有自己的小指令文件,自我优化只动那些,主干永远由你亲手改。

坑 2:每一步都调用 AI

早期我的 Agent 里,取数据用 AI、格式化用 AI、过滤白名单也用 AI。结果还没干什么正事,今天的 token 配额就用了大半。

规则很简单:有判断和语言理解的地方用 AI,其余全用普通代码。 取邮件、过滤白名单、写文件,全是普通 Python 能搞定的事。一个任务里通常只需要一次模型调用,就是本文示例的做法。

坑 3:没有 Git

Agent 会修改文件,有时候改的是你完全没预料到的地方。没有版本控制,被改坏的文件就真的没了。

代码语言:javascript
复制
# 第一天就做这几步
git init
echo "secrets/" >> .gitignore
echo "*.env" >> .gitignore
git add .
git commit -m "init: 第一版 agent 骨架"

设置好之后基本不用管,但它在你需要的时候会救命。

坑 4:一上来就搭"全能助理"

第一个月我的目标是让 Agent 管理所有事——日历、邮件、内容创作、财务……结果什么都没跑通。

最后真正能用的版本,是从一个任务开始,跑稳了再加下一个。先做一件事,做好,再做下一件。 这是唯一管用的节奏。

上下文窗口:你迟早会遇到的限制

200K token 听起来很多。但 Agent 每次启动,还没干活就吃掉不少:

代码语言:javascript
复制
每次会话自动加载
────────────────────────────────
CLAUDE.md 全文           → 每次必读,固定成本
memory/weekly-digest.md  → 如果让 Agent 读历史,会很大
当前任务的 prompt        → 每次必读
本周抓取的 newsletter    → 这个本身就 5K-10K
────────────────────────────────
真正留给模型推理的空间  =  总量 - 以上所有

两个最有效的控制手段:

  1. CLAUDE.md 越短越好,超过 100 行就要怀疑自己有没有在废话
  2. 历史文件不要让 Agent 全读。比如 weekly-digest.md 越积越大,每次只需要本周的,可以按周分文件存(weekly-digest-2024-w22.md 这样)

向量数据库、语义检索——不是第一个月需要的东西。一个普通 markdown 文件足够撑很久。

安全意识:从第一天就要有

AI Agent 有权限、会跑代码、能访问网络,是你机器上一个新的攻击面。

几个一开始就该养成的习惯:

  • 白名单优先:本文的 newsletters.json 就是个安全设计——只处理白名单内的邮件,工作邮件、私人邮件完全不动
  • secrets/ 永远不进 Git:已经在 .gitignore 里加了,每次 git commitgit status 确认一眼
  • 第三方插件装之前先读源码:一个插件是在你机器上以你的权限运行的代码,装陌生人写的东西要知道它在做什么
  • Agent 用它自己的账号:给 Agent 单独注册一个邮箱专门做信息聚合,不和主邮箱混

小结:今晚就能开始的事

你需要的东西:

  • 一台你已经有的电脑
  • Claude API Key(console.anthropic.com 注册,新账号有 $5 免费额度,跑这个 Agent 够用一个多月)
  • 两个 Python 包:anthropicpython-dotenv
  • 你邮箱的 IMAP 授权码
  • 10 分钟整理一下你订阅的 newsletter 发件人邮箱

完整项目结构:

代码语言:javascript
复制
tech-agent/
├── .gitignore
├── CLAUDE.md
├── requirements.txt
├── memory/
│   ├── notes.md
│   └── weekly-digest.md            ← 每周自动追加
├── projects/tech-newsletter/
│   ├── fetch_newsletters.py        ← 抓 newsletter
│   ├── analyze.py                  ← 调用 Claude 生成周报
│   ├── run.py                      ← 一键运行入口
│   ├── prompt.md                   ← 提示词(可随时改)
│   └── inbox.txt                   ← 中间文件(自动生成)
├── config/
│   └── newsletters.json            ← 订阅源白名单
└── secrets/
    └── .env                        ← 密钥(不进 Git)

先跑通这一个任务。稳了之后再加下一个——比如把周报里的"公众号选题建议"做成独立的 Agent,自动起标题、写大纲。

架构会自己长出来,不需要提前设计。

AI Agent 的核心,就是这个文件夹。先把文件夹建起来。

互动环节 你订阅了哪些技术 newsletter?哪个最值? 留言告诉我,我整理一份「中文开发者必订 Newsletter 清单」放下期。 另外:今天的代码你跑通了吗?卡在哪里——评论区实时答疑。 如果你跑通了,把你的周报截图发评论区,我会挑几份精彩的置顶 👇


关注「前端达人」,每周真实踩坑记录 + 可落地的工具方案。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 我们对 Agent 的误解从哪来
  • 文件夹即架构,这句话是什么意思
  • 我搭这个 Agent 是为了解决什么问题
  • 动手:搭一个真实跑通的技术邮件分析 Agent
    • 第一步:建文件夹骨架
    • 第二步:安装 Python 依赖
    • 第三步:申请 API Key 和邮箱授权码
    • 第四步:配置密钥(.env 文件)
    • 第五步:配置订阅源白名单
    • 第六步:写取件脚本(这是干活的代码)
    • 第七步:写提示词(告诉 Claude 怎么分析)
    • 第八步:写分析脚本(调用 Claude 生成周报)
    • 第九步:写主入口(一条命令跑完所有步骤)
    • 第十步:写 CLAUDE.md(Agent 的"宪法")
    • 第十一步:跑起来
  • 常见报错和处理方法
    • IMAP 连接失败
    • API Key 无效
    • 白名单匹配不到任何邮件
    • Token 超限
    • 中文乱码
    • 让它自动跑起来:cron 定时任务
  • 一定会出问题,这才是真正的学习
  • 我踩过的坑,挑最重要的说
    • 坑 1:让 Agent 自己改 CLAUDE.md
    • 坑 2:每一步都调用 AI
    • 坑 3:没有 Git
    • 坑 4:一上来就搭"全能助理"
  • 上下文窗口:你迟早会遇到的限制
  • 安全意识:从第一天就要有
  • 小结:今晚就能开始的事
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档