0. 前言
你在一家教育内容开发公司工作,随着新员工的持续加入,频繁的答疑需求导致了显著的时间与资源成本。你决定使用大模型技术来构建一个答疑机器人,提升答疑的准确率与效率。
1. 通过API调用大模型
通过API调用都需要一个秘钥, 可以在模型厂商官网申请
1.1 基础对话调用
先来尝试一个简单的对话。下面的代码创建了一个名为“公司小蜜”的助手,它可以回答关于公司运营的问题。你可以使用“选择项目管理工具”这个常见问题作为示例:
from openai import OpenAI
import os
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"), # 秘钥
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
def get_qwen_response(prompt):
response = client.chat.completions.create(
model="qwen-max",
messages=[
# system message 用于设置大模型的角色和任务
{"role": "system", "content": "你负责教育内容开发公司的答疑,你的名字叫公司小蜜,你要回答同事们的问题。"},
# user message 用于输入用户的问题
{"role": "user", "content": prompt}
]
)
return response.choices[0].message.content
response = get_qwen_response("我们公司项目管理应该用什么工具")
print(response)
1.2 多轮对话
在实际应用中,用户往往不会一次性提出完整的问题。想象一下这个场景:一位新员工先问「我们公司用什么项目管理工具」,得到回答后又接着问「那怎么申请账号呢」。如果大模型无法「记住」之前的对话,它就不知道用户问的是哪个工具的账号申请流程。
这就是多轮对话的意义所在:让大模型能够参考历史对话信息,理解上下文关联,从而给出连贯、准确的回复。
多轮对话的工作原理
在 OpenAI SDK 中,实现多轮对话的关键在于 messages 参数。这是一个列表,包含了对话的完整历史记录。每条消息都有两个关键字段:
- role:消息的角色,可以是
system(系统指令)、user(用户输入)或assistant(模型回复) - content:消息的具体内容
大模型会根据 messages 列表中的所有消息来生成回复,因此你需要将之前的对话历史(包括用户问题和模型回答)都保存在这个列表中。
def multi_turn_chat():
# 初始化对话历史,包含系统提示词
conversation_history = [
{"role": "system", "content": "你负责教育内容开发公司的答疑,你的名字叫公司小蜜,你要回答同事们的问题。"}
]
# 模拟多轮对话
user_questions = [
"我们公司项目管理应该用什么工具?",
"那我怎么申请这个工具的账号呢?",
"申请一般需要多久能批下来?"
]
for question in user_questions:
print(f"👤 用户:{question}")
# 将用户问题添加到对话历史
conversation_history.append({"role": "user", "content": question})
# 调用大模型,传入完整的对话历史
response = client.chat.completions.create(
model="qwen-max",
messages=conversation_history # 包含所有历史消息
)
# 获取模型回复
assistant_message = response.choices[0].message.content
print(f"🤖 小蜜:{assistant_message}\n")
# 将模型回复也添加到对话历史,以便下一轮对话使用
conversation_history.append({"role": "assistant", "content": assistant_message})
multi_turn_chat()
运行上面的代码,你会发现大模型能够理解"这个工具"指的是之前提到的项目管理工具,并针对性地回答账号申请和审批流程的问题。这就是多轮对话的作用。
简单来说, 就是我们每次对话, 都需要把以往的对话内容统统给大模型, 这样它就会理解上下文
小贴士:
- 对话历史会占用上下文窗口的空间。如果对话轮次过多,可能需要对历史消息进行截断或摘要处理。
- 在生产环境中,通常需要将对话历史持久化存储(如数据库),以支持用户跨会话的连续对话体验。
1. 3 流式输出
运行上面的代码后,你会注意到需要等待一段时间(约20秒)才能看到完整的回复。这是因为默认情况下,API 会等待模型生成完所有内容后才一次性返回结果。在实际应用中,这种等待可能会影响用户体验 —— 想象一下用户盯着一个空白界面等待20秒的场景!
幸运的是,你可以使用"流式输出"(Streaming)来优化这个问题。使用流式输出时,模型会像人类打字一样,一边思考一边输出,让用户能够立即看到部分回复,大大提升交互体验。接下来,看看如何实现流式输出…
💡 小贴士:流式输出只是改变了内容的展示方式,模型的思考过程和最终答案的质量都保持不变。你可以放心使用这个功能来优化你的应用体验。
要实现流式输出,只需在之前的代码基础上添加 stream=True 参数,并调整输出方式:
def get_qwen_stream_response(user_prompt,system_prompt):
response = client.chat.completions.create(
model="qwen-max",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
stream=True
)
for chunk in response:
yield chunk.choices[0].delta.content
response = get_qwen_stream_response(user_prompt="我们公司项目管理应该用什么工具",system_prompt="你负责教育内容开发公司的答疑,你的名字叫公司小蜜,你要回答同事们的问题。")
for chunk in response:
print(chunk, end="")
2. 大模型是如何工作的
近几十年来,人工智能经历了从基础算法到生成式AI的深刻演变。生成式AI通过学习大量数据可以创造出全新的内容,如文本、图像、音频和视频,这极大地推动了AI技术的广泛应用。常见的应用场景包括智能问答(如千问、GPT)、创意作画(如Stable Diffusion)以及代码生成(如灵码)等,涵盖了各个领域,让AI触手可及。

智能问答作为大模型最经典且广泛的应用之一,是我们探索大模型工作机制的最佳范例。接下来将介绍大模型在问答场景中的工作流程,帮助你更深入地理解其背后的技术原理。
2.1. 大模型的文本生成工作流程
让我们以一个简单的输入"ACP is a very"为例,来具体看看大模型是如何一步步处理这个输入,并最终生成完整句子的。

大模型的文本生成流程可以分为文本分词、Token 向量化、大模型推理、解码与自回归、输出文本五个阶段:

第一阶段:文本分词
计算机无法直接理解人类的文字。因此,第一步需要将我们输入的文本“ACP is a very”转换成计算机能够处理的数字格式。这个过程被称为Tokenization(分词)。
Token 是分词器(Tokenizer)把文本编码后得到的基本单元,每个 token 对应词表中的一个整数 ID。需要注意的是,token 通常是子词片段或字符片段,不一定等于一个完整的"词",也不一定具有独立语义。比如英文单词可能被拆成词根和后缀,中文可能按字或常见词组切分,空格和标点也可能被编码进 token。
以"ACP is a very"为例,Tokenizer 会将其编码成若干 token,并为每个 token 分配一个 ID。如果你对千问(Qwen)的分词细节感兴趣,可以查阅其官方文档:Tokenization 说明 。
第二阶段:Token 向量化
虽然我们得到了数字 ID 序列,但这些 ID 本身的数值大小并没有实际意义(比如,ID 500 并不意味着它在语义上就比 ID 50 更“重要”或“相关”)。为了让模型理解 token 的真实含义,我们需要将这些离散的 ID 转换成包含丰富语义信息的数学表示——向量 (Vector)。
这个转换通过一个被称为 Embedding 矩阵 的巨大表格完成。简单来说,就是用 token ID 在这个大表格里“查表”,取出对应的那一整行向量。每个 token 的向量都包含了其在多维度语义空间中的坐标。
此外,语言的顺序至关重要(“我帮你”和“你帮我”截然不同)。但基础的 Transformer 模型本身并不直接处理时序信息。因此,还需要额外给每个 token 向量添加位置信息 (Positional Encoding),让模型能够区分出 “我” 是第一个词,“你” 是第三个词,以此类推。
第三阶段:大模型推理
现在,携带了语义和位置信息的向量序列被送入 Transformer 模型的解码器层 (Decoder Blocks) 进行计算。这个过程被称为前向计算 (Forward Pass)。
向量序列会逐层穿过数十个甚至上百个结构相似的解码器层,在因果自注意力机制 (Causal Self-Attention) 和前馈神经网络 (Feed-Forward Network) 的作用下,文本向量中的信息被一次一次压缩和提取。我们最终会得到最后一个 token(也就是 “very”)所对应的隐藏状态向量 (Hidden State)。这个向量可以被认为是模型在阅读了 “ACP is a very” 之后,对接下来可能出现的内容的一个高度浓缩的“思考总结”。最后,这个“思考总结”向量会通过一个线性投影层,映射到整个词汇表的维度,得到一个庞大的分数向量 (Logits)。
第四阶段:解码与自回归
在得到 logits 之后,大模型还需要经过 softmax 函数计算,将这些 logits 重新映射为一种概率分布 ,表示每个 token 被选中的概率。最后,大模型会根据一个解码策略来决定最终输出哪个 token。解码策略主要分为两类:
- 近似确定性解码:如贪心解码(Greedy Decoding),每次选概率最高的 token,或 Beam Search 保留多个候选路径。
- 随机采样解码:如 Top-p(Nucleus Sampling)、Top-k Sampling,从高概率的候选集合中随机抽取。
解码策略详解请见 «prompt Engineer»
很多在线服务默认使用随机采样解码,因此即使输入完全相同,也可能出现"回答略有不同"的现象。
通过调整 temperature,你可以改变 softmax 输出概率分布的"尖锐程度"。temperature 越低,token 之间的概率差异越大,概率分布越集中于少数的高概率 token,模型输出越确定。temperature 越高,输出概率分布越平坦,模型的输出多样性越高。配合 top_p(控制参与采样的候选集合范围)等参数,你可以在模型输出的多样性与稳定性之间做权衡。你可以在下一小节中进一步了解这些参数。
例如,下图展示了模型预测出的部分候选 token 及其概率(实际词汇表远比这大得多):

一旦选定了下一个 token(比如模型选择了 “informative”),这个新生成的 token 就会被追加 (append) 到原始输入序列的末尾,形成新的输入 “ACP is a very informative”。然后,模型会基于这个新序列,重复第三和第四阶段,继续预测下一个 token。这个"文字接龙"的过程称为自回归生成(Autoregressive Generation)。
模型的自回归循环会持续进行,直到满足某个停止条件:
- 生成了特殊的终止符 (End-of-Sequence, EOS token)。
- 达到了预设的最大生成长度限制。
- 生成了用户指定的停用词序列。
不同模型系列的终止符不同。
| 模型 | 终止符 |
|---|---|
| Deepseek V3.2 | <|end▁of▁sentence|> |
| GPT 系列 | <|endoftext|> |
| GPT-OSS | <|return|> |
| LLaMA/Mistral | < /s> |
| Pad Token | <|endoftext|> |
| Qwen3 | <|im_end|> |
第五阶段:输出文本
最后,系统会将整个生成过程中的 token ID 序列转换回人类可读的字符串,并呈现给我们。
我们经常看到的流式输出 (Streaming) 效果,其实是服务端每生成一个或几个 token,就立刻将其解码并增量地发送到你的界面上。由于 token 可能是子词片段,所以在流式显示时,有时会看到一个词被分成几部分输出,或者空格的出现时机看起来有些奇怪,这都是正常现象。
2.2 影响大模型内容生成的随机性参数
假设在一个对话问答场景中,用户提问为:“在大模型ACP课程中,你可以学习什么?”。为了模拟大模型生成内容的过程,我们预设了一个候选Token集合,这些Token分别为:“RAG”、“提示词”、“模型”、“写作”、“画画”。大模型会从这5个候选Token中选择一个作为结果输出(next-token),如下所示。
用户提问:在大模型ACP课程中,你可以学习什么?
大模型回答:RAG
在这个过程中,有两个重要参数会影响大模型的输出:temperature 和 top_p,它们用来控制大模型生成内容的随机性和多样性。下面介绍这两个参数的工作原理和使用方式。
2.2.1 temperature:调整候选Token集合的概率分布
在大模型生成下一个词(next-token)之前,它会先为候选Token计算一个初始概率分布。这个分布表示每个候选Token作为next-token的概率。temperature(温度)是一个调节器,它通过改变候选Token的概率分布,影响大模型的内容生成。通过调节这个参数,你可以灵活地控制生成文本的多样性和创造性。
为了更直观地理解,下图展示了不同temperature值对候选Token概率分布的影响。

图中的低、中、高温度基于Qwen-Max模型的范围[0, 2)划分。
由上图可知,温度从低到高(0.1 -> 0.7 -> 1.2),概率分布从陡峭趋于平滑,候选Token“RAG”从出现的概率从0.8 -> 0.6 -> 0.3,虽然依然是出现概率最高的,但是已经和其它的候选Token概率接近了,最终输出也会从相对固定到逐渐多样化。
针对不同使用场景,可参考以下建议设置 temperature 参数:
- 明确答案(如生成代码):调低温度。
- 创意多样(如广告文案):调高温度。
- 无特殊需求:使用默认温度(通常为中温度范围)。
需要注意的是,当 temperature=0 时,虽然会最大限度降低随机性,但无法保证每次输出完全一致。如果想深入了解,可查阅 temperature的底层算法实现 。
简单理解就是: 温度越低, 模型越冷静,输出的结果就越明确; 温度越高,模型越沸腾, 输出的结果可能性越多
2.2.2 top_p:控制候选Token集合的采样范围
top_p 是一种筛选机制,用于从候选 Token 集合中选出符合特定条件的“小集合”。具体方法是:按概率从高到低排序,选取累计概率达到设定阈值的 Token 组成新的候选集合,从而缩小选择范围。
下图展示了不同top_p值对候选Token集合的采样效果。

图示中蓝色部分表示累计概率达到top_p阈值(如0.5或0.8)的Token,它们组成新的候选集合;灰色部分则是未被选中的Token。
如图,当top_p=0.5时,模型优先选择最高概率的Token,即“RAG”;而当top_p=0.8时,模型会在“RAG”、“提示词”、“模型”这三个Token中随机选择一个生成输出。
由此可见,top_p值对大模型生成内容的影响可总结为:
- 值越大 :候选范围越广,内容更多样化,适合创意写作、诗歌生成等场景。
- 值越小 :候选范围越窄,输出更稳定,适合新闻初稿、代码生成等需要明确答案的场景。
- 极小值(如 0.0001):理论上模型只选择概率最高的 Token,输出非常稳定。但实际上,由于分布式系统、模型输出的额外调整等因素可能引入的微小随机性,仍无法保证每次输出完全一致。
2.2.3 小结
是否需要同时调整temperature和top_p?
为了确保生成内容的可控性,建议不要同时调整top_p和temperature,同时调整可能导致输出结果不可预测。你可以优先调整其中一种参数,观察其对结果的影响,再逐步微调。
设置 temperature、top_p、seed 控制大模型输出,为何仍存在随机性?
即使将 temperature 设置为 0、top_p 设置为极小值(如 0.0001),,同一个问题的生成结果仍可能出现不一致。这是因为一些复杂因素可能引入微小的随机性,例如大模型运行在分布式系统中,或模型输出引入了优化。
举个例子: 分布式系统就像用不同的机器切面包。虽然每台机器都按照相同的设置操作,但由于设备之间的细微差异,切出来的面包片可能还是会略有不同。
3. 让大模型能够回答私域知识问题
回到最初的挑战:答疑机器人无法回答“我们公司项目管理用什么工具”这类内部问题。根本原因在于,大模型的知识来源于其训练数据,这些数据通常是公开的互联网信息,不包含任何特定公司的内部文档、政策或流程。
你可以把大模型想象成一台刚出厂的超级计算机:它的 CPU(推理能力)极其强大,硬盘(模型权重)里也预装了海量的通用知识。但对于你公司的“内部资料”,它的硬盘里是空白的。
面对这个问题,最直观的解决思路就是:在运行时,把公司的内部知识临时告诉它。
3.1 初步方案:在提示词中“喂”入知识
你可以来验证这个思路:将公司项目管理工具的说明文档,直接添加到给模型的指令(System Prompt)中,作为背景知识提供给它。
user_question = "我是软件一组的,请问项目管理应该用什么工具"
knowledge = """公司项目管理工具有两种选择:
1. **Jira**:对于软件开发团队来说,Jira 是一个非常强大的工具,支持敏捷开发方法,如Scrum和Kanban。它提供了丰富的功能,包括问题跟踪、时间跟踪等。
2. **Microsoft Project**:对于大型企业或复杂项目,Microsoft Project 提供了详细的计划制定、资源分配和成本控制等功能。它更适合那些需要严格控制项目时间和成本的场景。
在一般情况下请使用Microsoft Project,公司购买了完整的许可证。软件研发一组、三组和四组正在使用Jira,计划于2026年之前逐步切换至Microsoft Project。
"""
response = get_qwen_stream_response(
user_prompt=user_question,
# 将公司项目管理工具相关的知识作为背景信息传入系统提示词
system_prompt="你负责教育内容开发公司的答疑,你的名字叫公司小蜜,你要回答学员的问题。"+ knowledge,
temperature=0.7,
top_p=0.8
)
for chunk in response:
print(chunk, end="")
在提示词中加入相关的背景知识,大模型确实能够准确回答关于公司内部工具的问题。这个发现令人振奋,似乎你已经找到了解决私域知识问答的钥匙。
然而,当你试图将更多的公司文档(例如几十页的员工手册、上百页的技术规范)都用这种方式“喂”给大模型时,一个新的、更严峻的挑战出现了。
3.2 核心瓶颈:有限的上下文窗口
大模型接收我们输入(包括指令、问题和背景知识)的地方,被称为上下文窗口(Context Window)。你可以把它理解为计算机的“内存(RAM)”——它的容量是有限的。你无法将整个公司的知识库(成百上千份文档)一次性塞进这个有限的窗口里。一旦输入内容超过模型的最大限制,就会导致错误。这引出了一个核心问题:你需要对放入上下文窗口的内容进行筛选和管理。
3.3 解决之道:上下文工程 (Context Engineering)
简单粗暴地将信息塞进上下文,除了会超出窗口限制外,还会带来一系列“隐性”问题:
- 效率低:上下文越长,大模型处理所需的时间就越长,导致用户等待时间增加。
- 成本高:大部分模型是按输入和输出的文本量计费的,冗长的上下文意味着更高的成本。
- 信息干扰:如果上下文中包含了大量与当前问题无关的信息,就像在开卷考试时给了考生一本错误科目的教科书,反而会干扰模型的判断,导致回答质量下降。
你一定会意识到,成功的关键不在于“喂”给模型多少知识,而在于“喂”得有多准。
如何在正确的时间,将最相关、最精准的知识,动态地加载到大模型有限的上下文窗口中?——这门系统性地设计、构建和优化上下文的实践,就是上下文工程(Context Engineering)。
从这个角度看,许多大模型应用的失败,并非模型本身不够智能,而是“上下文”的失败。上下文工程正是释放大模型潜力的关键所在。
那么,上下文工程具体包含哪些技术呢?你可以通过下面这张概念图来快速了解它的核心版图:
上下文工程 (Context Engineering) 的核心技术
它是构建可靠、高效大模型应用的一系列关键技术的总和,主要包括:
- RAG (检索增强生成):从外部知识库(如公司文档)中检索信息,为模型提供精准的回答依据。
- Prompt (提示词工程):通过精心设计的指令,精确地引导模型的思考方式和输出格式。
- Tool (工具使用):赋予模型调用外部工具(如计算器、搜索引擎、API)的能力,以获取实时信息或执行特定任务。
- Memory (记忆机制):为模型建立长短期记忆,使其能够在连续对话中理解历史上下文。
你可能已经注意到,这里出现了一些新名词。别担心,你不需要立刻掌握所有细节。这里目的是为了让你建立一个整体认知:接下来要学习的几个知识,都是上下文工程的一部分。
为了让你更容易理解和吸收,在后续的课程中,这些概念将作为独立的主题,逐一详细拆解和实践。
现在,你需要聚焦于当前最紧迫的问题——如何解决私域知识问答。在这个技术版图中,RAG 是最直接、最有效的解决方案。接下来,你将深入学习它。
3.4 技术方案:RAG(检索增强生成)
RAG(Retrieval-Augmented Generation,检索增强生成) 就是实现上下文工程的强大技术方案。它的核心思想是:
在用户提问时,不再将全部知识库硬塞给大模型,而是先自动检索出与问题最相关的私有知识片段,然后将这些精准的片段与用户问题合并后,一同传给大模型,从而生成最终的答案。这样既避免了提示词过长的问题,又能确保大模型获得相关的背景信息。
构建一个 RAG 应用通常会分为两个阶段
3.4.1 第一阶段:建立索引

建立索引是为了将私有知识文档或片段转换为可以高效检索的形式。通过将文件内容分割并转化为多维向量(使用专用 Embedding 模型),并结合向量存储保留文本的语义信息,方便进行相似度计算。向量化使得模型能够高效检索和匹配相关内容,特别是在处理大规模知识库时,显著提高了查询的准确性和响应速度。
这些向量经过 Embedding 模型处理后不仅很好地捕捉文本内容的语义信息,而且由于语义已经向量化,标准化,便于之后与检索语义向量进行相关度计算。
3.4.2 第二阶段:检索与生成

检索生成是根据用户的提问,从索引中检索相关的文档片段,这些片段会与提问一起输入到大模型生成最终的回答。这样大模型就能够回答私有知识问题了。
总的来说,基于 RAG 结构的应用,既避免了将整个参考文档作为背景信息输入而导致的各种问题,又通过检索提取出了与问题最相关的部分,从而提高了大模型输出的准确性与相关性。
4. 本节小结
在本节课程中,我们学习了以下内容:
如何使用大模型 API
通过实际的代码示例,我们掌握了如何通过 API 调用千问大模型,并学习了如何通过流式输出来优化用户体验。
大模型的工作原理与局限性
我们初步探索了大模型如“黑盒”般的工作流程,理解了其回答具有随机性的原因,并学习了如何通过调整 temperature 和 top_p 等参数来控制生成内容。更重要的是,我们直面了大模型的核心局限:其知识完全依赖于训练数据,无法获知你公司的内部文档等私域知识。
上下文工程(Context Engineering)与 RAG 的核心思想
为了解决大模型的知识局限性,我们首先尝试了直接在提示词中“投喂”知识的方法,但很快就遇到了“上下文窗口限制”的瓶颈。这引出了一个至关重要的概念——上下文工程(Context Engineering),即智能地为大模型筛选、组织和提供最相关背景信息的核心方法论。作为该思想的具体实现,我们学习了 RAG(检索增强生成) 技术方案。通过“先检索、后生成”的模式,RAG 能有效地将精准的外部知识与大模型结合,从而在不超出限制的前提下,让模型能够回答私域知识问题。
除了本节课程中的示例展示的任务之外,你还可以让大模型完成更多类型的任务,如内容生成、结构化信息提取、文本分类、情感分析等。同时在你的大模型应用中引入 RAG 方案能够扩展大模型所能处理的知识范围。
有些模型支持网络搜索的参数,这个参数可以让大模型在生成回答时利用互联网搜索结果来丰富其回复内容。也是利用RAG思想