50行Python手搓一个原生AI Agent:彻底看懂智能体的本质
你是不是也被各种AI Agent框架搞晕了?LangChain、CrewAI、AutoGPT… 每个框架都有几百个类、几千行代码,文档看到头大,却始终没搞懂Agent到底是什么。
今天我要告诉你一个真相:AI Agent的核心,其实就是一个while循环。所有复杂的框架,本质上都是在这个循环外面包了一层又一层的抽象。只要你理解了这个最基本的循环,你就能看懂所有Agent框架的源码,甚至能自己写出比框架更灵活的实现。
这篇文章我会用纯原生Python,不依赖任何Agent框架,只用50行代码,带你实现一个完整的、具备工具调用能力的AI Agent。你会亲眼看到,所谓的"智能体",到底是怎么工作的。
AI Agent的本质:一个循环而已
在写代码之前,我们先搞清楚一个最核心的问题:什么是AI Agent?
很多人会给你讲一堆高大上的概念:感知层、认知层、执行层、记忆系统、规划模块… 这些都对,但都太抽象了。
从工程实现的角度看,AI Agent = 大语言模型 + 工具集 + 一个无限循环。
这个循环就是著名的"ReAct循环":
- 思考(Thought):模型根据当前状态,决定下一步要做什么
- 行动(Action):调用对应的工具执行操作
- 观察(Observation):获取工具执行的结果
- 重复:把结果返回给模型,继续下一轮思考
就是这么简单。所有复杂的Agent系统,都是在这个基础循环上扩展出来的。
我画了一张最简单的Agent架构图,你一看就懂:
看到了吗?整个流程没有任何神秘的地方。大模型只是这个循环中的一个"思考"环节,它的作用就是判断:我现在需要调用什么工具?或者我已经有足够的信息回答用户了吗?
50行代码实现完整AI Agent
好了,理论讲完了,现在开始写代码。我们只需要安装一个依赖:openaiSDK。
pipinstallopenai python-dotenv然后创建一个simple_agent.py文件,把下面的代码复制进去。我数过了,去掉空行和注释,正好50行左右。
importosimportjsonfromdotenvimportload_dotenvfromopenaiimportOpenAI# 加载环境变量load_dotenv()client=OpenAI(api_key=os.getenv("OPENAI_API_KEY"))MODEL="gpt-4o-mini"# 用最便宜的模型就行,效果足够好# 定义工具函数:这就是Agent的"手脚"defget_weather(city:str)->str:"""查询指定城市的天气"""# 这里模拟API调用,实际项目中替换成真实的天气APIweather_data={"北京":"晴天,25-32℃,微风","上海":"多云,23-28℃,东南风3级","深圳":"雷阵雨,26-30℃,南风4级"}returnweather_data.get(city,f"抱歉,没有找到{city}的天气信息")defget_current_time()->str:"""获取当前系统时间"""fromdatetimeimportdatetimereturndatetime.now().strftime("%Y年%m月%d日 %H:%M:%S")defcalculate(expression:str)->str:"""执行数学计算"""try:returnf"计算结果:{eval(expression)}"exceptExceptionase:returnf"计算错误:{str(e)}"# 工具定义:告诉模型有哪些工具可用TOOLS=[{"type":"function","function":{"name":"get_weather","description":"查询指定城市的天气情况","parameters":{"type":"object","properties":{"city":{"type":"string","description":"城市名称,例如:北京"}},"required":["city"]}}},{"type":"function","function":{"name":"get_current_time","description":"获取当前系统时间","parameters":{"type":"object","properties":{}}}},{"type":"function","function":{"name":"calculate","description":"执行数学计算,支持加减乘除和括号","parameters":{"type":"object","properties":{"expression":{"type":"string","description":"数学表达式,例如:123 * 456"}},"required":["expression"]}}}]# 工具映射:方便根据函数名找到对应的函数tool_map={"get_weather":get_weather,"get_current_time":get_current_time,"calculate":calculate}defrun_agent(user_query:str)->str:"""运行AI Agent的主函数"""messages=[{"role":"system","content":"你是一个有用的AI助手。你可以使用提供的工具来帮助用户解决问题。如果需要调用工具,请使用函数调用格式。当你有足够的信息回答用户问题时,请直接给出最终答案,不要继续调用工具。"},{"role":"user","content":user_query}]whileTrue:# 调用大模型思考response=client.chat.completions.create(model=MODEL,messages=messages,tools=TOOLS,tool_choice="auto")message=response.choices[0].message messages.append(message)# 如果模型不需要调用工具,直接返回回答ifnotmessage.tool_calls:returnmessage.content# 执行所有工具调用fortool_callinmessage.tool_calls:function_name=tool_call.function.name function_args=json.loads(tool_call.function.arguments)print(f"\n🤖 Agent正在调用工具:{function_name}({function_args})")# 调用对应的工具函数function_response=tool_map[function_name](**function_args)print(f"📦 工具返回结果:{function_response}")# 将工具结果添加到消息历史messages.append({"role":"tool","tool_call_id":tool_call.id,"name":function_name,"content":function_response})# 测试一下if__name__=="__main__":print("="*50)print("50行Python实现的AI Agent")print("="*50)whileTrue:user_input=input("\n请输入你的问题(输入'退出'结束): ")ifuser_input.lower()in["退出","exit","quit"]:breakprint("\n🧠 Agent正在思考中...")result=run_agent(user_input)print(f"\n✅ Agent回答:{result}")代码逐行解析
别看代码短,它包含了一个完整AI Agent的所有核心组件:
1. 工具定义(第13-35行)
这是Agent的"手脚"。我们定义了三个简单的工具:查询天气、获取时间、数学计算。每个工具都有清晰的描述和参数定义,大模型会根据这些描述来决定什么时候调用哪个工具。
2. 工具映射(第68-72行)
一个简单的字典,把函数名映射到实际的函数对象。这样当模型说"我要调用get_weather函数"的时候,我们就能快速找到对应的函数并执行。
3. 主循环(第85-112行)
这就是Agent的心脏!整个run_agent函数就是一个无限循环:
- 第89-95行:调用大模型,让它根据当前的消息历史思考下一步
- 第99-100行:如果模型说"我不需要工具了,我可以回答了",就直接返回结果
- 第103-111行:如果模型需要调用工具,就解析参数,执行工具函数,然后把结果返回给模型
- 然后循环继续,直到模型认为任务完成
就是这么简单!没有任何复杂的抽象,没有任何黑箱操作。每一步你都能看得清清楚楚。
运行效果演示
现在我们来运行一下这个Agent,看看它的表现:
================================================== 50行Python实现的AI Agent ================================================== 请输入你的问题(输入'退出'结束): 现在几点了? 🧠 Agent正在思考中... 🤖 Agent正在调用工具: get_current_time({}) 📦 工具返回结果: 2026年05月28日 19:52:37 ✅ Agent回答: 现在的时间是2026年05月28日 19:52:37。 请输入你的问题(输入'退出'结束): 北京今天天气怎么样?适合穿什么衣服? 🧠 Agent正在思考中... 🤖 Agent正在调用工具: get_weather({'city': '北京'}) 📦 工具返回结果: 晴天,25-32℃,微风 ✅ Agent回答: 北京今天是晴天,气温在25-32℃之间,有微风。建议穿短袖、短裤等轻薄透气的衣服,注意防晒。 请输入你的问题(输入'退出'结束): 1234乘以5678等于多少?再加上9876呢? 🧠 Agent正在思考中... 🤖 Agent正在调用工具: calculate({'expression': '1234 * 5678'}) 📦 工具返回结果: 计算结果: 7006652 🤖 Agent正在调用工具: calculate({'expression': '7006652 + 9876'}) 📦 工具返回结果: 计算结果: 7016528 ✅ Agent回答: 1234乘以5678等于7006652,再加上9876等于7016528。看到了吗?这个只有50行代码的Agent,已经能够:
- 自主判断是否需要调用工具
- 正确解析工具参数
- 连续调用多个工具完成复杂任务
- 根据工具返回的结果生成自然语言回答
这和你用LangChain写出来的Agent,本质上没有任何区别。
为什么我建议你先写原生实现
很多人一上来就用LangChain,结果遇到问题根本不知道怎么调试。比如Agent突然不调用工具了,或者调用了错误的工具,你根本不知道是提示词的问题,还是框架的问题,还是模型本身的问题。
但是如果你先写过这个原生的实现,你就会明白:
- 工具调用本质上就是模型输出了一个特定格式的JSON
- 消息历史是Agent唯一的"记忆"
- 框架所谓的"AgentExecutor",其实就是一个while循环
当你理解了这些本质之后,再去看任何Agent框架的源码,都会觉得一目了然。而且在实际项目中,很多时候原生实现比框架更灵活,性能更好,也更容易调试。
扩展方向:从50行到生产级
这个50行的Agent虽然简单,但它是一个完整的骨架。你可以在这个基础上,很容易地扩展出各种高级功能:
- 增加更多工具:比如网页搜索、文件读写、数据库查询、发送邮件等
- 增加记忆系统:用向量数据库存储长期记忆,实现上下文感知
- 增加规划能力:让Agent先把复杂任务拆解成步骤,再一步步执行
- 增加错误处理:处理工具调用失败、模型输出格式错误等情况
- 增加流式输出:让Agent的回答能够实时显示给用户
- 增加多轮对话:支持用户和Agent进行连续的对话
总结
AI Agent不是什么神秘的黑科技,它的核心就是一个"思考-行动-观察"的循环。所有复杂的框架,都是在这个循环的基础上构建起来的。
通过这50行代码,我希望你能真正理解Agent的本质。不要被各种高大上的概念和复杂的框架吓倒。编程的真谛,就是把复杂的问题拆解成简单的步骤,然后用代码一步步实现。
现在你已经有了一个可以工作的Agent骨架,接下来就尽情发挥你的想象力,用它来解决你自己的问题吧。
