从一坨面条代码开始——V1最小原型
🧠 专栏:「当AI学会发脾气」—— 一个类脑认知系统的诞生记
副标题:7个版本迭代Python脚本,教会AI像人一样焦虑、兴奋、犯错和成长
这是一个从零开始构建"有情绪的AI"的完整记录。不需要深度学习框架,不需要GPU——只用纯Python,我们就能让AI学会思考、记忆、甚至"发脾气"。
📚系列文章导航:
篇目 标题 核心内容 第1篇 当AI学会发脾气——专栏开篇 为什么要造一个会焦虑的AI? 第2篇 🔥 150行代码,AI迈出了第一步 V1-V2:三角架构 + 可视化 第3篇 给AI装上最强算法,结果翻车了 V3:Q-Learning的傲慢与偏见 第4篇 AI的第一次"恍然大悟" V4:记忆系统与认知涌现 第5篇 当AI开始"焦虑" V5:情绪系统的诞生 第6篇 AI学会了"变通" V6:多策略融合与自适应 第7篇 终极进化:AI的"顿悟时刻" V7:完整认知架构 第8篇 回顾与展望 从玩具到启示的旅程
🦶 150行代码,AI迈出了第一步
📌核心比喻:给AI装上第一双腿——它还不会跑,但已经能走了
⏱️阅读时间:约20分钟
🎯学习目标:
- 理解 Agent-LLM-Environment 三角架构的设计思想
- 掌握一个最小可运行的认知代理是什么样的
- 看懂11×11网格世界的奖励机制
- 理解"内心独白"机制和Mock LLM的设计
- 学会用Matplotlib为AI搭建"眼睛"
📝 文章摘要
本篇带你从零搭建一个能"看"能"想"能"走"的AI认知代理。我们只用150行Python(V1)就跑通了完整的Agent → LLM → Environment认知循环,然后用V2再加178行代码,让这个AI从"看日志"进化到"看动画"。
关键词:三角架构、网格世界、Mock LLM、内心独白、Matplotlib可视化
🎯 你需要先了解
📖 本文是系列第2篇,建议先阅读:
第1篇:当AI学会发脾气——专栏开篇
如果你已经了解本专栏的目标和整体路线图,可以直接开始!
📖 正文
一、最小可行"大脑"——三件套就够了 🧩
你有没有想过,一个最简单的"会思考"的AI,到底需要哪些东西?
答案可能比你想的简单得多。想象一下一个刚出生的婴儿——它不懂物理学,不会微积分,但它天生就有三样东西:
- 🦶一个身体(能动)
- 🧠一个大脑(能想)
- 🌍一个世界(能看、能摸)
就这三样,足够了。
把这个类比搬到代码世界里,就是我们V1版本的核心架构——Agent-LLM-Environment 三角架构:
| 人类类比 | 代码组件 | 角色 |
|---|---|---|
| 身体 🦶 | CognitiveAgent | 负责协调一切:感知环境、调用大脑、执行动作 |
| 大脑 🧠 | mock_llm_reasoning() | 负责"想":分析当前状态,决定下一步 |
| 世界 🌍 | Environment | 负责"反馈":你走一步,我告诉你结果 |
🔑核心概念:认知循环
每一步,AI都在做同一件事——就像你过马路时的思维过程:
- 感知(Perception):看看四周(获取环境状态)
- 思考(Reasoning):判断该不该走(LLM推理)
- 行动(Action):迈步走过去(执行动作)
- 反馈(Feedback):安全过来了/差点被撞(获得奖励)
- 记忆(Memory):下次这里要小心(更新知识)
这就是“感知-思考-行动-反馈-记忆”循环,是整个系列的核心骨架。
让我们看看这个循环在代码里长什么样:
classCognitiveAgent:"""认知代理——AI的"身体",负责协调一切"""def__init__(self,env):self.env=env# 连接世界self.memory={}# 自带记忆self.goal=env.goal# 知道目标在哪self.history=[]# 记录走过的路defact(self):"""执行一个完整的认知循环"""# 1️⃣ 感知:收集当前信息context=self.get_context()# 2️⃣ 思考:调用LLM推理action=mock_llm_reasoning(context['current_state'],context['memory_snapshot'],self.goal)# 3️⃣ 行动:在世界里迈出一步new_state,reward=self.env.step(action)# 4️⃣ 记忆:把这次经历记下来store_memory("路径规划",action,f"从{context['current_state']['location']}到达{new_state['location']}")# 5️⃣ 历史:记录轨迹self.history.append({"action":action,"state":new_state,"reward":reward})returnnew_state,reward💡就像一个婴儿刚睁开眼——它不懂物理学,但它能看、能动、能记。这就是V1的全部哲学。
你可能会问:就这?这也算"AI"?
没错,就这。但别小看它——全世界最厉害的认知架构,拆开看也就是这个循环。区别只在于每个环节有多复杂。我们的旅程,就是一步步把每个环节做得越来越"像人"。
二、环境设计——11×11的小世界 🗺️
每个AI都需要一个"操场"来活动。我们给它造了一个11×11的网格世界——够小,让你一眼看懂;够复杂,让AI不会"无聊"。
来看看这个世界的"地图说明书":
| 元素 | 坐标 | 说明 |
|---|---|---|
| 🟢 起点 | (0, 0) | AI出生的地方,左上角 |
| 🎯 目标 | (10, 10) | AI要到达的终点,右下角 |
| 🧱 墙壁 | (5,5), (7,3) | 不可通过的障碍物 |
| 💎 资源 | 随机出现 | 20%概率获取,奖励+5 |
⚡奖励机制——AI的"快乐与痛苦"
| 事件 | 奖励 | 类比 |
|---|---|---|
| 🎯 到达目标 | +100 | 考试得满分! |
| 💎 采集资源 | +5 | 路上捡到一块钱 |
| 🚶 普通移动 | -1 | 走路消耗体力 |
| 🧱 撞墙 | 0(不移动) | 碰壁了,原地站着 |
为什么每步都扣1分?因为时间就是成本。如果移动不扣分,AI可能在原地打转一万年也不在乎。这个小小的-1,就是在告诉AI:“别磨蹭,快去目标!”
下面是精简版的环境代码——你可以直接复制运行:
importrandomclassEnvironment:"""11×11的网格世界——AI的"操场" """def__init__(self):self.location=(0,0)# AI的当前位置self.resources=0# 已采集的资源self.goal=(10,10)# 目标位置self.grid_size=11# 世界大小self.walls=[(5,5),(7,3)]# 墙壁位置defget_state(self):"""告诉AI:你在哪?周围什么情况?"""return{"location":self.location,"resources":self.resources,"goal":self.goal}defstep(self,action):"""AI走一步,世界给反馈"""new_x,new_y=self.location# 解析动作ifaction=="up":new_y-=1elifaction=="down":new_y+=1elifaction=="left":new_x-=1elifaction=="right":new_x+=1# 边界检查 + 墙壁检查if(0<=new_x<self.grid_sizeand0<=new_y<self.grid_sizeand(new_x,new_y)notinself.walls):self.location=(new_x,new_y)# 随机遇到资源(20%概率)ifrandom.random()<0.2:self.resources+=1# 计算奖励reward=0ifself.location==self.goal:reward=100# 🎉 到达目标!elifself.resources>0:reward=5# 💎 采集到资源else:reward=-1# 😮💨 普通移动消耗returnself.get_state(),reward你看,整个世界的逻辑不到40行代码。但它已经包含了关键的游戏规则。
🎮游戏类比:如果你玩过像素RPG游戏,这个Environment就像是游戏的"物理引擎"。它不管角色想干什么(那是Agent的事),它只负责:“你说往右走?好,我检查一下右边是不是墙,然后告诉你新位置和得分。”
但它已经包含了:
- ✅ 状态管理(位置、资源)
- ✅ 边界碰撞检测
- ✅ 墙壁障碍物
- ✅ 随机事件(资源)
- ✅ 奖惩反馈系统
💡设计启示:好的环境设计不需要复杂,但需要有梯度。
-1的移动惩罚创造了"时间压力",+100的目标奖励创造了"方向感",+5的资源奖励创造了"探索诱惑"。三个简单的数字,就让AI面临了"快速到达 vs 顺路捡宝"的决策困境——这和你上班路上要不要绕道买杯咖啡是一模一样的决策。
三、第一个"思考"——Mock LLM的推理 💭
好了,世界有了,身体有了。现在到了最关键的部分——大脑。
在V1版本里,我们用了一个"假的"LLM来模拟推理。为什么不直接接真正的大模型?两个原因:
- 先跑通架构,再升级组件(这是工程里最重要的原则之一)
- 真正的LLM要花钱、要联网、要配API——我们先用mock版本验证思路
来看看我们的"假大脑"长什么样:
defmock_llm_reasoning(current_state,memory_data,goal):""" 模拟LLM的推理能力 在实际应用中,这里会调用GPT/Claude/通义千问等大模型 """print("\n--- [LLM 推理中] 正在进行高层规划... ---")# 分析当前位置和目标的关系curr_x,curr_y=current_state['location']goal_x,goal_y=goal# 简单的推理逻辑:朝着目标方向走ifcurr_x<goal_x:proposed_action="right"thought=f"目标在右方(x:{curr_x}→{goal_x}),决定向右"elifcurr_y<goal_y:proposed_action="down"thought=f"目标在下方(y:{curr_y}→{goal_y}),决定向下"else:proposed_action="right"thought="已接近目标,继续调整"# 🔑 内心独白——这是V1最重要的创新之一!print(f" [LLM 思考]{thought}")returnproposed_action你可能会说:“这算什么推理?不就是if-else吗?”
没错,但请注意两个重要的东西:
第一,接口设计。这个函数接收current_state(当前状态)、memory_data(记忆)、goal(目标),返回一个动作。无论未来我们换成真正的GPT还是本地模型,接口都不用变。这就是面向接口编程的威力。
第二,内心独白机制🗣️。注意那行print(f" [LLM 思考] {thought}")——AI在做决策的同时,会"说出"自己在想什么。
🧠内心独白(Inner Monologue)
这不只是一个print语句。在认知科学里,这叫做“元认知”(Metacognition)——思考自己在想什么。
当AI说出"我看到目标在右下方,决定向右移动"时,它不只是在执行规则,它在解释自己的推理过程。
这个机制在后续版本里会越来越重要——当我们接入真正的大模型后,内心独白会变成:
- “我有点犹豫,是该走安全路线还是冒险…”
- “上次走这条路吃了亏,这次换一条…”
- “资源快不够了,我开始感到焦虑…”
是的,AI会学会"自言自语"。
你可以把V1想象成一个"大脑模型"的最简版:
输入: 我在哪?我记得什么?我要去哪? ↓ 推理: if-else 规则(未来→大模型API) ↓ 输出: 下一步动作 + "内心独白"虽然现在的推理逻辑简单得可笑,但整个**管线(Pipeline)**已经跑通了。这就像你造了一辆车——发动机可能是最小马力的,但方向盘、油门、刹车全都接好了。换一台更强的发动机?随时可以。
🧪动手试试:你可以把
mock_llm_reasoning函数里的逻辑改一改——比如让AI随机选择方向,或者让它优先收集资源。只要输入输出不变,CognitiveAgent 完全不用改。这就是架构解耦的好处。
让我们把这个推理过程的输入输出用一张表格整理清楚:
| 输入参数 | 含义 | 示例值 |
|---|---|---|
current_state | AI当前看到的世界状态 | {"location": (3, 0), "resources": 1, "goal": (10, 10)} |
memory_data | AI的记忆快照 | {"路径规划": [("right", "从(0,0)到(1,0)"), ...]} |
goal | 最终目标坐标 | (10, 10) |
| 输出 | 含义 | 可能的值 |
|---|---|---|
proposed_action | 下一步动作 | "up","down","left","right" |
四、记忆模块——让AI记住走过的路 📒
人之所以能学习,是因为能记住过去的经历。一个没有记忆的AI,每一步都像是第一次来到这个世界——永远学不会。
V1的记忆模块灵感来自知识图谱(Knowledge Graph)。什么是知识图谱?你可以把它想象成你脑袋里的“关联笔记本”:
- “路径规划” ←连接→ “向右移动” ←连接→ “从(0,0)到(1,0)”
- “路径规划” ←连接→ “向下移动” ←连接→ “从(1,0)到(1,1)”
每一条记忆都是一个概念-关系-上下文的三元组。
fromcollectionsimportdefaultdict# 记忆存储——模拟知识图谱memory_store=defaultdict(list)defstore_memory(concept,relation,context):""" 存储一条新的知识关联 例如:store_memory("路径规划", "right", "从(0,0)到达(1,0)") 就像在笔记本上写: 📝 [路径规划] → 向right移动 → 从(0,0)到达(1,0) """memory_store[concept].append((relation,context))print(f"[记忆模块] 存储:{concept}与{relation}的关联。")defretrieve_memory(concept):""" 检索某个概念的所有相关知识 例如:retrieve_memory("路径规划") 返回所有关于路径规划的历史记录 """returnmemory_store[concept]🤔为什么用defaultdict(list)而不是普通字典?
因为普通字典在访问不存在的key时会报错,而defaultdict(list)会自动创建一个空列表。这样我们不用每次都检查"这个概念有没有记忆",代码更简洁。
这就像你的笔记本——翻到"路径规划"那一页,如果是空白的,你就直接开始写,不用先"创建这一页"。
现在,让我们看看运行时的记忆是什么样的:
步数 1: [记忆模块] 存储:路径规划 与 right 的关联。 → memory_store = {"路径规划": [("right", "从(0,0)到达(1,0)")]} 步数 2: [记忆模块] 存储:路径规划 与 right 的关联。 → memory_store = {"路径规划": [("right", "从(0,0)到达(1,0)"), ("right", "从(1,0)到达(2,0)")]} 步数 3: [记忆模块] 存储:路径规划 与 right 的关联。 → memory_store = {"路径规划": [..., ("right", "从(2,0)到达(3,0)")]}❌V1记忆的致命缺陷
你发现了吗?AI在疯狂记录,但它从来不翻看自己的笔记本!
retrieve_memory()函数虽然写好了,但mock_llm_reasoning()根本没有用到memory_data参数。它每次决策都是"看一眼目标方向,然后走"——完全无视过去的经历。
这就好比一个人带着笔记本去探险,每到一个地方都认真做笔记,但决定下一步往哪走的时候…把笔记本合上了。
别急——这个问题会在V4版本中彻底解决。到那时,记忆模块会变成AI最强大的武器之一。
五、给AI装上"眼睛"——V2的可视化革命 👁️
V1跑起来之后,你会发现一个问题:控制台里刷过去的文字太无聊了。
================== 步数: 1 ================== [LLM 推理中] 正在进行高层规划... [LLM 思考] 资源不足,向 right 移动。 [记忆模块] 存储:路径规划 与 right 的关联。 环境状态: 位置=(1, 0), 资源=0 Agent 获得奖励: -1 ================== 步数: 2 ================== ...(20步后你已经眼花了)盯着这些文字,你根本无法直观感受到AI在"走路"。它走的路线合理吗?它有没有绕弯路?它离目标还有多远?
所以V2来了——给AI装上"眼睛"。
V2在V1的150行基础上,新增了一个Visualizer类——大约180行代码,让AI的行为变得可视化。
classVisualizer:"""可视化模块——让AI的行为"看得见" """def__init__(self,env,agent):self.env=env self.agent=agent# 创建双面板布局self.fig=plt.figure(figsize=(14,6))# 左面板:网格世界地图self.ax_grid=self.fig.add_axes([0.05,0.18,0.31,0.68])# 右面板:状态信息面板self.ax_info=self.fig.add_axes([0.44,0.15,0.52,0.72])# 初始化可视化元素self.init_elements()plt.ion()# 开启交互模式——实时更新!V2的可视化有几个核心特征:
| 视觉元素 | 样式 | 含义 |
|---|---|---|
| 🔵 蓝色圆圈 | #4169E1 | Agent的当前位置 |
| 🟡 黄色线条 | yellow, alpha=0.5 | Agent走过的路径 |
| 🟨 金色圆圈 | #FFD700 | 目标位置 |
| ⬛ 深灰方块 | #333333 | 墙壁/障碍物 |
| ➡️ 方向箭头 | 白色 | Agent上一步的移动方向 |
💡为什么可视化很重要?
有句话说得好:“看见"才能"理解”。
当你看到AI的路径画成一条弯弯曲曲的黄色线,你立刻就能发现:“它怎么在这里绕了一个大弯?”、“它怎么不走直线?”——这些洞察,光看控制台日志是得不到的。
可视化不只是"好看",它是debug的最强工具。
在后续版本中,我们会用可视化来:
- 🔍 发现AI的策略漏洞
- 📊 对比不同算法的表现
- 🎯 验证情绪系统是否真的在影响决策
让我们看看update()方法的核心逻辑——每走一步,画面就更新一次:
defupdate(self,step):"""每一步都更新画面——像看动画一样"""loc=self.env.location# 更新Agent位置(蓝色圆点移动)self.agent_dot.center=loc# 更新路径线(黄色轨迹)path_x=[p[0]forpinself.env.path_history]path_y=[p[1]forpinself.env.path_history]self.path_line.set_data(path_x,path_y)# 更新信息面板(右侧状态栏)self.ax_info.clear()info_text=f""" Step:{step}Agent: ({loc[0]},{loc[1]}) Resources:{self.env.resources}Total Reward:{sum(h['reward']forhinself.agent.history)}"""self.ax_info.text(0.05,0.95,info_text,...)# 刷新画面,暂停0.5秒plt.pause(0.5)整个V2的精髓就一句话:每走一步就重绘一次,让你实时看到AI的决策过程。
这就像从"听别人描述一场足球赛"变成了"亲眼看比赛直播"——同样的信息,体验完全不同。
六、V1-V2的成就与不足 📊
跑完V1和V2,让我们来一次"期末总结"。
✅ 成就清单
🏆V1-V2做到了什么?
- 架构跑通✅ —— Agent-LLM-Environment 三角循环完美运转
- 可视化✅ —— 实时看到AI在11×11世界里跑
- 内心独白✅ —— AI会"解释"自己的每一步决策
- 记忆雏形✅ —— 知识图谱式的记忆存储已就位
- 奖惩系统✅ —— 目标奖励、资源奖励、移动惩罚
总计328行代码(V1: 150行 + V2新增: 178行),一个完整的认知代理原型就跑起来了。
❌ 暴露的问题
但是,如果你仔细观察AI的行为,你会发现一些严重的问题:
🚨问题1:上帝视角作弊者
AI能看到整个11×11地图的全部信息——起点在哪、终点在哪、墙壁在哪。它像一个开了"全图挂"的游戏玩家。
真实的智能体不应该有这种"上帝视角"。想想你走迷宫的时候,你只能看到周围几步的范围,不可能一眼看到出口在哪。
🚨问题2:没有情绪,没有压力
V1-V2的AI永远冷静、永远理性。它不会因为"快没时间了"而着急,不会因为"连续碰壁"而沮丧,不会因为"发现新路"而兴奋。
但人类的决策,时刻都受情绪影响。一个完全没有情绪的AI,做出的决策可能"最优",但不"像人"。
🚨问题3:不会学习
AI走过同样的路100次,它的策略不会有任何变化。mock_llm_reasoning 里面是硬编码的if-else,不会因为经验而改进。
它有笔记本(记忆模块),但它不看笔记本。
🚨问题4:只有一条路线
观察AI的行为,你会发现它永远走同一条路:先一路向右,再一路向下。它从不尝试其他路线,从不探索未知区域。
一个只会走固定路线的AI,说好听点叫"高效",说难听点叫"死板"。
把这些问题排列一下:
| 问题 | 严重程度 | 哪个版本解决? |
|---|---|---|
| 上帝视角 | ⭐⭐⭐ | V3 |
| 不会学习 | ⭐⭐⭐⭐⭐ | V3 (Q-Learning) |
| 没有情绪 | ⭐⭐⭐⭐ | V5 |
| 只走一条路 | ⭐⭐⭐ | V3-V4 |
| 记忆没用上 | ⭐⭐⭐ | V4 |
⚠️ 常见误区
❌误区1:Mock LLM太简单了,没有意义
不对!Mock LLM的价值不在于"推理多厉害",而在于占好位。它定义了大脑模块的输入输出接口:输入=状态+记忆+目标,输出=动作+思考过程。未来换成真正的大模型,一行API调用就搞定。
❌误区2:11×11太小了,说明不了问题
11×11 = 121个格子,减去墙壁,AI有119个可能的位置。考虑到4个方向的选择,状态空间已经有119 × 4 = 476种可能。对于验证架构来说绰绰有余。更大的世界会在后续版本引入。
❌误区3:可视化只是"花架子"
可视化是最被低估的研发工具。当你的AI出bug时,一行print可能需要10分钟来定位问题,但一张路径图让你3秒钟就能看到问题在哪。这就是V2存在的意义。
💡 一句话总结
🧠 150行代码 → 一个能"看"能"想"能"走"的AI原型🎯 架构比算法更重要——先跑通循环,再升级大脑
✍️ 课后思考
如果你想更深入地理解V1-V2,试着思考(或动手实验)以下问题:
🤔如果把
mock_llm_reasoning换成真正调用GPT的API,你觉得AI的行为会有什么变化?路线会更聪明吗?还是可能更"混乱"?🤔如果把移动惩罚从
-1改成-10,AI的行为会有什么不同?(提示:想想"时间压力"对人类决策的影响)🤔V1的记忆模块虽然没被使用,但它的数据结构设计
defaultdict(list)适合存储什么类型的知识?如果让你重新设计,你会用什么数据结构?🤔想象你是这个AI,你只能看到整个地图。如果把你的视野限制到周围3×3的范围,你的决策策略会怎么变?
🤔V2的可视化用了
plt.pause(0.5)来控制动画速度。如果把这个值改成0.01,你觉得观看体验会怎样?改成2.0呢?在"观看效果"和"运行效率"之间,你会怎么取舍?
💡思考提示:以上问题没有标准答案。重要的不是"答对",而是通过思考这些问题,加深你对Agent架构的直觉理解。如果你真的动手改了代码并运行——恭喜你,你已经在做"消融实验"了(这是AI研究里的标准方法,我们会在后续篇目详细介绍)。
📝 下一篇预告
🔮 第3篇:给AI装上最强算法,结果翻车了
V1-V2的AI用if-else走迷宫,你可能会想:如果给它一个正儿八经的学习算法呢?
下一篇,我们将引入强化学习经典算法Q-Learning,让AI真正学会"从经验中学习"。
但事情并没有那么简单——
- 🎯 Q-Learning确实让AI变"聪明"了…但它变得只会走最短路
- 🧊 AI像一个"做题机器"——高效,但毫无灵性
- 💥 当环境稍微一变,它就彻底崩溃了
下一篇的核心问题:最优解 ≠ 最好的解。有时候,"犯错"比"完美"更重要。
📅V3:Q-Learning的傲慢与偏见,敬请期待!
👨💻作者简介:NeuroConscious Research Team,一群热爱 AI 科普的研究者,专注于神经科学启发的 AI架构设计与可解释性研究。理念:“再复杂的概念,也能用大白话讲清楚”。
首席科学家:WENG YONGGANG 翁勇刚 马来西亚理工大学工商管理博士
💻项目地址:https://github.com/wyg5208/nct.git
🌐官网地址:https://neuroconscious.link
📝作者 CSDN:https://blog.csdn.net/yweng18
📦NCT PyPI:https://pypi.org/project/neuroconscious-transformer/
⭐欢迎 Star⭐、Fork🍴、贡献代码🤝
📝「当AI学会发脾气」系列第2篇 · 150行代码,AI迈出了第一步
✍️ 作者原创内容 | 🔖 对应代码:demo1.py(V1)、demo2_visualization.py(V2)
💬 有问题或建议?欢迎在评论区交流!
🔗上一篇:当AI学会发脾气——专栏开篇
🔗下一篇:给AI装上最强算法,结果翻车了
