nanoAgent:103 行极简实现到完整 Agent
"问题不在于你看到了什么,而在于你看见了什么。" ------ 梭罗
前言
2026 年,AI Agent 生态迎来了前所未有的爆发。从 LangGraph 到 CrewAI,从 AutoGen 到 Google ADK,框架层的繁荣让开发者目不暇接。然而,一个仅有 103 行 Python 代码的项目 nanoAgent 却在 GitHub 上引发了另一场讨论:Agent 的本质到底有多简单?
这个由开发者 sanbuphy 创建的极简项目,没有向量数据库,没有复杂的图编排,没有记忆管理中间件------它仅靠 OpenAI 的 Function Calling 接口和一个 while 循环,就实现了能执行 bash 命令、读写文件的完整 Agent。项目 README 的一句话精准概括了它的哲学:"If you can read ~100 lines of Python, you understand agents."
在 Agent 框架日趋复杂化的今天,nanoAgent 的存在像一面镜子------它照出的不是一个玩具,而是所有 Agent 框架共有的最小公分母。理解这 103 行,就是理解 Agent 的第一性原理。
一、极简哲学:为什么 103 行就够了?

1.1 Agent 的本质是什么?
在深入代码之前,我们先厘清一个概念。当我们说"AI Agent"时,我们到底在说什么?
根据 Lilian Weng 在其经典博文《LLM Powered Autonomous Agents》中的定义,一个 Agent 系统的核心组件包括:
- 规划(Planning):将大任务拆解为子任务
- 记忆(Memory):短期上下文与长期知识
- 工具使用(Tool Use):调用外部能力
- 行动(Action):执行具体操作并观察结果
这四个组件构成了 Agent 的核心循环。但请注意------规划可以退化为"让 LLM 自己决定下一步",记忆可以退化为"消息历史列表",工具可以是最基础的 bash/read/write,行动就是执行函数调用。
当一切退化到最小状态,Agent 的本质就暴露出来了:
while 任务未完成:
1. 把上下文发给 LLM
2. LLM 决定下一步(调用工具 or 返回结果)
3. 如果调用工具,执行它,把结果追加到上下文
4. 重复
这就是 nanoAgent 的全部逻辑。它没有"框架",因为它不需要框架。
1.2 与 Karpathy 的 "Software 2.0" 精神一脉相承
Andrej Karpathy 在 2017 年提出 "Software 2.0" 的概念:未来的软件不再是人类手写的逻辑规则,而是由数据和优化目标驱动的神经网络权重。Agent 正是这一理念的最新体现------你不需要编写"如果用户说X就执行Y"的规则树,你只需要给 LLM 提供工具列表和任务描述,让它自己决定调用链。
nanoAgent 的极简性恰好证明了这一点:当你的"程序"是 GPT-4o-mini 的推理能力时,你真正需要编写的"胶水代码"极少。
1.3 Less is More 的工程价值
极简不是炫技,而是有实际工程价值的:
- 可审计性:103 行代码,10 分钟读完,每个决策点都透明
- 可调试性:没有抽象层遮蔽,出错时你能直接看到是哪一行的问题
- 可移植性:一个文件,一个依赖(openai),任何 Python 环境都能跑
- 教学价值:新成员通过阅读这 103 行就能理解 Agent 的核心机制
二、架构总览

在逐行解析之前,先通过架构图理解 nanoAgent 的整体结构:
#mermaid-svg-HLhiuwWqeV5KD08p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HLhiuwWqeV5KD08p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HLhiuwWqeV5KD08p .error-icon{fill:#552222;}#mermaid-svg-HLhiuwWqeV5KD08p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HLhiuwWqeV5KD08p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HLhiuwWqeV5KD08p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HLhiuwWqeV5KD08p .marker.cross{stroke:#333333;}#mermaid-svg-HLhiuwWqeV5KD08p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HLhiuwWqeV5KD08p p{margin:0;}#mermaid-svg-HLhiuwWqeV5KD08p .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HLhiuwWqeV5KD08p .cluster-label text{fill:#333;}#mermaid-svg-HLhiuwWqeV5KD08p .cluster-label span{color:#333;}#mermaid-svg-HLhiuwWqeV5KD08p .cluster-label span p{background-color:transparent;}#mermaid-svg-HLhiuwWqeV5KD08p .label text,#mermaid-svg-HLhiuwWqeV5KD08p span{fill:#333;color:#333;}#mermaid-svg-HLhiuwWqeV5KD08p .node rect,#mermaid-svg-HLhiuwWqeV5KD08p .node circle,#mermaid-svg-HLhiuwWqeV5KD08p .node ellipse,#mermaid-svg-HLhiuwWqeV5KD08p .node polygon,#mermaid-svg-HLhiuwWqeV5KD08p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HLhiuwWqeV5KD08p .rough-node .label text,#mermaid-svg-HLhiuwWqeV5KD08p .node .label text,#mermaid-svg-HLhiuwWqeV5KD08p .image-shape .label,#mermaid-svg-HLhiuwWqeV5KD08p .icon-shape .label{text-anchor:middle;}#mermaid-svg-HLhiuwWqeV5KD08p .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HLhiuwWqeV5KD08p .rough-node .label,#mermaid-svg-HLhiuwWqeV5KD08p .node .label,#mermaid-svg-HLhiuwWqeV5KD08p .image-shape .label,#mermaid-svg-HLhiuwWqeV5KD08p .icon-shape .label{text-align:center;}#mermaid-svg-HLhiuwWqeV5KD08p .node.clickable{cursor:pointer;}#mermaid-svg-HLhiuwWqeV5KD08p .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HLhiuwWqeV5KD08p .arrowheadPath{fill:#333333;}#mermaid-svg-HLhiuwWqeV5KD08p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HLhiuwWqeV5KD08p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HLhiuwWqeV5KD08p .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HLhiuwWqeV5KD08p .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HLhiuwWqeV5KD08p .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HLhiuwWqeV5KD08p .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HLhiuwWqeV5KD08p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HLhiuwWqeV5KD08p .cluster text{fill:#333;}#mermaid-svg-HLhiuwWqeV5KD08p .cluster span{color:#333;}#mermaid-svg-HLhiuwWqeV5KD08p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HLhiuwWqeV5KD08p .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HLhiuwWqeV5KD08p rect.text{fill:none;stroke-width:0;}#mermaid-svg-HLhiuwWqeV5KD08p .icon-shape,#mermaid-svg-HLhiuwWqeV5KD08p .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HLhiuwWqeV5KD08p .icon-shape p,#mermaid-svg-HLhiuwWqeV5KD08p .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HLhiuwWqeV5KD08p .icon-shape .label rect,#mermaid-svg-HLhiuwWqeV5KD08p .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HLhiuwWqeV5KD08p .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HLhiuwWqeV5KD08p .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HLhiuwWqeV5KD08p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} nanoAgent 架构(103 行)
核心循环
工具定义层
包含 tool_calls
无 tool_calls
tool_calls 响应
messages + tools
用户输入
run_agent 函数
LLM 响应
工具调度器
返回最终结果
execute_bash
read_file
write_file
将结果追加到 messages
OpenAI API
Function Calling
这个架构图揭示了 nanoAgent 的三个关键层次:
- 入口层:接收用户输入,构建初始 messages
- 核心循环:LLM 调用 → 工具调度 → 结果回注 → 再次调用
- 工具层:三个最小工具(bash、read、write)的实现
三、103 行逐行解析

3.1 初始化与客户端(第 1-10 行)
python
import os
import json
import subprocess
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
base_url=os.environ.get("OPENAI_BASE_URL")
)
这 10 行完成了 Agent 的"基础设施"搭建。注意 base_url 参数------这是 nanoAgent 能对接任意 OpenAI 兼容 API 的关键。无论是本地的 Ollama、vLLM,还是第三方代理服务,只要兼容 OpenAI 的接口格式,就能直接使用。
这种设计体现了接口标准化的思想。OpenAI 的 Function Calling 格式已经事实上成为了行业标准,nanoAgent 通过依赖这一标准,实现了模型无关性。
3.2 工具定义层(第 12-50 行)
python
tools = [
{
"type": "function",
"function": {
"name": "execute_bash",
"description": "Execute a bash command",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
},
},
# read_file 和 write_file 类似定义...
]
工具定义采用 JSON Schema 格式,这是 OpenAI Function Calling 协议的标准方式。每个工具包含三个要素:
- name:工具的唯一标识符
- description:自然语言描述,LLM 靠这个理解何时该用这个工具
- parameters:JSON Schema 格式的参数定义
这里的 description 字段至关重要------它是 LLM 决策的依据。一个好的 description 应该明确说明工具的能力边界和适用场景。
3.3 工具实现层(第 52-70 行)
python
def execute_bash(command):
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout + result.stderr
def read_file(path):
with open(path, "r") as f:
return f.read()
def write_file(path, content):
with open(path, "w") as f:
f.write(content)
return f"Wrote to {path}"
这是 Agent 的"手和脚"。三个函数分别对应:
- execute_bash:系统级操作的万能接口。通过 bash,Agent 可以安装包、运行脚本、查询系统状态------几乎所有计算机能做的事情都可以通过这个入口完成
- read_file:获取信息的能力。Agent 通过读取文件来理解代码、配置和数据
- write_file:产出结果的能力。Agent 可以创建文件、修改代码、生成报告
注意一个关键设计决策:nanoAgent 将 bash 作为一等公民工具 。这意味着它不需要为每个操作实现专用工具------curl 替代 HTTP 客户端,grep 替代搜索工具,git 替代版本控制工具。这是极简主义的核心策略:用通用工具替代专用工具。
3.4 核心循环(第 75-100 行)
python
def run_agent(user_message, max_iterations=5):
messages = [
{"role": "system", "content": "You are a helpful assistant. Be concise."},
{"role": "user", "content": user_message},
]
for _ in range(max_iterations):
response = client.chat.completions.create(
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
messages=messages,
tools=tools,
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f"[Tool] {name}({args})")
if name not in functions:
result = f"Error: Unknown tool '{name}'"
else:
result = functions[name](**args)
messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": result})
return "Max iterations reached"
这是整个 Agent 的心脏。让我们拆解这个循环的每一步:
Step 1:构建初始上下文
messages 列表是 Agent 的"短期记忆"。初始包含 system prompt 和用户消息。
Step 2:调用 LLM
将当前 messages 和 tools 定义发送给 LLM。LLM 返回的可能是:
- 一段普通文本(任务完成)
- 一个或多个 tool_calls(需要执行工具)
Step 3:判断是否需要工具调用
python
if not message.tool_calls:
return message.content
这是循环的终止条件。当 LLM 认为任务完成或无需工具时,返回最终结果。
Step 4:执行工具并回注结果
python
for tool_call in message.tool_calls:
# 执行工具
result = functions[name](**args)
# 将结果追加到上下文
messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": result})
关键点:工具结果以 "tool" 角色追加到 messages。这样 LLM 在下一轮调用时能看到工具的执行结果,从而做出后续决策。
Step 5:循环控制
max_iterations=5 是一个安全阀,防止 Agent 陷入无限循环。在实际使用中,你可以根据任务复杂度调整这个值。
3.5 入口点(第 101-103 行)
python
if __name__ == "__main__":
import sys
task = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "Hello"
print(run_agent(task))
简洁的命令行接口。使用方式:
bash
python agent.py "找到所有 .py 文件并统计总代码行数"
四、功能扩展:从 103 行到生产级
nanoAgent 的极简设计天然支持扩展。以下是几种常见的扩展路径:
4.1 添加记忆系统
基础版 nanoAgent 没有跨会话记忆。我们可以用一个 Markdown 文件实现简单的持久化记忆:
python
MEMORY_FILE = "agent_memory.md"
def load_memory():
if not os.path.exists(MEMORY_FILE):
return ""
with open(MEMORY_FILE, 'r') as f:
content = f.read()
# 只保留最近 50 行,防止上下文溢出
lines = content.split('\n')
return '\n'.join(lines[-50:]) if len(lines) > 50 else content
def save_memory(task, result):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
entry = f"\n## {timestamp}\n**Task:** {task}\n**Result:** {result}\n"
with open(MEMORY_FILE, 'a') as f:
f.write(entry)
在 run_agent 中,将 load_memory() 的结果注入 system prompt,任务完成后调用 save_memory(),就实现了跨会话的记忆持久化。
4.2 添加任务规划
nanoAgent 的 agent-plus.py 版本增加了一个 create_plan 函数,让 Agent 在执行前先进行任务分解:
python
def create_plan(task):
response = client.chat.completions.create(
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
messages=[
{"role": "system", "content": "Break down the task into 3-5 simple, actionable steps. Return as JSON array."},
{"role": "user", "content": f"Task: {task}"}
],
response_format={"type": "json_object"}
)
plan_data = json.loads(response.choices[0].message.content)
steps = plan_data.get("steps", [task]) if isinstance(plan_data, dict) else plan_data
for i, step in enumerate(steps, 1):
print(f" {i}. {step}")
return steps
这是一种 Plan-then-Execute 模式。Agent 先将复杂任务分解为子步骤,然后逐步执行。这比直接让 Agent 处理复杂任务更可靠,因为每个子步骤的上下文更聚焦。
4.3 添加安全沙箱
基础版的 execute_bash 直接在宿主系统上执行命令,存在安全风险。一个简单的改进是添加命令白名单和超时控制:
python
ALLOWED_PATTERNS = ['ls', 'cat', 'grep', 'find', 'wc', 'head', 'tail', 'echo', 'pwd']
BLOCKED_PATTERNS = ['rm -rf', 'sudo', 'chmod 777', 'mkfs', 'dd if=']
def execute_bash_safe(command, timeout=30):
# 检查黑名单
for pattern in BLOCKED_PATTERNS:
if pattern in command:
return f"Error: Blocked dangerous command pattern: {pattern}"
try:
result = subprocess.run(
command, shell=True, capture_output=True,
text=True, timeout=timeout
)
return result.stdout + result.stderr
except subprocess.TimeoutExpired:
return "Error: Command timed out"
更进一步,可以使用 Docker 容器作为沙箱,将 Agent 的执行环境与宿主完全隔离。
五、代码示例:完整 103 行实现
以下是 nanoAgent 的完整代码,可以直接复制运行:
python
"""
nanoAgent - 103 行极简 AI Agent
使用 OpenAI Function Calling 实现的最小 Agent
工具:execute_bash, read_file, write_file
"""
import os
import json
import subprocess
from openai import OpenAI
# ========== 初始化客户端 ==========
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
base_url=os.environ.get("OPENAI_BASE_URL")
)
# ========== 工具定义(JSON Schema 格式) ==========
tools = [
{
"type": "function",
"function": {
"name": "execute_bash",
"description": "Execute a bash command on the system",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "The bash command to execute"}
},
"required": ["command"],
},
},
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read the contents of a file at the given path",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Path to the file to read"}
},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Write content to a file at the given path",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Path to the file to write"},
"content": {"type": "string", "description": "Content to write to the file"},
},
"required": ["path", "content"],
},
},
},
]
# ========== 工具实现 ==========
def execute_bash(command):
"""执行 bash 命令,返回 stdout + stderr"""
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout + result.stderr
def read_file(path):
"""读取文件内容"""
with open(path, "r") as f:
return f.read()
def write_file(path, content):
"""写入文件内容"""
with open(path, "w") as f:
f.write(content)
return f"Wrote to {path}"
# 工具名称到函数的映射
functions = {
"execute_bash": execute_bash,
"read_file": read_file,
"write_file": write_file,
}
# ========== Agent 核心循环 ==========
def run_agent(user_message, max_iterations=5):
"""
Agent 核心逻辑:
1. 构建上下文(messages)
2. 调用 LLM
3. 如果 LLM 返回 tool_calls,执行工具并回注结果
4. 重复直到 LLM 不再调用工具(任务完成)或达到最大迭代次数
"""
messages = [
{"role": "system", "content": "You are a helpful assistant. Be concise."},
{"role": "user", "content": user_message},
]
for iteration in range(max_iterations):
# 调用 LLM
response = client.chat.completions.create(
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
messages=messages,
tools=tools,
)
message = response.choices[0].message
messages.append(message)
# 如果没有工具调用,说明任务完成
if not message.tool_calls:
return message.content
# 执行所有工具调用
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f"[Tool] {name}({args})")
# 执行工具
if name not in functions:
result = f"Error: Unknown tool '{name}'"
else:
result = functions[name](**args)
# 将工具结果追加到上下文
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result),
})
return "Max iterations reached"
# ========== 入口 ==========
if __name__ == "__main__":
import sys
task = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "Hello"
print(run_agent(task))
运行示例:
bash
# 安装依赖
pip install openai
# 设置环境变量
export OPENAI_API_KEY='your-key-here'
export OPENAI_MODEL='gpt-4o-mini' # 可选
# 运行
python agent.py "列出当前目录下所有 Python 文件"
python agent.py "创建一个 hello.py,内容是打印 Hello World"
python agent.py "读取 README.md 并总结其内容"
六、权衡分析:极简的代价
极简设计不是免费的午餐。nanoAgent 在获得简洁性的同时,也做出了明确的权衡。
6.1 与主流框架的对比
| 维度 | nanoAgent(103 行) | LangGraph | CrewAI | OpenAI Agents SDK |
|---|---|---|---|---|
| 代码量 | 103 行 | ~50,000 行 | ~30,000 行 | ~15,000 行 |
| 依赖 | 1 个(openai) | 20+ | 15+ | 5+ |
| 多 Agent 支持 | ❌ 单 Agent | ✅ 图编排 | ✅ 角色组队 | ✅ Handoff |
| 记忆系统 | ❌ 无(需自行扩展) | ✅ 内置检查点 | ✅ 内置 | ✅ 内置 |
| 流式输出 | ❌ 同步阻塞 | ✅ | ✅ | ✅ |
| 错误恢复 | ❌ 简单重试 | ✅ 重试+回退 | ✅ | ✅ |
| MCP 支持 | ❌ | ✅ | ✅ | ✅ |
| 学习成本 | ⭐ 10 分钟 | ⭐⭐⭐ 2-3 天 | ⭐⭐ 1 天 | ⭐⭐ 半天 |
| 适用场景 | 学习/原型/脚本 | 复杂工作流 | 团队协作 | 生产级应用 |
| 可调试性 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
6.2 nanoAgent 的优势
- 零学习成本:任何会写 Python 的人都能在 10 分钟内理解并修改
- 零依赖地狱 :只需
pip install openai,没有版本冲突 - 完全透明:没有抽象层,每个决策都可见
- 极致可移植:一个文件,复制粘贴即可使用
6.3 nanoAgent 的局限
- 单 Agent 限制:无法编排多个 Agent 协作
- 无状态管理:每次运行都是无状态的,没有断点续传
- 同步阻塞:工具执行是同步的,无法并行
- 无流式输出:用户必须等待完整响应
- 安全风险:直接执行 bash 命令,没有沙箱隔离
6.4 何时选择 nanoAgent?
- 学习 Agent 原理:最好的入门教材
- 快速原型验证:验证一个 Agent 想法是否可行
- 简单自动化脚本:文件整理、代码生成等简单任务
- 嵌入到现有系统:作为更大系统的一个组件
何时不选择 nanoAgent?
- 需要多 Agent 协作
- 需要复杂的错误恢复
- 需要生产级的监控和日志
- 需要流式输出和实时反馈
七、总结:看见 Agent 的本质
nanoAgent 的价值不在于它能做什么------103 行代码的局限性是显而易见的。它的价值在于它揭示了什么。
当你读完这 103 行,你会意识到:
- Agent 不是魔法:它就是一个循环(调用 LLM → 执行工具 → 回注结果)
- LLM 是引擎,循环是骨架:框架只是给这个骨架添加了血肉
- Function Calling 是关键接口:它让 LLM 从"对话机器"变成了"行动者"
- 工具定义的质量决定了 Agent 的能力上限:description 写得好不好,直接影响 LLM 的决策质量
正如 Rich Hickey 在《Simple Made Easy》中所强调的:简单(simple)不等于容易(easy),简单意味着没有交织的复杂性。 nanoAgent 是简单的------它没有不必要的复杂性。而当你需要更多功能时,你可以在理解这个简单内核的基础上,有选择地添加复杂性。
这就是 nanoAgent 教给我们的最重要的一课:在添加任何抽象之前,先理解你正在抽象的东西。
参考文献
- Weng, L. (2023). "LLM Powered Autonomous Agents." lilianweng.github.io. https://lilianweng.github.io/posts/2023-06-23-agent/
- Yao, S., et al. (2023). "ReAct: Synergizing Reasoning and Acting in Language Models." ICLR 2023. arXiv:2210.03629
- Schick, T., et al. (2023). "Toolformer: Language Models Can Teach Themselves to Use Tools." NeurIPS 2023. arXiv:2302.04761
- Karpathy, A. (2017). "Software 2.0." Medium. https://medium.com/@karpathy/software-2-0-a64152b37c35
- OpenAI. (2024). "Function Calling Guide." OpenAI Platform Documentation. https://platform.openai.com/docs/guides/function-calling
本系列覆盖 AI 大模型基础、Agent 开发、MCP 协议、Skill 开发、RAG、模型微调、部署推理 七大方向,从入门到实战的全栈内容持续更新中。
所有文章的 Markdown 源文件、可运行代码、高清配图已整理成完整资料包。
👍 点赞 + ⭐ 关注,评论区扣「1」,挨个发你领取方式 👇