跳到主要内容

LLM 基础速通

副标题:Token、上下文、温度、幻觉与 RAG(前端够用版)

目标:用前端能理解的方式,把“够用的基础”讲清楚——你要能解释、能排障、能做产品决策。

目录

LLM 是什么:从“预测下一个词”到“产品能力”

把大语言模型理解成一个“基于上下文,预测下一段文本”的系统就够用了。对你做应用开发来说,更重要的是:

  • 能力边界:擅长总结、改写、生成、分类、对话、工具编排;不擅长保证事实正确
  • 输出不确定:同样输入可能得到不同输出(可用参数控制)
  • 对上下文敏感:你给它什么,它就按什么“演”

因此应用层的工作,本质是:把不确定的能力,变成可控的产品体验。

Token 与上下文窗口:为什么越聊越贵、越聊越跑偏

Token 是什么

你可以把 Token 粗略理解为“文本片段的计费单位”。中文通常 1 个汉字 ≠ 1 个 Token,但你不需要精确换算,关键是知道:

  • 输入 Token:system + user + 历史对话 + 检索到的文档片段
  • 输出 Token:模型生成的回答
  • 成本 = 输入 + 输出(通常按 Token 计费)

一个可落地的 Token 预算算例(前端视角)

假设你做一个“文档问答”页面,想控制每次对话的成本上限。你可以把一次请求的 Token 拆成 4 块:

  • system:规则与角色(例如 200 tokens)
  • history:历史对话(例如 1500 tokens)
  • rag_context:检索证据(例如 3 段 * 350 = 1050 tokens)
  • user:用户问题(例如 80 tokens)

那么输入大约是:(200 + 1500 + 1050 + 80 = 2830) tokens
如果你把 max_tokens 设置为 600,那么单次请求的总量大约是:(2830 + 600 = 3430) tokens。

你可以据此在产品里做两件事:

  • 成本可控:把 max_tokens、RAG 段数、history 长度变成硬限制
  • 体验可解释:当超预算时提示“已自动压缩对话历史/减少引用段落”

实操建议:先定“每次回答允许的最大证据段数(TopN)”和“max_tokens”,再决定历史对话保留策略。

上下文窗口(Context Window)

上下文窗口是模型一次能“看到”的最大 Token 数。超出部分会被截断或需要你自己压缩/摘要。

产品层常见现象:

  • 越聊越贵:历史对话越来越长
  • 越聊越容易跑偏:历史里混入了错误/无关信息,模型被带偏
  • 长文档问答不准:把整本文档塞进上下文不现实

应用层对策(你会在后续章节用到):

  • 对话摘要:把历史压缩成结构化记忆(要点/偏好/约束)
  • 检索替代堆上下文:用 RAG 只塞相关片段
  • 限制输入输出:UI 侧限制字数、服务端限制 max_tokens

示例:对话摘要(Memory)怎么写才“能用”

很多项目的摘要失败,是因为把历史“概括成一段话”,结果信息丢失或不可控。建议用结构化摘要,并固定字段:

你是对话摘要器。请把以下对话压缩成“可用于后续对话”的结构化记忆。

输出为 JSON,字段必须包含:
- user_goal: 用户目标(1 句话)
- constraints: 约束(数组,最多 5 条)
- decisions: 已确认的关键决策(数组,最多 5 条)
- open_questions: 未解决问题(数组,最多 5 条)
- glossary: 专有名词解释(对象,可选)

规则:
- 不要编造;缺失就留空数组
- 严禁输出多余字段

前端怎么用这份摘要:

  • UI 状态 → constraints/decisions:让“已选项/已确认”进入上下文
  • 继续追问 → open_questions:直接用未解问题驱动下一轮追问

常见坑:上下文不是越长越好

  • 把整篇文档塞进去:成本暴涨,还会稀释关键信息,准确率反而下降
  • 把无关历史也带上:会把模型带偏(尤其在多任务对话里)
  • 没有硬限制:上线后很容易被“长对话用户”拖垮成本

温度(Temperature)与 Top-p:如何控制“稳定 vs 发散”

你可以把它理解成“随机性旋钮”:

  • 温度低:更稳定、更像“按规则办事”(适合问答、抽取、格式化)
  • 温度高:更发散、更有创意(适合文案、头脑风暴)
  • Top-p:另一种控制采样范围的方式(实际使用中常与温度二选一或搭配)

应用建议(经验型):

  • 结构化输出/需要稳定:温度低(或 Top-p 小一些)
  • 创意生成:温度稍高
  • 多步骤任务:把“生成”与“校验/格式化”拆成两次调用(一次负责想,另一次负责收敛)

示例:同一个任务,不同温度的差异

任务:把一段中文需求改写成 3 条验收标准。

  • 温度低(更稳定):更像“按模板填空”,适合结构化、抽取、总结
  • 温度高(更发散):会补充更多“想象的细节”,适合脑暴,但更容易跑题/编造

推荐默认值(可以当产品默认参数):

  • 问答/总结/抽取temperature=0.2~0.4
  • 写作/文案/创意temperature=0.7~1.0
  • Top-p:如果使用 top_p,通常就把 temperature 固定较低(或反过来二选一),避免双重随机导致不可控

前端产品化建议:把参数变成“用户可理解的开关”

  • “更严谨/更有创意”滑杆 → 映射到 temperature
  • “回答更短/更详细” → 映射到 max_tokens + 章节要求(而不仅仅是 max_tokens)

幻觉:为什么会胡说,以及怎么降低

为什么会幻觉

模型的目标是“生成看起来合理的文本”,不是“保证事实正确”。当它缺信息时,可能会:

  • 编造引用、编造数字、编造链接
  • 把旧知识当新知识
  • 把用户的暗示当事实

怎么降低(应用层可控手段)

从易到难给一组可落地的手段:

  • 明确约束:不知道就说不知道;禁止编造引用/链接
  • 要求引用证据:回答必须给出来源片段(RAG)
  • 工具替代猜测:需要实时信息就调用工具/接口,不让它猜
  • 分步推理外显/内隐:让模型先列检查清单再输出(或用“自检”二次调用)
  • 输出校验:JSON schema 校验、字段缺失补问、格式不对重试

可直接复用的“拒答 + 引用”模板(推荐用于 RAG)

你是严谨的助手。你只能基于【证据】回答。

规则:
1) 如果证据不足以支持结论:请明确回答“根据当前证据无法确定”,并列出需要补充的信息。
2) 严禁编造链接、编号、数据、来源。
3) 每个关键结论后必须标注引用,如 [S1] [S2]。

输出格式:
- 结论(带引用)
- 依据(引用列表)
- 不确定点与需要补充的问题

示例:二次“自检”降低幻觉(便宜但有效)

第一次生成后,再用一个更短的检查 Prompt 做校验:

请检查下面回答是否存在“无证据推断/编造引用/数字不一致/与证据矛盾”。
若存在,请输出:问题列表 + 修正后的回答(仍需引用)。

实战经验:这类“自检”对引用完整性、格式稳定性提升明显,成本通常远低于一次完整重写。

Function Calling / Tools:让模型做“可控的事”

当你需要模型“做事”而不是“聊天”,最重要的思想是:

  • 模型负责决策/抽取(要调用哪个工具、参数是什么)
  • 系统负责执行(真正发请求、查库、算价格、读文件)

常见工具场景:

  • 查询订单/权限判断
  • 生成 PRD 的结构化字段(而不是一大段散文)
  • 文档检索(先检索,再回答)

工程要点:

  • 工具参数要可验证:字段类型、必填项、长度限制
  • 工具输出要可追溯:日志里记录 tool 输入输出(脱敏)
  • 避免越权:服务端再次做权限校验,不信任模型

示例:用 Tools 把“猜”变成“查”(端到端思路)

场景:用户问“我的订单 12345 现在是什么状态?”
如果让模型直接回答,它只能猜;正确做法是让模型调用工具查订单。

1)工具定义(JSON Schema 思路)

{
"name": "getOrderStatus",
"description": "查询订单状态",
"parameters": {
"type": "object",
"properties": {
"orderId": { "type": "string", "description": "订单号" }
},
"required": ["orderId"]
}
}

2)服务端执行(关键是:不信任模型、二次鉴权)

// 伪代码:服务端收到模型的 tool 调用请求后
async function handleGetOrderStatus(userId: string, orderId: string) {
// 1) 鉴权:这个 user 是否有权限查这个订单
await assertOrderPermission(userId, orderId);
// 2) 真查:从数据库/内部 API 查询
return await ordersService.getStatus(orderId);
}

3)把工具结果回注给模型生成“可解释回答”

让模型基于工具结果输出,同时要求“不确定就说不确定”,避免它补细节。

前端加分点:把工具执行过程做成“可展开的步骤”(类似调试面板),用户信任会显著提升。

RAG:文档问答/企业知识库的关键能力

RAG(Retrieval-Augmented Generation)可以理解为:

先“检索相关资料”,再“基于资料回答”,并尽量“引用来源”。

它解决的是:把“我不知道”变成“我去文档里查一下再回答”

典型流程(你后面会实战):

  1. 文档切分(chunk)
  2. 每个 chunk 做 Embedding(向量表示)
  3. 用户提问也做 Embedding
  4. 向量检索取 TopK 相关 chunk
  5. 把 chunk 作为“证据”塞进 Prompt,让模型回答并引用

关键点(很多项目翻车就翻在这里):

  • 切分策略:太大检索不准,太小上下文不够
  • 引用输出:让用户能点开“证据来源”
  • 评估与迭代:用固定问题集回归测试命中率/引用率

图示:RAG 的最小可行 Pipeline(Mermaid)

示例:证据输入格式(让引用变得“可实现”)

把检索到的 chunk 组织成“可引用编号”,并带元数据:

【证据】
[S1] doc=《用户手册》 page=12 title=安装
内容:...(chunk 文本)

[S2] doc=《FAQ》 section=退款
内容:...(chunk 文本)

然后在规则里要求:

  • 每个关键结论后必须写 [S1]/[S2]
  • 没证据就拒答或提出需要补充的问题

最小评估表(作品集可直接用)

  • 检索命中率:正确证据是否出现在 TopK
  • 引用准确率:引用是否真的支撑结论
  • 失败类型:没命中 / 命中但噪音大 / 引用缺失 / 生成跑题

前端工程师的“够用速记卡”

  • Token/上下文
    • 对话越长越贵、越容易跑偏:用摘要 + RAG + 限制
  • 温度/Top-p
    • 低:稳定;高:发散;结构化输出优先低
  • 幻觉
    • 靠约束 + 引用 + 工具 + 校验降低
  • Tools
    • 模型决定、系统执行;权限/校验在服务端做二次防线
  • RAG
    • 企业知识库/文档问答核心;切分、检索、引用、评估缺一不可

附:你可以直接抄走的“默认参数组合”

  • 结构化问答(带引用)
    • temperature:0.2~0.4
    • max_tokens:400~800(视你的 UI 长度)
    • RAG:TopK=10,重排后 TopN=3~5(塞进 Prompt)
    • 规则:必须引用;无证据拒答;输出固定结构
  • 创意写作(不需要引用)
    • temperature:0.8~1.0
    • max_tokens:800~1500
    • 规则:先给大纲再展开,避免跑题

最小可运行 Demo(LLM 基础版)

目标:用一个最小脚本跑通“预算估算 → 调用参数 → 输出校验”的闭环

最小可运行代码(Token 预算 + 调用参数示例)

// demo-llm-budget.ts(伪代码)
function roughTokenEstimate(text: string) {
// 粗略估算:中文按 1 字 ≈ 1.3 token,英文按 1 单词 ≈ 1 token
const chineseChars = text.match(/[\u4e00-\u9fff]/g)?.length || 0;
const words = text.split(/\s+/).filter(Boolean).length;
return Math.ceil(chineseChars * 1.3 + words);
}

const system = "你是严格助手,必须引用来源。";
const user = "公司年假怎么计算?";
const rag = "证据片段..."; // 模拟RAG

const inputTokens =
roughTokenEstimate(system) + roughTokenEstimate(user) + roughTokenEstimate(rag);
const maxTokens = 600;

console.log("inputTokens:", inputTokens);
console.log("maxTokens:", maxTokens);

完整 Demo 结构(LLM 基础最小骨架)

demo-llm-basic/
scripts/
demo-llm-budget.ts
README.md

常见坑排查清单

  • 预算超标:RAG 片段过长 → 减少 TopN 或做摘要
  • 温度太高:结构化输出变得不稳定 → 降到 0.2~0.4
  • 引用缺失:Prompt 未强制引用 → 加“必须引用”规则

完整可运行代码(Token/上下文估算)

源码目录:docs/demos/token-demo

node docs/demos/token-demo/index.js