基础知识 · 小白入门

AI & Agent 入门 101 —— 给小白的全图谱

13 个最重要的概念,从「AI 是什么」一路讲到「怎么动手做一个 Agent」。全程不夹生黑话,看完能跟工程师对话不脱节。

作者 栗子 更新于 2026 年 5 月 约 25 分钟
CHAPTER 01

AI / ML / DL / 神经网络 —— 这些词到底什么关系?

这四个词被混着用了几十年,但它们是同心圆关系:外圈最大,内圈最具体。

1.1 一张图记住四者关系

从外到内,每一层都是上一层的子集:

  • AI(人工智能) —— 最大的一圈。所有让机器表现出智能行为的方法都算,包括 1970 年代的专家系统、围棋穷举搜索、规则引擎。"会下棋"和"会聊天"都属于 AI。
  • ML(机器学习 / Machine Learning) —— AI 的子集。不靠人写规则,而是让机器从数据里"自己学规律"。比如垃圾邮件分类、信用卡欺诈检测,都属于 ML。
  • DL(深度学习 / Deep Learning) —— ML 的子集。用多层神经网络做学习。"深度"指的是层数多(几十到几百层),不是"理解得深"。
  • 神经网络(Neural Network) —— DL 的具体实现工具。一组用矩阵乘法堆出来的数学结构,灵感来自生物神经元,但其实跟生物脑差得远。
类比: AI 是"医学",ML 是"内科",DL 是"心血管科",神经网络是"心导管手术这门具体技术"。聊天的时候随便用,但讨论实现时要分清。

1.2 LLM 在哪一层?

LLM(Large Language Model)属于深度学习这一层,用的是一种叫 Transformer 的神经网络结构(2017 年 Google 那篇 Attention Is All You Need)。"大"指的是参数量大 —— 从几十亿到上万亿不等。

1.3 一个常见误解:AI ≠ 神经网络

很多人以为 AI 就是神经网络,但 AlphaGo 的核心其实是蒙特卡洛树搜索(一种搜索算法)+ 神经网络辅助评估;自动驾驶是 感知(神经网络)+ 规划(搜索/优化)+ 控制(控制论) 的拼盘;很多金融风控模型至今还在用梯度提升树(GBDT)而不是神经网络。神经网络很强,但不是 AI 的全部。

CHAPTER 02

LLM 是怎么工作的 —— Token、上下文、概率采样

如果你只看一段,看这段:LLM 的本质是「给定前面的文字,猜下一个 token 是什么」的概率机器。仅此而已。它的所有"聪明"都从这一个简单动作里涌现出来。

2.1 Token 是什么 —— 文字的最小单位

LLM 不是按"字"或"词"思考的,而是按 token。一个 token 大概是:

  • 英文里 ≈ 0.75 个单词。比如 understanding 可能被切成 under + stand + ing 三个 token。
  • 中文里 ≈ 1.5 个汉字。"机器学习" 可能是 1 ~ 2 个 token。
  • 标点符号、空格、换行也都是 token。

为什么要切成 token?因为词汇表必须有限(一般是 5 万 ~ 20 万),不可能为世界上每个单词都分配一个编号,否则模型存不下。Token 是"够用又不太多"的折中。

2.2 上下文窗口 —— LLM 能"看到"多少

每次对话,你输入的内容 + 系统提示 + 历史聊天记录全部加起来不能超过上下文窗口(context window)。这个长度按模型而定:

  • GPT-3.5 早期:4K tokens(约 3000 汉字)
  • GPT-4 / Claude 3:128K ~ 200K tokens(一本短篇小说)
  • Gemini 1.5 / Claude 4:1M ~ 2M tokens(《红楼梦》全本还有富余)

超出窗口怎么办?模型看不到。聊天软件会自动截断或摘要历史消息塞回去 —— 这就是为什么长聊天到后面,AI 会"忘"了你十几条之前说过的话。

2.3 概率采样 —— 它不是在"想",是在"掷骰子"

当 LLM 看完输入后,它内部会对词汇表里所有 token 算一个概率分布:下一个 token 是 苹果 的概率 18%、香蕉 12%、桃子 7%……然后从这个分布里采样一个出来。所谓"温度(temperature)"参数就是控制采样的随机性 —— 温度 0 时永远选概率最高的(确定输出),温度高时偶尔选低概率的(创意输出)。

所以:同一个 prompt 多次提问,答案可能不一样,不是"它今天心情不好",而是采样真的有随机性。如果你想要稳定输出,把 temperature 调到 0。

2.4 自回归 —— 它一次只吐一个 token

LLM 不是"一口气写完",而是一个 token 一个 token 地往外吐,每吐一个就把它接到输入末尾,再去预测下一个。这就是你看到 ChatGPT "一个字一个字往外蹦"的视觉效果由来 —— 这不是动画特效,是模型真的在那么干。

CHAPTER 03

模型是怎么炼成的 —— 三阶段训练

现代 LLM 几乎都遵循"三阶段"配方:先吞下半个互联网,再被人类老师"调教",最后被人类反复"打分"调味。

3.1 阶段 1:Pretraining(预训练) —— 让它"读完世界"

把整个互联网(书、网页、代码、Wikipedia、Reddit、论文…)切成 token 喂给模型,目标只有一个:给定前 N 个 token,预测第 N+1 个 token。这个阶段:

  • 耗时几个月、烧几千块到几万块 H100 GPU、电费几千万到上亿美元。
  • 产物是一个「续写器」:你给一段话,它能很自然地续下去,但不会"对话",也不会听指令。
  • 训练完后的模型叫 Base Model(基座模型)。比如 LLaMA-3-Base、Qwen-Base。

3.2 阶段 2:SFT(Supervised Fine-Tuning,监督微调)—— 教它"听话"

人工标注一批(指令, 理想回答)的样本(一般几万到几十万条),让模型在这些数据上继续训练。目标是让它学会"接到指令就给答案",而不是傻乎乎地续写。比如:

  • 输入:用三句话解释什么是 RAG。
  • 理想输出:RAG 是检索增强生成……(人工写好)

SFT 之后,模型变成 Instruct Model(指令模型),开始有"对话感"。

3.3 阶段 3:RLHF / DPO —— 教它"喜欢人类喜欢的"

RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习):让模型对同一个 prompt 生成几个回答,让人类标注员选出"哪个更好",再用这些偏好数据去微调模型。这个阶段做的事情是:

  • 让模型回答更有帮助(helpful)
  • 让模型更无害(harmless)—— 拒绝危险请求
  • 让模型更诚实(honest)—— 不知道就说不知道

DPO(Direct Preference Optimization)是 2023 年后流行的简化方案,效果接近 RLHF 但不需要训练单独的奖励模型,工程上更省事。

关键认知:"GPT-4 好用"不只是因为它大,更因为 RLHF/DPO 阶段被人类反复调教过。一个没经过这一步的纯基座模型即使更大,使用体验也会很差 —— 它会答非所问、会拒绝得莫名其妙、会胡乱发挥。
CHAPTER 04

Prompt 工程 —— 跟 AI 说话的技术

同一个模型,会不会用差距巨大。Prompt 工程就是研究"怎么说能让模型给出最好的答案"。

4.1 三种基本角色:System / User / Assistant

大部分对话型 LLM 的输入由三种角色构成:

  • System Prompt(系统提示):在对话最开头,定义模型的身份、风格、规则。比如"你是一个只用中文回答的法律顾问,绝不给具体诉讼建议。"这是产品经理塑造 AI 人格的主战场。
  • User:用户输入。
  • Assistant:模型自己输出,下一轮会作为历史回填。

4.2 Few-shot Prompting —— 给它几个例子

不要只说"请把英文翻成中文",而是给它几个例子:

例1:Hello → 你好
例2:Good morning → 早上好
例3:See you later → ?

有了例子,模型对你想要的格式、风格、详略都心里有数了。这是最便宜、最不需要技巧的提升方法。

4.3 Chain of Thought(CoT,思维链)—— 让它「想出声来」

对于复杂推理任务(数学、逻辑、多步规划),加一句 "Let's think step by step""请一步步推理",模型会先把推理过程写出来再给答案。准确率往往能提升 20%+。

2024 年之后的推理模型(o1、DeepSeek R1、Claude Thinking)把这个机制内置了 —— 它在内部就先「想」很久才输出答案,你不再需要手动加 CoT。

4.4 几个反直觉的小技巧

  • 明确说"不要做什么",往往不如说"应该做什么"。大模型对"否定"的处理常常翻车。
  • 用 Markdown 结构化你的 prompt。用标题、列表、分隔符(---)把"任务""上下文""输出格式"分开,比塞成一大段强得多。
  • 把"输出格式"写成 JSON schema,模型遵循度比中文描述好。
  • 对模型「说好话」有时确实管用。"这个任务对我很重要,请认真完成" 这种 prompt 在某些榜单上确实显示出小幅提升 —— 不是它真有感情,是训练数据里被认真对待的指令也常常带这种语气。
CHAPTER 05

幻觉是什么 —— 为什么它一本正经地胡说八道

幻觉(Hallucination):LLM 自信满满地编造出一个不存在的事实、不存在的论文、不存在的 API、不存在的人物。这不是 bug,这是它的工作机制决定的。

5.1 为什么会幻觉?

回到第 2 章那句话:LLM 是「预测下一个 token」的概率机器。它没有"真假"概念,只有"下一个最自然的 token 是什么"。当你问它一个它没见过的问题(比如某个冷门论文的作者),它不会说"我不知道",它会按"作者名字看起来应该长这样"去合成一个答案。结果就是:引用一篇根本不存在的论文,作者名字是真人组合,DOI 是编的

5.2 哪些情况最容易幻觉?

  • 具体数字、日期、引用 —— 越精确的事实,幻觉概率越高
  • 近期的事 —— 训练数据有截止日期,模型对截止日期之后的事会瞎编
  • 小众领域 —— 训练数据里出现频次少的,记得就模糊
  • 追问到非常深的细节 —— 浅层概念它知道,但你一直追问 N 层之后,它开始 freestyle

5.3 怎么办?

不能消除,但可以缓解:

  • RAG(下一节讲)—— 让它先查资料再回答
  • 工具调用(搜索、计算器、数据库)—— 让它"做"而不是"猜"
  • 引用要求 —— prompt 明确要求"每个事实都要给出来源 URL"
  • 降低温度 —— 减少随机性
  • 关键事实人类复核 —— 这是底线
认知校准:不要把 LLM 当成搜索引擎用,它给的具体引用 80% 都需要核实。它擅长的是"理解 / 改写 / 推理 / 创作",不是"记住事实"。
CHAPTER 06

RAG vs Fine-tune —— 让模型「懂」你的两条路

想让 LLM 知道你公司的内部资料,有两条路:把资料"喂给它看"(RAG)或"教它记住"(Fine-tune)。9 成场景该选第一条。

6.1 RAG —— Retrieval-Augmented Generation(检索增强生成)

工作流程:

  1. 把你的资料切成小块,每块算一个向量(embedding),存进向量数据库。
  2. 用户提问时,把问题也变成向量,去库里搜语义最近的几块
  3. 把搜到的几块原文 + 用户的问题一起塞给 LLM,让它基于这些资料回答。

优点:实时(资料更新立即生效)、便宜(不用训练)、可控(看得见用了哪些资料)。
缺点:检索到无关内容会污染答案;切块策略不好会丢失上下文;多跳推理(要综合多个文档)效果一般。

6.2 Fine-tune —— 微调

把你的资料组织成(问, 答)对,在基座模型上继续训练。优点是模型"内化"了知识,不需要每次都检索;缺点是:

  • 贵 —— 即便用 LoRA 等高效方案也得几千到几万人民币起步
  • 慢 —— 资料更新就得重新训练
  • 容易"灾难性遗忘"原本的能力
  • 调出来的模型只能是你的专属版本,不能用最新的 GPT-4o

6.3 怎么选?

需求推荐
让模型回答时引用公司知识库RAG
资料更新频繁(每天/每周)RAG
要让模型学会一种特殊"风格"或"领域语言"Fine-tune
极致延迟敏感(每次省下检索一跳)Fine-tune
不知道选哪个RAG(先用着,需要再升级)

2026 年的实践趋势:长上下文 + RAG + Karpathy 风格的 LLM Wiki 越来越主流;纯微调正在退场,只有少数特别明确的场景还在用。

CHAPTER 07

什么是 Agent —— 它和 Chatbot 不是一回事

如果一个东西只会聊天,它是 Chatbot;如果它能看任务 → 自己分步 → 调工具 → 反思 → 拿结果,它才是 Agent。

7.1 一句话定义

Agent = LLM + 工具 + 记忆 + 循环 + 目标

分别意味着:

  • LLM:大脑,做判断、写代码、解析意图。
  • 工具:手脚,能读文件、调 API、跑命令、搜网页。
  • 记忆:上下文 + 长期存储,记得任务进展和过去发生了什么。
  • 循环:不是"问一次答一次",而是反复调用 LLM直到任务完成或失败。
  • 目标:一个明确的成功标准("修好这个 bug"、"订到符合条件的机票")。

7.2 ReAct 循环 —— Agent 的核心运行模式

2022 年提出的 ReAct(Reason + Act)模式被几乎所有 Agent 框架沿用:

循环 {
  Thought(思考):现在该做什么?
  Action(行动):调用某个工具
  Observation(观察):拿到工具结果
}
直到觉得 done 为止 → Final Answer

Cursor、Claude Code、Devin、AutoGPT —— 内核都是这个循环,区别只在工具集、记忆策略、控制流的复杂度。

7.3 Chatbot vs Agent —— 一张表说清

ChatbotAgent
输出一段文字一系列动作 + 最终结果
调用次数每轮 1 次 LLM一个任务可能调用 LLM 几十到几百次
有无副作用无(只生成文字)有(写文件、发邮件、改数据库)
错了怎么办用户重新问自己反思、重试、求助
典型代表ChatGPT 网页版Claude Code、Cursor、Devin、Manus
CHAPTER 08

Tool Use / Function Calling —— Agent 的「手」

LLM 自己只会输出文字。要让它"做事",必须给它一套工具,并教它什么时候该调哪个工具。这就是 Tool Use。

8.1 一个工具长什么样

工具就是一个函数,需要告诉 LLM 三件事:

  • 名字search_web
  • 描述"用关键词搜索互联网,返回最相关的 10 条标题和摘要"
  • 参数 schema{ query: string, top_k?: number }

当用户提问"今天上海天气",LLM 看到自己有 search_web 这个工具,会输出一个特殊格式的"调用请求":

{
  "tool": "search_web",
  "args": { "query": "上海今天天气" }
}

Agent 框架接到这个请求后,真的去执行这个函数,把结果("23-28℃,多云")回填给 LLM,LLM 再用人话总结给用户。

8.2 Function Calling 是什么

OpenAI 在 2023 年 6 月把这套机制官方化,叫 Function Calling。意思是:模型在训练时就被教会"看到工具描述,按 JSON 格式输出调用请求",准确率比让你在 prompt 里手动教高得多。后来所有主流 LLM(Claude、Gemini、Qwen、DeepSeek)都内置了这个能力。

8.3 几种常见工具类型

  • 检索类:搜网页、查文档、问数据库
  • 执行类:跑 shell 命令、执行代码、操作浏览器
  • 读写类:读文件、写文件、改代码、提交 git
  • 外部服务类:发邮件、订机票、调企业内部 API
  • 计算类:调用真正的计算器(LLM 自己算 17 位数加法会算错)

8.4 Tool Use 让幻觉问题减轻一半

很多原本会"幻觉"的问题,给它工具就解决了:

  • 问最新新闻 → 给它search_web
  • 问精确算账 → 给它execute_python
  • 问内部资料 → 给它query_knowledge_base

这是 2023 年之后 Agent 真正能"干活"的关键转折 —— 工具让模型从"凭记忆瞎说"变成"动手验证"。

CHAPTER 09

Memory —— Agent 的短期与长期记忆

LLM 本身没有记忆 —— 它每次推理都是"从零开始"。Agent 想做长任务,就必须自己造一套记忆系统。

9.1 短期记忆 —— Context 里的「便签」

当前对话/任务的所有信息(system prompt、历史消息、最近调用的工具结果)都塞在 LLM 的上下文窗口里,这就是短期记忆。它的特点:

  • 速度极快(就是模型输入)
  • 容量有限(窗口大小约束)
  • 任务一结束就清空

当对话变长、token 用爆窗口时,Agent 会做压缩 / 摘要 / 滑窗:把老消息摘要成一段,丢掉细节,只保留要点。这就是为什么长聊天里 AI 会"忘"前面的细节。

9.2 长期记忆 —— 跨会话的「档案柜」

真正想"记得用户、记得过往任务",需要把信息持久化到外部存储。常见做法:

  • 键值对存储:用户偏好、个人档案。"用户喜欢简洁回答" → 下次对话时塞进 system prompt。
  • 向量数据库:把过往对话/任务结果向量化存起来,需要时按语义检索。
  • 结构化数据库:日程、任务清单、订单记录这种有 schema 的数据,老老实实进 SQL/NoSQL。
  • 文件系统 / Markdown 知识库:Karpathy 提倡的 LLM Wiki 路线 —— 把记忆做成结构化 Markdown 文件夹,Agent 像程序员读代码一样读它。

9.3 记忆策略 —— 工程上最难的部分之一

"什么该记"、"什么该忘"、"什么时候该回忆"是 Agent 工程里最有讲究的设计点。错误的记忆策略会导致:

  • 记得太少 → Agent 像"金鱼",每次都从头开始
  • 记得太多 → 上下文爆炸 + 召回噪音 + 成本飙升
  • 记得错的 → 把过去的错误事实当"真实"反复使用
CHAPTER 10

Multi-Agent —— 一群 Agent 协作

单个 Agent 很强,但有个上限:上下文不够长、思路容易跑偏、专长不够。Multi-Agent 就是让多个 Agent 分工协作。

10.1 一个最经典的 Multi-Agent 模式

用户 → 总管 Agent(拆解任务)
        ├── 调研 Agent(搜资料)
        ├── 编码 Agent(写代码)
        ├── 审核 Agent(找 bug)
        └── 汇总 Agent(生成报告)

每个 Agent 有自己的独立 system prompt、独立工具、独立上下文。它们之间通过消息传递协作,像一个有分工的团队。

10.2 几种主流模式

  • Manager-Worker:一个总管派活给多个 worker(最常见,也叫 supervisor 模式)
  • Pipeline:流水线,一个做完交下一个(适合明确分阶段的任务)
  • Debate:两个 Agent 互相挑刺,在辩论中找到更好答案
  • Society:一群 Agent 自由通信,模拟"小镇"或"公司"(如斯坦福小镇实验)

2025 年开源生态里 三省六部 Edict(基于 OpenClaw)就是中文圈一个有意思的尝试:把"太子分拣 / 三省规划审核派发 / 六部执行"这套帝国制度做成 12 个 Agent 协作框架。

10.3 Multi-Agent 不是银弹

很多场景单 Agent + 长上下文反而更稳:

  • 多 Agent 之间消息传递会丢失上下文细节
  • 每个 Agent 都要钱,整体成本几倍翻
  • 调试比单 Agent 难得多 —— 出错时不知道是哪一环

Anthropic 在 2024 年的研究显示:很多被宣称需要 Multi-Agent 的任务,单 Agent + Tool Use 就能做得更好。所以非必要不上 Multi-Agent

CHAPTER 11

Agent Harness —— 让模型真能干活的「外壳」

这是这个博客的核心主题。一句话:Harness 就是 Agent = LLM + 工具 + 记忆 + 循环 + 目标 等式右边除了 LLM 之外的所有东西。

11.1 为什么需要 Harness 这个词

2024 年中之后,业界发现一件事:同一个 LLM,套上不同 harness,能力差距巨大。

同样是 Claude 3.5 Sonnet:

  • 裸 API 调用 → 一个聊天工具
  • 装进 Cursor → 一个 IDE 助手
  • 装进 Claude Code → 一个能写完整项目的工程师
  • 装进 Computer Use → 一个能操作电脑的"虚拟员工"

差别不在模型,在外壳。Anthropic 工程师有一句话:"If you're not the model, you're the harness." —— 你做不了 LLM,那就好好做 harness。

11.2 Harness 的"八根支柱"

本博客的 Agent Harness 章节会逐根展开:

  1. 身份与权限(Identity) —— 这个 Agent 是"谁",能用谁的身份做事
  2. 沙箱与运行时(Sandbox) —— 它在哪里干活,怎么不污染主机
  3. 工具系统(Tools) —— 它能做什么动作,调用接口长什么样
  4. 技能与策略(Skills) —— 复合能力的封装与复用
  5. 记忆系统(Memory) —— 它"记得"什么,怎么记
  6. 编排与控制流(Orchestration) —— 任务怎么拆、怎么调度、怎么重试
  7. 观测与评测(Observability & Eval) —— 怎么知道它干得好不好
  8. 安全与对齐(Safety) —— 怎么防它失控、防它被骗、防它被注入恶意指令

11.3 一个直观比喻

LLM 是赛车手 —— 反应快、判断准、爱踩油门。
但你不能让他光着膀子站在马路上跑。
Harness 就是赛车 + 赛道 + 计时器 + 维修组:
赛车(沙箱)让他在受控环境里加速;
赛道(编排)告诉他往哪开;
计时器(观测)记录每一圈的成绩;
维修组(工具)在他需要的时候递扳手。
没有这套 harness,再好的车手也只是个会跑步的人。
CHAPTER 12

MCP 协议 —— Agent 时代的「USB-C」

2024 年底 Anthropic 抛出 Model Context Protocol(MCP),2025 年成为行业默认。一句话:它是一份"工具与 LLM 之间怎么对话"的统一规范,让 Agent 接工具像电脑接外设一样即插即用。

12.1 MCP 之前的乱象

每家 LLM 厂商有自己的 function calling 格式:OpenAI 用一套,Anthropic 用一套,Google 又一套。每接入一个新工具就要写胶水代码、调试格式、踩兼容坑。生态分裂。

12.2 MCP 的核心抽象

MCP 把世界分成两边:

  • MCP Server:包装一类资源 —— 你的文件系统、你的数据库、你的 GitHub、你的 Notion、你的浏览器…… 每个 Server 用一份 JSON schema 暴露自己的能力。
  • MCP Client / Host:跑 LLM 的那一端 —— Claude Desktop、Cursor、Cline、各种 Agent 框架。它们连上 Server 后,自动就能让 LLM 调用 Server 的工具。

用户的视角变成:装一个新 MCP Server,AI 就立刻多了一项能力,不用改代码、不用换框架。

12.3 为什么这件事很重要

MCP 之于 Agent,相当于USB-C 之于硬件

  • 统一接口 → 工具开发者写一次到处能用
  • 用户能装能卸 → 工具变成"可热插拔"的能力
  • 催生了一个 MCP Server 生态 —— 现在 GitHub 上几千个 MCP Server,覆盖几乎所有常用工具

2026 年的 Agent 框架(OpenClaw、各种 IDE 助手)几乎都原生支持 MCP,"接什么工具"这件事,从工程师写代码变成了用户点几下安装。

CHAPTER 13

动手路径 —— 从读到做的 6 个起点

看完这 12 章,你已经有了完整的"地图"。现在该选一个口子真正动手了。下面是一条可操作的入门路径,按"动手成本从低到高"排序。

13.1 起点 ① —— 把 ChatGPT/Claude 用得真的好(0 代码)

大部分人甚至连这一步都没做扎实。具体功课:

  • 系统学一遍 prompt 工程的 7 种基本套路(角色、Few-shot、CoT、结构化输出、自我审查、约束输出、上下文裁剪)
  • 买一个会员(GPT Plus / Claude Pro 二选一),坚持用 1 个月 → 形成肌肉记忆
  • 把生活中至少 5 件事固定用 AI 处理:邮件、会议纪要、阅读总结、代码 review、复杂决策的"反方陈述"

13.2 起点 ② —— 试一个 CLI Agent(小代码)

装一个 Claude CodeCursorCline,给它一个真实的小项目(不是 hello world,而是真要解决的事)。比如:"帮我把这堆图片按拍摄日期分类"。亲手观察它怎么调用工具、怎么自我修正 —— 这是理解 Agent 最快的方法。

13.3 起点 ③ —— 装几个 MCP Server(中等代码)

给 Claude Desktop 装上 filesystemgithubweb-search 三个 MCP Server。然后让它做一些"以前要打开 5 个软件才能做的事"。亲手感受 Tool Use 把 LLM 从"聊天"变成"干活"的那个分水岭。

13.4 起点 ④ —— 在 OpenAI / Anthropic SDK 里裸写一个 Agent(200 行代码)

不要用任何框架。用最朴素的方式实现 ReAct 循环:

  1. 定义两个工具:search_webread_url
  2. 写一个 while 循环:调 LLM → 解析 tool call → 执行 → 把结果回填 → 再调 LLM
  3. 给它一个任务:"帮我整理 2025 年 AI 重要事件"

这 200 行代码会让你彻底理解"Agent 不是魔法"。看懂这个,你看任何开源 Agent 框架都不怵。

下面给一份可以直接复制到 IDE 跑通的完整示例。三步走:

  1. 装依赖(三个,都是 Python 生态最常见的库):
    pip install openai httpx beautifulsoup4
  2. 把你的 OpenAI API Key 设进环境变量(去 platform.openai.com/api-keys 申请):
    export OPENAI_API_KEY="sk-..."        # macOS / Linux
    set OPENAI_API_KEY=sk-...             # Windows CMD
    $env:OPENAI_API_KEY="sk-..."          # Windows PowerShell
  3. 把下面整段保存为 tiny_agent.py,然后 python tiny_agent.py
# =============================================================
# tiny_agent.py —— 200 行裸写一个 Agent,不依赖任何 Agent 框架
#
# 核心思路:所谓 Agent,就是一个 while 循环:
#   1. 把"用户任务 + 历史对话"喂给 LLM
#   2. LLM 要么直接回答(循环结束),要么说"我想调某个工具"
#   3. 我们本地执行那个工具,把结果塞回历史
#   4. 回到第 1 步,直到 LLM 不再要求调工具
#
# 就这么多,没了。看懂这 200 行,你就理解所有 Agent 框架了。
# =============================================================

import os
import json
import httpx                      # 用来发 HTTP 请求(比 requests 更现代)
from bs4 import BeautifulSoup     # 用来从 HTML 里抽正文
from openai import OpenAI         # OpenAI 官方 SDK
from urllib.parse import quote_plus

# 创建 OpenAI 客户端,会自动读 OPENAI_API_KEY 环境变量
client = OpenAI()

# 选用的模型。gpt-4o-mini 便宜够用;要更强可换 "gpt-4o" 或 "gpt-4.1"
MODEL = "gpt-4o-mini"


# =============================================================
# 第一部分:定义两个"工具"(其实就是两个普通 Python 函数)
# =============================================================

def search_web(query: str) -> str:
    """
    用 DuckDuckGo 的 HTML 端点做搜索(不需要 API key,免费)。
    返回前 5 条结果的"标题 + 链接 + 摘要",每条之间用空行分隔。
    """
    # DuckDuckGo 的"轻量版"搜索页,专门给爬虫用,HTML 很干净
    url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
    headers = {"User-Agent": "Mozilla/5.0 (tiny-agent demo)"}
    html = httpx.get(url, headers=headers, timeout=15).text

    soup = BeautifulSoup(html, "html.parser")
    results = []
    # DuckDuckGo HTML 版的结果块都是 .result 这个 class
    for item in soup.select(".result")[:5]:
        title_el = item.select_one(".result__a")
        snippet_el = item.select_one(".result__snippet")
        if not title_el:
            continue
        title = title_el.get_text(strip=True)
        link = title_el.get("href", "")
        snippet = snippet_el.get_text(strip=True) if snippet_el else ""
        results.append(f"- {title}\n  {link}\n  {snippet}")

    if not results:
        return "没有搜到相关结果,请换个关键词试试。"
    return "\n\n".join(results)


def read_url(url: str) -> str:
    """
    抓取一个网页,去掉 HTML 标签,返回纯文本。
    为了不把 LLM 撑爆,只返回前 4000 个字符(够它判断要不要继续读)。
    """
    headers = {"User-Agent": "Mozilla/5.0 (tiny-agent demo)"}
    try:
        html = httpx.get(url, headers=headers, timeout=15, follow_redirects=True).text
    except Exception as e:
        return f"抓取失败:{e}"

    soup = BeautifulSoup(html, "html.parser")
    # 把 script / style 标签整个删掉,避免噪音
    for tag in soup(["script", "style", "noscript"]):
        tag.decompose()
    text = soup.get_text("\n", strip=True)
    # 折叠过多的空行
    lines = [ln for ln in text.split("\n") if ln.strip()]
    text = "\n".join(lines)
    return text[:4000]


# =============================================================
# 第二部分:把工具"描述"给 LLM
#
# LLM 不会自动知道我们写了什么函数,必须用一段 JSON Schema 告诉它:
#   - 这个工具叫什么名字
#   - 它干什么用
#   - 它要哪些参数、每个参数是什么类型
# LLM 会根据这段描述,自己决定何时、用什么参数调它。
# =============================================================

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "在互联网上搜索信息,返回相关网页的标题、链接和摘要。当你需要查找新闻、事件、资料时使用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索关键词,用自然语言或英文都可以"
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "read_url",
            "description": "打开一个网页 URL,读取它的正文内容。当 search_web 给了你链接,需要看具体内容时使用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "description": "要打开的网页地址,必须是完整的 http:// 或 https:// 开头的 URL"
                    }
                },
                "required": ["url"]
            }
        }
    }
]

# 工具名 -> 真实函数 的映射,方便循环里按名字找
TOOL_IMPL = {
    "search_web": search_web,
    "read_url": read_url,
}


# =============================================================
# 第三部分:ReAct 循环(整个 Agent 的核心,就这十几行)
# =============================================================

def run_agent(user_task: str, max_steps: int = 10) -> str:
    """
    跑一个 Agent,直到它回答完任务或达到最大步数。
    """
    # messages 是对话历史,第一条是 system prompt(给 Agent 立人设)
    messages = [
        {
            "role": "system",
            "content": (
                "你是一个善于搜索和总结的研究助手。"
                "拿到任务后,先用 search_web 找资料,必要时用 read_url 读具体网页,"
                "最后给出有条理的中文总结。"
                "不要编造来源,所有结论都要基于搜到的内容。"
            )
        },
        {"role": "user", "content": user_task},
    ]

    for step in range(1, max_steps + 1):
        print(f"\n========== 第 {step} 轮 ==========")

        # ===== 调 LLM =====
        resp = client.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=TOOLS,          # 把工具列表传进去
            tool_choice="auto",   # 让模型自己决定要不要调工具
        )
        msg = resp.choices[0].message

        # ===== 情况 A:LLM 没要求调工具,说明它给出最终答案了 =====
        if not msg.tool_calls:
            print("【LLM 给出最终答案】")
            return msg.content or ""

        # ===== 情况 B:LLM 要求调一个或多个工具 =====
        # 先把 LLM 这条"我要调工具"的消息加到历史里(必须)
        messages.append(msg)

        # 依次执行每个工具调用
        for tool_call in msg.tool_calls:
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)
            print(f"【LLM 想调用工具】{name}({args})")

            # 在本地真的把函数跑起来
            try:
                result = TOOL_IMPL[name](**args)
            except Exception as e:
                result = f"工具执行出错:{e}"

            # 打印一下结果的前 200 字,方便你观察 Agent 在干什么
            print(f"【工具返回】{result[:200]}{'...' if len(result) > 200 else ''}")

            # 把工具结果回填到历史里,role 必须是 "tool",且要带上 tool_call_id
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result,
            })

        # 进入下一轮:把"工具结果"再喂给 LLM,让它决定下一步

    return "达到最大步数,仍未给出最终答案。"


# =============================================================
# 入口:跑一个示例任务
# =============================================================

if __name__ == "__main__":
    task = "帮我整理 2025 年 AI 领域的 3 个重要事件,每个写一句话即可。"
    answer = run_agent(task)
    print("\n\n========== 最终答案 ==========")
    print(answer)

↑ 把它保存为 tiny_agent.py,跑起来你会在控制台看到 Agent 一轮一轮的思考过程:先搜了什么、点开了哪个链接、最后怎么总结。整个文件去掉注释只有不到 100 行真实代码。

看完这段代码,至少有 3 个"原来如此"的瞬间:

  • 工具不是魔法 —— 它就是两个普通 Python 函数,加一段 JSON 描述。LLM 看到描述后,自己决定何时调用、传什么参数。
  • "Agent 在思考"是个错觉 —— 实际上每一轮都是一次独立的 LLM 调用。所谓"记忆",是因为我们每次都把整个对话历史重新喂回去。
  • 循环退出条件极其朴素 —— 模型这次没要求调工具,就说明它觉得"够了,可以回答了"。没有什么复杂的"决策状态机"。

所有开源 Agent 框架(LangChain、AutoGen、CrewAI、OpenHands…)本质上都是在这个循环外面包了更多东西:错误重试、并行 tool call、记忆压缩、子 Agent、流式输出、可观测性…… 但核心永远是这十几行 while 循环

13.5 起点 ⑤ —— 试一个真正的 Harness 框架

OpenClaw 或类似的本地 Agent 框架,跑通它的 Skills 系统。看一个真正的 production-grade harness 是怎么把"沙箱、记忆、工具、编排、观测"组织起来的。这一步会让你看到第 11 章那"八根支柱"在代码里长什么样。

13.6 起点 ⑥ —— 读完本博客 Agent Harness 章节

这是给愿意走深的人准备的。Agent Harness 章节有 6 篇长文,把上述八根支柱全部展开,重点放在企业级视角 —— 怎么让 Agent 跨系统、长链路、安全可控地工作。这是"业余玩具"和"生产可用 Agent"之间的那道坎。

13.7 模型怎么选 —— 2026 年中现状

场景推荐
日常聊天 / 写作 / 阅读总结ChatGPT、Claude(任选)、Kimi
写代码 / 做 Agent 大脑Claude(编码最强)、GPT、DeepSeek
复杂推理 / 数学 / 科研OpenAI o-系列、DeepSeek R1、Claude Thinking
本地部署(隐私 / 离线)LLaMA、Qwen、DeepSeek(蒸馏版)
中文场景 / 国内合规Qwen、DeepSeek、Kimi、豆包
不确定选啥Claude(综合体验最稳)

13.8 一句送给小白的话

不要等"完全学会"再动手。这个领域 6 个月就换一代,知识半衰期极短。边用边读边写,能用起来比能讲清楚重要 10 倍。这篇文章的目标不是让你"懂 AI",是让你知道哪些词不需要查、哪些事不再神秘、下一步该看什么。剩下的,去用。