Jules Robineau

TL;DR:只有重建系统,你才能真正理解它。我在学校用 Go 重写了 TCP、DNS 协议和 Modbus,每次都是为了从内部理解它。一位同事刚刚用 LLM 做了同样的事。他用 Go 写了一个小型智能体,最终理解了工具调用和上下文窗口。LLM 只是另一个需要揭秘的系统。重建一个微型版本,你就能从用户转变为工程师。

写给想要掌握 LLM 而不仅仅是使用它们的开发者。

一位同事、一个 Go 智能体、一次顿悟

这周,我正在帮助一位同事提升 LLM 水平。我解释概念:上下文、token、工具。token 是模型读取并计数的文本小片段。他听着,但有些地方没理解。

后来他兴高采烈地回来。他用 Go 写了一个小型 CLI。一个简单的聊天循环,调用模型并运行其工具。现在他理解了。工具调用、上下文窗口、循环。不是因为我解释了,而是因为他自己重建了它。

我对这种顿悟再熟悉不过。我在其他主题上多次体会过。方法总是相同的。要理解某件事,我就重建它。

只有重建系统才能理解它

阅读文档给你一张地图。重建则让你亲身感受地形。两者并不相同。地图说“这里有一条河”。地形让你感受水流。

当你重写一个系统时,你无法再蒙混过关。每个字节都必须放在正确的位置。每个边缘情况都落在你身上。你不再以为自己理解了。你要么真正理解,要么代码失败。

这不是学术练习。恰恰相反。你重建是为了之后更好地行动。更快地调试。弯曲工具。构建现成库无法提供的功能。

C 语言中的 TCP:第一次

第一次是在学校,用 C 语言。我重写了 TCP 和 UDP 的一部分。著名的三次握手。以及逐字段的头部解析。

TCP 用三条消息打开连接:SYN、SYN-ACK、ACK。在此之前,这只是课堂上的一行内容。之后,这些字节是我自己放入数据包的。

我没有发明任何东西。协议已经存在四十年。但重新实现它改变了我看待网络的方式。从那以后,抓包不再神秘。它是我亲手写过的格式。

DNS:重写协议以操纵其子域名

后来,我着手 DNS。DNS 将像 jrobineau.com 这样的名称转换为 IP 地址。我用 Go 自己重写了它。头部、问题、答案,一个字节一个字节地写。

重建它时,你会遇到文档一带而过的细节。域名是一系列标签。每个标签前缀是其长度。“www”是一个 3,然后是 w、w、w。

然后一个想法出现了。如果我控制标签,我就控制字节。我可以将自己的数据放入子域名中。这就是 DNS 数据渗出的原理,在授权的安全场景中。

我的服务器接收查询,解析名称,并恢复隐藏在其中的数据。我甚至处理了名称压缩,这是协议中一个棘手的角落。没有现成的库能告诉我这些。重建它做到了。

Modbus:重建它以找出 bug 归属

在一份涉及工业硬件的工作中,我们使用 Modbus。Modbus 是一个驱动控制器和传感器的老协议。我们使用的库很糟糕。很多 bug,很多奇怪的行为。

无法判断痛苦来自哪里。是协议?库?我们的代码?所以我做了唯一能解决这个问题的事。我用 Go 自己重写了 Modbus。

结论:协议没问题。罪魁祸首是库。一旦协议被重建,真正的回报就来了。我可以按照自己的方式围绕它构建工具。

我把它变成了一个带有 Gin 式 API 的小型 Go 库。你为每个寄存器范围声明一个处理器。你添加日志和恢复中间件。一个 1979 年的工业协议,拥有 Web 框架的舒适感。这就是根据你的需求弯曲知识。

LLM 是另一个需要重建的系统

回到 LLM。2026 年,AI 被当作魔法出售。一个你与之对话的黑盒。你仍然是一个用户,有点被动,有点受其摆布。

但 LLM 智能体不是魔法。它是一个循环。你向模型发送消息。它回复,有时请求工具。你运行工具。你将结果发回。然后重新开始。

上下文窗口是模型当前看到的一切。你的循环决定什么进入,什么丢弃。模型什么都不记得。是你在每一轮中喂给它过去的内容。

以下是用 Go 实现的整个循环。剥去表层,只剩下这个。

func runAgent(ctx context.Context, client LLM, tools map[string]Tool, goal string) (string, error) {
    // The context window is this list. You alone fill it.
    msgs := []Message{{Role: "user", Content: goal}}

    for {
        // 1. You send the whole context to the model.
        reply, err := client.Complete(ctx, msgs, tools)
        if err != nil {
            return "", err
        }
        msgs = append(msgs, reply)

        // 2. No tool requested? The model is done, you return.
        if len(reply.ToolCalls) == 0 {
            return reply.Content, nil
        }

        // 3. You run each tool yourself, not the model.
        for _, call := range reply.ToolCalls {
            out := tools[call.Name].Run(ctx, call.Args)
            // 4. You feed the result back into the context. Loop again.
            msgs = append(msgs, Message{Role: "tool", Content: out})
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

一旦你写完这个,恐惧就会消退。一个“智能体”就是这个循环加上几个好工具。工具调用只是模型告诉你调用哪个函数。仅此而已。

重建,是的,但不是一切,也不是永远

目标不是一辈子重写一切。我不会把自己的 TCP 栈部署到生产环境。我使用系统的,而且我是对的。

你重建一次,以理解。然后你信任,因为你知道盒子里有什么。这是赢得的信任,而不是盲目的信任。

当 stakes 很高时进行重建。产品核心的协议。你经常调试的工具。像 LLM 这样的新技术,每个人都停留在表面。这就是理解带来回报的地方。

通过重建学习技术的清单

下一个让你印象深刻的技术,不要只是使用它。重建它的一部分。

  • [ ] 瞄准核心,而不是舒适。智能体循环,而不是整个供应商 API
  • [ ] 保持小巧。一个 CLI、一个文件、一个下午通常就够了
  • [ ] 亲手写一次格式。字节能教给你文档隐藏的东西
  • [ ] 寻找解锁细节。DNS 标签、上下文循环
  • [ ] 故意破坏它。你很快就能看到限制和陷阱
  • [ ] 库让你失望?重建以了解真正 bug 的归属
  • [ ] 一旦你理解了,就弯曲它。构建现成库无法提供的工具
  • [ ] 然后丢弃你的玩具版本。带着真正的心理模型回到生产库

要记住的

开发者的座右铭没有改变。使用工具而不理解它意味着受其摆布。重建它,即使是玩具,也能夺回控制权。

TCP、DNS、Modbus、LLM 智能体。每一次,方法都相同。重建以理解。理解以弯曲。LLM 只是列表上的下一个系统。

为团队培训 LLM,或想要可靠的 Go 后端?这就是我做的。写信给我。我们不忍受我们的工具。我们理解它们。


来源: RFC 1035, DNS format · RFC 9293, TCP · Modbus Application Protocol · Anthropic, Building effective agents