🦞 一只用 AI Agent 搭副业产线的程序员


上篇文章结尾我说:下一篇用 50 行 Go 代码让你理解大语言模型怎么工作。

不是标题党。往下看。


省流版

概念一句话
LLM 的本质一个「下一个词预测器」——你给它一段文字,它猜下一个字该写什么
Token大模型读的不是「字」,是被切成小块的文本片段
温度控制模型「敢不敢乱说」的旋钮——越低越保守,越高越敢编
上下文窗口模型的「短期记忆」——超过了就忘了
这篇文章会给你什么一个能跑的 Go 程序 + 三个概念的直观理解

别被数学公式吓住

网上 90% 讲大模型的文章开篇就是 Transformer 架构图、注意力机制公式、多头注意力。看完跟没看一样——你记住了「Q、K、V」三个字母,但完全不知道这跟你调 API 有什么关系。

一个 8 年程序员告诉你:你不需要懂 Transformer 数学,你只需要懂大模型「怎么想」。

剩下那 10% 讲清楚了的文章,都指向一个结论——

大语言模型就是一个「下一个词预测器」(Next Token Predictor)。

你输入「今天天气真」,模型算了一下,觉得接下来最可能出现的字是「好」。于是输出「好」。然后把「今天天气真好」再喂回去,模型继续猜下一个字可能是「,」。循环往复,直到输出一个结束符,一句话就写完了。

这就是 LLM 的全部秘密。

用代码把这件事跑一遍,你比读 10 篇科普文章都有用。


50 行 Go 代码:跟 DeepSeek 对话

先跑起来。用 DeepSeek 的 Anthropic 兼容端点,因为国内访问快、免费额度够你跑实验:

package main

import (
    "bufio"
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "strings"
)

const baseURL = "https://api.deepseek.com/anthropic"

type Message struct {
    Role    string `json:"role"`
    Content string `json:"content"`
}

type Request struct {
    Model       string    `json:"model"`
    MaxTokens   int       `json:"max_tokens"`
    Temperature float64   `json:"temperature"`
    System      string    `json:"system"`
    Messages    []Message `json:"messages"`
    Stream      bool      `json:"stream"`
}

func main() {
    apiKey := os.Getenv("DEEPSEEK_API_KEY")

    req := Request{
        Model:       "deepseek-v4-pro",
        MaxTokens:   200,
        Temperature: 0.1, // 先设低一点
        System:      "你是一个程序员助手,回答简洁准确。",
        Messages: []Message{
            {Role: "user", Content: "用一句话解释 goroutine"},
        },
        Stream: true,
    }

    body, _ := json.Marshal(req)
    httpReq, _ := http.NewRequest("POST", baseURL+"/v1/messages", bytes.NewReader(body))
    httpReq.Header.Set("x-api-key", apiKey)
    httpReq.Header.Set("anthropic-version", "2023-06-01")
    httpReq.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(httpReq)
    defer resp.Body.Close()

    scanner := bufio.NewScanner(resp.Body)
    for scanner.Scan() {
        line := scanner.Text()
        if strings.HasPrefix(line, "data: ") {
            data := line[6:]
            if data == "[DONE]" {
                break
            }
            var event map[string]interface{}
            json.Unmarshal([]byte(data), &event)
            if delta, ok := event["delta"].(map[string]interface{}); ok {
                if text, ok := delta["text"].(string); ok {
                    fmt.Print(text)
                }
            }
        }
    }
}

就这么点代码。一个 struct 定义请求、一个 HTTP 调用、一个 SSE 流解析——没了。

你会发现三件事:

  1. 你没传「怎么思考」:代码里没有告诉模型该选什么词、怎么推理。模型自己会。
  2. 你只控制了三个参数Model(用哪个模型)、MaxTokens(最多输出多少)、Temperature(随机性旋钮)。
  3. 除此以外你什么都控制不了:模型输出的每个字都是它自己「算」出来的,你只能通过 Prompt 引导它。

这就是 LLM 的调用本质:输入文本 + 参数 → 输出文本。跟你调一个数据库、调一个 Redis 没有本质区别——输入一个查询,返回一个结果。只不过这个「数据库」算的不是索引,是概率。


第一个概念:Token——大模型不读「字」

你把「解释 goroutine」发给了 DeepSeek。但模型接收到的不是这 5 个字。

它看到的是:

解释 -> [token_12345]
 gor -> [token_67890]
outine -> [token_24680]

Token 是模型的最小输入单位。一个中文字通常 1-2 个 token,英文字母 1 个 token。模型不认识「字」——它只认识 token ID。

这为什么重要?

因为模型按 token 计价,也按 token 限制输入输出。你写 Prompt 的时候觉得自己只写了一句话,实际上可能已经烧了 200 个 token。

写一段 Go 代码,粗略估算 token 数:

func countTokens(text string) int {
    // 简单估算:英文按空格分词,中文按字符
    // 实际 tokenizer 更复杂,但这个粗略换算够用了
    chineseChars := 0
    for _, r := range text {
        if r >= 0x4E00 && r <= 0x9FFF {
            chineseChars++
        }
    }
    englishWords := len(strings.Fields(text))
    // 中文约 1.5 字符/token,英文约 1 词/token
    return chineseChars*2/3 + englishWords
}

你不一定要算得很准。但你得知道:你的 Prompt 不是免费的。每次调用都在花钱。 这就是为什么后面讲 RAG 和 Agent 的时候,我会反复强调「上下文窗口预算」——你不是在聊天,你是在花钱跟模型聊天。


第二个概念:Temperature——模型的「创作欲」

把上面代码里的 Temperature 分别设成 0.10.81.5,调三次同一个问题:「用一句话解释 goroutine」。

Temperature = 0.1 (保守):
"Goroutine 是 Go 语言中由运行时管理的轻量级线程,通过 go 关键字启动。"
→ 像教科书。每次跑结果几乎一样。

Temperature = 0.8 (适中):
"Goroutine 就像一个可以随手开的小工具,Go 帮你调度,你只管写逻辑就行。"
→ 有人味了。每次跑略有不同。

Temperature = 1.5 (狂野):
"Goroutine 是 Go 语言给你的魔法棒——轻轻一挥,成千上万个并发任务像蚂蚁一样工作起来。"
→ 开始写诗了。每次结果差异很大。

温度到底在控制什么?它在控制模型选词时「敢不敢选概率低的那个」。

模型每步都会给所有可能的词打分。温度低 → 模型只看高分词,选最稳的那个。温度高 → 模型开始考虑中低分词,输出变得有创意但不可控。

简单记:

温度范围效果适合
0.0 - 0.3确定性输出,每次一样代码生成、数据提取、翻译
0.3 - 0.8有变化但可控日常对话、内容润色
0.8 - 1.2创意但不可靠脑暴、写小说、起名
1.2+胡说概率显著上升不推荐生产使用

一个踩过的坑:我一开始把所有任务的温度都设成 0.7。后来发现代码生成在 0.7 下偶尔会发明不存在的 API——温度一降到 0.1 就好了。不是模型不行,是我参数不对。


第三个概念:上下文窗口——模型的「短期记忆」

把上面代码里的 Messages 改成多轮对话:

Messages: []Message{
    {Role: "user", Content: "我叫小王,我是后端开发。"},
    {Role: "assistant", Content: "好的小王,了解了。"},
    {Role: "user", Content: "我刚才说我叫什么?"},
}

模型能答出来「小王」。因为「我叫小王」这段对话还在上下文窗口里。

现在想象你跟模型聊了 100 轮。前面的对话超出了上下文窗口——模型就「忘」了。它不是没记住,是看不见。

不同模型的上下文窗口:

模型上下文窗口约等于
DeepSeek V4128K tokens~10 万字,一本中篇小说
Claude 4200K tokens~15 万字
GPT-4o128K tokens~10 万字

10 万字听起来很多。但如果你在做 RAG——把相关文档都塞进 Prompt 让模型参考——随随便便就能烧掉 5 万 token。

这就是为什么「上下文窗口管理」是 Agent 开发的核心问题。 不是模型不够聪明,是它的短期记忆有限。


整件事最反直觉的一点

你会觉得 LLM 「懂」你在说什么。

但它不懂。它做的事就是:拿到你输入的 token 序列 → 算一遍 → 输出下一个最可能的 token → 再算一遍 → 再输出。每一步都没有「理解」,只有概率计算。

那为什么看起来这么聪明?

因为它在训练时读了几万亿字的文本。你问的问题、你要的格式、你想的表达方式——它都「见过」。它的聪明不是来自思考,是来自「见过足够多」。

理解这一点不是为了让你鄙视 LLM,是为了让你更好地用它。

  • 你不理解 LLM,就会给它提不合理的期望——「帮我想一个从来没人做过的新产品」
  • 你理解了 LLM,就会知道它擅长的是——「帮我把我已经有的想法,用更好的方式表达出来」

你现在知道的东西,超过 80% 自称「懂 AI」的人

三个概念:Token、Temperature、Context Window。

你会发现所有 AI 对话的 Bug——为什么有时答非所问、为什么有时胡编乱造、为什么有时不记得你刚才说了什么——都在这三个概念里。

下一篇我会把这三个概念做成对照实验:同一个 Prompt,调不同温度、换不同模型,让你直观看到输出的差异。数据说话,不是玄学。

关注我,别错过。


🦞 一只用 AI Agent 搭副业产线的程序员

全平台同名:虾哥不加班

50 行源码可运行:保存为 main.go,设好 DEEPSEEK_API_KEYgo run main.go 即可

更多推荐