文章

大模型 AI Coding 原理指南

1. 从机器学习到 LLM

大型语言模型(Large Language Models,LLM)是一种基于深度学习的神经网络系统,通过在海量文本数据上进行训练,习得了人类语言的模式与规律。这使得它们能够理解上下文、回答问题、生成创意内容、编写代码等多种复杂任务。本指南将系统地介绍LLM的核心概念、工作原理、能力与限制。

1.1 LLM 的基本原理

LLM的核心工作原理是通过分析输入的文本序列,预测下一个最可能出现的词或标记(token)。这种看似简单的任务,经过大规模训练后,使模型获得了惊人的语言理解和生成能力。

1.1.1 从机器学习流程到 LLM

和传统的机器学习类似,LLM 的原理本质也为:准备数据→定义模型→选择损失函数→应用优化算法→训练循环→评估性能。

  • 数据准备:

    • 收集大规模(TB级别)文本语料库(如维基百科、书籍、网页文本等)进行清洗和处理

    • 通过子词切分(如BPE算法)将文本转化为语义单元(Token)向量表示,结合位置编码保留序列信息,形成高维稠密特征矩阵。

  • 模型定义:

    • 定义 Transformer 架构,设计超参数、上下文窗口大小、特殊标记、位置编码等;

    • 定义了参数的大小。

  • 损失函数:

    • 使用交叉熵损失进行标记预测、文本分类等。

  • 训练循环:进行多阶段训练,将于 1.2.1 详细介绍

    • 预训练:在大规模无标注文本上进行自监督学习

    • 微调:在特定任务的数据上进行有监督学习

    • RLHF 对齐:强化学习

  • 评估性能。

1.1.2 Transformer 架构介绍

在处理序列数据(如文本)时,早期NLP模型主要使用循环神经网络(RNN)、长短期记忆网络(LSTM)或门控循环单元(GRU)。这些模型虽然能捕捉序列中的时序关系,但存在两个主要问题:

  1. 难以并行计算(需要顺序处理)

  2. 长序列信息传递中的梯度消失问题

2017年,Google在论文《Attention Is All You Need》中提出了Transformer架构,通过自注意力(Self-Attention)机制解决了这些问题。

Self-Attention 机制可以看作是一种"动态卷积",它能根据上下文动态调整对不同位置的关注度:

  1. 对于输入序列中的每个元素(如单词),计算三个向量:查询(Query)、键(Key)和值(Value)

  2. 通过计算Query与所有Key的相似度,得到注意力分数

  3. 将注意力分数归一化(使用Softmax)

  4. 用这些分数对Value进行加权求和

这使得模型能够捕捉序列中任意距离的依赖关系,不受位置限制。

完整的 Transformer 架构包含编码器(Encoder)和解码器(Decoder)两部分:

  1. 编码器:处理输入序列,由多个相同的层堆叠而成,每层包含:

    • 多头自注意力(Multi-Head Self-Attention)机制

    • 前馈神经网络

    • 残差连接和层归一化

  2. 解码器:生成输出序列,结构类似编码器,但增加了:

    • 掩码多头自注意力(防止看到未来信息)

    • 编码器-解码器注意力(关注输入序列)

基于Transformer的两大模型家族:

  1. BERT:

    • 主要使用Transformer的编码器部分

    • 双向上下文理解(可以同时看到左右上下文)

    • 预训练任务:掩码语言模型(MLM)和下一句预测(NSP)

    • 适合理解任务(如分类、问答等)

  2. GPT系列:

    • 主要使用Transformer的解码器部分

    • 单向上下文理解(只能看到左侧上下文)

    • 预训练任务:下一个词预测

    • 适合生成任务(如文本生成、翻译等)

大语言模型(LLM)通常指的是GPT这类生成式模型,它们通过大规模的参数和训练数据,展现出强大的文本生成和理解能力。

1.2 LLM 核心技术概念

1.2.1 语言模型

如上述所说,大语言模型(LLM)是通过预测下一个词的监督学习方式进行训练的。

模型会根据当前输入 Context 预测下一个词(token) 的概率分布。通过不断比较模型预测和实际的下一个 token,并更新模型参数最小化两者差异,语言模型逐步掌握了语言的规律,学会了预测下一个 token。

在训练过程中,研究人员会准备大量句子或句子片段作为训练样本,要求模型一次次预测下一个 token,通过反复训练促使模型参数收敛,使其预测能力不断提高。经过在海量文本数据集上的训练,语言模型可以达到十分准确地预测下一个 token 的效果。这种以预测下一个token为训练目标的方法使得语言模型获得强大的语言生成能力

大型语言模型主要可以分为两类:基础语言模型和指令调优语言模型。

基础语言模型(Base LLM)通过反复预测下一个 token 来训练的方式进行训练,没有明确的目标导向。因此,如果给它一个开放式的 prompt ,它可能会通过自由联想生成戏剧化的内容。而对于具体的问题,基础语言模型也可能给出与问题无关的回答。例如,给它一个 Prompt ,比如”中国的首都是哪里?“,很可能它数据中有一段互联网上关于中国的测验问题列表。这时,它可能会用“中国最大的城市是什么?中国的人口是多少?”等等来回答这个问题。但实际上,您只是想知道中国的首都是什么,而不是列举所有这些问题。

相比之下,指令微调的语言模型(Instruction Tuned LLM)则进行了专门的训练,以便更好地理解问题并给出符合指令的回答。例如,对“中国的首都是哪里?”这个问题,经过微调的语言模型很可能直接回答“中国的首都是北京”,而不是生硬地列出一系列相关问题。指令微调使语言模型更加适合任务导向的对话应用。它可以生成遵循指令的语义准确的回复,而非自由联想。因此,许多实际应用已经采用指令调优语言模型。熟练掌握指令微调的工作机制,是开发者实现语言模型应用的重要一步。

那么,如何将基础语言模型转变为指令微调语言模型呢?这也就是训练一个指令微调语言模型(例如ChatGPT)的过程。

  • 首先,在大规模文本数据集上进行无监督预训练,获得基础语言模型。 这一步需要使用数千亿 token 甚至更多的数据,在大型超级计算系统上可能需要数月时间。

  • 之后,使用包含指令及对应回复示例的小数据集对基础模型进行有监督 fine-tune,这让模型逐步学会遵循指令生成输出,可以通过雇佣承包商构造适合的训练示例。 接下来,为了提高语言模型输出的质量,常见的方法是让人类对许多不同输出进行评级,例如是否有用、是否真实、是否无害等。

  • 然后,您可以进一步调整语言模型,增加生成高评级输出的概率。这通常使用基于人类反馈的强化学习(RLHF)技术来实现。

相较于训练基础语言模型可能需要数月的时间,从基础语言模型到指令微调语言模型的转变过程可能只需要数天时间,使用较小规模的数据集和计算资源即可。

1.2.2 参数

我们之前在学习机器学习的线性拟合的时候,接触了这个公式:

这里 w 和 b 其实就是参数,我们会用 BP 算法对参数进行拟合和优化。

在更复杂的 Transformer 等架构中,会出现相当数量的参数来进行初始化、梯度下降等。

现在的大模型几乎都是 Billion 级别的参数了。如:

  • GPT-3:175B(1750亿)

  • Qwen-72B:720亿参数

  • DeepSeek:提供了 1.5B~671B 不等参数的模型。

1.2.3 Token 与上下文窗口

token:

  • 定义:LLM处理文本的基本单位,可能是单词、词的一部分或标点符号

  • 重要性:API调用计费基于token数量,模型的上下文窗口也以token计算

  • 转换过程:文本通过分词器(tokenizer)转换为token序列

  • 语言差异:英文约0.75个token/单词,中文约1.5个token/字符

那么 token 是如何拆分的呢?

  • 例如,对于 "Learning new things is fun!" 这句话,每个单词都被转换为一个 token 。

  • 而对于较少使用的单词,可能会拆分为多个 token 。如 "Prompting as powerful developer tool",单词 "prompting" 会被拆分为三个 token,即"prom"、"pt"和"ing"。

得出结论:分词方式也会对语言模型的理解能力有影响。语言模型以 token 而非原词为单位进行建模,这一关键细节对分词器的选择及处理会产生重大影响。

当然,模型一次处理 token 的数量也是有限的。

上下文窗口:

  • 定义:模型一次能处理的最大token数量

  • 影响:决定了模型能"记住"多少之前的对话或文档内容

由于上下文窗口的限制,复杂任务通常依赖于微调,或用检索增强生成RAG等技术动态地向模型提供相关的上下文片段。

不同模型有不同的 token 限制,需要注意的是,这里的 token 限制是输入的 Prompt 和输出的 completion 的 token 数之和,因此输入的 Prompt 越长,能输出的 completion 的上限就越低。

ChatGPT3.5-turbo(2023) 的 token 上限是 4K,DeepSeek-V3 (2025)的 token 上限是 128K,Claude4 的 token 上限为 200K

1.3 实践:编写一个简单的模型

以下是使用 PyTorch 实现的一个非常简单的基础语言模型

该代码由 Claude 生成。

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from collections import Counter

# 1. 文本处理工具
class TextProcessor:
    def __init__(self, vocab_size=10000):
        self.vocab_size = vocab_size
        self.word2idx = {"<PAD>": 0, "<UNK>": 1, "<BOS>": 2, "<EOS>": 3}
        self.idx2word = {0: "<PAD>", 1: "<UNK>", 2: "<BOS>", 3: "<EOS>"}
        self.vocab_size = vocab_size

    def build_vocab(self, texts):
        words = []
        for text in texts:
            words.extend(text.split())

        counter = Counter(words)
        common_words = [word for word, _ in counter.most_common(self.vocab_size - 4)]

        for i, word in enumerate(common_words, 4):
            self.word2idx[word] = i
            self.idx2word[i] = word

    def encode(self, text):
        return [self.word2idx.get(word, self.word2idx["<UNK>"])
                for word in text.split()]

    def decode(self, indices):
        return " ".join([self.idx2word[idx] for idx in indices])


# 2. 数据集类
class SimpleDataset(Dataset):
    def __init__(self, texts, processor, seq_length=50):
        self.processor = processor
        self.seq_length = seq_length
        self.encoded_texts = []

        for text in texts:
            encoded = self.processor.encode(text)
            if len(encoded) > seq_length:
                encoded = encoded[:seq_length]
            else:
                encoded += [0] * (seq_length - len(encoded))
            self.encoded_texts.append(encoded)

    def __len__(self):
        return len(self.encoded_texts)

    def __getitem__(self, idx):
        encoded_text = self.encoded_texts[idx]
        x = torch.tensor(encoded_text[:-1])
        y = torch.tensor(encoded_text[1:])
        return x, y


# 3. 模型类
class SimpleLLM(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim, num_layers):
        super(SimpleLLM, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        self.self_attention = nn.MultiheadAttention(
            embed_dim=embedding_dim,
            num_heads=8,
            batch_first=True
        )
        self.feed_forward = nn.Sequential(
            nn.Linear(embedding_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, embedding_dim)
        )
        self.layer_norm1 = nn.LayerNorm(embedding_dim)
        self.layer_norm2 = nn.LayerNorm(embedding_dim)
        self.output_layer = nn.Linear(embedding_dim, vocab_size)

    def forward(self, x):
        embedded = self.embedding(x)
        attention_output, _ = self.self_attention(embedded, embedded, embedded)
        attention_output = self.layer_norm1(attention_output + embedded)
        ff_output = self.feed_forward(attention_output)
        ff_output = self.layer_norm2(ff_output + attention_output)
        output = self.output_layer(ff_output)
        return F.log_softmax(output, dim=-1)


# 4. 训练函数
def train_model(model, train_loader, num_epochs, learning_rate=0.001):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = model.to(device)
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    criterion = nn.CrossEntropyLoss()

    for epoch in range(num_epochs):
        model.train()
        total_loss = 0
        for batch_idx, (x, y) in enumerate(train_loader):
            x, y = x.to(device), y.to(device)
            optimizer.zero_grad()
            output = model(x)
            loss = criterion(output.view(-1, output.size(-1)), y.view(-1))
            loss.backward()
            optimizer.step()
            total_loss += loss.item()

            if batch_idx % 10 == 0:
                print(f"Epoch {epoch + 1}, Batch {batch_idx}, Loss: {loss.item():.4f}")

        avg_loss = total_loss / len(train_loader)
        print(f"Epoch {epoch + 1} completed, Average Loss: {avg_loss:.4f}")


# 5. 推理函数
def generate_text(model, processor, prompt, max_length=50):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.eval()

    # 编码输入提示
    input_ids = processor.encode(prompt)
    input_ids = torch.tensor(input_ids).unsqueeze(0).to(device)

    generated = []
    with torch.no_grad():
        for _ in range(max_length):
            outputs = model(input_ids)
            next_token_logits = outputs[:, -1, :]
            next_token = torch.argmax(next_token_logits, dim=-1)
            generated.append(next_token.item())
            input_ids = torch.cat([input_ids, next_token.unsqueeze(0)], dim=1)

            if next_token.item() == processor.word2idx["<EOS>"]:
                break

    return processor.decode(generated)


# 6. 主函数
def main():
    # 示例训练数据
    train_texts = [
        "你好 我 是 AI",
        "今天 天气 真 不错",
        "我 喜欢 学习 编程",
        # 添加更多训练数据...
    ]

    # 初始化文本处理器和词表
    processor = TextProcessor(vocab_size=1000)
    processor.build_vocab(train_texts)

    # 创建数据集和数据加载器
    dataset = SimpleDataset(train_texts, processor)
    train_loader = DataLoader(dataset, batch_size=2, shuffle=True)

    # 初始化模型
    model = SimpleLLM(
        vocab_size=processor.vocab_size,
        embedding_dim=256,
        hidden_dim=512,
        num_layers=1
    )

    # 训练模型
    train_model(model, train_loader, num_epochs=10)

    # 生成文本
    prompt = "你好 我"
    generated_text = generate_text(model, processor, prompt)
    print(f"输入: {prompt}")
    print(f"生成: {generated_text}")


if __name__ == "__main__":
    main()

运行起来之后,里面有的时候是 ”你好我是AI“ 有的时候是”你好我喜欢学习编程“

但是如果增大 epoch 次数,则更倾向于准确答案。

各部分对应代码:

  1. 数据准备 TextProcessor 类:构建词汇表 (build_vocab)、文本编码 (encode)

    SimpleDataset 类:将文本编码为索引并填充/截断到固定长度 (__init__ 方法)

    • 主函数中的 processor.build_vocab(train_texts)dataset 创建

  1. 模型定义 SimpleLLM 类:定义模型结构(嵌入层、自注意力层、前馈网络等)

  1. 损失函数选择 train_model 函数中的 criterion = nn.CrossEntropyLoss()

  1. 优化算法应用 train_model 函数中的 optimizer = torch.optim.Adam(...)

  1. 训练循环 train_model 函数中的 for epoch in range(num_epochs): 循环和内部的数据加载循环

  1. 评估性能 • 代码中未显式包含验证集评估,但 generate_text 函数可视为生成性能的定性评估。


Token 和上下文窗口的体现:

  1. Token

• 定义:在 TextProcessor.encode() 中,通过 text.split() 将文本按空格分割为单词级别的 Token。

• 示例:句子 "今天 天气 不错" 会被分割为 ["今天", "天气", "不错"],再转换为索引如 [4, 5, 6]

  1. 上下文窗口

• 固定长度:在 SimpleDataset__init__ 方法中,所有文本被截断或填充到 seq_length(默认为 50)。

• 模型输入:在 __getitem__ 中,xencoded_text[:-1](前 49 个 Token),yencoded_text[1:](后 49 个 Token)。

• 注意力机制:SimpleLLM 中的 nn.MultiheadAttention 允许模型在训练时关注整个输入序列(长度 seq_length-1)的所有 Token。


关键代码段说明:

• Token 处理(TextProcessor 类):

def encode(self, text):
    return [self.word2idx.get(word, self.word2idx["<UNK>"]) 
            for word in text.split()]  # 按空格分割 Token

• 上下文窗口限制(SimpleDataset 类):

if len(encoded) > seq_length:
    encoded = encoded[:seq_length]  # 截断
else:
    encoded += [0] * (seq_length - len(encoded))  # 填充

• 模型关注完整上下文(SimpleLLM 类):

self.self_attention = nn.MultiheadAttention(
    embed_dim=embedding_dim,
    num_heads=8,
    batch_first=True  # 处理整个输入序列
)

2. ChatBot 实现

2.1 OpenAI ChatCompletion

无论是 Python、JavaScript、Java 还是 Golang,无论是 Langchain、Langchain4j、SpringAI 等任何一个大模型对话框架,OpenAI 相关的 API 和 SDK 都是最有通用性的。

详情见:https://platform.openai.com/docs/api-reference/chat

2.1.1 Request 连接参数配置

在调用 ​OpenAI ChatCompletion API​(或兼容接口)时,无论使用哪种语言(Python/JS/Java等)或框架(LangChain/LangChain4j/SpringAI等),都必须配置以下 ​三个核心参数​ 以确保连接成功:

  • apiKey:

    • 作用​:身份验证密钥,用于访问 OpenAI 或兼容 API 服务(如 Azure OpenAI、本地部署的 OpenAI 兼容模型)。

    • 示例值​:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  • baseUrl:

    • API 服务的基础地址。默认 OpenAI 官方域名为 https://api.openai.com/v1

    • 若使用代理或自托管模型,需替换为对应地址(如 http://localhost:8000/v1)。

  • modelName:

    • 作用​:指定调用的模型名称(如 gpt-3.5-turbogpt-4 或开源模型如 llama3-70b)。

以下为 LangChain4j 的示例:

public class Main {
    public static void main(String[] args) {
        String apiKey = "sk-xxxxxxxxxxxxxxxxxx";

        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("https://api.hunyuan.cloud.tencent.com/v1")
                .apiKey(apiKey)
                .modelName("hunyuan-turbo")
                .build();

        String answer = model.chat("你好!你是谁?");
        System.out.println(answer); 
        // 我是腾讯开发的人工智能助手,可以帮你回答问题、提供建议,甚至讲笑话哦!
    }
}

2.1.2 Request 影响模型输出

一般来说,入参的请求体如下:

{
  "model": "gpt-4-turbo",
  "messages": [
    {
      "role": "system",
      "content": "你是一名资深技术顾问,用中文回答,回答需包含代码示例和详细解释。"
    },
    {
      "role": "user",
      "content": "请用Python演示如何调用OpenAI的ChatCompletion API,要求流式输出。"
    }
  ],
  "temperature": 0.7,
  "max_tokens": 1000,
  "top_p": 0.9,
  "frequency_penalty": 0.5,
  "presence_penalty": 0.3,
  "stream": true,
  "stop": ["\n\n", "###"],
  "response_format": {
    "type": "json_object"
  },
  "tools" :[]
}

  1. model

    • 指定使用的AI模型,比如这里用最新版的 gpt-4-turbo(性能更强,价格更便宜)

  2. messages

    • 对话消息列表,包含角色和内容:

      • system:系统指令(设定AI的角色和行为)

      • user:用户的提问或指令

      • (如果有多轮对话还会包含 assistant:AI之前的回复)

  1. temperature​ (0-2)

    • 控制回答的随机性:

      • 0 = 最确定/保守

      • 1 = 平衡(推荐)

      • 2 = 最大随机性

  2. max_tokens

    • 限制AI生成内容的最大长度(1个token≈0.75个英文单词)

  3. top_p(0-1)

    • 控制词汇选择的集中程度:

      • 0.9 = 只考虑概率最高的90%词汇

      • 与temperature二选一使用

  1. frequency_penalty (-2到2)

    • 惩罚重复用词:正值减少重复内容

  2. presence_penalty (-2到2)

    • 鼓励新话题:正值让AI更倾向提及新内容

  3. stream

    • 设为 true 时开启流式传输(数据会分块实时返回)

  4. stop

    • 遇到这些字符串时停止生成(比如设 ["。"] 会在句号处停止)

  5. response_format

    • 强制返回JSON格式(仅gpt-4-turbo支持)

  6. tools

    • 定义AI可以调用的外部工具

2.1.3 Response

{
  "id": "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
  "object": "response",
  "created_at": 1741476542,
  "status": "completed",
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "max_output_tokens": null,
  "model": "gpt-4.1-2025-04-14",
  "output": [
    {
      "type": "message",
      "id": "msg_67ccd2bf17f0819081ff3bb2cf6508e60bb6a6b452d3795b",
      "status": "completed",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "In a peaceful grove beneath a silver moon, a unicorn named Lumina discovered a hidden pool that reflected the stars. As she dipped her horn into the water, the pool began to shimmer, revealing a pathway to a magical realm of endless night skies. Filled with wonder, Lumina whispered a wish for all who dream to find their own hidden magic, and as she glanced back, her hoofprints sparkled like stardust.",
          "annotations": []
        }
      ]
    }
  ],
  "parallel_tool_calls": true,
  "previous_response_id": null,
  "reasoning": {
    "effort": null,
    "summary": null
  },
  "store": true,
  "temperature": 1.0,
  "text": {
    "format": {
      "type": "text"
    }
  },
  "tool_choice": "auto",
  "tools": [],
  "top_p": 1.0,
  "truncation": "disabled",
  "usage": {
    "input_tokens": 36,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 87,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 123
  },
  "user": null,
  "metadata": {}
}

关注几个地方:

关于 output:

  • type: "message"
    表示这是一个完整的AI回复消息(区别于工具调用或其他类型响应)。

  • role: "assistant"
    标明这是AI(助手)的回复,与用户输入(user)或系统指令(system)区分。

  • content

    • type: "output_text":纯文本输出(如果是工具调用会是 tool_call)。

    • text:具体的生成内容(这里是关于独角兽的英文故事)。

    • annotations:附加标记(如引用来源、代码块等,这里为空)。

关于 token:

  • input_tokens(36)
    用户输入(messages中所有内容)消耗的token数,包括:

    • 系统指令 + 用户问题 + 历史对话(如果有)。

  • output_tokens​ (87)
    AI生成文本(output.text)消耗的token数。

  • total_tokens(123)
    本次请求总token数(input + output),直接影响API调用成本。

2.2 对话历史的存储

2.3 对话记忆的存储(RAG)

License:  CC BY 4.0