前几天刷 Hacker News,看到一个帖子,标题就一句话:用 9 行 Python 写一个 Agent。
点进去是个 gist。别看短,是真能干活的那种——你跟它说"看看当前目录有什么",它自己跑 ls,拿到结果,再用人话告诉你。零依赖,连 requests 都没装,纯标准库。
原版为了硬塞进 9 行,把好几条语句挤在同一行里,手机上根本没法看。我把它展开成正常排版,逻辑一个字没改,原版链接放文末:
import json, sysfrom subprocess import getoutput as shfrom urllib.request import Request, urlopenurl = sys.argv[1]h = [] # 对话历史b = dict( model="gpt-5.6", input=h, # 注意:引用,不是拷贝 tools=[dict(type="custom", name="sh")],)while p := input("> "): h += [dict(role="user", content=p)] while True: req = Request( url, json.dumps(b).encode(), {"Content-Type": "application/json"}, ) r = json.load(urlopen(req)) o = r["output"] h += o calls = [i for i in o if i["type"] == "custom_tool_call"] used = r["usage"]["total_tokens"] / 10500 if not calls: print(o[-1]["content"][0]["text"], f"\n[{used:06.3f}%]") break h += [dict( type="custom_tool_call_output", call_id=i["call_id"], output=sh(i["input"]), ) for i in calls]
整段代码就四块,一块块拆。
Import 部分,进货。 三样东西:处理 JSON 的、发 HTTP 请求的,还有最关键的 subprocess.getoutput,改名叫 sh。这个 sh 就是整个 Agent 唯一的"手"——给它一个字符串,它当 shell 命令执行,把输出还给你。
三个变量,搭台子。url 是 API 地址,命令行传进来。h 存对话历史。b 是请求体,声明用什么模型、带什么工具——工具就一个,叫 sh。
这里有个很妙的细节:b 里的 input 字段直接引用了列表 h。Python 里这是同一个对象,往 h 里塞东西,请求体自动就更新了,不用每次重新组装。代码能压到 9 行,一半靠这种小聪明。
外层循环,聊天。 等你输入,把你说的话追加到历史里。这层没什么新鲜的,ChatGPT 网页版也是这个结构。
戏肉在里面那层。
内层循环,干活。 把整个历史发给模型,拿回输出,然后看一件事——模型的回复里有没有工具调用?
没有:说明模型觉得活干完了。把它的文字回答打印出来,顺带显示上下文用了百分之几(那个 /10500 就是按 1M 窗口折算),跳出内层循环,回去等你说下一句。
有:说明模型想干活。把它要执行的命令扔给 sh 跑,结果追加到历史里,然后不问你,直接再发一轮请求。模型看到命令结果,可能继续调工具,也可能给出答案。循环往复,直到它不再要求执行任何东西。
就这么多。
它和一个普通聊天脚本的区别在哪?
全在内层那个 while True。
Chatbot 的结构是回合制:你说一句,它答一句,像打乒乓球。
Agent 的结构是,你说一句,它自己跟自己打好几个来回——跑命令、看结果,不满意接着跑——直到它认为任务完成了,才回来向你汇报。你新招了个助理,交代完事情你就去忙别的了,他自己想办法把事办完,这才叫助理;每敲一下键盘都要请示你的,那叫输入法。
业内说的 Agentic Loop,感知、行动、观察、再行动,听着挺玄,落到代码上就是:把工具执行结果塞回对话历史,再请求一次。
一行代码的事。
那 Claude Code 那几十万行在干嘛?
既然内核 9 行就写完了,Claude Code 这种产品里剩下的代码都在干什么?
我拿这 9 行和我天天在用的 Claude Code 逐项对了一遍,差距集中在五个地方。
第一,安全。这是最要命的一个。
9 行版里,模型说执行什么就执行什么,中间没有任何确认。它今天理解对了你的意思,跑 ls;明天理解错了,跑 rm -rf ~。代码层面毫无防御,等于把一个不设密码的 root shell 交给了一个概率模型。
Claude Code 在这里砌了好几堵墙。每个命令执行前有权限确认,你可以配置哪些命令免批、哪些永远要问;有沙箱模式限制文件和网络访问;还有 Hooks 机制,让你在工具执行前插一段自己的脚本,返回特定退出码就能直接否决这次执行。
第二,工具的精度。
9 行版只有一个 sh,什么活都靠 shell 硬干。改文件?让模型现编一段 sed。搜代码?现编 grep。这些命令模型经常写错,写错了还不自知,一个静默失败的 sed 能把你的文件改得面目全非。
Claude Code 给的是一套专用工具:Read 读文件带行号,Edit 做精确的字符串替换——匹配不上直接报错,绝不静默写坏文件——Grep 结构化搜索。工具越专用,模型犯错的空间越小。这跟给新员工发操作手册是一个道理,你不会让他什么流程都自己发明。
第三,上下文管理。
注意 9 行版特意留的那个百分比显示。作者知道要出事:历史列表 h 只进不出,聊得越久越膨胀,命令输出稍微大一点——比如模型手贱 cat 了一个大文件——窗口直接爆掉,程序就死了。
我平时用 Claude Code 跑长任务,经常看到它在上下文快满的时候自动压缩:把旧的工具输出丢掉,保留任务脉络的总结,然后接着干。这件事说起来一句话,做好了极难。压掉什么、留下什么,直接决定 agent 会不会"失忆"。
第四,System Prompt。
9 行版没有一个字的 system prompt。模型对"自己是谁、该怎么干活、什么不能碰"一无所知,全靠基座模型的素养裸奔。
Claude Code 的 system prompt 有几万字,从输出格式、安全规则,到"什么时候该问用户、什么时候自己决定",全是一条条打磨过的行为规范。9 行版雇了个裸奔的天才,Claude Code 给天才配了整套入职培训。
第五,生态。
Skills、MCP、subagents、多模型调度、断点恢复、成本控制……这些是让 agent 从"能跑"变成"能进生产环境"的东西。9 行版一概没有,倒也不打算有——它的使命就是把骨架亮给你看。
说点我的判断
把这五条摆出来,结论已经在纸面上了:
Agent 的门槛,从来不在那个循环,而在循环外面那一圈。
内核是 1% 的代码,工程是 99%。这 99% 里没多少水分——权限确认防的是真刀真枪的删库事故,上下文压缩救的是跑到一半爆窗口的长任务。每一堵墙背后,都是有人翻过车。
话说回来,我还是建议每个想做 agent 开发的人亲手把这 9 行跑一遍。记得在容器或虚拟机里跑,理由上面说了。框架文档看一百遍,比不上亲眼看一次模型在你的终端里自己敲命令、自己看结果、自己决定下一步。
看懂了这 9 行,再去看 Claude Code、看各家 agent 框架,你就不会被唬住了。它们做的所有事,都在回答同一个问题:
这个循环,怎么才能放心地、长时间地、便宜地转下去?
原版 gist:gist.github.com/tosh/6e91a9dbf08dd630c535e7345ac7f0b5