02 LLM 应用基础
学习目标:能构建一个稳定 LLM 应用接口,支持结构化输出、多轮状态和基础函数调用。这是 Agent 的地基——Agent 首先是一个稳定的大模型应用,而不是一段随意 prompt。 重点度:必会(8 分) 前置要求:Python / TypeScript、HTTP API、基础后端开发
概述#
在谈 Agent、Tool Calling、MCP 之前,先要回答最基本的问题:一个稳定的 LLM 应用接口长什么样?
如果连结构化输出都拿不稳、多轮对话状态都管不好,那建在上面的 Agent 系统一定是不稳定的。本章是整个路线的入门基石,但入门不等于可以糊弄——Agent 的一切失败最终都追溯到这一层的不稳定。
Prompt 与 System Prompt#
Prompt(提示词)#
Prompt 是用户送给模型的输入文本。Chat 场景中,prompt 会被组织成结构化的 messages 格式:
system: 你是一个代码审查助手,必须严格遵守以下规则……
user: 请帮我 review 这段 Python 代码
assistant: (模型将生成的回复)System Prompt(系统提示词)#
System Prompt 是定义模型角色、规则、约束的"任务说明书"。在 Agent 系统里它通常包含:
- 角色定义:你是什么 Agent、能做什么、不能做什么
- 工具说明:有哪些工具可用、何时用哪个
- 行为约束:必须先确认、必须引用来源、必须 JSON 输出等
- 安全规则:禁止执行的操作、敏感操作需人工审批
⚠️ 实战坑:System Prompt 不是"写一次就行"。Agent 跑长任务时,System Prompt 会被反复注入到上下文,过长的 system prompt 会持续吃 token 预算(见 Ch3 Context 工程)。生产 Agent 的 system prompt 要精炼、可版本化、可灰度。
结构化输出与 JSON Schema#
为什么必须结构化#
Agent 系统的下游(工具调用、状态机、UI 渲染)都需要机器可读的输出。自然语言回答在 demo 阶段好看,到了 Agent 系统里就是灾难——下游无法解析"我觉得应该调用 search_web 这个工具"这种句子。
JSON Schema 约束#
主流模型厂商都支持通过 JSON Schema 约束输出:
# OpenAI 风格的 structured output
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "提取这篇文章的标题、作者、关键词"}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "article_meta",
"strict": True,
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"author": {"type": "string"},
"keywords": {"type": "array", "items": {"type": "string"}}
},
"required": ["title", "author", "keywords"],
"additionalProperties": False
}
}
}
)关键实践#
- strict 模式:开
strict: true,让模型严格按 schema 输出,不要靠 prompt 祈祷 - enum 约束:枚举字段必须用
enum,不要让模型自由发挥 - preflight validation:拿到输出后必须再校验一次,模型偶尔会幻觉出多余字段
- 降级策略:JSON 解析失败 → 重试一次(带"上次输出错了,请严格按 schema"提示)→ 仍失败 → 降级到非结构化输出或拒绝执行
Function Calling 基础#
为什么需要 Function Calling#
Agent 的本质是"让模型决策调用工具"。Function Calling 是模型厂商提供的原生能力:模型看到工具描述后,自己判断该不该调用、调用哪个、传什么参数。
⚠️ 重要区别:Function Calling 不是"模型执行函数",而是"模型告诉你该调用什么函数、传什么参数"。真正的执行永远在你的代码里。这是新手最大的误解。
基本流程#
1. 用户: "查一下北京明天天气"
2. Agent 把 tools=[get_weather] 注入到请求
3. 模型返回: {tool: "get_weather", args: {city: "北京", date: "2026-07-02"}}
4. Agent 代码执行 get_weather(city="北京", date="2026-07-02")
5. Agent 把结果回灌给模型: {role: "tool", content: "晴, 28℃"}
6. 模型基于结果生成最终回答: "北京明天晴天, 气温约 28℃"Tool Schema 写法#
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市指定日期的天气。仅支持中国主要城市。",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名, 如 '北京'"},
"date": {"type": "string", "format": "date", "description": "日期, YYYY-MM-DD"}
},
"required": ["city", "date"]
}
}
}]实战坑(高频问题)#
| 现象 | 根因 | 排查/解决 |
|---|---|---|
| 模型选错工具 | description 模糊、多个工具语义重叠 | 重写 description、加枚举、加 router |
| 模型填错参数 | schema 缺约束、缺枚举、缺范围 | 加 enum/format/required;preflight 校验 |
| 模型不调用工具 | system prompt 没强调"该用工具时必须用" | 在 system prompt 中明确工具使用规则 |
| 模型循环调用同一工具 | 无终止条件、无 step budget | 见 Ch5 Agent Runtime / Ch9 预算治理 |
Function Calling 的"工具选择"和"参数填写"问题是 Agent 工程最常见的 bug 源,详见 Ch4 Tool Calling 与 MCP。
多轮对话状态与流式输出#
多轮对话状态#
Agent 不是单次问答,需要管理多轮上下文。基本结构:
messages = [
{"role": "system", "content": "..."},
{"role": "user", "content": "第一轮问题"},
{"role": "assistant", "content": "第一轮回答"},
{"role": "user", "content": "第二轮问题"}, # 模型能看到前面对话
]状态管理的关键问题:
- 上下文长度限制:每轮累积,迟早超出模型窗口 → 见 Ch3 Context 工程
- 会话存储:哪份对话属于哪个用户/哪个会话?通常按 session_id 持久化到 Redis/DB
- 状态恢复:用户中断后能否继续?需要 checkpoint 机制 → 见 Ch7 Workspace/Memory
流式输出(Streaming)#
LLM 生成是逐 token 的,流式输出能显著降低 TTFT(首字延迟):
stream = client.chat.completions.create(
model="gpt-4o",
messages=[...],
stream=True
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True) # 边生成边打印Agent 场景的特殊考虑:
- Function Calling 流式:流式过程中模型可能输出 tool_call,需要边接收边解析(OpenAI 用
tool_calls[].function.arguments的增量拼接) - 结构化输出流式:JSON 没生成完时不能直接 parse,需要增量 JSON parser(如
partial-json) - 打断与取消:用户中途取消时,要能取消流(关闭 stream + 通知后端停止),避免空烧 token
模型选择与参数控制#
模型选择维度#
| 维度 | 说明 | 选型示例 |
|---|---|---|
| 能力等级 | 推理/编码/工具调用能力 | GPT-5 > Claude Opus > Sonnet > Haiku |
| 上下文窗口 | 单次能塞多少 token | 200K / 1M / 10M 级别差异大 |
| 工具调用能力 | Function Calling 准确率 | 不是所有模型都强,Anthropic/OpenAI 较强 |
| 输出速度 | TPOT、tokens/s | Haiku/Flash 类小模型快 |
| 成本 | input/output token 单价 | 差距 10-50 倍 |
| 合规 | 国内备案、数据出境 | 国内业务通常选通义/DeepSeek/智谱 |
实战策略:路由分流——简单分类/提取用小模型,复杂推理/工具调用用大模型,单条请求成本可降一个数量级。
关键参数#
- temperature:0-2,越高越随机。结构化输出/工具调用建议 0-0.3;创意写作 0.7-1.0
- top_p:核采样,与 temperature 二选一(不要同时调)
- max_tokens:输出上限,防失控。Agent 长任务必须设
- stop:停止序列,遇到即停止生成。用于结构化输出场景
- seed(部分模型支持):可复现,调试 eval 时有用
⚠️ 实战坑:调高 temperature 不能"提升推理能力",只会让模型更"放飞"。Agent 系统的工具调用决策建议 temperature=0,需要多样性时再单独调。
阶段产出#
完成本章后应该能:
- 写出有清晰角色、规则、约束的 System Prompt
- 用 JSON Schema 约束模型输出,并做 preflight 校验和降级
- 实现 Function Calling 完整流程(注入 tools → 模型决策 → 执行 → 回灌)
- 管理多轮对话状态(按 session 持久化、支持恢复)
- 实现流式输出,包括 Function Calling 增量解析和用户打断
- 根据任务选型模型并配置参数(temperature/max_tokens/stop)
下一章 Ch3 进入 RAG 与 Context 工程——Agent 怎么获取知识、怎么管理运行时上下文预算。Context 工程是 Agent 能不能跑长任务的核心难题,单纯堆 RAG 解决不了。