47 · 神经网络原理揭秘与本地 AI Agent 调度
🔗 知识图谱导航:阅读本文前,建议先回顾《31 · NumPy 高性能数组计算》里的矩阵运算,以及《46 · 模型调优:GridSearchCV、学习曲线与模型持久化》里的训练评估思路。本文会把这些基础推进到“手写神经网络 + 本地 AI Agent 调度”。 NexDo Time · 2026-04-17 · 预计阅读 28 分钟
痛点与架构
神经网络和 AI Agent 常被讲得很玄:一边是矩阵、梯度、激活函数,另一边是本地模型、接口、工具路由。新手真正卡住的地方通常不是“知道名词”,而是不知道数据到底从哪里进去、经过了谁、最后为什么能得到输出。
这一篇把复杂系统拆成两条线:第一条线用 XOR 小数据手写一个三层神经网络,让你看见前向传播、反向传播和损失下降;第二条线用 Ollama 的本地接口设计一个轻量 Agent,让你看懂“用户问题 -> 意图识别 -> 工具选择 -> 大模型回答”的调度流程。
输入数据/用户问题
│
├─ 神经网络线:X -> W/b -> sigmoid -> loss -> backward -> predict
│
└─ Agent 线:query -> intent -> tool -> OllamaClient -> response
步步为营:核心逻辑自适应拆解
这一篇的知识密度较高,所以拆成 7 个小步骤。每个 Step 都先给出文末完整源码里的真实片段,再给出可以单独复制运行的演示代码。你不用一次吞下整篇源码,先把每一块小积木跑通,再回到文末看完整脚本。
Step 1:用 NeuralNetwork 看懂神经网络的矩阵流水线
痛点与机制:
刚接触神经网络时,最容易被“层、权重、激活”这些词吓住。把它想成一条工厂流水线:输入 X 是原材料,W1/W2 是机器参数,b1/b2 是校准旋钮,sigmoid 是质检门,把任何数字压到 0 到 1 之间。这个 Step 先不训练,只看一次前向传播,让新手明确数据从 2 个输入特征流到 4 个隐藏神经元,再流到 1 个预测概率。
核心源码(逐字来自文末完整源码):
class NeuralNetwork:
"""3层全连接神经网络:输入层→隐藏层→输出层"""
def __init__(self, input_dim: int, hidden_dim: int, output_dim: int,
lr: float = 0.1):
rng = np.random.RandomState(42)
self.W1 = rng.randn(input_dim, hidden_dim) * 0.5
self.b1 = np.zeros((1, hidden_dim))
self.W2 = rng.randn(hidden_dim, output_dim) * 0.5
self.b2 = np.zeros((1, output_dim))
self.lr = lr
self.losses: List[float] = []
@staticmethod
def sigmoid(z: np.ndarray) -> np.ndarray:
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
@staticmethod
def sigmoid_deriv(a: np.ndarray) -> np.ndarray:
return a * (1.0 - a)
def forward(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
Z1 = X @ self.W1 + self.b1
A1 = self.sigmoid(Z1)
Z2 = A1 @ self.W2 + self.b2
A2 = self.sigmoid(Z2)
return Z1, A1, Z2, A2
def backward(self, X: np.ndarray, y: np.ndarray,
A1: np.ndarray, A2: np.ndarray) -> None:
n = X.shape[0]
delta2 = (A2 - y) * self.sigmoid_deriv(A2)
dW2 = A1.T @ delta2 / n
db2 = delta2.mean(axis=0, keepdims=True)
delta1 = (delta2 @ self.W2.T) * self.sigmoid_deriv(A1)
dW1 = X.T @ delta1 / n
db1 = delta1.mean(axis=0, keepdims=True)
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
def train(self, X: np.ndarray, y: np.ndarray, epochs: int = 10000) -> None:
for _ in range(epochs):
_, A1, _, A2 = self.forward(X)
loss = float(-np.mean(y * np.log(A2 + 1e-9) + (1 - y) * np.log(1 - A2 + 1e-9)))
self.losses.append(loss)
self.backward(X, y, A1, A2)
def predict(self, X: np.ndarray) -> np.ndarray:
_, _, _, A2 = self.forward(X)
return (A2 >= 0.5).astype(int)
可运行演示(补齐 Mock 数据与 print 反馈):
from typing import List, Tuple
import numpy as np
class NeuralNetwork:
"""3层全连接神经网络:输入层→隐藏层→输出层"""
def __init__(self, input_dim: int, hidden_dim: int, output_dim: int,
lr: float = 0.1):
rng = np.random.RandomState(42)
self.W1 = rng.randn(input_dim, hidden_dim) * 0.5
self.b1 = np.zeros((1, hidden_dim))
self.W2 = rng.randn(hidden_dim, output_dim) * 0.5
self.b2 = np.zeros((1, output_dim))
self.lr = lr
self.losses: List[float] = []
@staticmethod
def sigmoid(z: np.ndarray) -> np.ndarray:
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
@staticmethod
def sigmoid_deriv(a: np.ndarray) -> np.ndarray:
return a * (1.0 - a)
def forward(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
Z1 = X @ self.W1 + self.b1
A1 = self.sigmoid(Z1)
Z2 = A1 @ self.W2 + self.b2
A2 = self.sigmoid(Z2)
return Z1, A1, Z2, A2
def backward(self, X: np.ndarray, y: np.ndarray,
A1: np.ndarray, A2: np.ndarray) -> None:
n = X.shape[0]
delta2 = (A2 - y) * self.sigmoid_deriv(A2)
dW2 = A1.T @ delta2 / n
db2 = delta2.mean(axis=0, keepdims=True)
delta1 = (delta2 @ self.W2.T) * self.sigmoid_deriv(A1)
dW1 = X.T @ delta1 / n
db1 = delta1.mean(axis=0, keepdims=True)
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
def train(self, X: np.ndarray, y: np.ndarray, epochs: int = 10000) -> None:
for _ in range(epochs):
_, A1, _, A2 = self.forward(X)
loss = float(-np.mean(y * np.log(A2 + 1e-9) + (1 - y) * np.log(1 - A2 + 1e-9)))
self.losses.append(loss)
self.backward(X, y, A1, A2)
def predict(self, X: np.ndarray) -> np.ndarray:
_, _, _, A2 = self.forward(X)
return (A2 >= 0.5).astype(int)
X = np.array([[0, 1]], dtype=float)
network = NeuralNetwork(input_dim=2, hidden_dim=4, output_dim=1, lr=0.5)
Z1, A1, Z2, A2 = network.forward(X)
print("输入 X 形状:", X.shape)
print("第一层权重 W1 形状:", network.W1.shape)
print("隐藏层激活 A1 形状:", A1.shape)
print("输出层结果 A2:", np.round(A2, 4).tolist())
print("直觉:2 个输入特征 -> 4 个隐藏神经元 -> 1 个二分类概率")
Step 2:用 XOR 训练证明网络能学非线性关系
痛点与机制:
XOR 像一个小型逻辑谜题:两个输入相同输出 0,不同输出 1,单条直线分不开。隐藏层就像在纸上多折了一次,把原本拧巴的数据摊平。这里训练 5000 轮后打印预测表和损失曲线,新手能看到“模型不是背概念,而是真的从错误里一点点调参数”。
核心源码(逐字来自文末完整源码):
def mode_xor() -> None:
print(f"[{nexdo_time()}] XOR 神经网络训练")
# XOR 数据集
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y = np.array([[0], [1], [1], [0]], dtype=float)
nn = NeuralNetwork(input_dim=2, hidden_dim=4, output_dim=1, lr=1.0)
nn.train(X, y, epochs=5000)
# 预测结果
preds = nn.predict(X)
rows = []
for xi, yi, pi in zip(X, y, preds):
correct = "✓" if int(yi[0]) == int(pi[0]) else "✗"
rows.append([f"[{int(xi[0])},{int(xi[1])}]", int(yi[0]), int(pi[0]), correct])
print_table(["输入", "真实", "预测", "正确"], rows, "XOR 预测结果")
# 损失曲线(ASCII)
sampled = nn.losses[::500]
max_loss = max(sampled)
print("\n 训练损失曲线(每500轮)")
height = 8
for h in range(height, 0, -1):
threshold = h * max_loss / height
line = f" {threshold:6.3f} │"
for l in sampled:
line += "█" if l >= threshold else " "
print(line)
print(f" └{'─'*len(sampled)}")
print(f" 最终损失: {nn.losses[-1]:.6f} 准确率: {(preds == y.astype(int)).mean():.0%}")
可运行演示(补齐 Mock 数据与 print 反馈):
import time
from typing import List, Tuple
import numpy as np
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def print_table(headers: list, rows: list, title: str = "") -> None:
if title:
print(f"\n{'='*65}\n {title}\n{'='*65}")
col_widths = [max(len(str(h)), max((len(str(r[i])) for r in rows), default=0))
for i, h in enumerate(headers)]
print(f"┌{'┬'.join('─'*(w+2) for w in col_widths)}┐")
print(f"│{'│'.join(f' {str(h):<{w}} ' for h, w in zip(headers, col_widths))}│")
print(f"├{'┼'.join('─'*(w+2) for w in col_widths)}┤")
for row in rows:
print(f"│{'│'.join(f' {str(v):<{w}} ' for v, w in zip(row, col_widths))}│")
print(f"└{'┴'.join('─'*(w+2) for w in col_widths)}┘")
class NeuralNetwork:
"""3层全连接神经网络:输入层→隐藏层→输出层"""
def __init__(self, input_dim: int, hidden_dim: int, output_dim: int,
lr: float = 0.1):
rng = np.random.RandomState(42)
self.W1 = rng.randn(input_dim, hidden_dim) * 0.5
self.b1 = np.zeros((1, hidden_dim))
self.W2 = rng.randn(hidden_dim, output_dim) * 0.5
self.b2 = np.zeros((1, output_dim))
self.lr = lr
self.losses: List[float] = []
@staticmethod
def sigmoid(z: np.ndarray) -> np.ndarray:
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
@staticmethod
def sigmoid_deriv(a: np.ndarray) -> np.ndarray:
return a * (1.0 - a)
def forward(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
Z1 = X @ self.W1 + self.b1
A1 = self.sigmoid(Z1)
Z2 = A1 @ self.W2 + self.b2
A2 = self.sigmoid(Z2)
return Z1, A1, Z2, A2
def backward(self, X: np.ndarray, y: np.ndarray,
A1: np.ndarray, A2: np.ndarray) -> None:
n = X.shape[0]
delta2 = (A2 - y) * self.sigmoid_deriv(A2)
dW2 = A1.T @ delta2 / n
db2 = delta2.mean(axis=0, keepdims=True)
delta1 = (delta2 @ self.W2.T) * self.sigmoid_deriv(A1)
dW1 = X.T @ delta1 / n
db1 = delta1.mean(axis=0, keepdims=True)
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
def train(self, X: np.ndarray, y: np.ndarray, epochs: int = 10000) -> None:
for _ in range(epochs):
_, A1, _, A2 = self.forward(X)
loss = float(-np.mean(y * np.log(A2 + 1e-9) + (1 - y) * np.log(1 - A2 + 1e-9)))
self.losses.append(loss)
self.backward(X, y, A1, A2)
def predict(self, X: np.ndarray) -> np.ndarray:
_, _, _, A2 = self.forward(X)
return (A2 >= 0.5).astype(int)
def mode_xor() -> None:
print(f"[{nexdo_time()}] XOR 神经网络训练")
# XOR 数据集
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y = np.array([[0], [1], [1], [0]], dtype=float)
nn = NeuralNetwork(input_dim=2, hidden_dim=4, output_dim=1, lr=1.0)
nn.train(X, y, epochs=5000)
# 预测结果
preds = nn.predict(X)
rows = []
for xi, yi, pi in zip(X, y, preds):
correct = "✓" if int(yi[0]) == int(pi[0]) else "✗"
rows.append([f"[{int(xi[0])},{int(xi[1])}]", int(yi[0]), int(pi[0]), correct])
print_table(["输入", "真实", "预测", "正确"], rows, "XOR 预测结果")
# 损失曲线(ASCII)
sampled = nn.losses[::500]
max_loss = max(sampled)
print("\n 训练损失曲线(每500轮)")
height = 8
for h in range(height, 0, -1):
threshold = h * max_loss / height
line = f" {threshold:6.3f} │"
for l in sampled:
line += "█" if l >= threshold else " "
print(line)
print(f" └{'─'*len(sampled)}")
print(f" 最终损失: {nn.losses[-1]:.6f} 准确率: {(preds == y.astype(int)).mean():.0%}")
mode_xor()
Step 3:用激活函数给神经网络装上转弯能力
痛点与机制:
如果没有激活函数,多层神经网络再深也只是几次线性变换叠加,效果像只会走直线的车。Sigmoid、Tanh、ReLU 就像不同的方向盘:Sigmoid 适合把结果压成概率,ReLU 让正数直接通过、负数归零,训练时更利落。运行输出里的表格和 ASCII 曲线能帮读者把公式变成直觉。
核心源码(逐字来自文末完整源码):
def mode_activations() -> None:
print(f"[{nexdo_time()}] 激活函数对比")
x_vals = [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]
def sigmoid(x: float) -> float:
return 1 / (1 + math.exp(-x))
def tanh(x: float) -> float:
return math.tanh(x)
def relu(x: float) -> float:
return max(0.0, x)
def leaky_relu(x: float) -> float:
return x if x > 0 else 0.01 * x
rows = []
for x in x_vals:
rows.append([f"{x:5.1f}",
f"{sigmoid(x):.4f}",
f"{tanh(x):.4f}",
f"{relu(x):.4f}",
f"{leaky_relu(x):.4f}"])
print_table(["x", "Sigmoid", "Tanh", "ReLU", "LeakyReLU"], rows, "激活函数值对比")
# ASCII 折线图(ReLU vs Sigmoid)
print("\n 激活函数曲线(Sigmoid=S, ReLU=R)")
width, height = 50, 10
print(f" {'─'*width}")
for h in range(height, -1, -1):
yv = h / height
line = " "
for col in range(width):
xv = -3.0 + col * 6.0 / width
s_val = sigmoid(xv)
r_val = min(1.0, max(0.0, relu(xv)))
s_match = abs(s_val - yv) < 0.06
r_match = abs(r_val - yv) < 0.06
if s_match and r_match:
line += "+"
elif s_match:
line += "S"
elif r_match:
line += "R"
else:
line += "·"
print(line)
print(f" {'─'*width}")
print(f" x轴: [-3, +3] y轴: [0, 1] S=Sigmoid R=ReLU")
可运行演示(补齐 Mock 数据与 print 反馈):
import math
import time
from typing import List
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def print_table(headers: list, rows: list, title: str = "") -> None:
if title:
print(f"\n{'='*65}\n {title}\n{'='*65}")
col_widths = [max(len(str(h)), max((len(str(r[i])) for r in rows), default=0))
for i, h in enumerate(headers)]
print(f"┌{'┬'.join('─'*(w+2) for w in col_widths)}┐")
print(f"│{'│'.join(f' {str(h):<{w}} ' for h, w in zip(headers, col_widths))}│")
print(f"├{'┼'.join('─'*(w+2) for w in col_widths)}┤")
for row in rows:
print(f"│{'│'.join(f' {str(v):<{w}} ' for v, w in zip(row, col_widths))}│")
print(f"└{'┴'.join('─'*(w+2) for w in col_widths)}┘")
def mode_activations() -> None:
print(f"[{nexdo_time()}] 激活函数对比")
x_vals = [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]
def sigmoid(x: float) -> float:
return 1 / (1 + math.exp(-x))
def tanh(x: float) -> float:
return math.tanh(x)
def relu(x: float) -> float:
return max(0.0, x)
def leaky_relu(x: float) -> float:
return x if x > 0 else 0.01 * x
rows = []
for x in x_vals:
rows.append([f"{x:5.1f}",
f"{sigmoid(x):.4f}",
f"{tanh(x):.4f}",
f"{relu(x):.4f}",
f"{leaky_relu(x):.4f}"])
print_table(["x", "Sigmoid", "Tanh", "ReLU", "LeakyReLU"], rows, "激活函数值对比")
# ASCII 折线图(ReLU vs Sigmoid)
print("\n 激活函数曲线(Sigmoid=S, ReLU=R)")
width, height = 50, 10
print(f" {'─'*width}")
for h in range(height, -1, -1):
yv = h / height
line = " "
for col in range(width):
xv = -3.0 + col * 6.0 / width
s_val = sigmoid(xv)
r_val = min(1.0, max(0.0, relu(xv)))
s_match = abs(s_val - yv) < 0.06
r_match = abs(r_val - yv) < 0.06
if s_match and r_match:
line += "+"
elif s_match:
line += "S"
elif r_match:
line += "R"
else:
line += "·"
print(line)
print(f" {'─'*width}")
print(f" x轴: [-3, +3] y轴: [0, 1] S=Sigmoid R=ReLU")
mode_activations()
Step 4:用 OllamaClient 封装本地大模型接口
痛点与机制:
本地大模型服务可以理解成一台开在 localhost 上的“私人大脑”。OllamaClient 的职责不是做智能推理,而是把 Python 字典打包成 JSON,通过 /api/chat、/api/generate 这些接口送给 Ollama。这个演示只打印接口地址,不真实访问网络,保证没安装 Ollama 的读者也能先理解客户端封装。
核心源码(逐字来自文末完整源码):
class OllamaClient:
"""轻量 Ollama 客户端,仅用 urllib(零外部依赖)"""
def __init__(self, base_url: str = "http://localhost:11434"):
self.base_url = base_url
def _post(self, path: str, payload: Dict[str, Any], timeout: int = 30) -> Dict:
data = json.dumps(payload).encode()
req = urllib.request.Request(
f"{self.base_url}{path}",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode())
except urllib.error.URLError as e:
return {"error": str(e)}
def generate(self, model: str, prompt: str) -> str:
result = self._post("/api/generate", {"model": model, "prompt": prompt, "stream": False})
return result.get("response", result.get("error", "无响应"))
def chat(self, model: str, messages: List[Dict[str, str]]) -> str:
result = self._post("/api/chat", {"model": model, "messages": messages, "stream": False})
return result.get("message", {}).get("content", result.get("error", "无响应"))
def list_models(self) -> List[str]:
try:
req = urllib.request.Request(f"{self.base_url}/api/tags")
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read().decode())
return [m["name"] for m in data.get("models", [])]
except Exception as e:
return [f"连接失败: {e}"]
可运行演示(补齐 Mock 数据与 print 反馈):
import json
import urllib.error
import urllib.request
from typing import Any, Dict, List
class OllamaClient:
"""轻量 Ollama 客户端,仅用 urllib(零外部依赖)"""
def __init__(self, base_url: str = "http://localhost:11434"):
self.base_url = base_url
def _post(self, path: str, payload: Dict[str, Any], timeout: int = 30) -> Dict:
data = json.dumps(payload).encode()
req = urllib.request.Request(
f"{self.base_url}{path}",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode())
except urllib.error.URLError as e:
return {"error": str(e)}
def generate(self, model: str, prompt: str) -> str:
result = self._post("/api/generate", {"model": model, "prompt": prompt, "stream": False})
return result.get("response", result.get("error", "无响应"))
def chat(self, model: str, messages: List[Dict[str, str]]) -> str:
result = self._post("/api/chat", {"model": model, "messages": messages, "stream": False})
return result.get("message", {}).get("content", result.get("error", "无响应"))
def list_models(self) -> List[str]:
try:
req = urllib.request.Request(f"{self.base_url}/api/tags")
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read().decode())
return [m["name"] for m in data.get("models", [])]
except Exception as e:
return [f"连接失败: {e}"]
client = OllamaClient(base_url="http://localhost:11434")
print("Ollama 基础地址:", client.base_url)
print("模型列表接口:", f"{client.base_url}/api/tags")
print("聊天接口:", f"{client.base_url}/api/chat")
print("说明:这里先只看封装,不真实访问网络,避免本地没启动 Ollama 时报错。")
Step 5:用 LocalAIAgent 把用户问题路由到合适工具
痛点与机制:
Agent 不神秘,可以先把它理解成前台分诊护士:用户说“计算”,就分到 math;说“写 Python”,就分到 code;说“分析数据”,就分到 analyze。_detect_intent() 先用关键词做轻量分流,run() 再组装系统提示词和历史消息。演示里用 Mock 客户端替代真实模型,是为了让读者立刻看见路由结果。
核心源码(逐字来自文末完整源码):
class LocalAIAgent:
"""本地 AI Agent 调度器"""
TOOLS = {
"math": "数学计算与公式推导",
"code": "代码生成与调试",
"explain": "概念解释与教学",
"analyze": "数据分析与洞察",
}
def __init__(self, model: str = "llama3.2", ollama_url: str = "http://localhost:11434"):
self.model = model
self.client = OllamaClient(ollama_url)
self.history: List[Dict[str, str]] = []
def _detect_intent(self, query: str) -> str:
"""简单意图识别(关键词匹配,无需LLM)"""
q = query.lower()
if any(w in q for w in ["计算", "公式", "数学", "求", "等于"]):
return "math"
if any(w in q for w in ["代码", "python", "函数", "实现", "写"]):
return "code"
if any(w in q for w in ["分析", "数据", "统计", "趋势"]):
return "analyze"
return "explain"
def run(self, query: str) -> Dict[str, Any]:
intent = self._detect_intent(query)
tool = self.TOOLS[intent]
system_prompt = f"你是一个专注于{tool}的AI助手,请简洁准确地回答。"
self.history.append({"role": "user", "content": query})
messages = [{"role": "system", "content": system_prompt}] + self.history[-4:]
response = self.client.chat(self.model, messages)
self.history.append({"role": "assistant", "content": response})
return {"intent": intent, "tool": tool, "response": response}
可运行演示(补齐 Mock 数据与 print 反馈):
from typing import Any, Dict, List
class OllamaClient:
"""演示用假客户端:不访问网络,只返回固定回答。"""
def __init__(self, base_url: str = "mock://ollama"):
self.base_url = base_url
def chat(self, model: str, messages: List[Dict[str, str]]) -> str:
last_user = messages[-1]["content"]
return f"Mock模型收到:{last_user}"
class LocalAIAgent:
"""本地 AI Agent 调度器"""
TOOLS = {
"math": "数学计算与公式推导",
"code": "代码生成与调试",
"explain": "概念解释与教学",
"analyze": "数据分析与洞察",
}
def __init__(self, model: str = "llama3.2", ollama_url: str = "http://localhost:11434"):
self.model = model
self.client = OllamaClient(ollama_url)
self.history: List[Dict[str, str]] = []
def _detect_intent(self, query: str) -> str:
"""简单意图识别(关键词匹配,无需LLM)"""
q = query.lower()
if any(w in q for w in ["计算", "公式", "数学", "求", "等于"]):
return "math"
if any(w in q for w in ["代码", "python", "函数", "实现", "写"]):
return "code"
if any(w in q for w in ["分析", "数据", "统计", "趋势"]):
return "analyze"
return "explain"
def run(self, query: str) -> Dict[str, Any]:
intent = self._detect_intent(query)
tool = self.TOOLS[intent]
system_prompt = f"你是一个专注于{tool}的AI助手,请简洁准确地回答。"
self.history.append({"role": "user", "content": query})
messages = [{"role": "system", "content": system_prompt}] + self.history[-4:]
response = self.client.chat(self.model, messages)
self.history.append({"role": "assistant", "content": response})
return {"intent": intent, "tool": tool, "response": response}
agent = LocalAIAgent(model="mock-llm", ollama_url="mock://ollama")
queries = [
"帮我计算 8 乘以 7",
"用 Python 写一个求和函数",
"分析这组销售数据趋势",
"解释什么是反向传播",
]
for query in queries:
result = agent.run(query)
print(f"问题: {query}")
print(f"意图: {result['intent']} | 工具: {result['tool']}")
print(f"回答: {result['response']}")
print("-" * 50)
Step 6:用 mode_agent 演示 Ollama 缺席时的优雅降级
痛点与机制:
工程代码不能假设外部服务永远在线。mode_agent() 先检查模型列表,如果发现 Ollama 没启动,就跳过真实调用,但仍展示意图识别和架构流程。这像餐厅发现后厨暂时停火:不能继续上菜,但前台仍要清楚告诉客人原因和下一步怎么做。
核心源码(逐字来自文末完整源码):
def mode_agent() -> None:
print(f"[{nexdo_time()}] 本地 AI Agent 调度器演示")
# 检查 Ollama 是否可用
client = OllamaClient()
models = client.list_models()
ollama_available = not any("连接失败" in m for m in models)
rows = [
["Ollama服务", "http://localhost:11434", "✓ 在线" if ollama_available else "✗ 未启动"],
["可用模型", ", ".join(models[:3]) if ollama_available else "N/A", ""],
]
print_table(["组件", "地址/值", "状态"], rows, "AI Agent 环境检查")
# 演示意图识别(不依赖Ollama)
agent = LocalAIAgent()
test_queries = [
"帮我计算 1+1 等于多少",
"用Python写一个快速排序",
"解释什么是梯度下降",
"分析这组数据的趋势",
]
intent_rows = []
for q in test_queries:
intent = agent._detect_intent(q)
intent_rows.append([q[:25]+"...", intent, LocalAIAgent.TOOLS[intent]])
print_table(["查询", "识别意图", "调用工具"], intent_rows, "意图识别演示(无需Ollama)")
if ollama_available:
print(f"\n[{nexdo_time()}] 调用 Ollama 生成回答...")
result = agent.run("用一句话解释什么是反向传播")
print(f"\n 意图: {result['intent']} 工具: {result['tool']}")
print(f" 回答: {result['response'][:200]}")
else:
print(f"\n ℹ Ollama 未启动,跳过实际调用。")
print(f" 启动方式: ollama serve 然后: ollama pull llama3.2")
# 展示 Agent 调度架构
print("""
Agent 调度流程:
┌──────────────────────────────────────────────────────┐
│ 用户输入 → 意图识别 → 工具路由 → LLM调用 → 返回 │
│ │
│ 意图识别:关键词匹配(轻量,无需LLM) │
│ 工具路由:math/code/explain/analyze │
│ LLM调用:POST /api/chat → Ollama本地推理 │
│ 历史管理:保留最近4轮对话上下文 │
└──────────────────────────────────────────────────────┘
""")
可运行演示(补齐 Mock 数据与 print 反馈):
import time
from typing import Any, Dict, List
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def print_table(headers: list, rows: list, title: str = "") -> None:
if title:
print(f"\n{'='*65}\n {title}\n{'='*65}")
col_widths = [max(len(str(h)), max((len(str(r[i])) for r in rows), default=0))
for i, h in enumerate(headers)]
print(f"┌{'┬'.join('─'*(w+2) for w in col_widths)}┐")
print(f"│{'│'.join(f' {str(h):<{w}} ' for h, w in zip(headers, col_widths))}│")
print(f"├{'┼'.join('─'*(w+2) for w in col_widths)}┤")
for row in rows:
print(f"│{'│'.join(f' {str(v):<{w}} ' for v, w in zip(row, col_widths))}│")
print(f"└{'┴'.join('─'*(w+2) for w in col_widths)}┘")
class OllamaClient:
"""演示用假客户端:模拟 Ollama 未启动,但程序仍能优雅降级。"""
def __init__(self, base_url: str = "http://localhost:11434"):
self.base_url = base_url
def list_models(self) -> List[str]:
return ["连接失败: 演示环境未启动 Ollama"]
def chat(self, model: str, messages: List[Dict[str, str]]) -> str:
return "演示环境跳过真实大模型调用"
class LocalAIAgent:
"""本地 AI Agent 调度器"""
TOOLS = {
"math": "数学计算与公式推导",
"code": "代码生成与调试",
"explain": "概念解释与教学",
"analyze": "数据分析与洞察",
}
def __init__(self, model: str = "llama3.2", ollama_url: str = "http://localhost:11434"):
self.model = model
self.client = OllamaClient(ollama_url)
self.history: List[Dict[str, str]] = []
def _detect_intent(self, query: str) -> str:
"""简单意图识别(关键词匹配,无需LLM)"""
q = query.lower()
if any(w in q for w in ["计算", "公式", "数学", "求", "等于"]):
return "math"
if any(w in q for w in ["代码", "python", "函数", "实现", "写"]):
return "code"
if any(w in q for w in ["分析", "数据", "统计", "趋势"]):
return "analyze"
return "explain"
def run(self, query: str) -> Dict[str, Any]:
intent = self._detect_intent(query)
tool = self.TOOLS[intent]
system_prompt = f"你是一个专注于{tool}的AI助手,请简洁准确地回答。"
self.history.append({"role": "user", "content": query})
messages = [{"role": "system", "content": system_prompt}] + self.history[-4:]
response = self.client.chat(self.model, messages)
self.history.append({"role": "assistant", "content": response})
return {"intent": intent, "tool": tool, "response": response}
def mode_agent() -> None:
print(f"[{nexdo_time()}] 本地 AI Agent 调度器演示")
# 检查 Ollama 是否可用
client = OllamaClient()
models = client.list_models()
ollama_available = not any("连接失败" in m for m in models)
rows = [
["Ollama服务", "http://localhost:11434", "✓ 在线" if ollama_available else "✗ 未启动"],
["可用模型", ", ".join(models[:3]) if ollama_available else "N/A", ""],
]
print_table(["组件", "地址/值", "状态"], rows, "AI Agent 环境检查")
# 演示意图识别(不依赖Ollama)
agent = LocalAIAgent()
test_queries = [
"帮我计算 1+1 等于多少",
"用Python写一个快速排序",
"解释什么是梯度下降",
"分析这组数据的趋势",
]
intent_rows = []
for q in test_queries:
intent = agent._detect_intent(q)
intent_rows.append([q[:25]+"...", intent, LocalAIAgent.TOOLS[intent]])
print_table(["查询", "识别意图", "调用工具"], intent_rows, "意图识别演示(无需Ollama)")
if ollama_available:
print(f"\n[{nexdo_time()}] 调用 Ollama 生成回答...")
result = agent.run("用一句话解释什么是反向传播")
print(f"\n 意图: {result['intent']} 工具: {result['tool']}")
print(f" 回答: {result['response'][:200]}")
else:
print(f"\n ℹ Ollama 未启动,跳过实际调用。")
print(f" 启动方式: ollama serve 然后: ollama pull llama3.2")
# 展示 Agent 调度架构
print("""
Agent 调度流程:
┌──────────────────────────────────────────────────────┐
│ 用户输入 → 意图识别 → 工具路由 → LLM调用 → 返回 │
│ │
│ 意图识别:关键词匹配(轻量,无需LLM) │
│ 工具路由:math/code/explain/analyze │
│ LLM调用:POST /api/chat → Ollama本地推理 │
│ 历史管理:保留最近4轮对话上下文 │
└──────────────────────────────────────────────────────┘
""")
mode_agent()
Step 7:用 main 把多个演示模块做成命令行工具
痛点与机制:
教程脚本最终要交给普通用户运行,argparse 就是遥控器。用户不需要改源码,只要输入 --mode xor、--mode activations 或 --mode agent,就能切换不同演示。这个设计比临时在终端里等待键盘输入更稳定,也更适合复制到终端、CI 或服务器里执行。
核心源码(逐字来自文末完整源码):
def main() -> None:
parser = argparse.ArgumentParser(description="神经网络原理与本地AI Agent演示")
parser.add_argument("--mode", choices=["xor", "activations", "agent", "all"],
default="all")
args = parser.parse_args()
dispatch = {
"xor": mode_xor,
"activations": mode_activations,
"agent": mode_agent,
"all": lambda: [mode_xor(), mode_activations(), mode_agent()],
}
dispatch[args.mode]()
print(f"\n[{nexdo_time()}] 完成")
可运行演示(补齐 Mock 数据与 print 反馈):
import argparse
import sys
import time
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def mode_xor() -> None:
print("执行 XOR 神经网络训练模块")
def mode_activations() -> None:
print("执行激活函数对比模块")
def mode_agent() -> None:
print("执行本地 AI Agent 调度模块")
def main() -> None:
parser = argparse.ArgumentParser(description="神经网络原理与本地AI Agent演示")
parser.add_argument("--mode", choices=["xor", "activations", "agent", "all"],
default="all")
args = parser.parse_args()
dispatch = {
"xor": mode_xor,
"activations": mode_activations,
"agent": mode_agent,
"all": lambda: [mode_xor(), mode_activations(), mode_agent()],
}
dispatch[args.mode]()
print(f"\n[{nexdo_time()}] 完成")
for mode in ["xor", "activations", "agent"]:
print(f"\n$ python 47-python-neural-agent.py --mode {mode}")
sys.argv = ["47-python-neural-agent.py", "--mode", mode]
main()
极客实战:完整源码与运行
现在,把上面的积木拼起来,将下面完整代码保存为 47-python-neural-agent.py。神经网络部分完全本地运行;Agent 部分会优雅检查 Ollama 是否启动,未启动时也会给出清晰提示,不会让脚本崩掉。
#!/usr/bin/env python3
"""
47-neural-agent.py
① numpy从零实现3层神经网络,训练XOR问题
② 本地AI Agent调度器(urllib调用Ollama,零外部依赖)
用法:
python 47-neural-agent.py --mode xor # 训练XOR神经网络
python 47-neural-agent.py --mode activations # 激活函数对比
python 47-neural-agent.py --mode agent # AI Agent调度器演示
python 47-neural-agent.py --mode all
"""
import argparse
import json
import math
import time
import urllib.request
import urllib.error
from typing import List, Tuple, Dict, Any
import numpy as np
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def print_table(headers: list, rows: list, title: str = "") -> None:
if title:
print(f"\n{'='*65}\n {title}\n{'='*65}")
col_widths = [max(len(str(h)), max((len(str(r[i])) for r in rows), default=0))
for i, h in enumerate(headers)]
print(f"┌{'┬'.join('─'*(w+2) for w in col_widths)}┐")
print(f"│{'│'.join(f' {str(h):<{w}} ' for h, w in zip(headers, col_widths))}│")
print(f"├{'┼'.join('─'*(w+2) for w in col_widths)}┤")
for row in rows:
print(f"│{'│'.join(f' {str(v):<{w}} ' for v, w in zip(row, col_widths))}│")
print(f"└{'┴'.join('─'*(w+2) for w in col_widths)}┘")
# ── 3层神经网络(numpy实现)─────────────────────────────────────────────────
class NeuralNetwork:
"""3层全连接神经网络:输入层→隐藏层→输出层"""
def __init__(self, input_dim: int, hidden_dim: int, output_dim: int,
lr: float = 0.1):
rng = np.random.RandomState(42)
self.W1 = rng.randn(input_dim, hidden_dim) * 0.5
self.b1 = np.zeros((1, hidden_dim))
self.W2 = rng.randn(hidden_dim, output_dim) * 0.5
self.b2 = np.zeros((1, output_dim))
self.lr = lr
self.losses: List[float] = []
@staticmethod
def sigmoid(z: np.ndarray) -> np.ndarray:
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
@staticmethod
def sigmoid_deriv(a: np.ndarray) -> np.ndarray:
return a * (1.0 - a)
def forward(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
Z1 = X @ self.W1 + self.b1
A1 = self.sigmoid(Z1)
Z2 = A1 @ self.W2 + self.b2
A2 = self.sigmoid(Z2)
return Z1, A1, Z2, A2
def backward(self, X: np.ndarray, y: np.ndarray,
A1: np.ndarray, A2: np.ndarray) -> None:
n = X.shape[0]
delta2 = (A2 - y) * self.sigmoid_deriv(A2)
dW2 = A1.T @ delta2 / n
db2 = delta2.mean(axis=0, keepdims=True)
delta1 = (delta2 @ self.W2.T) * self.sigmoid_deriv(A1)
dW1 = X.T @ delta1 / n
db1 = delta1.mean(axis=0, keepdims=True)
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
def train(self, X: np.ndarray, y: np.ndarray, epochs: int = 10000) -> None:
for _ in range(epochs):
_, A1, _, A2 = self.forward(X)
loss = float(-np.mean(y * np.log(A2 + 1e-9) + (1 - y) * np.log(1 - A2 + 1e-9)))
self.losses.append(loss)
self.backward(X, y, A1, A2)
def predict(self, X: np.ndarray) -> np.ndarray:
_, _, _, A2 = self.forward(X)
return (A2 >= 0.5).astype(int)
def mode_xor() -> None:
print(f"[{nexdo_time()}] XOR 神经网络训练")
# XOR 数据集
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y = np.array([[0], [1], [1], [0]], dtype=float)
nn = NeuralNetwork(input_dim=2, hidden_dim=4, output_dim=1, lr=1.0)
nn.train(X, y, epochs=5000)
# 预测结果
preds = nn.predict(X)
rows = []
for xi, yi, pi in zip(X, y, preds):
correct = "✓" if int(yi[0]) == int(pi[0]) else "✗"
rows.append([f"[{int(xi[0])},{int(xi[1])}]", int(yi[0]), int(pi[0]), correct])
print_table(["输入", "真实", "预测", "正确"], rows, "XOR 预测结果")
# 损失曲线(ASCII)
sampled = nn.losses[::500]
max_loss = max(sampled)
print("\n 训练损失曲线(每500轮)")
height = 8
for h in range(height, 0, -1):
threshold = h * max_loss / height
line = f" {threshold:6.3f} │"
for l in sampled:
line += "█" if l >= threshold else " "
print(line)
print(f" └{'─'*len(sampled)}")
print(f" 最终损失: {nn.losses[-1]:.6f} 准确率: {(preds == y.astype(int)).mean():.0%}")
def mode_activations() -> None:
print(f"[{nexdo_time()}] 激活函数对比")
x_vals = [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]
def sigmoid(x: float) -> float:
return 1 / (1 + math.exp(-x))
def tanh(x: float) -> float:
return math.tanh(x)
def relu(x: float) -> float:
return max(0.0, x)
def leaky_relu(x: float) -> float:
return x if x > 0 else 0.01 * x
rows = []
for x in x_vals:
rows.append([f"{x:5.1f}",
f"{sigmoid(x):.4f}",
f"{tanh(x):.4f}",
f"{relu(x):.4f}",
f"{leaky_relu(x):.4f}"])
print_table(["x", "Sigmoid", "Tanh", "ReLU", "LeakyReLU"], rows, "激活函数值对比")
# ASCII 折线图(ReLU vs Sigmoid)
print("\n 激活函数曲线(Sigmoid=S, ReLU=R)")
width, height = 50, 10
print(f" {'─'*width}")
for h in range(height, -1, -1):
yv = h / height
line = " "
for col in range(width):
xv = -3.0 + col * 6.0 / width
s_val = sigmoid(xv)
r_val = min(1.0, max(0.0, relu(xv)))
s_match = abs(s_val - yv) < 0.06
r_match = abs(r_val - yv) < 0.06
if s_match and r_match:
line += "+"
elif s_match:
line += "S"
elif r_match:
line += "R"
else:
line += "·"
print(line)
print(f" {'─'*width}")
print(f" x轴: [-3, +3] y轴: [0, 1] S=Sigmoid R=ReLU")
# ── 本地 AI Agent 调度器 ─────────────────────────────────────────────────────
class OllamaClient:
"""轻量 Ollama 客户端,仅用 urllib(零外部依赖)"""
def __init__(self, base_url: str = "http://localhost:11434"):
self.base_url = base_url
def _post(self, path: str, payload: Dict[str, Any], timeout: int = 30) -> Dict:
data = json.dumps(payload).encode()
req = urllib.request.Request(
f"{self.base_url}{path}",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode())
except urllib.error.URLError as e:
return {"error": str(e)}
def generate(self, model: str, prompt: str) -> str:
result = self._post("/api/generate", {"model": model, "prompt": prompt, "stream": False})
return result.get("response", result.get("error", "无响应"))
def chat(self, model: str, messages: List[Dict[str, str]]) -> str:
result = self._post("/api/chat", {"model": model, "messages": messages, "stream": False})
return result.get("message", {}).get("content", result.get("error", "无响应"))
def list_models(self) -> List[str]:
try:
req = urllib.request.Request(f"{self.base_url}/api/tags")
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read().decode())
return [m["name"] for m in data.get("models", [])]
except Exception as e:
return [f"连接失败: {e}"]
class LocalAIAgent:
"""本地 AI Agent 调度器"""
TOOLS = {
"math": "数学计算与公式推导",
"code": "代码生成与调试",
"explain": "概念解释与教学",
"analyze": "数据分析与洞察",
}
def __init__(self, model: str = "llama3.2", ollama_url: str = "http://localhost:11434"):
self.model = model
self.client = OllamaClient(ollama_url)
self.history: List[Dict[str, str]] = []
def _detect_intent(self, query: str) -> str:
"""简单意图识别(关键词匹配,无需LLM)"""
q = query.lower()
if any(w in q for w in ["计算", "公式", "数学", "求", "等于"]):
return "math"
if any(w in q for w in ["代码", "python", "函数", "实现", "写"]):
return "code"
if any(w in q for w in ["分析", "数据", "统计", "趋势"]):
return "analyze"
return "explain"
def run(self, query: str) -> Dict[str, Any]:
intent = self._detect_intent(query)
tool = self.TOOLS[intent]
system_prompt = f"你是一个专注于{tool}的AI助手,请简洁准确地回答。"
self.history.append({"role": "user", "content": query})
messages = [{"role": "system", "content": system_prompt}] + self.history[-4:]
response = self.client.chat(self.model, messages)
self.history.append({"role": "assistant", "content": response})
return {"intent": intent, "tool": tool, "response": response}
def mode_agent() -> None:
print(f"[{nexdo_time()}] 本地 AI Agent 调度器演示")
# 检查 Ollama 是否可用
client = OllamaClient()
models = client.list_models()
ollama_available = not any("连接失败" in m for m in models)
rows = [
["Ollama服务", "http://localhost:11434", "✓ 在线" if ollama_available else "✗ 未启动"],
["可用模型", ", ".join(models[:3]) if ollama_available else "N/A", ""],
]
print_table(["组件", "地址/值", "状态"], rows, "AI Agent 环境检查")
# 演示意图识别(不依赖Ollama)
agent = LocalAIAgent()
test_queries = [
"帮我计算 1+1 等于多少",
"用Python写一个快速排序",
"解释什么是梯度下降",
"分析这组数据的趋势",
]
intent_rows = []
for q in test_queries:
intent = agent._detect_intent(q)
intent_rows.append([q[:25]+"...", intent, LocalAIAgent.TOOLS[intent]])
print_table(["查询", "识别意图", "调用工具"], intent_rows, "意图识别演示(无需Ollama)")
if ollama_available:
print(f"\n[{nexdo_time()}] 调用 Ollama 生成回答...")
result = agent.run("用一句话解释什么是反向传播")
print(f"\n 意图: {result['intent']} 工具: {result['tool']}")
print(f" 回答: {result['response'][:200]}")
else:
print(f"\n ℹ Ollama 未启动,跳过实际调用。")
print(f" 启动方式: ollama serve 然后: ollama pull llama3.2")
# 展示 Agent 调度架构
print("""
Agent 调度流程:
┌──────────────────────────────────────────────────────┐
│ 用户输入 → 意图识别 → 工具路由 → LLM调用 → 返回 │
│ │
│ 意图识别:关键词匹配(轻量,无需LLM) │
│ 工具路由:math/code/explain/analyze │
│ LLM调用:POST /api/chat → Ollama本地推理 │
│ 历史管理:保留最近4轮对话上下文 │
└──────────────────────────────────────────────────────┘
""")
def main() -> None:
parser = argparse.ArgumentParser(description="神经网络原理与本地AI Agent演示")
parser.add_argument("--mode", choices=["xor", "activations", "agent", "all"],
default="all")
args = parser.parse_args()
dispatch = {
"xor": mode_xor,
"activations": mode_activations,
"agent": mode_agent,
"all": lambda: [mode_xor(), mode_activations(), mode_agent()],
}
dispatch[args.mode]()
print(f"\n[{nexdo_time()}] 完成")
if __name__ == "__main__":
main()
$ python 47-python-neural-agent.py --mode xor
[2026-04-18 11:34:14] XOR 神经网络训练
=================================================================
XOR 预测结果
=================================================================
┌───────┬────┬────┬────┐
│ 输入 │ 真实 │ 预测 │ 正确 │
├───────┼────┼────┼────┤
│ [0,0] │ 0 │ 0 │ ✓ │
│ [0,1] │ 1 │ 1 │ ✓ │
│ [1,0] │ 1 │ 1 │ ✓ │
│ [1,1] │ 0 │ 0 │ ✓ │
└───────┴────┴────┴────┘
训练损失曲线(每500轮)
0.705 │█
0.616 │████
0.528 │█████
0.440 │█████
0.352 │██████
0.264 │██████
0.176 │██████
0.088 │████████
└──────────
最终损失: 0.052908 准确率: 100%
[2026-04-18 11:34:14] 完成
$ python 47-python-neural-agent.py --mode activations
[2026-04-18 11:34:15] 激活函数对比
=================================================================
激活函数值对比
=================================================================
┌───────┬─────────┬─────────┬────────┬───────────┐
│ x │ Sigmoid │ Tanh │ ReLU │ LeakyReLU │
├───────┼─────────┼─────────┼────────┼───────────┤
│ -3.0 │ 0.0474 │ -0.9951 │ 0.0000 │ -0.0300 │
│ -2.0 │ 0.1192 │ -0.9640 │ 0.0000 │ -0.0200 │
│ -1.0 │ 0.2689 │ -0.7616 │ 0.0000 │ -0.0100 │
│ 0.0 │ 0.5000 │ 0.0000 │ 0.0000 │ 0.0000 │
│ 1.0 │ 0.7311 │ 0.7616 │ 1.0000 │ 1.0000 │
│ 2.0 │ 0.8808 │ 0.9640 │ 2.0000 │ 2.0000 │
│ 3.0 │ 0.9526 │ 0.9951 │ 3.0000 │ 3.0000 │
└───────┴─────────┴─────────┴────────┴───────────┘
激活函数曲线(Sigmoid=S, ReLU=R)
──────────────────────────────────────────────────
·································RRRRRRRRRRRRRRR++
·································R·····SSSSSSSSSSS
································R·SSSSSSS·········
······························S+SSS···············
···························SSS+···················
·······················SSSSS·R····················
····················SSSS····R·····················
················SSSSS······RR·····················
··········SSSSSSS··········R······················
SSSSSSSSSSSS··············R·······················
+++RRRRRRRRRRRRRRRRRRRRRRR························
──────────────────────────────────────────────────
x轴: [-3, +3] y轴: [0, 1] S=Sigmoid R=ReLU
小结与 NexDo Time ⚡
这一篇你完成了两件关键事:第一,用 NumPy 手写了一个能学习 XOR 的三层神经网络;第二,用 Ollama 接口思路搭了一个本地 Agent 调度骨架。前者训练的是“模型参数”,后者组织的是“任务流程”,两者合在一起,就是现代 AI 应用的底层肌肉。
5 分钟微操挑战:把 mode_xor() 里的隐藏层神经元从 4 改成 2、8、16,分别观察最终损失和准确率变化。然后思考一个问题:隐藏层越大一定越好吗?
Don’t wait for next time, do it in the next moment.