Skip to content

LLM 接口与提示词工程 ​

这是整个系列的地基。你会发现,后面 RAG、Function Calling、Agent、多 Agent……所有复杂的东西,本质都是在这一篇的几个基础概念上做乘法:messages、Token、流式、提示词。

这一篇回答一个核心问题:怎么跟大模型「正确地对上话」。 很多人一上来就去记一堆 API 参数,却没先搞清楚 LLM 的本质。读完你会发现,只要把「LLM 到底是什么」这一件事想透,后面所有的参数、套路、优化,都会自动串成一条线。


1. LLM 是什么:一个「按字收费的远程打字员」 ​

在讨论任何 API、任何参数、任何框架之前,先把 LLM 的本质画像立起来。用你熟悉的场景打个比方:

LLM ≈ 一个按字计费的远程打字员

  • 它文化很好(预训练时灌了海量语料),但完全没记性——每次都是全新雇佣,必须把背景重新交代一遍;
  • 它按字双向收费(Token 计价),你发的和它回的都算钱;
  • 它有精力上限(上下文窗口),交代的资料太长它直接罢工;
  • 它只会打字,不会做事——除非你额外给它工具和「操作手册」(这是后面 Function Calling 的伏笔)。

把这四条记牢:后面所有的参数、套路、成本优化,都只是在解决这四条限制里的某一条。 这是本篇的「总纲」。

1.1 一个必须立刻建立的认知:记忆不在模型侧 ​

这可能是整个系列最重要的一个底层认知,值得单独强调:

模型本身是无状态的,每一次调用都是一个纯函数。 它「记得」上一句,不是因为它有记忆,而是你把完整的对话历史原样发过去,它读一遍,再续写一句。

python
# 你以为:模型"记住"了对话
# 真相:每次都把完整历史重新发一遍
messages = [
    {"role": "user",      "content": "我养了只猫"},
    {"role": "assistant", "content": "真可爱,什么品种?"},
    {"role": "user",      "content": "它叫什么名字好?"},   # ← 前两句被原样重放,模型才"接得上"
]

这个认知,是后面「多轮对话失忆」「上下文截断」「Memory 机制」(08 篇)一切现象的根因。搞不懂它,后面那些「为什么对话突然失忆」的现象你就永远解释不通。


2. 为什么需要「提示词工程」 ​

2.1 一个新手 100% 会遇到的挫败 ​

很多人第一次调 LLM 的反应是:「我写了一句『帮我写代码』,它回了一堆我根本用不上的东西。」

这不是模型不行,是你交给它的「交接文档」写得烂。对比两种写法:

写法输出工程属性
❌ 帮我写代码语言不定、功能随意、结构漂移不可控,没法复用
✅ 你是资深后端工程师。用 Python 写一个查询订单的 REST 接口,输入订单号,返回 JSON(含 id/金额/状态)。只输出代码,不解释。可直接运行可控,可模板化

2.2 好的 Prompt 里藏着五个维度的约束 ​

拆开那个「✅」的写法,你会发现它精确地锁住了五个维度:

  1. 角色(资深后端工程师)——定回答口径和视角;
  2. 技术栈(Python)——定实现语言;
  3. 输入输出契约(订单号 → JSON)——定做什么;
  4. 字段 schema(id/金额/状态)——定结果的精确结构;
  5. 格式要求(只输出代码,不解释)——定输出形式。

一句话记住这件事的定位,非常有用:

把 Prompt 当成「调用对方的接口文档」来写——参数、返回值、约束一样都不能少。

你写接口文档越规范,下游越不会来回找你对;你写 Prompt 越结构化,模型越不会跑偏。提示词工程,本质上就是「软件工程思维」在自然语言上的应用。


3. 核心协议:messages 契约 ​

这是本篇的技术骨干。掌握了它,你就拿到了国内外几乎所有主流大模型的通用钥匙。

3.1 为什么所有国产大模型「都像 OpenAI」 ​

2023 年 OpenAI 的 /v1/chat/completions 一声不响地成了事实标准。今天你能叫得上名字的厂商——DeepSeek、通义、智谱、混元、月之暗面——几乎都提供了「OpenAI 兼容」端点。

这件事对你的含义就一句话:同一份调用代码,只要换 base_url 和 api_key 就能切换模型。 这个兼容性,是你以后做「多模型路由」「成本优化」的基石——也解释了为什么 05 篇的框架能「改一行配置就换模型」。

3.2 messages 的结构 ​

一个完整的请求体长这样(Python 示意):

python
messages = [
    {"role": "system",    "content": "你是一位资深架构师,回答精炼,只给结论不废话。"},
    {"role": "user",      "content": "为什么会有循环依赖?"},
    {"role": "assistant", "content": "因为两个 Bean 互相依赖,实例化时……"},
    {"role": "user",      "content": "那怎么解决?"},
]

关键点:messages 是一个按时序排列的数组,不是某个「会话对象」。每次请求都要把完整历史重放(呼应 1.1 的无状态)。四种角色:

角色含义优先级
system人设、规则、约束最高(但非铁律,见下)
user用户真实输入—
assistant上一轮的模型回答由客户端原样重放
tool工具调用结果配合 Function Calling 用

一个必须警惕的事实:system 优先级高,但不是铁律——恶意构造的 user 内容仍然可能诱导模型违反 system(这就是后面的「提示注入攻击」,10 篇会专门讲)。所以安全边界永远不能只押在 system 一句话上,关键动作要在代码侧二次校验。

3.3 一次最小调用 ​

python
import httpx

def chat(system_prompt: str, question: str, model: str = "deepseek-chat"):
    resp = httpx.post(
        "https://api.deepseek.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {KEY}"},
        json={
            "model": model,
            "messages": [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": question},
            ],
            "temperature": 0.2,
        },
    )
    return resp.json()["choices"][0]["message"]["content"]

这段代码里没有「大模型魔法」——就是一个 HTTPS POST + JSON。你要清楚一件事:LangChain、任何框架,底层干的就是这件事,它们只是加了一层「类型安全的外壳」。 想透这一层,你往后用任何框架都不会「被框架糊弄」,而是清楚地知道它在帮你发哪个请求。

3.4 几个关键参数 ​

只讲你一定会用到的几个,其余用到再查:

参数作用建议
temperature随机性 0~2代码/结构化场景设低(0~0.2);文案设中高(0.7~1.0)
max_tokens回复长度上限设太小会被硬截断(不是优雅结束)
stream是否流式交互场景开,批处理关

一个反直觉的坑:temperature=0 也不是严格确定的(浮点采样仍有扰动),要追求可复现还得配合 seed,且仍只是「近似」。别指望 temperature=0 给你「绝对可复现」。


4. 提示词工程的 4 个套路 ​

「提示词工程」听着玄,其实就是 4 个可模板化的套路。它们不是「文采」,是「工程技巧」。

4.1 角色锚定 ​

用 system 把「回答口径」锁死:

txt
system: 你是资深架构师,只用中文,只给结论和关键代码,不废话。

作用:让模型在整段对话里,稳定地保持一个「人设」和一个「口径」,避免输出漂移。

4.2 Few-shot:给例子比讲规则更有效 ​

这是最反直觉但最有效的一招。要模型稳定输出「分类标签」,与其反复强调「只输出标签」,不如给它看 3 个例子:

txt
system: 把问题分类为 BUG / FEATURE / QUESTION,只输出分类。

示例:
  输入:登录后 500 报错  → BUG
  输入:希望支持钉钉登录 → FEATURE
  输入:怎么改密码?     → QUESTION

user: 登录按钮点不动了

模型输出:BUG(稳稳的)。

原理:LLM 本质是「续写」,给它看了 3 条「输入→输出」的模式,它会强烈倾向续写同样的模式。需要格式稳定的场景,优先用 Few-shot,而不是拼命强调规则。 因为「例子」比「规则」对模型更有约束力。

4.3 结构化输出 ​

别让模型返回自然语言再费劲解析,直接要求 JSON:

txt
system: 抽取订单信息,只返回 JSON,字段 {orderId, amount, status},不要解释。

再配合请求参数 response_format={"type": "json_object"} 开启 JSON 模式,保证一定是合法 JSON。

这招的意义在于:把「不可控的自然语言」变成「可解析的结构化数据」,让下游程序能稳定消费模型的输出。这是「模型输出 → 系统输入」的关键一环。

4.4 思维链(CoT) ​

适用于需要推理的任务(写 SQL、算法、方案设计)。核心一句咒语:

「一步步思考,先列出推理步骤,再给最终结论。」

对简单题没必要(纯浪费 Token),对复杂题立竿见影——模型把推理过程「展开」后,最终答案准确率显著上升。判断标准:这个任务你自己也需要「打草稿」才能做对,就让模型也「打草稿」(CoT)。


5. Token 治理:成本是 LLM 场景的命门 ​

一个有 Token 意识的工程师和一个没有的,在同一个需求上写出来的代码,成本可能差 10 倍。这是本篇的「命门」,也是「高效」维度在 LLM 场景的直接体现。

5.1 Token 是什么 ​

模型既不按字符、也不按词计费,而是按 Token(子词单元)。粗换算:英文 1 Token ≈ 0.75 词;中文 1 汉字 ≈ 1.5~2 Token(汉字拆得更碎)。

为什么这个概念重要:因为「成本 = 输入 Token + 输出 Token」,而你写的每一个 Prompt 字、模型回的每一个字,都在这笔账里。有了 Token 意识,你才会去抠「这句话值不值得发」。

5.2 三个省钱的硬手段 ​

手段做法节省
模型分级简单任务用便宜模型,复杂任务才上旗舰30%~90%
Prompt 精简去寒暄、去冗余示例、压缩 Few-shot10%~30%
Prompt 缓存稳定的 system/长文档放前段,命中厂商缓存输入成本降到 1/4~1/10

选型启示(务实的一条):做「批量分类、格式转换、简单抽取」这类任务,优先用便宜档模型,成本比旗舰低一个数量级。别一上来就堆最贵的——「杀鸡用牛刀」在 LLM 场景是要真金白银付代价的。


6. 流式输出:让用户 1 秒内看到响应 ​

同一个「写 500 字」的请求,非流式要等 8~15 秒整段生成完(用户像看白屏);流式 0.3~1 秒就出首字(打字机效果)。

任何面向用户的 LLM 应用都该上流式。 实现上就是把 stream=True 加到请求,响应变成一段段 SSE 事件,客户端负责把 delta.content 拼接起来。这个细节交给框架即可,你只需要知道「为什么必须流式」——因为它直接决定用户的「体感延迟」,而体感比真实延迟更能决定用户走不走。


7. 最常踩的 3 个坑 ​

现象根因正确姿势
多轮对话突然「失忆」拼接历史超过上下文窗口,被静默截断加对话摘要,或走 RAG(03 篇)
回答说一半就没了max_tokens 设太小被硬截断放大上限,或要求「分条回答」
高并发下偶发 429触发厂商限流加指数退避重试 + 分级降级

这里多说一句「失忆」这个坑——它是新手最困惑的现象,而它的根因,正是 1.1 讲的那个「模型无状态 + 历史重放」。历史重放有个隐藏的陷阱:当历史超长、超过窗口时,系统会「静默截断」最旧的消息,而不是报错。 所以你会看到「刚才还聊得好好的,怎么突然就忘了前面的事」——不是模型坏了,是前面的历史被挤出去了。


8. 记住这一句 ​

Prompt 是交接文档,Token 是计费单位,messages 是状态载体,流式是交互标配。

这四件事吃透,后面 RAG、Function Calling、Agent……所有复杂场景都只是在这四把钥匙上做乘法。

下一篇预告:LLM 能说话了,但它不知道你的私有知识——你问它「公司的请假流程」,它要么不知道、要么编一个。这就到了 03 · RAG 架构与工程落地。

Released under the MIT License.