
很多人问我:搭一个 AI Agent 需要学多少东西?
框架要选哪个?向量数据库怎么接?记忆系统怎么设计?编排层用 LangChain 还是自己写?
我想说一件事——这些问题在你搭第一个 Agent 之前,一个都不需要想。
因为 AI Agent 的核心架构,说到底就是一个文件夹。
这不是简化问题,是字面意思。
大部分人接触 Agent 的路径:
看文章 → 里面提到"工具调用、记忆系统、编排层、多 Agent 协作" → 脑子里建起一套复杂的技术印象 → 不知道从哪下手 → 放弃。
这条路我走过。当时以为搭一个 Agent 需要先弄懂一整套分布式系统,像搭微服务一样,要有独立的记忆服务、工具注册表、调度器……
结果有一天打开一个开源 Agent 项目的源码,翻了半天,发现里面就是一堆 markdown 文件和几个 Python 脚本。
那一刻我有点懵:就这?
对,就这。
把那些听起来很厉害的概念,逐个还原成它们实际的样子:
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 依赖
逐行翻译:
CLAUDE.md 的文本文件memory/ 里的 markdown 文件projects/ 里的几个 Python 脚本run.py,十几行代码串起所有步骤config/ 里的几个 JSON 配置没有隐藏的魔法层。一切都是文件。
看到这个文件夹结构,再去看市面上任何 Agent 项目,你都能一眼看出它的骨架。
先讲背景,下面的代码你才能看懂为什么这样写。
作为前端开发者 + 内容创作者,我订阅了 50+ 个技术类邮件:JavaScript Weekly、CSS Weekly、ByteByteGo、Frontend Focus、Node Weekly、ThisWeb……每周收到 100+ 封。
问题来了:
简单的邮件摘要解决不了这些。我要的不是"今天哪封邮件重要",而是:
本周 100+ 封 newsletter → Agent 自动处理 → 我看一份 5 分钟的报告
报告里包含:
1. 本周技术热点(按主题聚合)
2. 高价值内容 TOP 5(按价值排序,附原文链接)
3. 公众号选题建议(3-5 个,带写作切入点)
4. 值得深入学习的知识点
把"读邮件 + 整理 + 找选题"这三个动作,从每周 4-5 小时压缩到 30 分钟以内。
下面是完整的实现。所有代码我都跑通过,复制就能用。
打开终端(Mac 用 Terminal,Windows 用 PowerShell),执行:
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 文件夹长这样:
tech-agent/
├── CLAUDE.md ← 空文件,等会儿填
├── memory/
│ └── notes.md ← 空文件
├── projects/tech-newsletter/
├── config/
└── secrets/
在 tech-agent/ 根目录创建一个 requirements.txt:
anthropic>=0.40.0
python-dotenv>=1.0.0
然后安装:
pip install -r requirements.txt
如果你 Mac 提示 "externally-managed-environment",加上
--break-system-packages参数。
就这两个包:
anthropic:Anthropic 官方 SDK,用来调用 Claude APIpython-dotenv:从 .env 文件读取密钥用的(这样不用把密钥写死在代码里)需要两样东西,提前准备好:
1. Claude API Key
去 https://console.anthropic.com 注册账号,左边菜单 → API Keys → Create Key。新账号有 $5 免费额度,跑这个 Agent 能用一个多月。
2. 邮箱 IMAP 授权码
这不是你登录邮箱的密码,是邮箱专门给"第三方程序"用的临时密码。每个邮箱的获取方法不一样:
把授权码复制下来,下一步要用。
.env 文件)在 secrets/ 下新建 .env 文件,填入你的密钥:
# 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:
echo "secrets/" >> .gitignore
echo "*.env" >> .gitignore
这是这个 Agent 的关键设计:只处理白名单里的邮件,工作邮件、私人邮件完全不动。安全且高效。
在 config/newsletters.json 里列出你订阅的 newsletter 发件人邮箱:
{
"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:
# 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 标准库。
注意几个关键设计:
newsletters.json 里的发件人——这是安全底线在 projects/tech-newsletter/ 下新建 prompt.md:
你是阿森的技术内容分析师。阿森是一名前端开发者,运营着「前端达人」公众号,每周需要从订阅的技术 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 文件里有个好处:改提示词不用动代码,对非技术同学也友好。
在 projects/tech-newsletter/ 下创建 analyze.py:
# 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()
这里的流水线设计很重要,画出来给你看:
fetch_newsletters.py → analyze.py
(普通 Python 取数据) (唯一一次 AI 调用)
↓ ↓
inbox.txt weekly-digest.md
(中间文件) (最终输出)
整个流程只调用一次 Claude API。这是省钱的关键——以前我习惯每一步都用 AI,token 蹭蹭涨。后来发现"取数据"用普通代码、"理解+决策"才用 AI,成本降 80% 以上。
在 projects/tech-newsletter/ 下创建 run.py:
# 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 里写:
# CLAUDE.md
## 我是谁
我是阿森的技术内容分析助手。阿森是「前端达人」公众号作者,订阅了 50+ 个技术 newsletter,每周需要:
- 了解技术热点动态
- 寻找公众号选题
- 标记值得深入学习的知识点
## 工作原则
- 任务完成前不中断,遇到不确定情况先告知再继续
- 只读取 config/newsletters.json 白名单内的邮件
- 每周分析结果追加写入 memory/weekly-digest.md(保留历史)
## 当前能力
- 抓取并分析本周 newsletter(运行 projects/tech-newsletter/run.py)
## 禁止事项
- 不修改本文件(CLAUDE.md)
- 不向任何第三方服务发送邮件原文
- 不自动删除或标记已读邮件(只读,不写)
- 不读取白名单之外的邮件(保护工作邮件、私人邮件隐私)
回到项目根目录 tech-agent/,执行:
python projects/tech-newsletter/run.py
顺利的话你会看到:
📬 技术 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 分钟内你就能拿到这份报告。
imaplib.IMAP4.error: [AUTHENTICATIONFAILED]
99% 的原因:EMAIL_PASS 填的是邮箱登录密码,不是授权码。
QQ / 163 / Gmail 都需要单独生成一个授权码(参见第三步)。普通的登录密码是不能用于 IMAP 的。
anthropic.AuthenticationError: 401
检查 secrets/.env 里的 ANTHROPIC_API_KEY 是否正确,注意:
sk-ant- 开头,不是 sk-(那是 OpenAI 的)✅ 已写入 inbox.txt(0 期 newsletter)
最常见的原因:newsletters.json 里的发件人邮箱写错了。
正确做法:打开一封真实的 newsletter,复制完整的发件人地址,粘到 JSON 里。注意大小写不敏感(代码里都 .lower() 了),但拼写要一致。
anthropic.BadRequestError: prompt is too long
Newsletter 普遍很长,加起来容易爆。三个解法(在 fetch_newsletters.py 里改):
max_per_source:从 2 改成 1(每个源只取最新一期)body_truncated 的长度:从 2000 改成 1000days:从 7 天改成 3 天正文输出乱码通常是编码问题。fetch_newsletters.py 里的 decode_str() 和 get_body() 已经处理了大多数情况(UTF-8、GBK、Base64 编码的邮件头都能解)。
如果还有问题,在 get_body() 里加一行 print(charset),看看邮件用的是什么编码,大概率是 GBK,改一行即可。
手动跑一次是验证,每周自动跑才是真的 Agent。
macOS / Linux,用系统内置的 cron:
crontab -e
添加一行(每周一早上 8:00 自动运行):
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 的时间表达式,含义:
0:第 0 分钟8:8 点*:每月每一天都可以*:每个月1:星期一(0 是周日)>> cron.log 2>&1 的意思是把所有输出(包括错误)都追加到日志文件,出问题时可以查。
Windows,用任务计划程序:
操作 → 创建任务 → 触发器:每周一 08:00
程序:C:\Python311\python.exe
参数:C:\tech-agent\projects\tech-newsletter\run.py
设置完成后,每周一早上电脑自动跑一次,周报自动出现在 memory/weekly-digest.md 里。
Agent 第一次跑,大概率会出问题。可能是鉴权失败、周报质量不对、白名单没匹配到……
不要慌。 处理路径:
报错出现
│
▼
完整复制报错粘贴给 Claude
"帮我解释这个报错,并告诉我下一步怎么改"
│
├─ 周报质量差 → 改 prompt.md(不用动代码)
├─ 没匹配到邮件 → 检查 newsletters.json 的发件人邮箱
├─ 数据没进来 → 看 inbox.txt 是否生成、内容对不对
└─ 行为诡异 → 99% 是 CLAUDE.md 的指令有问题
定位问题的最快路径:倒着查。先看最后一步(周报)拿到了什么,再往上追数据断在哪。
我曾经做了一个"自我优化"层,让 Agent 根据行为记录自动更新指令文件。几天后,它悄悄改掉了我明确写入的规则,Agent 开始往预期外的方向漂移。
现在的做法:CLAUDE.md 受保护,禁止任何自动化写入。每个 project 有自己的小指令文件,自我优化只动那些,主干永远由你亲手改。
早期我的 Agent 里,取数据用 AI、格式化用 AI、过滤白名单也用 AI。结果还没干什么正事,今天的 token 配额就用了大半。
规则很简单:有判断和语言理解的地方用 AI,其余全用普通代码。 取邮件、过滤白名单、写文件,全是普通 Python 能搞定的事。一个任务里通常只需要一次模型调用,就是本文示例的做法。
Agent 会修改文件,有时候改的是你完全没预料到的地方。没有版本控制,被改坏的文件就真的没了。
# 第一天就做这几步
git init
echo "secrets/" >> .gitignore
echo "*.env" >> .gitignore
git add .
git commit -m "init: 第一版 agent 骨架"
设置好之后基本不用管,但它在你需要的时候会救命。
第一个月我的目标是让 Agent 管理所有事——日历、邮件、内容创作、财务……结果什么都没跑通。
最后真正能用的版本,是从一个任务开始,跑稳了再加下一个。先做一件事,做好,再做下一件。 这是唯一管用的节奏。
200K token 听起来很多。但 Agent 每次启动,还没干活就吃掉不少:
每次会话自动加载
────────────────────────────────
CLAUDE.md 全文 → 每次必读,固定成本
memory/weekly-digest.md → 如果让 Agent 读历史,会很大
当前任务的 prompt → 每次必读
本周抓取的 newsletter → 这个本身就 5K-10K
────────────────────────────────
真正留给模型推理的空间 = 总量 - 以上所有
两个最有效的控制手段:
weekly-digest.md 越积越大,每次只需要本周的,可以按周分文件存(weekly-digest-2024-w22.md 这样)向量数据库、语义检索——不是第一个月需要的东西。一个普通 markdown 文件足够撑很久。
AI Agent 有权限、会跑代码、能访问网络,是你机器上一个新的攻击面。
几个一开始就该养成的习惯:
newsletters.json 就是个安全设计——只处理白名单内的邮件,工作邮件、私人邮件完全不动secrets/ 永远不进 Git:已经在 .gitignore 里加了,每次 git commit 前 git status 确认一眼你需要的东西:
anthropic 和 python-dotenv完整项目结构:
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 清单」放下期。 另外:今天的代码你跑通了吗?卡在哪里——评论区实时答疑。 如果你跑通了,把你的周报截图发评论区,我会挑几份精彩的置顶 👇
关注「前端达人」,每周真实踩坑记录 + 可落地的工具方案。