news 2026/10/14 3:53:13

learn-claude-code-s01-s04-学习笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
learn-claude-code-s01-s04-学习笔记

Learn Claude Code s01–s04 学习笔记与薄弱点复盘

学习对象:learnclaudecode 渐进式教程「工具与执行」板块(s01 Agent Loop / s02 Tool Use / s03 Permission / s04 Hooks)

学习方式:不逐行读代码,而是「跑通 → 改坏 → 回看」三步法
每课都配一份零依赖、无需 API Key 的可运行 demo,先看它怎么跑,再动手改坏它,最后回看教程原始代码。

本文既是四课的知识总结,也是一份自我错题本——最后两章专门记录我答错和掌握不牢的地方。


目录

  • 零、全局心智模型(最重要的一章)
  • 一、s01 Agent Loop:循环
  • 二、s02 Tool Use:工具注册表
  • 三、s03 Permission:权限门控
  • 四、s04 Hooks:可插拔钩子
  • 五、我的薄弱点清单(错题本)
  • 六、四章横向对比速查表
  • 七、自测题(附答案)

零、全局心智模型(最重要的一章)

这四课真正的收获不是四段代码,而是一个心智模型。它是理解后面全部 16 课的地基。

0.1 一句话哲学

程序只做三件事:判断、执行、回填。决策权永远在模型手里。

很多人(包括我)在学这四课时,会本能地把"智能"和"主体性"安到程序身上,觉得程序会"自己想办法"“自动降级”“重试”。这是最大的误区。

真相是:Agent 程序是一根管道 + 一串关卡,它没有任何"想办法"的能力。所有"聪明"的行为都发生在模型那一侧。

0.2 三份契约:模型侧 vs 程序侧

位置内容谁在用
TOOLS定义工具说明书(名字、描述、参数结构)模型读,据此决定调什么
TOOL_HANDLERS名字 → 函数 的字典程序查表用
工具函数本体真正干活的代码程序执行

三者解耦,但必须对齐。少任何一份,工具就不工作——而且失败方式完全不同(见 五、薄弱点)。

0.3 贯穿四课的设计哲学

① 错误即数据(Error as Data)

四课里出现了四次几乎一模一样的写法:

returnf"[权限拒绝] ..."# s01 / s03 —— 命令被拦returnf"[错误] 未知工具:{name}"# s02 —— 工具不存在returnf"[工具执行异常] ..."# s02 —— 工具抛异常returnf"[钩子拦截] ..."# s04 —— 被钩子拦下

它们全都返回一句字符串,而不是抛异常。

为什么?因为抛异常会中断循环,而返回字符串会被包装成tool_result回填进messages,模型下一轮就能读到"我被拒了",然后自己决定怎么办。

Agent 的健壮性不来自"提前想到所有失败",而来自"把所有失败都变成可读的信息"。

② 注册表思想(Registry / Dispatch Map)

s02 和 s04 是同构的:

主循环不认识什么靠哪张表
s02任何具体工具TOOL_HANDLERS
s04任何具体钩子HOOKS

共同点:把"会变动的东西"从主循环里搬出去,放进一张表。

这是软件设计里"开闭原则"的具体落地——对修改关闭,对扩展开放。Claude Code 有几十个工具、无数钩子,主循环从没为任何单个工具或钩子改过。


一、s01 Agent Loop:循环

1.1 它要解决的问题

模型只能"回答",不能"做事"。怎么让它能查文件、跑命令、多轮推理?

答案:一个循环。让模型一边做事一边把结果看回来。

1.2 五步流程

① 把用户问题作为第一条消息 messages = [{"role": "user", "content": query}] ② 把【整个 messages】+ 工具定义一起发给模型 ③ 追加模型回答,检查 stop_reason: 不是 tool_use → return,循环结束(唯一出口) 是 tool_use → 继续 ④ 执行模型要求的工具,收集结果 ⑤ 把结果包装成 tool_result,以 user 身份追加回 messages → 回到 ②

1.3 核心代码

defagent_loop(query,llm):messages=[{"role":"user","content":query}]# 第 1 步whileTrue:response=llm(messages)# 第 2 步messages.append({"role":"assistant","content":response["content"]})# 第 3 步ifresponse["stop_reason"]!="tool_use":return# ← 唯一出口results=[]# 第 4 步forblockinresponse["content"]:ifblock["type"]=="tool_use":output=run_bash(block["input"]["command"])results.append({"type":"tool_result","tool_use_id":block["id"],"content":output,})messages.append({"role":"user","content":results})# 第 5 步

1.4 知识点

  • messages列表就是 Agent 的全部记忆。唯一的可变状态。每轮 append 两样:assistant 回答 + 工具结果。读代码时盯着它的每次 append,就抓住了循环的心跳。
  • 循环没有"任务完成"的概念。它唯一的刹车是stop_reason != "tool_use"。模型一直要工具,它就一直转。(所以真实产品必须有 max turns、上下文压缩等机制兜底。)
  • 每轮把整个列表发出去。所以模型永远知道之前发生了什么——这是"多轮"的实现方式,代价是 token 持续增长(s08 的主题)。
  • 工具结果用role: "user"回填。反直觉但很重要:在 API 的消息协议里,工具结果不是"模型的回答",而是"环境对模型的输入"。

1.5 动手验证

  • 把mock_llm里if turn == 1:改成if turn >= 1:→ 循环永不停机,messages无限增长,Ctrl+C才能停。亲眼看到"没有退出条件"有多可怕。
  • 把工具的返回文本改成"rm -rf test"相关 → 观察[权限拒绝]如何被当作正常结果回填,循环并不中断。

1.6 一个关键细节

block.input["command"]说明模型输出的是结构化参数,不是纯文本。这就是为什么第 2 步要传tools=TOOLS——模型得先知道每个工具的参数 schema,才能填对。


二、s02 Tool Use:工具注册表

2.1 它要解决的问题

模型说"我要调用get_time",程序怎么知道该执行哪段代码?

错误答案(很多人第一反应):写一串 if/elif。

ifname=="run_bash":run_bash(**params)elifname=="get_time":get_time(**params)# 加一个工具就要改一次循环 ← 灾难

正确答案:查表。

2.2 核心代码

TOOL_HANDLERS={"run_bash":run_bash,"get_time":get_time,"count_lines":count_lines,}defrun_tool(name,params):handler=TOOL_HANDLERS.get(name)# ← 关键:.get() 而不是 []ifhandlerisNone:returnf"[错误] 未知工具:{name},可用工具:{list(TOOL_HANDLERS)}"try:returnhandler(**params)# ← 关键:** 解包字典为关键字参数exceptExceptionase:returnf"[工具执行异常]{type(e).__name__}:{e}"

2.3 知识点

① 函数是一等公民(本课最重要的 Python 概念)

{"get_time": get_time}—— 右边那个get_time不带引号,它是函数对象本身,被当作值存进了字典。

理解这一点,你就理解了为什么工具系统能设计得这么干净。函数可以像数字、字符串一样被传递和存储,这是 Python 的灵魂,也是所有插件系统的实现基础。

② 加一个新工具 = 改三处,主循环零改动

改哪里作用谁看
TOOLS列表加一条工具说明书模型
写一个def xxx()具体实现程序
TOOL_HANDLERS加一行注册程序

主循环agent_loop一个字都不用改。

③.get()与[]的区别(易错点)

写法键不存在时后果
TOOL_HANDLERS[name]抛KeyError程序直接崩
TOOL_HANDLERS.get(name)返回None可以走if handler is None分支,把错误变成数据

一个中断程序,一个变成可处理的数据。这个区别在写任何健壮代码时都会反复出现。

④**params解包

handler(**params)把{"path": "."}自动变成handler(path=".")。字典与函数参数之间的无缝转换。

(对比:handler(*params)是位置参数解包,字典会被拆成 key 传给函数,基本必然报错。)

⑤ 参数级错误处理:工具炸了不能炸掉循环

try/except包住handler(**params),异常转成字符串回填。这是"错误即数据"的第二次出现。

2.4 动手验证

  • 加一个自己的工具(如roll_dice):三处各改一处,跑通。然后确认主循环真的没动。
  • 把TOOL_HANDLERS里某一行删掉,但TOOLS里定义还在 → 模型仍然会要求调用它,程序返回"未知工具"。这就是"三份契约必须对齐"的实证。

三、s03 Permission:权限门控

3.1 它要解决的问题

模型要执行rm -rf /,凭什么让它执行?

答案:在工具执行【之前】加一道门,所有工具共用。

3.2 决策是三态,不是布尔

defcheck_permission(name,params):ifnameinREADONLY_TOOLS:return"allow","只读工具,无副作用"ifname=="run_bash":low=str(params.get("command","")).lower()ifany(dinlowfordinDANGEROUS):return"deny","包含破坏性操作(不可逆)"ifany(low.startswith(s)orf"{s}"inlowforsinSAFE):return"allow","只读命令,安全"return"ask","未识别命令,需要用户确认"return"ask","未知工具,默认需要确认"
决策含义例子
allow放行dir /b、get_time
deny直接拦死,连问都不问rm -rf、format、shutdown
ask交给用户拍板curl、pip install、未识别的命令

为什么必须三态?因为很多操作既不该无条件放行,也不该一刀切拦死。curl联网:可能是正当需求,也可能在偷传数据——只有用户能判断。三态设计把决定权交给了最终用户,而不是写代码的人。

3.3 三个硬性原则

① 权限门必须在 handler 执行【之前】

defrun_tool(name,params):decision,reason=check_permission(name,params)# 先判断ifdecision=="deny":returnf"[权限拒绝]{reason}"# ... 放行之后才轮到执行handler=TOOL_HANDLERS.get(name)returnhandler(**params)

先问再干,不是干完再问。如果判断放在执行之后,命令已经跑完了才告诉你"被拒绝了"——文件已经删了。

② 权限粒度是【参数级】的

同一个run_bash工具:

  • rm -rf build→DENY
  • dir /b→ALLOW

所以权限不能只按"工具名"配,必须能看到参数内容。这是本课最核心的认知。

③ 判断与执行分离

check_permission()不执行任何东西,它只返回一个决策字符串。这个拆分让权限逻辑可以被单独测试、单独审计。

3.4 "错误即数据"的第三次出现

deny不抛异常,而是return "[权限拒绝]..."。

最精彩的观察:demo 里模型被拒三次后,自己改用了dir /b完成了任务。

是程序让它换方案的吗?不是。程序只是把三句"被拒绝了"回填进messages。模型下一轮读到这些文本,自己决定"删不了那就看看目录吧"。

你不需要写任何"失败了该怎么办"的逻辑。你只要把"不行"说清楚,模型自己会绕路。

3.5 动手验证

  • --ask交互模式跑一次,亲手输入y/n,体会ask把决定权交给用户。
  • 把"rm "从DANGEROUS挪到NEED_CONFIRM→ 从"拦死"变"问你"。思考:真实产品里哪个更合理?(Claude Code 默认对删除操作是询问而非拦死,因为用户有时确实想删东西。)
  • 把check_permission挪到handler()后面 → 命令已执行完才说"被拒"。

四、s04 Hooks:可插拔钩子

4.1 它要解决的问题

想在 Agent 执行的各个节点"加点动作"——记日志、改参数、拦特殊命令、改结果——难道每次都要去改主循环?

答案:主循环只预留"插座",具体插什么设备随时加。

4.2 核心代码

HOOKS={"before_llm":[],# 调模型之前"after_llm":[],# 调模型之后"before_tool":[],# 执行工具之前"after_tool":[],# 执行工具之后}defregister(hook_point,name,fn):HOOKS[hook_point].append((name,fn))

注册钩子:

register("before_llm","上下文监控",context_watch)register("before_tool","审计日志",audit_hook)register("before_tool","参数规范化",normalize_hook)register("before_tool","敏感信息保护",secret_guard)register("after_tool","输出整理",tidy_result)

主循环里只有四个触发点:

fire("before_llm",messages)response=llm(messages)fire("after_llm",response)# ...fire("before_tool",name,params)result=handler(**params)fire("after_tool",name,params,result)

主循环不认识任何一个具体钩子。加钩子只改register那一行。

4.3 钩子的三种能力

能力例子实现协议
观察审计日志、上下文监控做点事,return None
改写参数规范化(ls→dir /b)原地修改params字典
拦截敏感信息保护(.env)return一个字符串= 直接短路

“返回非None即拦截”——和 s02 的未知工具、s03 的权限拒绝是同一个协议。这是第四次见到它。

4.4 两个精妙细节

① 钩子改参数后,权限检查看到的是【改写之后】的参数

执行顺序:参数规范化先把ls改成dir /b,然后权限门判断dir /b。

如果允许钩子改参数,安全检查就【必须】放在改写之后—— 否则钩子可以把危险命令伪装成安全的,绕过权限。真实系统里这个顺序是安全底线。

② 钩子之间可以共享状态

before_tool记开始时间,after_tool算耗时——两个独立函数怎么传数据?

用模块级变量。这是钩子系统里的经典模式:

TIMING={}defstart_timer(name,params):TIMING[name]=time.time()defreport_timer(name,params,result):print(f"{name}耗时{time.time()-TIMING[name]:.2f}s")

4.5 与 s02 的对称性(本课最大的认知收获)

主循环不认识什么靠哪张表
s02任何具体工具TOOL_HANDLERS
s04任何具体钩子HOOKS

同一个思想,用了两遍。理解了 s02,s04 就只是换个对象重来一次。


五、我的薄弱点清单(错题本)

这一章是本文最有价值的部分。以下是我在学习过程中确实答错、或掌握不牢的知识点,按重要性排序。

薄弱点 1(严重):把"主体性"和"智能"安到程序身上

踩坑次数:3 次。

场景我的错误回答正确理解
s01 命令被权限拦截“程序自己换个方案继续执行”程序只return一句拒绝文本,换方案的是模型
s02 调用不存在的工具“程序可能会去自己查询吧,查完之后再运行”程序只查表 + 返回错误字符串,它连"查询"这个动作都没有
s03 模型改用安全命令“重试机制干的”代码里根本没有重试机制,是模型的剧本在换方案

为什么我会反复踩这个坑?

因为我们平时用的软件(浏览器、Word、资源管理器)都是程序在替你做决定——点"删除",程序自己就把文件删了。这个经验让我形成直觉:“程序是能动的主体”。

但 Agent 是个新物种:程序退化成了"管道 + 关卡",智能全在模型那一侧。

正确的锚定句(请背下来):

程序只做三件事:判断、执行、回填。

凡是程序做的,代码里一定能找到对应那几行;找不到,就是模型做的。

自证实验(我要亲手做一次):

删掉mock_llm里if turn == 3:那一整段(就是要求dir /b的那段),重新运行。

如果程序真有"重试/降级机制",那么删除后它应该自己去执行dir /b(因为"任务还没完成嘛")。但它不会——它安安静静地结束了。

这证明换成dir /b的是模型的剧本,不是程序。

这个知识点为什么关键:s04 Hooks 是在"判断"前后加观察点;s11 Error Recovery 会学"错误重试",但那是在程序里显式写的循环,不是凭空长出来的;s17 Autonomous Agents 的"自主循环"同理。


薄弱点 2(中等):.get()与[]的区别,以及None的两面性

我的回答:“.get() 返回 None,所以保证了程序继续执行” —— 方向对,但没说到根上。

准确表述:

写法键不存在时后果
TOOL_HANDLERS[name]抛KeyError程序直接崩
TOOL_HANDLERS.get(name)返回None走if handler is None分支,把错误变成数据

关键词是KeyErrorvsNone—— 一个中断程序,一个变成可处理的数据。

反向联想(也是我的坑):函数没有return时默认返回None,读它的属性就会炸:

TypeError:'NoneType'objectisnotsubscriptable

从报错倒推根因的链条(必须形成肌肉记忆):

报错最后一行 → 定位到 agent_loop 的 response["content"] → 说明 response 是 None → response 来自 llm(messages) → 模型函数为什么返回 None? → 所有 if 都没命中 → 少了兜底 return

30 秒定位根因,比记住任何语法都重要。


薄弱点 3(中等):替换代码块时,连带删掉了相邻的兜底逻辑

具体经过:把mock_llm的if turn == 2:块替换成turn == 2+turn == 3时,把末尾那个end_turn兜底return也一起删了,导致程序在 turn 4 崩溃。

教训:替换代码块前,先扫一眼上下文边界,确认相邻结构(兜底 return、else分支、循环结束语句)是否该保留。

这个兜底return的双重身份:它既是"防御性编程"(防止函数返回None),也真实地扮演了模型说"我干完了"的角色—— 循环正是从这里正常退出的。


薄弱点 4(易忽略):模拟模式和真实模式的行为差异

我的困惑:明明在TOOLS里加了roll_dice定义,为什么跑起来毫无变化?

原因:模拟模式下的模型根本不读TOOLS。mock_llm就是一段写死的 if/else 剧本。

模拟模式真实模式
模型响应脚本预设真实推理
读TOOLS定义吗不读读
新增工具后要改哪前 3 处 +mock_llm剧本只改前 3 处
主要用途零成本观察循环结构验证真实行为

关键区分:给模型看的说明书(TOOLS)和程序里的实现(函数 + 注册表)是两份独立的东西。

漏注册表 → 报"未知工具";漏函数 → 报错;漏TOOLS定义 → 真实模式下模型根本不知道有这个工具。


薄弱点 5(概念模糊):对"权限拒绝"与"循环继续"的关系理解不清

我的初答:“工具被拒绝后,程序的处理方式是自己换个方案继续执行。”

正确理解:程序里没有任何"换个方案"的代码。翻遍run_bash和agent_loop,找不到一句try: other_plan()。

它只做了两件事:

return"[权限拒绝]..."# 返回一句文本messages.append(...)# 回填continue# 循环继续

是模型在下一轮看到"我被拒了",自己决定换方案。

结论:控制权始终在模型手里,程序只负责"传话"和"设栏杆"。想明白这一点,s03(权限)、s04(Hooks)、s11(错误恢复)全都变得理所当然。


薄弱点自评总表

#薄弱点严重度已掌握?验证方式
1把主体性/智能安到程序身上高待用实验自证删turn == 3段落后运行
2.get()vs[],None的两面性中已理解删注册表某行后运行
3替换代码块时删掉兜底逻辑中已修复重跑不再报NoneType
4模拟模式 vs 真实模式差异低已理解对比--real行为
5权限拒绝后"谁在换方案"高待巩固用自己的话复述一遍

六、四章横向对比速查表

6.1 四课解决的问题

课核心问题一句话答案主循环改了吗
s01模型怎么"做事"?一个循环,靠stop_reason退出——
s02模型说调工具,怎么找到代码?查TOOL_HANDLERS注册表没改
s03凭什么让它执行?执行前加一道三态权限门没改
s04怎么在各节点加动作?主循环留 4 个插座,钩子可插拔没改

注意最后一列的"没改"—— 这正是这四课最漂亮的地方:每一课都在不碰主循环的前提下,给 Agent 加上一层能力。

6.2 "错误即数据"四次出现的位置

位置返回的文本谁写的
权限拒绝[权限拒绝] ...run_tool
未知工具[错误] 未知工具: xxxrun_tool
工具异常[工具执行异常] ...run_tool
钩子拦截[钩子拦截] ...before_tool钩子

四处的写法几乎完全一样。这就是整个 Agent Harness 的核心哲学。

6.3 决策流程全景(一次工具调用的完整生命周期)

模型返回 tool_use(name, params) ↓ 【s04】before_tool 钩子依次执行 ├─ 改写参数(原地改 params 字典) └─ 返回字符串 → 拦截,直接结束(跳过下面全部) ↓ 【s03】check_permission(name, params) —— 检查的是【改写后】的参数 ├─ deny → return "[权限拒绝]" ← 结束 ├─ ask → 问用户,被拒 → return ← 结束 └─ allow → 继续 ↓ 【s02】TOOL_HANDLERS.get(name) ├─ None → return "[错误] 未知工具" ← 结束 └─ 有 → handler(**params) ├─ 抛异常 → return "[工具执行异常]" ← 结束 └─ 正常 → result ↓ 【s04】after_tool 钩子依次改写 result ↓ 包装成 tool_result → messages.append → 下一轮循环

这张图就是 s01–s04 的全部成果。后面 16 课都是在它的某些节点上继续加东西。


七、自测题(附答案)

题目

  1. Agent 循环的退出条件是什么?这个条件由谁决定?
  2. 给 Agent 加一个新工具,需要改哪几处?主循环要不要动?
  3. 模型调用一个注册表里不存在的工具,从"模型说要调"到"循环继续",中间程序做了哪几步?
  4. 为什么TOOL_HANDLERS.get(name)不能写成TOOL_HANDLERS[name]?
  5. 权限决策为什么设计成三态(allow/deny/ask)而不是两态?
  6. 权限检查代码必须放在什么位置?为什么?
  7. rm -rf build被 DENY、dir /b被 ALLOW,这说明权限的什么特性?
  8. 模型被拒绝三次后改用安全命令完成的任务。"换方案"是谁做的?
  9. 主循环预留了几个钩子插座?分别在哪?
  10. 钩子"拦截"的实现协议是什么?
  11. 为什么权限检查必须发生在钩子改写参数【之后】?
  12. 在TOOLS里加了工具定义,但模拟模式下毫无变化,为什么?

答案

点击展开
  1. stop_reason != "tool_use"。由模型决定——程序只是读到这个值然后return。
  2. 三处:TOOLS加定义、写实现函数、TOOL_HANDLERS加注册。主循环不用动。(真实模式自动生效;模拟模式还要改mock_llm剧本。)
  3. ①TOOL_HANDLERS.get(name)查表 → ② 返回None→ ③ 命中if handler is None→ ④ 返回"[错误] 未知工具..."字符串 → ⑤(由agent_loop)包装成tool_result回填messages→ ⑥ 循环继续。
  4. []在键不存在时抛KeyError直接中断程序;.get()返回None,才能走兜底分支把错误变成可处理的数据。
  5. 因为很多操作既不该无条件放行,也不该一刀切拦死。ask把决定权交给最终用户,而不是写代码的人。
  6. 必须放在handler()执行之前。放在之后会导致命令已经执行完了才报告"被拒绝"——破坏已经发生了。
  7. 权限粒度是参数级的:不能只看工具名,必须能看到参数内容。
  8. 模型自己。程序只把三句拒绝信息回填进messages,模型读到后自己换方案。程序只会三件事:判断、执行、回填。
  9. 四个:before_llm/after_llm/before_tool/after_tool。
  10. 钩子返回一个字符串即表示拦截,该字符串直接作为工具结果;返回None表示放行。
  11. 因为钩子能改参数。如果权限检查在改写之前,钩子就能把危险命令伪装成安全的,绕过权限。这是安全底线。
  12. 因为模拟模式的mock_llm根本不读TOOLS,它只是一段写死的 if/else 剧本。只有真实模式下模型才会读工具定义。

附:四份可运行 Demo

文件对应课程演示内容
s01_agent_loop.pys01五步循环全流程打印,观察messages增长
s02_tool_use.pys02工具注册表、未知工具兜底、工具异常兜底
s03_permission.pys03三态权限门、参数级粒度、模型被拒后自行换方案
s04_hooks.pys04四个钩子插座 + 钩子三能力(观察/改写/拦截)

运行方式(零依赖、无需 API Key):

python s01_agent_loop.py# 模拟模式python s03_permission.py--ask# 交互式权限询问python s04_hooks.py--real# 真实模式(需 ANTHROPIC_API_KEY)

结语

「工具与执行」这四课,表面上是四个功能,实际上是同一个思想的四次变体:

把会变动的东西从主循环里搬出去,放进一张表;把所有失败都变成可读的信息,回填给模型。

主循环从头到尾没为任何工具、任何权限规则、任何钩子改过一行——这就是这套架构最漂亮的地方。

而你这四课最大的收获,可能不是这四段代码,而是一个认知上的转变:

Agent 里,程序不是大脑,是血管和栏杆。智能在模型那一侧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 3:53:11

高并发文章系统架构实战:缓存穿透击穿雪崩的完整解决方案

搞高并发文章系统这个项目,最初只是某公司的内容平台想解决一个看似简单的问题:热门文章一上线,流量瞬间涌入,数据库经常被拖垮。真正动手之后才发现,这个场景简直是高并发架构的"集齐龙珠"现场——读多写少…

作者头像 李华
网站建设 2026/10/14 3:53:10

A2A与MCP桥接实战:用a2a-mcp-server打通多智能体协作链路

如果最近在搞多智能体系统,你大概率会碰见两个词:MCP 和 A2A。MCP(Model Context Protocol)解决的是“模型怎么调用外部工具”,A2A(Agent-to-Agent)解决的是“智能体之间怎么互相协作”。而标题…

作者头像 李华
网站建设 2026/10/14 3:53:02

Kafka为什么快?从顺序写到零拷贝的底层设计全解析

做消息队列这块的同学,大概率都被人问过同一个问题:Kafka为什么这么快?不少人在面试里被问到,也有不少人在选型时纠结过:同样是消息中间件,为什么Kafka能在百万级TPS的场景下撑住,而很多传统MQ动…

作者头像 李华
网站建设 2026/10/14 3:52:54

WooCommerce 3.5.2 安装包复活指南:旧环境兼容与避坑实践

简介:Woocommerce 3.5.2 安装包面向使用 WordPress 搭建在线商店的开发者与站长,尤其适合需要稳定旧版本、排查兼容性问题的中初级用户。该版本在性能优化、WordPress 5.x 兼容、安全补丁、支付网关支持、税费计算与多语言等方面均有改进,可用…

作者头像 李华
网站建设 2026/10/14 3:52:50

Windows下配置winutils-master.zip解决Hadoop本地运行时缺失

简介:在Windows系统上进行Hadoop跨平台调试时,常因缺少原生的Windows运行组件而出现连接失败或命令无法执行。这套winutils-master工具包覆盖Hadoop 2.6.0至3.0.0版本,面向需要在Windows本地编写与调试MapReduce任务、维护Hadoop集群连接的大…

作者头像 李华