LLM 应用开发是什么:零基础也可以读懂的科普文(极简版)

一灰灰blogLLMLLM约 10808 字大约 36 分钟

LLM 应用开发教学版教程

面向对象:初级程序员 / 对 LLM 有基础认知、希望真正做出 LLM 应用的人

本教程不追求“模型原理深度”,而是帮助你一步步理解:为什么要这样做、如果不这样会出现什么问题

LLM 应用开发是什么:零基础也可以读懂的科普文(极简版)open in new window


第 0 章:为什么现在的程序员,绕不开 LLM?

在正式开始写代码之前,我们先停下来思考一个问题:

如果你已经会写后端、前端、脚本、接口,那 LLM 对你来说到底意味着什么?

很多初学者一开始接触 LLM,会有两种极端反应:

  • 一种是:“这不就是个更聪明的聊天机器人吗?”
  • 另一种是:“以后是不是不用写代码了?”

这两种理解,其实都不太准确。

这一章,我们只做一件事:

帮你建立一个“正确的位置感”:LLM 在软件系统中,究竟扮演什么角色。


0.1 传统程序解决问题的方式,有什么局限?

先回到你熟悉的世界。

在没有 LLM 之前,一个程序通常是这样工作的:

  • 输入是结构化的(表单、参数、JSON)
  • 逻辑是确定的(if / else、规则、流程)
  • 输出是可预期的

这套模式非常稳定,也非常可靠。

但它有一个明显前提:

问题本身,必须是“可以被提前描述清楚的”。

举几个你可能遇到过的真实场景:

  • 用户输入一段“很随意”的自然语言描述
  • 文档是 PDF / Word / Markdown,结构混乱
  • 用户的问题,每次问法都不一样

这时你会发现:

👉 规则开始爆炸 👉 if / else 越来越多 👉 覆盖不全、维护困难

不是你写得不好,而是问题本身已经不适合用纯规则解决了


0.2 LLM 带来的真正变化是什么?

LLM 并没有取代程序员,它真正改变的是这一点:

程序第一次拥有了“理解非结构化输入”的能力。

注意这里的关键词:

  • 不是“理解世界”
  • 而是“理解语言形式上的意图”

这意味着什么?

以前你需要做的事情是:

把用户输入 → 强行变成结构化数据 → 再处理

而现在你可以:

把“理解这件事”本身,交给模型

程序员的角色开始发生变化:

  • 从“写规则的人”
  • 变成“设计约束和流程的人”

这也是为什么我们说:

LLM 应用开发,本质是工程问题,而不是模型问题。


0.3 什么叫“LLM 应用开发”?

很多人一开始会把 LLM 应用理解为:

“写几个 Prompt,调个 API,就完了”

但在真实项目中,很快你就会遇到这些问题:

  • 模型有时会胡说八道
  • 对话一长,它就开始“失忆”
  • 不同用户,需要不同风格的回答
  • 数据是私有的,模型根本没见过

于是你会发现:

真正的 LLM 应用 = 模型 + 工程系统

一个完整的 LLM 应用,通常至少包含:

  • 模型调用(API)
  • 提示词设计(Prompt)
  • 上下文与记忆管理
  • 外部知识接入(RAG)
  • 工具 / 接口调用(Function Calling)

本教程接下来所有内容,都会围绕这些组件展开。


0.4 在开始之前,你需要一个心理预期

在正式进入技术细节前,有三点非常重要的心理预期:

  1. LLM 不是银弹
    它能解决一类问题,但不是所有问题。

  2. LLM 会犯错,而且看起来很自信
    你必须为它设计“兜底机制”。

  3. 写好 LLM 应用,比写 Demo 难得多
    真正的难点在工程设计,而不是 API 调用。

如果你带着这个预期继续往下读,后面的内容会顺很多。


本章小结

这一章你只需要记住三件事:

  • LLM 的价值,在于处理非结构化语言
  • LLM 应用开发,是一门工程能力
  • 后面的每一章,都是为了解决一个“你一定会遇到的问题”

下一章,我们不会立刻写代码,而是先回答一个更基础的问题:

LLM 到底能做什么?又有哪些事情,是它天然做不好的?


第 1 章:LLM 能做什么?不能做什么?

在真正开始调用模型之前,我们必须先回答一个非常现实的问题:

哪些问题适合交给 LLM?哪些问题一开始就不该用它?

如果这个判断一开始就错了,后面 Prompt 写得再好、RAG 再复杂,效果也很难理想。

这一章的目标只有一个:

帮你建立对 LLM 能力边界的直觉判断。


1.1 LLM 最擅长做的事情是什么?

先说结论:

LLM 最擅长的,是“处理语言本身”。

这里的“处理”,并不是指“懂世界”,而是指:

  • 理解语言里的意图
  • 生成符合上下文的自然语言
  • 在已有信息基础上进行组织、改写、总结

你可以把 LLM 想象成一个:

极其擅长阅读、写作和模仿语言风格的系统

基于这一点,它在下面这些场景中,往往表现得很好。


场景一:信息整理与改写

例如:

  • 把一段很长的文字总结成要点
  • 把技术文档改写成用户能看懂的说明
  • 把零散的会议记录整理成结构化结论

这些任务的共同点是:

  • 输入是自然语言
  • 输出仍然是自然语言
  • 不要求“绝对精确”,而是“整体合理”

LLM 在这类任务上,往往比纯规则系统简单得多、效果也更好。


场景二:基于上下文的问答

例如:

  • “帮我解释一下这段代码在干什么”
  • “根据这份文档,回答用户的问题”
  • “用更通俗的方式解释这个概念”

只要你能把必要的信息提供给模型,它就能基于上下文进行回答。

这也是后面 RAG 技术存在的根本原因。


场景三:语言风格与角色模拟

你可能已经体验过:

  • 让模型用“老师的口吻”解释问题
  • 用“客服风格”回复用户
  • 模拟某种固定的写作风格

这并不是模型“在演戏”,而是因为:

语言风格本身,就是一种可以被学习的模式。


1.2 那它不擅长什么?

理解 LLM 的能力边界,比理解它的能力本身更重要。

下面这些事情,是 LLM 天然就不擅长,甚至是“危险的”。


不擅长一:精确计算和绝对正确性

LLM 并不是计算器。

当你问它:

  • 精确的数学结果
  • 严格的逻辑证明
  • 每一步都不能出错的推导

有可能给你一个“看起来合理,但实际上错误”的答案

原因很简单:

它追求的是“语言上的合理性”,而不是“结果上的正确性”。


不擅长二:最新、私有或未提供的信息

模型的训练数据是有限的。

如果你问它:

  • 最新发生的事情
  • 公司内部的数据
  • 你电脑里的某个文件内容

在没有额外手段的情况下,它只能:

猜一个最像真的答案

这正是“幻觉”问题的来源之一。


不擅长三:替你做判断和承担责任

LLM 可以给你建议,但它:

  • 不对结果负责
  • 不知道真实世界的后果
  • 无法替你做价值判断

所以在:

  • 法律
  • 医疗
  • 金融决策

等高风险场景中,它只能作为辅助,而不能作为最终决策者


1.3 一个非常重要的结论

到这里,我们可以得出一个对后续所有章节都非常重要的结论:

LLM 适合做“语言层面的智能”,而不是“事实层面的权威”。

这句话你可以多读几遍。

它几乎解释了后面所有技术为什么存在:

  • Prompt:为了让模型“更好地理解你想要什么”
  • 上下文管理:为了让模型“别忘了已经说过什么”
  • RAG:为了让模型“基于真实资料说话”
  • Function Calling:为了让模型“不要自己瞎编,而是去调用系统”

本章小结

这一章,你只需要记住三点:

  1. LLM 擅长处理自然语言
  2. LLM 不保证事实绝对正确
  3. 用不用 LLM,首先是一个工程判断

下一章,我们终于要开始真正“用模型”了。

但在写任何复杂逻辑之前,我们会先做一件很基础、但非常关键的事情:

把 LLM 接进你的程序,搞清楚你到底在和什么交互。


第 2 章:第一次把 LLM 接进你的程序

在前两章,我们一直在讲“为什么”和“能不能”。

从这一章开始,我们终于要做一件非常具体的事情:

把大语言模型,当成一个真正的系统组件,接进你的程序里。

这一章不会追求复杂功能,它的目标只有一个:

让你清楚:你到底在和什么交互。

如果这一点没想清楚,后面所有 Prompt、RAG、Agent,都会变成“玄学调参”。


2.1 调用 LLM,本质上是在做什么?

先说一个非常容易被忽略的事实:

你调用 LLM,并不是在“和一个人聊天”。

从工程角度看,你在做的事情其实是:

  • 构造一次请求
  • 把一段上下文发给模型
  • 让模型基于这些上下文,生成下一段文本

也就是说:

每一次调用,模型都是“一次性地”看完整个输入。

它不会自动记住你上一次说了什么,除非:

👉 你把这些内容,再一次发给它

这个认知,对理解后面的“上下文管理”非常重要。


2.2 messages:你真正传给模型的是什么?

在大多数 LLM API 中,你都会看到一个类似的结构:

  • model
  • messages

其中,messages 往往是一个列表,看起来像是“聊天记录”。

但从模型的视角来看:

它不是聊天记录,而是一段“完整的输入文本”。

模型并不知道哪一句是“刚刚发生的”, 它只看到:

“现在,有这样一段内容,请继续往下写。”


2.3 system / user / assistant 角色,到底有什么用?

messages 中,通常会看到三种角色:

  • system
  • user
  • assistant

很多初学者会以为:

“这些角色是给模型‘分身份’用的。”

但更准确的理解是:

它们是用来给模型不同“权重和语义提示”的。


system:全局约束和行为边界

system 的作用是:

告诉模型:你是谁,你应该如何整体地表现。

例如:

  • 你是一个严谨的技术助手
  • 回答时不要编造不存在的内容
  • 输出必须是 JSON 格式

你可以把 system 理解为:

整次生成过程的“最高优先级说明书”


user:具体任务和问题

user 消息,通常代表:

  • 当前用户提出的问题
  • 当前这一次调用想要完成的任务

它关注的是:

这一次你希望模型做什么。


assistant:历史输出,用来“延续上下文”

assistant 并不是模型自己生成的“实时内容”, 而是:

你主动提供给模型的、它“之前说过的话”。

这样做的目的只有一个:

让模型在新的生成中,延续之前的对话风格和内容。


2.4 一个重要但反直觉的结论

到这里,我们可以得出一个非常关键的结论:

模型并不“记得”任何东西,除非你把它再发给它。

所谓的“多轮对话”,在工程上其实是:

你不断把历史对话,重新打包发给模型。

这也解释了两个常见现象:

  • 对话越长,成本越高
  • 对话越长,模型越容易“跑偏”

这不是模型变笨了,而是:

输入变得又长又杂了。


2.5 为什么这一章不急着讲 Prompt 技巧?

你可能已经注意到:

这一章我们几乎没有讲“怎么写 Prompt”。

这是刻意的。

因为在你没有理解:

  • 模型怎么看输入
  • 角色是怎么影响生成的
  • 上下文是如何被一次性消费的

之前,所有 Prompt 技巧,都会变成:

“试出来的经验,而不是可复用的方法。”


本章小结

这一章你需要记住的,其实只有三点:

  1. LLM API 是一次性生成,不是持续对话
  2. messages 是完整输入,而不是增量补丁
  3. system / user / assistant 是语义提示工具

下一章,我们终于要开始面对一个所有人都会遇到的问题:

为什么我明明说得很清楚,模型却总是答非所问?

这将引出 LLM 应用开发中,最基础、也是最容易被误解的一件事:

Prompt 的本质。


第 3 章:为什么模型总是答非所问?——Prompt 的本质

如果你已经实际调用过 LLM,大概率遇到过这样的情况:

你觉得自己已经说得很清楚了, 但模型给你的回答却:

  • 跑题
  • 太泛
  • 或者完全不是你想要的形式

于是你开始不断修改 Prompt:

  • 多加几句话
  • 换一种说法
  • 反复“试到对”为止

这一章,我们要解决的正是这个问题:

为什么 Prompt 会失效?以及,什么才是“有效 Prompt”的本质。


3.1 一个常见误区:把 Prompt 当成“问题”

很多初学者写 Prompt 时,脑子里想的是:

“我该怎么把问题问清楚?”

但模型接收到的,并不是一个“问题”,而是:

一段用来继续生成文本的上下文。

这意味着:

  • 模型不会自动帮你补全目标
  • 也不会自动知道你“想用这个结果干嘛”

如果上下文里没有清楚地体现任务, 模型只能按照“语言概率”自由发挥。


3.2 Prompt 的真实角色:任务说明书

一个更准确的理解是:

Prompt 不是提问,而是任务说明书。

想象你把一项工作交给一个新同事:

  • 你会只丢一句话给他吗?
  • 还是会说明:
    • 要做什么
    • 用什么方式做
    • 结果要长什么样

LLM 也是一样。

当你只给它一句模糊的问题时, 它只能给你一个“看起来像回答”的文本。


3.3 一个有效 Prompt,至少要回答三个问题

从工程角度看,一个可控的 Prompt,通常需要明确三件事:


第一件事:你希望它“扮演什么角色”?

这通常通过 system 或开头的角色描述完成。

例如:

  • 你是一个资深 Java 后端工程师
  • 你是一个严谨的技术文档助手

角色并不是装饰,而是在限制生成空间


第二件事:你希望它“完成什么任务”?

这里要避免的,是只描述问题本身。

更好的方式是直接说明:

  • 请你做分析
  • 请你给出步骤
  • 请你输出结构化结果

也就是说:

直接告诉模型你要的“动作”,而不是只给素材。


第三件事:你希望“结果长什么样”?

这是最容易被忽略的一点。

模型并不知道你:

  • 是要一段解释
  • 还是一份列表
  • 还是 JSON / Markdown

如果你不说明, 它会选择“最通用、最安全”的输出方式。


3.4 Zero-shot 和 Few-shot,是怎么被“逼出来的”?

当你发现:

“我已经写得很清楚了,它还是不稳定”

通常不是模型不够聪明,而是:

它不知道你心里那个‘参考答案长什么样’。

这时,就会自然出现两种做法。


Zero-shot:只给说明,不给示例

  • 适合规则简单、边界清晰的任务
  • 成本低,但不稳定

Few-shot:直接给几个示例

  • 告诉模型:

    “照着这个样子来”

示例的作用不是“教知识”,而是:

在输入中,明确什么是“好输出”。


3.5 为什么 Prompt 永远不可能解决所有问题?

到这里,你可能会产生一个错觉:

“那我是不是只要把 Prompt 写得足够复杂就行了?”

很遗憾,答案是否定的。

原因在于:

  • Prompt 仍然只是文本
  • 模型仍然只是在“生成下一个词”

当任务涉及:

  • 真实世界的事实
  • 私有或大量数据
  • 严格的一致性要求

Prompt 的能力就会到达上限。

这不是技巧问题,而是能力边界问题。


本章小结

这一章,你需要记住的不是“Prompt 模板”,而是三个核心认知:

  1. Prompt 是任务说明书,不是问题
  2. 角色、任务、输出格式同样重要
  3. Prompt 有上限,不能解决所有问题

下一章,我们将面对另一个所有人都会遇到的现实问题:

聊着聊着,模型为什么就开始“忘事”了?

这将引出 LLM 应用开发中,非常关键的一环:

上下文与记忆管理。


第 4 章:聊着聊着它就忘了?——上下文与记忆管理

当你把 Prompt 写得越来越清楚之后,通常会遇到下一个问题:

对话刚开始时一切正常, 但聊着聊着,模型就开始:

  • 忘记前面说过的约束
  • 前后回答自相矛盾
  • 或者突然换了一种风格

很多初学者会直觉地认为:

“是不是模型不稳定?”

但实际上,问题几乎从来不在模型身上。

这一章,我们要彻底讲清楚:

LLM 为什么会‘忘事’,以及我们在工程上该如何应对。


4.1 一个必须先打破的误解:模型并没有记忆

先说结论:

LLM 本身并不具备持续记忆。

在第 2 章我们已经提到过:

  • 每一次调用
  • 模型看到的都是一次性完整输入

这意味着:

所谓“记住”,只是你把历史内容再次发给了它。

如果你没有把某段信息放进当前请求中, 那对模型来说,这段信息就是不存在的。


4.2 什么是上下文窗口?(直觉理解)

模型并不是可以无限读取输入的。

它有一个限制,通常被称为:

上下文窗口(Context Window)

你可以把它想象成:

模型在生成回答时, 能“同时看到”的最大文本长度。

当输入内容超过这个长度时:

  • 早期的内容会被截断
  • 或者根本无法被模型同时考虑

于是就会出现一种现象:

模型并不是选择性遗忘,而是根本“看不到”。


4.3 为什么对话越长,效果越容易变差?

即使没有超过上下文窗口, 对话变长也会带来新的问题。

原因主要有三个:


原因一:重要信息被淹没

当你把大量历史对话一股脑塞给模型时:

  • 新的任务
  • 旧的闲聊
  • 无关的中间推理

都会混在一起。

模型并不知道:

哪些是“你现在最关心的”。


原因二:早期约束逐渐失效

即使你在最开始的 system 中设定了规则,

随着输入变长:

  • 后续内容的影响会越来越大
  • 早期指令的“权重”会被稀释

于是你会感觉:

模型开始“不听话了”。


原因三:成本和延迟直线上升

上下文越长:

  • 每次调用消耗的 token 越多
  • 响应时间越长
  • 成本越不可控

这在真实系统中,往往是不可接受的。


4.4 工程上常见的三种记忆策略

既然不能无限塞上下文, 那工程上通常会怎么做?

实际上,常见的策略只有三种。


策略一:全量上下文(最简单,也最脆弱)

做法:

  • 把所有历史对话原样保留
  • 每次请求全部发送

优点:

  • 实现最简单
  • 短对话效果好

缺点:

  • 极易超出上下文窗口
  • 成本高
  • 长对话必然失控

适合:

  • Demo
  • 非常短的交互

策略二:摘要记忆(折中方案)

做法:

  • 定期把历史对话压缩成摘要
  • 只保留关键信息和结论

这样做的本质是:

用“信息密度”,换“上下文长度”。

优点:

  • 成本可控
  • 能保留整体语义

缺点:

  • 细节不可逆丢失
  • 摘要质量直接影响效果

策略三:向量记忆(为后面 RAG 做铺垫)

做法:

  • 把历史信息向量化
  • 在需要时进行检索
  • 只把相关内容送入上下文

这种方式的关键思想是:

不是“全记住”,而是“按需回忆”。

优点:

  • 可扩展
  • 不受上下文长度限制

缺点:

  • 实现复杂
  • 需要额外系统支持

4.5 一个重要的工程认知

到这里,你应该能意识到一件事:

上下文管理不是 Prompt 问题,而是系统设计问题。

很多“模型突然变差”的问题, 本质上都是:

  • 信息组织失败
  • 重要内容没有被正确送达

这也是为什么:

只靠 Prompt,永远做不好复杂对话系统。


本章小结

这一章你需要记住的核心点是:

  1. 模型没有记忆,只有你提供的上下文
  2. 上下文窗口是硬限制
  3. 记忆策略是工程取舍

下一章,我们将面对一个更棘手、也更危险的问题:

模型为什么会“一本正经地胡说八道”?

这将引出 LLM 应用开发中,绕不开的主题:

幻觉问题。


第 5 章:它为什么会“一本正经地胡说八道”?——幻觉问题

如果你已经按照前面的章节做过一些尝试,那么很可能遇到过下面这种情况:

模型回答得非常完整, 语气也很自信, 逻辑看起来还挺通顺, 但你越核对,越发现哪里不对。

很多人第一次遇到这种情况时,都会有点“崩溃”:

“这也太不靠谱了吧?”

这一章,我们要做的不是吐槽模型, 而是回答一个非常关键的问题:

模型为什么会胡说八道?而且还说得这么像真的?


5.1 先说结论:幻觉不是 Bug

先给出一个可能有点反直觉的结论:

幻觉不是模型的 Bug,而是它工作方式的自然结果。

回到最本质的一点:

  • LLM 的目标是
  • 在给定上下文下
  • 生成“最可能出现的下一个词”

注意这里的关键词是:

“最可能”,而不是“最正确”。

当模型面对一个它并不真正“知道答案”的问题时, 它并不会停下来告诉你“我不知道”。

相反,它会:

尽可能生成一个“看起来合理”的答案。

这就是幻觉的根源。


5.2 幻觉通常在什么情况下出现?

虽然幻觉不可避免,但它并不是随机发生的。

在工程实践中,幻觉往往集中出现在以下几类场景。


场景一:信息不完整或上下文模糊

当你给模型的信息不足时:

  • 问题范围很大
  • 关键条件没有说明
  • 或者上下文前后矛盾

模型只能“自行补全”。

而补全的依据,只能是训练时学到的语言模式。


场景二:询问模型并不具备的知识

例如:

  • 最新发生的事件
  • 公司内部的业务数据
  • 你本地系统的状态

在这些情况下,

模型既看不到真实数据,也无法访问外部系统。

于是它只能编一个。


场景三:要求模型给出“权威结论”

当你让模型:

  • 下判断
  • 给结论
  • 承担决策责任

幻觉风险会显著升高。

因为模型并不知道:

错了会有什么后果。


5.3 为什么幻觉在真实应用中很危险?

在一些轻量场景中,幻觉可能只是“有点不准”。

例如:

  • 聊天
  • 头脑风暴
  • 文案草稿

但在下面这些场景中,幻觉是不可接受的:

  • 企业知识问答
  • 技术文档查询
  • 法律、医疗、金融相关系统

原因很简单:

用户无法分辨哪些内容是模型“编的”。

如果系统本身又没有做校验或限制, 那么:

错误信息会被当成事实传播。


5.4 一个重要的工程判断:哪些幻觉可以容忍?

在进入解决方案之前,我们先明确一件事:

不是所有幻觉,都必须被彻底消除。

在工程上,你通常需要做的是:

  • 区分“可容忍的幻觉”
  • 和“必须消除的幻觉”

例如:

  • 写创意文案时,少量编造是可以接受的
  • 但在查询公司制度时,一句编造就是严重事故

这一步判断,决定了你后续系统的复杂度。


5.5 为什么 Prompt 解决不了幻觉?

很多初学者会尝试在 Prompt 中加上类似:

“如果不知道,请直接说不知道”

有时有效,但你很快会发现:

  • 它并不稳定
  • 在复杂场景下几乎必然失效

原因在于:

Prompt 只能影响生成倾向,无法改变模型是否“知道”。

当模型缺乏事实基础时, 再严格的 Prompt, 也只能降低幻觉概率,而无法消除。


5.6 解决幻觉的根本思路是什么?

到这里,其实答案已经呼之欲出了。

如果幻觉的根源在于:

模型没有真实、可靠的信息来源

那么解决思路也就很明确:

不要让模型凭空回答,让它基于你提供的真实资料回答。

也就是说:

  • 把“事实”从模型训练中解耦出来
  • 在推理阶段,把真实资料送给模型

这正是下一章要介绍的核心技术:

RAG(Retrieval-Augmented Generation)。


本章小结

这一章你需要牢牢记住的几点是:

  1. 幻觉不是 Bug,而是模型特性
  2. 幻觉在信息不足时几乎不可避免
  3. Prompt 无法根治幻觉
  4. 解决幻觉需要外部真实信息

下一章,我们将正式进入 LLM 应用开发中 最重要、也是最具工程价值的一项能力

让模型基于你的数据回答问题。

第 6 章:RAG —— 让模型基于真实资料回答问题(系统设计视角)

在上一章,我们已经得出了一个非常明确的结论:

只要模型缺乏可靠的事实来源,幻觉就一定会出现。

因此,真正严肃的 LLM 应用,必须回答一个问题:

系统如何为模型“提供事实”,而不是让它凭空猜?

RAG(Retrieval-Augmented Generation)并不是一个技巧,而是一种系统设计模式

这一章,我们会站在工程和系统设计的角度,把 RAG 讲清楚。


6.1 一个几乎所有人都会走过的弯路

在第一次做“基于文档的问答”时,很多人都会自然想到:

“那我是不是把文档内容直接塞进 Prompt 就行了?”

在文档很短、用户很少的情况下,这种做法确实能跑起来

但一旦进入真实系统,你很快就会遇到:

  • 文档内容远超上下文窗口
  • 成本随着文档长度线性上涨
  • 模型抓不住重点,回答质量反而下降

这时你会意识到一个事实:

问题不在模型,而在信息投喂方式。


6.2 RAG 的本质:把“找资料”和“生成回答”拆开

RAG 的核心思想可以用一句话概括:

让系统负责“找事实”,让模型负责“说人话”。

这一步拆分,在系统设计上非常重要。

  • 检索阶段(Retrieval):

    • 关注的是:
      • 找得准不准
      • 覆盖全不全
  • 生成阶段(Generation):

    • 关注的是:
      • 表达是否清晰
      • 是否遵循约束

通过这种拆分:

模型不再被当成“知识库”,而只是一个表达引擎。


6.3 为什么要用向量检索?(系统角度的解释)

在系统设计中,“检索”并不是新问题。

你可能会问:

“我能不能直接用关键词搜索?”

在很多场景下,答案是:不够好

原因在于:

  • 用户的提问方式是多样的
  • 文档里的表述方式也是多样的
  • 关键词很难穷举所有同义表达

向量检索的价值在于:

它检索的是“语义相似度”,而不是字面匹配。

从系统角度看,这意味着:

  • 检索层对用户输入更宽容
  • 上层生成质量更稳定

6.4 RAG 的标准系统架构

从工程角度看,一个典型的 RAG 系统,可以拆成下面几个明确的组件。


组件一:文档接入与预处理

这一层的目标只有一个:

把原始资料,变成“适合被检索”的形态。

常见职责包括:

  • 文档解析(PDF / Word / HTML 等)
  • 文档清洗(去噪、去无关内容)
  • 文档切分(Chunking)

切分的设计原则是:

  • 单个 Chunk 语义相对完整
  • 不要过长,也不要过短

这是一个典型的工程取舍点


组件二:向量化与存储

这一层的职责是:

  • 把每个 Chunk 转换成向量
  • 存入向量数据库

从系统设计角度看:

  • 这是一个离线为主的过程
  • 更新频率取决于文档变化频率

一个重要认知是:

Embedding 模型的选择,直接影响检索质量上限。


组件三:查询理解与检索

当用户发起请求时,系统并不会立刻调用 LLM。

而是先:

  • 对用户问题进行向量化
  • 在向量库中检索 Top-K 相关 Chunk

这里的关键设计点包括:

  • K 取多少
  • 是否做重排序(Re-ranking)
  • 是否引入元数据过滤

这些设计,决定了:

模型“能看到哪些事实”。


组件四:上下文构建与生成

只有在完成检索之后,系统才会:

  • 把检索到的内容
  • 组织成上下文
  • 交给模型生成最终回答

这里的系统职责是:

  • 控制上下文长度
  • 明确告诉模型:

    “只能基于这些资料回答”


6.5 RAG 系统中常见的设计误区

在实际项目中,RAG 常见的失败,并不是算法问题,而是设计问题。


误区一:把 RAG 当成一次性功能

RAG 不是“接一次就完了”的功能。

随着:

  • 文档规模增长
  • 用户问题变化

你必须不断调整:

  • 切分策略
  • 检索策略
  • 上下文构建方式

误区二:过度依赖模型能力

即使在 RAG 系统中:

模型仍然不会帮你纠错。

如果检索阶段拿错了资料, 模型只会:

一本正经地基于错误资料回答。


误区三:忽视失败场景设计

一个健壮的 RAG 系统,必须考虑:

  • 检索不到相关资料怎么办
  • 检索结果冲突怎么办
  • 是否要让模型明确说明“不知道”

这些都属于系统层面的责任。


6.6 一个重要的认知升级

到这里,你应该已经意识到一件事:

RAG 的难点,不在模型,而在系统设计。

模型只是整个链路中的一环。

真正决定系统质量的,是:

  • 数据准备是否合理
  • 检索是否稳定
  • 上下文是否清晰

本章小结

这一章你需要牢牢记住的,是下面几个系统级认知:

  1. RAG 是一种系统设计模式,而不是模型技巧
  2. 核心思想是“检索”和“生成”的职责分离
  3. 系统设计决定 RAG 的效果上限
  4. 模型只负责表达,不负责保证事实正确

理解了这些内容之后,你就具备了:

设计“可信 LLM 应用”的核心能力之一。

下一章,我们将继续解决一个现实问题:

即使模型知道该做什么,它也“什么都做不了”。

这将引出另一个关键能力:

Function Calling —— 让模型参与真实系统的执行流程。


第 7 章:Function Calling —— 让模型真正参与系统执行

在上一章,我们已经解决了一个非常关键的问题:

如何让模型“基于真实资料说话”。

但当你真的把 RAG 系统接入业务后,很快又会遇到一个新的现实困境。

模型现在:

  • 能理解问题了
  • 也能给出看起来很合理的回答

但问题是:

它只能“说”,不能“做”。

这一章,我们要解决的,正是这个问题。


7.1 一个非常常见、但很快会失败的做法

很多人在第一次尝试“让模型帮我干活”时,会这样设计系统:

“我让模型输出一段 JSON, 然后我在代码里解析这个 JSON, 再根据内容去调用对应逻辑。”

在 Demo 阶段,这种方式通常可以跑起来。

但一旦进入真实系统,你会发现问题接踵而至:

  • JSON 格式不稳定
  • 字段名偶尔变化
  • 缺字段、多字段
  • 模型输出夹杂自然语言

这时你会意识到:

模型并不知道哪些输出是“必须严格遵守的”。


7.2 Function Calling 要解决的核心问题

Function Calling 并不是为了“让模型会写代码”。

它真正解决的是一个系统层面的问题:

如何让模型在“自然语言理解”和“系统能力调用”之间, 建立一个稳定、可控的接口。

换句话说:

  • 模型负责:

    • 理解用户意图
    • 决定“要做什么”
  • 系统负责:

    • 提供“能做什么”
    • 执行具体逻辑

这是一次非常重要的职责划分。


7.3 从系统角度看 Function Calling 的工作方式

站在系统设计视角,Function Calling 可以被理解为:

模型参与决策,但不直接参与执行。

一个典型流程如下:

  1. 系统向模型声明:

    • 当前有哪些函数(工具)可用
    • 每个函数的用途和参数结构
  2. 模型根据用户输入:

    • 判断是否需要调用函数
    • 以及应该调用哪一个
  3. 系统接管执行:

    • 校验参数
    • 调用真实业务逻辑
  4. 执行结果再回传给模型,用于后续回答

在这个过程中:

模型从“输出文本”,升级为“输出结构化决策”。


7.4 为什么说 Function Calling 是“工程能力”,不是模型能力

一个非常容易被忽略的点是:

Function Calling 的可靠性,主要由系统保证,而不是模型保证。

即使模型判断错了:

  • 参数缺失
  • 类型不对
  • 调用了不该调用的函数

系统仍然可以:

  • 拒绝执行
  • 返回错误信息
  • 要求模型重新决策

这意味着:

风险被牢牢控制在系统边界内。


7.5 Function Calling 在系统中的典型使用场景

当你理解了它的系统定位,就会发现 Function Calling 非常适合下面这些场景。


场景一:查询类操作

例如:

  • 查询订单状态
  • 查询库存信息
  • 查询用户配置

模型负责理解“查什么”, 系统负责返回真实数据。


场景二:状态变更类操作

例如:

  • 创建订单
  • 修改配置
  • 提交审批

这里尤其重要的一点是:

模型永远不应该直接修改数据。

它只能“请求”, 是否执行,完全由系统决定。


场景三:多步流程中的一步

在更复杂的系统中:

  • 模型并不一次完成所有事情
  • 而是参与多个步骤的决策

这为后面引入 Agent 打下基础。


7.6 Function Calling 的设计边界

理解边界,比理解能力更重要。

在系统设计中,你需要明确:

  • 哪些能力可以暴露给模型
  • 哪些能力必须完全禁止

一个基本原则是:

模型只能调用“可逆、可校验、可回滚”的操作。

任何:

  • 高风险
  • 不可恢复
  • 责任重大的操作

都不应该直接暴露给模型。


7.7 一个重要的系统认知升级

到这里,你应该已经意识到:

Function Calling 不是让模型更聪明, 而是让系统更安全。

它的价值不在于:

  • 模型能做更多事

而在于:

  • 模型被限制在正确的位置上
  • 系统始终掌握最终控制权

本章小结

在这一章中,你需要记住下面几个关键点:

  1. 模型只能理解意图,不能直接执行
  2. Function Calling 是模型与系统之间的“安全接口”
  3. 决策与执行必须严格分离
  4. 风险控制是系统设计的首要目标

理解了这些内容之后,你已经具备了:

设计“可控 LLM 系统”的关键能力。

下一章,我们将把前面所有能力组合起来,回答一个常被过度神话的问题:

什么时候,我们才真的需要 Agent?


第 8 章:Agent 的工程化理解 —— 去神话版

在前面的章节中,我们已经一步步为模型补齐了能力:

  • 第 5 章:认识到模型会幻觉,不能被直接信任
  • 第 6 章:通过 RAG,让模型基于真实资料回答
  • 第 7 章:通过 Function Calling,让模型参与系统决策

到这里,很多人会自然地产生一个想法:

“那是不是把这些东西一组合,我就有了一个 Agent?”

答案是:

是的,但也正因为如此,Agent 并不神秘。

这一章,我们就从工程角度,把 Agent 这件事彻底讲清楚。


8.1 先回答一个尖锐的问题:你真的需要 Agent 吗?

在技术社区中,Agent 往往被描述成:

  • 能自主思考
  • 能自动规划
  • 能持续执行任务

听起来非常强大。

但在真实工程中,你需要先冷静下来问一句:

我的系统,真的需要这种复杂度吗?

因为 Agent 带来的,不只是能力提升,还有:

  • 系统复杂度指数级上升
  • 调试和测试难度显著增加
  • 不确定性扩大

一个非常务实的判断标准是:

如果一个请求,在一次 RAG + Function Calling 中就能解决, 那你根本不需要 Agent。


8.2 Agent 并不是一个新能力

一个常见误解是:

“Agent 是模型的新能力。”

但从工程角度看,这并不准确。

事实上,一个 Agent 通常只是下面这些能力的组合:

  • Prompt(任务描述)
  • 上下文管理(状态)
  • RAG(事实获取)
  • Function Calling(工具调用)
  • 一个控制循环(Loop)

也就是说:

Agent 的核心,不在模型,而在“控制逻辑”。


8.3 控制循环:Agent 真正的核心

如果一定要用一句话来定义 Agent:

Agent = 一个带状态的、可重复执行的决策循环。

一个最简化的 Agent 循环,可以抽象为:

  1. 读取当前状态
  2. 决定下一步要做什么
  3. 调用工具或获取信息
  4. 更新状态
  5. 判断是否结束,否则继续

注意这里的关键点:

循环是由系统控制的,而不是模型。

模型只是参与每一轮的“决策建议”。


8.4 为什么 Agent 必须由系统“牵着走”

很多失败的 Agent 实验,问题都出在这里:

  • 把过多控制权交给模型
  • 让模型自己决定是否继续

这在工程上是非常危险的。

一个健壮的 Agent 系统,通常会明确限制:

  • 最大执行步数
  • 每一步允许调用的工具
  • 明确的终止条件

这些限制的目的只有一个:

把不确定性关进笼子里。


8.5 Agent 适合解决什么问题?

在真实系统中,Agent 并不是“通用解法”。

它更适合下面这些场景:

  • 需要多步推理和多次信息获取
  • 步骤之间存在依赖关系
  • 中间结果会影响后续决策

例如:

  • 复杂任务拆解与执行
  • 多工具协作的问题解决
  • 长时间运行的辅助流程

而不适合:

  • 简单问答
  • 单步决策
  • 强一致性、高风险操作

8.6 一个现实的工程建议

如果你是第一次在项目中引入 Agent,我给你一个非常保守但实用的建议:

先把 Agent 当成“可多次调用的流程编排器”, 而不是“自主智能体”。

从工程视角看,这会让你:

  • 更容易调试
  • 更容易监控
  • 更容易回滚

本章小结

在这一章中,你需要牢牢记住下面几点:

  1. Agent 并不神秘,它是能力的组合
  2. 控制循环才是 Agent 的核心
  3. 系统必须始终掌握控制权
  4. 大多数场景下,你并不需要 Agent

如果你能带着这些认知去使用 Agent,那么它会成为:

一个强大但可控的工程工具。

而不是一个不可预测的风险源。


全文总结:从“用模型”到“设计系统”

写给工程师的 LLM 使用宣言

如果你完整读完了这套教程,我希望你至少记住一件事:

大语言模型不是一个可以被“信任”的系统组件。

它不保证正确性, 不理解后果, 也不会为错误负责。

真正可靠的,从来不是模型本身,而是你设计的系统。


不要问“模型还能多聪明”

在工程实践中,一个更有价值的问题是:

哪些不确定性,必须被系统兜住?

  • 用 Prompt 约束表达
  • 用 RAG 约束事实
  • 用 Function Calling 约束执行
  • 用 Agent 的控制循环约束流程

这不是限制模型,而是保护系统。


大多数失败,都不是模型失败

如果一个 LLM 应用:

  • 经常胡说八道
  • 行为不可预测
  • 出问题后无法追踪

那么问题通常不在模型,而在于:

系统把不该交给模型的责任,交给了模型。


成熟的 LLM 工程,是“去魔法化”的过程

当你不再期待模型“自动搞定一切”, 而是:

  • 明确边界
  • 明确职责
  • 明确失败路径

你会发现,LLM 反而变得可靠起来。


最后一句话

如果你想长期做 LLM 应用开发,请记住这句话:

不是让模型变强,而是让系统变稳。

只要你坚持这个原则, 无论技术如何变化,你都站在正确的一边。

Loading...