当前位置:首页>python>Python 速成(下)+ LLM API 入门:从零到跑通你的第一个大模型程序

Python 速成(下)+ LLM API 入门:从零到跑通你的第一个大模型程序

  • 2026-08-21 19:28:01
Python 速成(下)+ LLM API 入门:从零到跑通你的第一个大模型程序

还记得上篇结尾那个"简易智能客服"吗?我们用字典模拟了 LLM 的消息列表,build_messages 函数拼出 system 和 user 消息,假装有个模型在回答你。

说句实话——那是个"假模型"。它不会思考,只会查字典。今天带你真正调通一个大模型 API。

这篇学完,你能独立完成五件事:

  1. 🔌 跑通第一个 LLM 调用
  2. 💬 实现多轮对话
  3. ⌨️ 做出打字机效果的流式输出
  4. 📦 拿到程序可解析的结构化 JSON
  5. 🤖 亲手写一个能用的命令行 AI 助手

这些不是零散的知识点,它们就是 AI Agent 开发最核心的"骨架"——你后面学 LangGraph、学 Agent 编排,底层全是今天这些东西。

📌 阅读建议:准备好电脑,跟着每节示例动手敲。上篇的语法你已经会了,这篇全是"怎么用",光看是学不会的,必须敲。


01 虚拟环境与依赖管理:给你的"厨房"装上独立灶台

上篇我们把 Python 环境比作厨房,锅灶一应俱全。但有个问题你可能还没遇到:如果你在系统里直接 pip install 各种包,就像在唯一的灶台上乱炖——今天装 A 项目要的 requests 1.0,明天装 B 项目要的 requests 2.0,两个版本打架,A 项目就崩了。

这就是全局污染版本冲突。解决办法叫虚拟环境(venv):每个项目一个独立的小厨房,互不干扰。Python 自带这个功能,不用装任何东西。

# 创建虚拟环境:第一个 venv 是目录名,叫什么都行
python -m venv venv

创建好之后,目录里会多出一个 venv 文件夹,里面装着一份"独立的小 Python"。重点来了:你必须先激活它,pip 装的包才会进这个环境

# Mac / Linux 激活
source venv/bin/activate

# Windows PowerShell 激活
venv\Scripts\activate

激活成功后,命令行前面会出现 (venv) 前缀,比如 (venv) mac@...$。看到它,就说明你现在用的是独立环境。想退出,输入 deactivate 即可。

激活之后,安装我们这篇文章要用的两个包:

pip install requests openai

requests 是发 HTTP 请求用的,openai 是官方 SDK——别被名字骗了,它不只是给 OpenAI 用的,几乎所有国产大模型都兼容它,后面你会看到。

装完后,把依赖清单存下来,方便换电脑、同事协作时一键还原:

pip freeze > requirements.txt

requirements.txt 里每一行是一个包加版本号,比如 requests==2.32.3。别人拿到这个文件,一条命令就能装齐:

pip install -r requirements.txt

再补充一个实用技巧:每次开始写新项目,第一步永远是建虚拟环境。养成这个肌肉记忆,能帮你避开 90% 的依赖地狱。如果项目已经建好了但忘了建环境,也可以随时补——venv 目录删掉重建就行,不影响任何代码。

另外,pip 本身也值得知道两个常用选项:pip list 查看当前环境装了哪些包,pip uninstall 包名 卸载不需要的包。排查问题时先 pip list 看看版本,往往能一眼发现"版本不对"。

✅ 运行前检查清单(01 节)

  • [ ] 命令行出现 (venv) 前缀(虚拟环境已激活)
  • [ ] pip install requests openai 安装成功,无红色报错
  • [ ] pip list 里能看到 openai 和 requests

⚠️ 新手常见坑:

最常见的情况是——明明 pip install openai 成功了,运行代码却报 ModuleNotFoundError: No module named 'openai'。十有八九是用错了解释器:VS Code 右下角选的是系统 Python,而包装进了 venv。检查方法:在终端 which python(Mac)或 where python(Windows),看路径里有没有 venv。VS Code 里按 Ctrl+Shift+P(Mac 是 Cmd+Shift+P),输入 "Python: Select Interpreter",选带 ('venv': venv) 的那个。记住一句话:装包的环境和跑代码的环境必须是同一个。另外,Mac 用户如果提示 pip 找不到,试试 python3 -m pip install xxx,这是最不容易出错的调用方式。


02 先学会调用 HTTP API:大模型其实是个"外卖电话"

在碰 SDK 之前,我想先带你扒开一层皮,看看 LLM API 到底长什么样。说穿了特别简单:大模型 API 就是一个网址(URL),你往它那儿发一个请求,它给你返回一段文字

就像打电话叫外卖:你报需求(请求),商家做饭(服务器上的模型处理),骑手送餐(响应)。这个"打电话"的协议叫 HTTP。Python 里最常用的"电话机"就是 requests 库。先来个最简单的 GET 请求,试试水:

import requests

# GET 请求:从服务器"取"数据
resp = requests.get("https://httpbin.org/get")
print("状态码:", resp.status_code)   # 200 表示成功
print("响应内容:", resp.text[:200])  # 打印前 200 个字符

status_code 是 HTTP 状态码,200 就是"一切正常"。resp.text 是原始响应文本,通常是一大段 JSON。

但调用大模型用的是 POST 请求——POST 和 GET 的区别,可以粗暴理解为:GET 是"你给我看看",POST 是"我交给你处理一下"。你要把消息发给模型让它生成回答,自然是 POST。请求长这样:

import requests
import os

# 从环境变量读取密钥(上篇学过 os.getenv,这里直接复用)
api_key = os.getenv("DEEPSEEK_API_KEY")

resp = requests.post(
"https://api.deepseek.com/chat/completions",   # 接口地址
    headers={
"Authorization"f"Bearer {api_key}",      # 身份凭证:我是谁
"Content-Type""application/json",        # 我发送的是 JSON 格式
    },
    json={                                        # 请求体:我要干什么
"model""deepseek-v4-flash",
"messages": [
            {"role""user""content""你好,用一句话介绍你自己"}
        ],
    },
    timeout=30,   # 30 秒没响应就放弃,防止程序卡死
)

print("状态码:", resp.status_code)
data = resp.json()   # 把 JSON 字符串解析成 Python 字典
print(data["choices"][0]["message"]["content"])  # 取出模型回复

✅ 运行前检查清单(02 节)

  • [ ] 已设置环境变量:export DEEPSEEK_API_KEY="sk-你的密钥"(或用 .env 方案)
  • [ ] 代码里 os.getenv("DEEPSEEK_API_KEY") 不是 None(可先 print 验证)
  • [ ] 模型名写的是 deepseek-v4-flash(旧名 deepseek-chat 已弃用)

这段代码就是 LLM 调用的"原始形态",几个关键点:

  • Authorization 头Bearer 后面跟你的 API Key,相当于你的"工牌",服务器靠它认出你是谁、有没有钱。
  • json 参数:requests 会自动帮你把字典转成 JSON 字符串,并设置好 Content-Type。
  • resp.json():把服务器返回的 JSON 文本解析成 dict,然后一层层取数据——choices[0].message.content 就是模型说的话。这套结构就是上篇我们模拟过的那个 JSON,现在见到真身了。

状态码是排查问题的第一线索,记住这三个就够用了:

状态码
含义
常见原因
401
身份认证失败
API Key 写错、过期,或没设置环境变量
429
请求太多被限流
短时间内调用次数/并发超限,等一会儿再试
500
服务器内部错误
服务商的问题,重试几次通常能恢复

再补一个生产级细节:超时设置。 上面代码里的 timeout=30 不是摆设。如果没有超时,网络卡住时程序会一直挂在那里,像等一个永远不来的外卖。生产环境里,建议把超时拆成两段:timeout=(连接超时, 读取超时),比如 timeout=(5, 60)——5 秒连不上就放弃,连上后 60 秒内没返回也放弃。这样既不怕网络慢,也不怕服务器卡死。

关于 API Key 的安全,这是底线问题:绝不允许把密钥直接写死在代码里。一旦代码上传到 GitHub,等于把钱包密码公开了,别人能用你的额度疯狂调用,账单直接爆掉。正确做法是放环境变量:

# 方式一:终端里临时设置(每次开新终端都要重新设)
export DEEPSEEK_API_KEY="sk-你的密钥"

# 方式二(推荐):写进 .env 文件,用 python-dotenv 自动加载
pip install python-dotenv

.env 文件内容就一行:

DEEPSEEK_API_KEY=sk-你的密钥

代码里这样加载:

from dotenv import load_dotenv
import os

load_dotenv()   # 读取项目根目录的 .env 文件
api_key = os.getenv("DEEPSEEK_API_KEY")

千万别忘了把 .env 加进 .gitignore,否则一提交就把密钥交出去了。

⚠️ 新手常见坑:

① resp.json() 报错 JSONDecodeError,通常是状态码不是 200,返回的是错误文本,先打印 resp.status_code 和 resp.text 看真实内容;

② 密钥报 401,先确认环境变量真的读到了——在代码里 print(os.getenv("DEEPSEEK_API_KEY")) 看看是不是 None;

③ 请求体里 messages 必须是列表,少写一层中括号会直接 400 报错。


03 LLM API 核心概念:看懂"菜单"再点菜

学会了"打电话",现在要弄明白"怎么点菜"。LLM API 有五个核心概念,搞懂它们,任何一家厂商的接口你都能秒懂。

第一个概念:token(令牌)。 模型不看"字",它看的是 token——一种把文本切碎后的基本单位。粗略换算:英文大约 4 个字符算 1 个 token,中文 1 个字约等于 1~1.5 个 token。比如"你好世界"大概 4 个 token。

为什么重要?因为 token 就是钱:API 按 token 计费,你发出去的消息和模型吐出来的回复,都要按 token 数付钱。后面你会看到,每次调用的响应里都带着 usage 字段,告诉你这次花了多少 token。

怎么估算自己的一段话有多少 token? 最简单的办法:先看英文,一段 100 个英文单词的文本约等于 130~150 个 token;中文的话,100 个字大约 100~150 个 token。不用算得很准,心里有个数量级就行——你只需要知道"提示词越长越贵"这个方向感。很多厂商的控制台还提供 token 计算器,把文本贴进去就能看到精确数字,调试成本时很好用。

第二个概念:消息结构。 Chat 接口的请求体里,messages 是一个列表,里面每条消息有三个角色:

  • system:给模型立"人设"的,比如"你是一个耐心的编程老师"。它不直接给用户看,是幕后指令。
  • user:用户说的话。多轮对话中,每轮用户输入都追加一条。
  • assistant:模型自己的回答。多轮对话中,模型每次回复也要追加回去,模型才能"记得"自己说过什么。

一个多轮消息列表长这样:

messages = [
    {"role""system""content""你是一个简洁的旅行助手"},
    {"role""user""content""我想去成都玩三天"},
    {"role""assistant""content""好的!建议安排:第一天宽窄巷子+锦里,第二天熊猫基地,第三天都江堰。需要我细化行程吗?"},
    {"role""user""content""帮我细化第二天的行程"},
]

注意看:user 和 assistant 消息交替出现,这就是模型的"记忆"。模型本身没有记忆,你发多少条消息它就"看"多少条。上篇我们用字典模拟这个结构,现在你知道了,那是一比一还原的真实协议。

第三个概念:关键参数。 点菜时你可以提要求,这些参数就是要求:

参数
作用
建议
model
用哪个模型
如 deepseek-v4-flash
messages
对话内容列表
必填
temperature
随机性:0 稳定保守,1 天马行空
写代码用 0~0.3,聊天用 0.7~1.0
max_tokens
限制回复的最大 token 数
防止模型写超长废话
top_p
采样的概率阈值,和 temperature 类似
一般调一个就够,别两个一起调
stream
是否流式输出(逐字返回)
聊天体验用 True,后面细讲

第四个概念:OpenAI 兼容协议。 这是 2026 年最值得庆幸的一件事:几乎所有主流厂商都兼容 OpenAI 的接口格式——DeepSeek、通义千问、智谱、Kimi、OpenRouter 都是。这意味着你只需要学会一套 SDK,换模型时只改两行:base_url(接口地址)和 api_key(密钥),模型名再改一下,完事。这就是本篇只用 openai 一个 SDK 打天下的原因。

第五个概念:2026 年主流 API 现状。 帮你快速建立市场认知(数据截至 2026 年 8 月,价格变动快,以官网为准):

厂商
代表模型
输入价 ($/M token)
输出价 ($/M token)
特点
DeepSeek
V4 Flash
0.14
0.28
100 万 token 上下文,极致性价比
OpenAI
GPT-5.6 系列(Sol/Terra/Luna)
较高
较高
能力天花板,价格也天花板
Google
Gemini 3.6 Flash
1.5
7.5
输出速度约 239.6 token/s
阿里云百炼
通义千问 Qwen3.7
有免费额度
有免费额度
北京地域有免费额度,适合练手
智谱
GLM-5.2
国产,中文能力强
月之暗面
Kimi K3
长文本优势
字节
豆包
生态整合好

💡 补充:DeepSeek V4 Flash 的缓存命中输入价仅 $0.0028/M,约为正常输入的 1/50。这意味着"重复发相同前缀"几乎不花钱——具体怎么利用,看下面的深度洞察二。

新手入门,我强烈建议首选 DeepSeek:便宜(一次对话几分钱)、兼容 OpenAI 协议、中文效果好、注册就送额度。本篇所有示例都用 DeepSeek 的接口,base_url 是 https://api.deepseek.com。想体验别的模型?把 base_url 和 api_key 换成对应厂商的即可,代码一行都不用改。

顺便说下各家怎么拿密钥,免得你卡在第一步:DeepSeek 去 platform.deepseek.com 注册,控制台里"API Keys"页面创建;阿里云百炼去百炼控制台开通模型服务,北京地域有免费额度,适合学生党白嫖练手;OpenAI 去 platform.openai.com,但要绑卡。拿到密钥后统一放进 .env 文件,我们的代码只认环境变量,不认"复制粘贴进代码"。

深度洞察一:为什么 OpenAI 兼容协议成了"事实标准"?

这背后不是技术碾压,而是网络效应。OpenAI 是第一个把"对话式大模型"做成标准接口的厂商:/chat/completions + messages 结构 + 参数命名,这套设计足够简单、足够通用,于是 SDK 生态(openai-python 等)最先成熟。

后来者(DeepSeek、通义、智谱……)要做的不是另起炉灶,而是"兼容它"——因为兼容 = 免费获得整个开发者生态:开发者不用换 SDK 就能用你的模型,迁移成本趋近于零。这就是**事实标准(de facto standard)**的经典案例:不是官方强制,而是市场用脚投票。

对你这个学习者来说,这是天大的好事——学会一套 API,等于学会了所有厂商的 API。换模型只改三行:base_urlapi_keymodel

深度洞察二:token 计费背后的经济学

为什么输出价比输入价贵一倍(0.28 vs 0.14)?因为生成是自回归的:模型要一个 token 一个 token 地"想"出来,每生成一个都要重新计算一遍,这是最贵的环节;而输入是并行"读"进去的,便宜得多。

那为什么缓存命中的输入只要 $0.0028/M(比正常输入便宜 50 倍)?因为服务端有 KV cache:如果这次请求的前缀和上次完全一样,服务端直接复用缓存,省掉了重新"读"的成本。这解释了一个省钱技巧:system 人设和固定前缀尽量保持一致,命中缓存,输入成本直接降两个数量级。

深度洞察三:"上下文 = 记忆"的边界问题

很多人以为"上下文窗口越大 = 模型记忆越好",这是误解。上下文窗口不是记忆,是注意力预算:模型没有真正的记忆,你塞多少条消息它就"看"多少条,看完就忘。窗口再大(DeepSeek V4 Flash 有 100 万 token),也有两个硬伤:

  1. 中间遗忘:研究(Lost in the Middle)表明,模型对长上下文的"中间部分"关注度最低,重要信息放开头和结尾最容易被记住。
  2. 成本线性增长:每轮对话都要把全部历史重发一遍,聊得越久,输入 token 越多,账单越贵——窗口是 100 万 token,但你的钱包不是。

所以真实产品从不"无脑塞上下文",而是做记忆管理:截断、摘要、RAG 检索。第 05 节你会亲手实现前两种。

⚠️ 新手常见坑

① 把 temperature 和 top_p 同时调到很高,输出会变成"胡言乱语",这两个参数控制的是同一件事(随机性),调一个就行;

② 以为换模型要重学一套 API——不用,认准"OpenAI 兼容"四个字,换 base_url 就行;

③ 看到网上教程写 deepseek-chatdeepseek-reasoner 这两个旧模型名——它们已在 2026-07-24 弃用并映射到 V4 Flash,直接用 deepseek-v4-flash 即可。


04 你的第一个 LLM 程序:从"模拟"到"真实"只差 30 行

理论讲完了,动手。上篇我们模拟的 LLMClient,今天换成真实的 API 调用。先确认你已经装好 SDK:

pip install openai

然后新建一个 first_llm.py,把下面代码完整敲进去:

"""我的第一个 LLM 程序:调用 DeepSeek"""
import os
from openai import OpenAI

# 1. 创建客户端:告诉 SDK "去哪家店、用谁的钥匙"
client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),   # 密钥从环境变量读,绝不硬编码
    base_url="https://api.deepseek.com",     # 改成其他厂商的地址即可换模型
)

# 2. 组装消息:system 立人设,user 提问题
messages = [
    {"role""system""content""你是一个耐心的中文编程老师,回答要简洁。"},
    {"role""user""content""用一句话解释什么是 API?"},
]

# 3. 发起对话:核心就这一行
response = client.chat.completions.create(
    model="deepseek-v4-flash",   # 模型名
    messages=messages,           # 对话内容
    temperature=0.7,             # 适度创意
    max_tokens=500,              # 最多生成 500 个 token
)

# 4. 取出模型回复
reply = response.choices[0].message.content
print("模型回复:", reply)

# 5. 查看本次消耗(token 就是钱,养成看账单的习惯)
print(f"本次消耗 token:{response.usage.total_tokens}")

✅ 运行前检查清单(04 节)

  • [ ] 已安装 openai:pip install openai
  • [ ] 已设置密钥:终端 export DEEPSEEK_API_KEY="sk-你的密钥",或用 .env 方案(02 节讲过)
  • [ ] 代码里 os.getenv("DEEPSEEK_API_KEY") 不是 None
  • [ ] 模型名写的是 deepseek-v4-flash(旧名 deepseek-chat 已弃用)

逐行拆解,你就明白 SDK 帮我们做了什么:

  • OpenAI(...):创建客户端。SDK 内部会记住 base_url 和 api_key,后面所有请求都自动带上。想切换厂商,改这两行就行
  • client.chat.completions.create(...):发 POST 请求到 /chat/completions 接口。还记得 02 节我们用 requests 手写的请求体吗?SDK 帮你把 headers、JSON 序列化、状态码检查全包了。
  • response.choices[0].message.content:从返回对象里取模型回复。choices 是列表,通常只有一个元素;.message.content 就是文本。
  • response.usage.total_tokens:这次调用总共消耗的 token 数。这是你控制成本的第一手数据

💡 小提示:如果你的密钥放在 .env 文件里,记得在代码开头加上 load_dotenv()(02 节讲过),否则 os.getenv 会读到 None。

运行:

python first_llm.py

看到模型回复的那一刻,恭喜你——从"模拟"到"真实",你正式迈过了 AI 开发的第一道门槛

接下来升级:让模型输出 JSON。 真实开发中,你经常需要程序能直接解析的结果,而不是一段自然语言。方法很简单:在 system 提示词里明确格式要求,然后用 json.loads 解析:

import os
import json
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role""system""content""你是一个信息提取器。只输出 JSON,格式:{\"name\": 姓名, \"age\": 年龄, \"city\": 城市}"},
        {"role""user""content""张三今年 28 岁,住在杭州"},
    ],
    max_tokens=200,
)

raw = response.choices[0].message.content
print("模型原始输出:", raw)

data = json.loads(raw)   # 字符串 -> 字典
print("姓名:", data["name"])
print("年龄:", data["age"])

加一层错误处理,让程序在 API 出问题时优雅地提示,而不是崩溃。上篇学的 try/except 正好派上用场:

import os
from openai import OpenAI, APIError, AuthenticationError, RateLimitError

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

try:
    response = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=[{"role""user""content""你好"}],
    )
    print(response.choices[0].message.content)
except AuthenticationError:
    print("❌ 密钥无效,请检查 DEEPSEEK_API_KEY 环境变量")
except RateLimitError:
    print("❌ 请求太频繁被限流了,歇几秒再试")
except APIError as e:
    print(f"❌ API 错误:{e}")

顺便算一笔账(衔接上篇的成本计算):DeepSeek V4 Flash 输入 0.28/百万 token。假设一次对话用了 1000 个输入 token + 500 个输出 token,成本是:

cost = 1000 / 1_000_000 * 0.14 + 500 / 1_000_000 * 0.28
print(f"这一次对话大约花费 ${cost:.6f}")   # 0.00028 美元,约 2 厘人民币

没错,一次对话不到一分钱。这也是为什么建议新手用 DeepSeek 练手——可以放心大胆地写代码、跑实验,账单不会吓到你

⚠️ 新手常见坑:

① response.choices[0].message.content 取出来是 None,多半是开启了流式但按非流式方式取值,或者模型返回了 tool_calls(第 07 节会讲);

② json.loads 报错,先 print(raw) 看模型到底输出了什么——有时模型会在 JSON 前后加解释文字,解决办法是 system 提示词里写死"只输出 JSON,不要任何其他文字";

③ 报 ModuleNotFoundError: openai,回到 01 节检查虚拟环境是否激活。


05 多轮对话与上下文管理:让模型"记住"你说过的话

上篇的简易客服有个致命缺陷:它没有记忆。你问"订单到哪了",它回答后,你再问"那退款呢",它不知道你在说同一个订单。

真实的多轮对话,靠的就是把每轮消息都追加进 messages 列表——这就是模型的"记忆"。还记得 03 节深度洞察三吗?"上下文 = 注意力预算",现在你来亲手管理这份预算:

"""多轮对话:messages 就是模型的记忆"""
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

# 记忆的起点:system 人设
messages = [
    {"role""system""content""你是一个贴心的旅行规划助手,回答要简洁实用。"},
]

print("旅行助手已上线(输入 quit 退出)")
whileTrue:
    user_input = input("你:")
if user_input == "quit":
break

# 1. 把用户的话写进"记忆"
    messages.append({"role""user""content": user_input})

# 2. 带着全部记忆去问模型
    response = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=messages,
        max_tokens=400,
    )
    reply = response.choices[0].message.content

# 3. 把模型的回答也写进"记忆"
    messages.append({"role""assistant""content": reply})

    print(f"AI:{reply}")
    print(f"(当前记忆长度:{len(messages)} 条消息)")

跑起来试试:先问"我想去成都玩",再问"第二天吃什么",你会发现模型记得你们聊过成都。每一轮,messages 都在变长,模型"看"到的上下文越来越完整——这就是多轮对话的全部秘密。

但这里藏着一个大问题:上下文膨胀。每轮对话都要把全部历史发给模型,聊得越久,token 消耗越大,而且模型有上下文长度上限(虽然 DeepSeek V4 Flash 有 100 万 token 的窗口,但你的钱包没有)。聊到一定程度,要么报错,要么账单起飞。

最简单的截断策略:只保留最近 N 条消息。 像人一样,太久远的细节记不住就算了,记住最近聊的就行:

deftrim_messages(messages, max_len=10):
"""只保留 system 人设 + 最近 max_len 条消息"""
    system_msg = messages[0]          # 人设永远保留
    recent = messages[1:]             # 去掉 system,只看对话部分
if len(recent) > max_len:
        recent = recent[-max_len:]    # 只留最近 N 条
return [system_msg] + recent

注意:截断前先把 system 单独拎出来,否则人设会被一起丢掉,模型"人设崩塌"。

更高级的做法是"摘要压缩":聊久了,让模型把前面的对话总结成几句话,塞回 system 里,再接新对话。这个思路就是后面 RAG 和 Agent 记忆系统的雏形,先记住"有这回事"即可。

进阶需求:对话持久化。 程序关掉,记忆就没了。把 messages 存成 JSON 文件,下次启动再读回来:

import json

defsave_messages(messages, filename="history.json"):
"""把对话历史存成 JSON 文件"""
with open(filename, "w", encoding="utf-8"as f:
        json.dump(messages, f, ensure_ascii=False, indent=2)

defload_messages(filename="history.json"):
"""从 JSON 文件恢复对话历史"""
try:
with open(filename, "r", encoding="utf-8"as f:
return json.load(f)
except FileNotFoundError:
return [{"role""system""content""你是一个贴心的旅行规划助手。"}]

再聊聊"摘要压缩"这个更聪明的方案。 截断是"丢记忆",摘要压缩是"压缩记忆":当消息超过阈值时,让模型把前面的对话浓缩成几句话,然后把摘要塞进 system 消息,历史消息清空。这样既保留了关键信息,又控制了 token 消耗。

真实产品里,很多 Agent 的记忆系统就是这个思路的工程化版本——先记住概念,以后你写自己的记忆模块时,会回来感谢今天的自己。

⚠️ 新手常见坑:

① 忘记把 assistant 的回复 append 进 messages,模型就会"失忆",回答驴唇不对马嘴——记住:user 和 assistant 的消息都要追加

② 截断时把 system 消息也截掉了,模型人设崩塌——截断前记得把第一条 system 单独保留;

③ 存 JSON 文件时忘记 ensure_ascii=False,存进去全是 \u4f60\u597d 这种转义字符,人没法看。


06 流式输出:让 AI 像真人一样"打字"

你有没有注意过,ChatGPT 的回答是一个字一个字蹦出来的?这就是流式输出(streaming)

原理很简单:模型生成完整个回答再一次性返回,叫非流式;边生成边返回,一个字一个字传给你,叫流式。为什么要流式?两个理由:

  1. 用户体验:大模型生成一段 200 字的回答可能要几秒到十几秒。非流式模式下,用户盯着空白屏幕干等;流式模式下,文字实时蹦出来,用户感觉"AI 在打字",等待焦虑瞬间消失。
  2. 延迟感知:流式模式下第一个字往往 1 秒内就出来了,用户感知到的延迟大幅降低。

代码差异极小——加一个 stream=True,然后遍历响应:

"""流式输出:打字机效果"""
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

# 关键:stream=True
stream = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role""system""content""你是一个诗人。"},
        {"role""user""content""写一首关于夏天的五言绝句"},
    ],
    stream=True,   # 开启流式
)

# 遍历每一个"碎片",逐字打印
print("AI:", end="")
for chunk in stream:
    delta = chunk.choices[0].delta.content   # 这一小片文字
if delta:                                # 有的碎片是空的,要跳过
        print(delta, end="", flush=True)     # flush=True 强制立即输出,不打进缓冲区
print()

对比一下非流式和流式的差异,就两处:

对比项
非流式
流式
请求参数
不写 stream 或 stream=False
stream=True
取数据
response.choices[0].message.content
(一次性拿全)
遍历 for chunk in response,取 chunk.choices[0].delta.content
返回时机
全部生成完才返回
边生成边返回
用户体验
干等
打字机效果

新手最容易踩的坑:流式响应里,chunk.choices[0].delta.content 经常是 None——尤其是第一个碎片(可能只包含 role 信息)和最后一个碎片(结束标记)。如果你直接 print(delta),会打出 None;如果 delta + "x" 会直接 TypeError 崩溃。所以必须判空if delta: 再处理。

另外注意:流式模式下默认不返回 usage 统计。如果一定要统计成本,在请求里加 stream_options={"include_usage": True},最后一个碎片就会带上 chunk.usage 字段(第 08 节的助手就是这么做的)。

⚠️ 新手常见坑:

① 忘了 flush=True,文字会攒在缓冲区里一次性蹦出来,打字机效果变"憋大招";

② 把 delta.content 当成 message.content 取——流式里没有 message,只有 delta

③ 流式循环里做耗时的打印格式化,会拖慢"打字"节奏,保持循环体轻量。


07 结构化输出与 Function Calling:让模型听程序的话

前面几节,模型一直在"说人话"。但真实的 Agent 系统里,程序需要的是机器能直接解析的结果——比如一个 JSON,而不是一段可能带废话的自然语言。这就是结构化输出

第一种方式:JSON Mode。 在请求里加 response_format={"type": "json_object"},强制模型输出合法 JSON:

"""情感分析:让模型输出结构化 JSON"""
import os
import json
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role""system""content""你是情感分析器。只输出 JSON,格式:{\"sentiment\": 情感, \"score\": 分数(0-1), \"reason\": 理由}"},
        {"role""user""content""分析这句话的情感:加班到半夜终于上线了,成就感爆棚!"},
    ],
    response_format={"type""json_object"},   # 强制 JSON 输出
    max_tokens=200,
)

result = json.loads(response.choices[0].message.content)
print("情感:", result["sentiment"])
print("分数:", result["score"])
print("理由:", result["reason"])

跑一下,你会拿到类似 {"sentiment": "正面", "score": 0.95, "reason": "..."} 的结构化结果。程序拿到它,就能做判断、进分支、存数据库——这就是 Agent 能"自动化"的基础。

第二种方式(也是 Agent 的灵魂):Function Calling(函数调用)。 这是 2026 年 AI 开发必须掌握的能力。概念一句话:你告诉模型"你有哪些工具可用",模型判断该用哪个工具、传什么参数,然后把工具结果交给你,你再把结果回传给模型生成最终回答

听起来绕,跑一遍就懂了。经典的"查天气"例子:

"""Function Calling:让模型调用你的工具函数"""
import os
import json
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

# 1. 定义一个"工具函数":真实项目中这里会调用天气 API
defget_weather(city: str) -> dict:
"""模拟查询天气"""
    weather_map = {
"北京""晴,25℃",
"上海""多云,28℃",
"广州""小雨,30℃",
    }
return {"city": city, "weather": weather_map.get(city, "数据未知")}

# 2. 用 tools 参数告诉模型:你有一个叫 get_weather 的工具,长这样
tools = [
    {
"type""function",
"function": {
"name""get_weather",
"description""查询指定城市的天气",
"parameters": {
"type""object",
"properties": {
"city": {"type""string""description""城市名,如:北京"}
                },
"required": ["city"],
            },
        },
    }
]

messages = [{"role""user""content""北京今天天气怎么样?"}]

# 3. 第一轮调用:模型看到问题,决定"我需要查天气工具"
response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=messages,
    tools=tools,          # 告诉模型有哪些工具
)

msg = response.choices[0].message

if msg.tool_calls:
# 4. 模型要求调用工具:取出工具名和参数
    tool_call = msg.tool_calls[0]
    print("模型想调用工具:", tool_call.function.name)
    print("参数:", tool_call.function.arguments)

    args = json.loads(tool_call.function.arguments)   # 参数是 JSON 字符串
    result = get_weather(args["city"])                # 5. 程序执行工具函数

# 6. 把"模型的工具调用请求"和"工具执行结果"都追加进消息
    messages.append(msg)                              # assistant 的 tool_calls 消息
    messages.append({
"role""tool",                               # 工具结果用 role="tool"
"tool_call_id": tool_call.id,                 # 与调用请求对应
"content": json.dumps(result, ensure_ascii=False),
    })

# 7. 第二轮调用:模型"看到"了工具结果,生成最终回答
    final = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=messages,
    )
    print("最终回答:", final.choices[0].message.content)

跑起来,你会看到完整闭环:模型说"我想调用 get_weather,参数是北京"→ 你的代码执行工具拿到天气 → 模型基于天气数据回答"北京今天晴,25℃"。

注意:模型本身不知道北京天气,它只是学会了"用你的工具"——这就是 Agent 能联网、能查数据库、能操作系统的底层机制。

再补一个控制参数:tool_choice 默认情况下(tool_choice="auto"),模型自己判断要不要用工具、用哪个;你也可以强制它必须用某个工具(tool_choice={"type": "function", "function": {"name": "get_weather"}}),或者强制不许用(tool_choice="none")。调试阶段,如果你发现模型该调工具却不调,先试试强制指定,能帮你快速确认是"模型没识别到意图"还是"工具定义有问题"。

整个流程就是 Agent 循环的雏形:模型决策 → 程序执行 → 结果回传 → 模型再决策。你后面学 LangGraph,学的就是把这种循环编排成复杂的工作流。今天先把这个闭环跑通,地基就打牢了。

⚠️ 新手常见坑:

① 忘了把 msg(含 tool_calls 的 assistant 消息)追加回 messages,第二轮调用会报错"tool_call_id 不存在"——tool_calls 消息和 tool 结果必须成对出现

② tool_call.function.arguments 是 JSON 字符串,不是字典,必须 json.loads 解析;

③ 模型有时会"幻觉"工具名或参数,防御办法是在 description 里写清楚每个参数的含义和示例值。


08 实战:命令行 AI 助手,把今天学的全串起来

现在,把前面所有知识点组装成一件完整的作品:一个命令行 AI 助手。它具备:

  • 多轮对话(messages 记忆)
  • 流式输出(打字机效果)
  • 三个命令:/quit 退出、/save 保存对话、/cost 查看花费
  • 异常处理(密钥错误、限流不崩溃)
  • 环境变量安全读取

新建 assistant.py,完整代码如下:

"""命令行 AI 助手:多轮对话 + 流式输出 + 命令系统"""
import os
import json
from openai import OpenAI, AuthenticationError, RateLimitError, APIError
from dotenv import load_dotenv

load_dotenv()   # 读取 .env 文件中的密钥

# 价格常量(美元/百万 token,DeepSeek V4 Flash,以官网为准)
PRICE_IN = 0.14
PRICE_OUT = 0.28

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

# 对话记忆 + 成本统计
messages = [{"role""system""content""你是一个友好的全能助手,回答简洁实用。"}]
total_cost = 0.0# 累计花费(美元)

defstream_chat(messages):
"""流式调用模型,返回完整回复和本次 token 消耗"""
global total_cost
    reply_parts = []
    usage = None# 用量统计:开启 include_usage 后,最后一个碎片会带上
    stream = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=messages,
        stream=True,
        max_tokens=800,
        stream_options={"include_usage"True},   # 让最后一个碎片带上 token 统计
    )
for chunk in stream:
        delta = chunk.choices[0].delta.content
if delta:
            print(delta, end="", flush=True)   # 打字机效果
            reply_parts.append(delta)
if chunk.usage:                        # 最后一个碎片带用量统计
            usage = chunk.usage
    full_reply = "".join(reply_parts)
# 估算成本:输入约等于历史全部 token,这里简化按输出 + 预估输入算
if usage:
        cost = usage.prompt_tokens / 1_000_000 * PRICE_IN + \
               usage.completion_tokens / 1_000_000 * PRICE_OUT
else:
        cost = 0.0# 拿不到统计时按 0 处理,不影响主流程
    total_cost += cost
return full_reply, cost

defsave_history():
"""把对话保存成 JSON 文件"""
    filename = "chat_history.json"
with open(filename, "w", encoding="utf-8"as f:
        json.dump(messages, f, ensure_ascii=False, indent=2)
    print(f"\n💾 对话已保存到 {filename}")

print("🤖 AI 助手已上线!输入 /quit 退出,/save 保存对话,/cost 查看花费")

whileTrue:
    user_input = input("\n你:").strip()
ifnot user_input:
continue

# -------- 命令系统 --------
if user_input == "/quit":
        print("👋 再见!")
break
if user_input == "/save":
        save_history()
continue
if user_input == "/cost":
        print(f"💰 本次会话累计花费:${total_cost:.4f}(约 {total_cost * 7.2:.2f} 元)")
continue

# -------- 正常对话 --------
    messages.append({"role""user""content": user_input})
    print("AI:", end="")

try:
        reply, cost = stream_chat(messages)
        messages.append({"role""assistant""content": reply})
        print(f"\n(本次花费 ${cost:.5f})")
except AuthenticationError:
        print("\n❌ 密钥无效,请检查 .env 文件里的 DEEPSEEK_API_KEY")
except RateLimitError:
        print("\n❌ 请求太频繁,休息几秒再试")
except APIError as e:
        print(f"\n❌ 出错了:{e}")

# 简单防膨胀:记忆超过 20 条就截断(保留 system + 最近 18 条)
if len(messages) > 20:
        system_msg = messages[0]
        messages = [system_msg] + messages[-18:]
        print("(已自动精简历史记忆)")

✅ 运行前检查清单(08 节)

  • [ ] 已安装 openai 和 python-dotenv:pip install openai python-dotenv
  • [ ] 项目根目录有 .env 文件,内容为 DEEPSEEK_API_KEY=sk-你的密钥
  • [ ] .env 已加入 .gitignore(防止密钥泄露)
  • [ ] 虚拟环境已激活(命令行有 (venv) 前缀)

功能点讲解

  1. 命令系统if/elif 分支判断输入是否以 / 开头,实现 /quit/save/cost 三个命令——这就是最朴素的"意图路由",上篇学的控制流直接复用。
  2. stream_chat 函数:封装了流式调用。注意 chunk.usage 只在最后一个碎片出现,并且需要 stream_options={"include_usage": True} 才会返回,用它拿 token 统计。
  3. 成本统计:每次调用后按价格常量换算美元,total_cost 累加。/cost 命令随时查账。
  4. 记忆截断:messages 超过 20 条就保留 system + 最近 18 条,防止无限膨胀(05 节学的,直接用上)。
  5. 异常处理:密钥错、限流、网络错误都被捕获,程序不崩,用户还能继续聊。

运行效果预览(真实运行时的样子):

🤖 AI 助手已上线!输入 /quit 退出,/save 保存对话,/cost 查看花费

你:帮我写一段 Python 代码,计算 1 到 100 的和
AI:最简单的方式是用内置函数 sum:

    total = sum(range(1, 101))
print(total)   # 5050

也可以用循环:total = 0
for i in range(1, 101):
    total += i
print(total)
(本次花费 $0.00008)

你:/cost
💰 本次会话累计花费:$0.0002(约 0.00 元)

你:/save
💾 对话已保存到 chat_history.json

你:/quit
👋 再见!

一次完整会话花费不到一分钱,这就是 DeepSeek 入门练手的底气。这个程序虽然只有 80 行,但它已经是一个"最小可用 Agent 外壳"了——有记忆、有工具(命令)、有成本控制、有异常兜底。把它跑通,你就真正拥有了"从零到跑通大模型程序"的能力。

动手改造建议,给你三个由易到难的方向:① 给 /save 加上时间戳文件名,每次保存不覆盖(chat_20260818.json);② 加一个 /clear 命令一键清空历史(保留 system 人设);③ 把 07 节的 get_weather 工具接进来,让助手能回答"北京天气怎么样"——这一步做完,你的助手就从"聊天机器人"升级成"会干活的 Agent"了。别小看这三个练习,它们正好覆盖了文件操作、状态管理、工具调用三个 Agent 开发的核心能力。

⚠️ 新手常见坑:

① stream_chat 里 chunk.usage 可能不存在,直接访问会 AttributeError——用 if chunk.usage: 判空;

② /save 保存的是整个 messages 列表(含 system 人设),下次加载后注意别重复加 system;

③ 输入空内容直接回车,input().strip() 后是空字符串,记得 continue 跳过,否则白花一次调用费。


09 进阶方向与最佳实践:从"跑通"到"专业"

跑通助手只是起点。真实生产环境里,还有四个问题绕不开:并发、重试、缓存、成本。每个给你一个最小方案,够你用一阵子。

1. 并发调用:一次问模型十个问题。 上篇学过 asyncio,openai SDK 也提供异步接口。想在单线程里并行发多个请求,用 asyncio.gather

"""并发调用:同时问模型多个问题"""
import os
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

asyncdefask(question: str) -> str:
    resp = await client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=[{"role""user""content": question}],
        max_tokens=200,
    )
return resp.choices[0].message.content

asyncdefmain():
    questions = ["1+1=?""中国的首都是?""Python 是谁发明的?"]
# 三个请求同时发出,总耗时约等于最慢的一个
    results = await asyncio.gather(*[ask(q) for q in questions])
for q, r in zip(questions, results):
        print(f"问:{q}\n答:{r}\n")

asyncio.run(main())

2. 重试与退避:网络抖动不慌。 429 限流、500 服务器错误,重试几次通常能成。先装库,再用 tenacity 三行搞定:

pip install tenacity
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
defcall_with_retry(client, messages):
"""最多重试 3 次,每次等待时间指数增长(1s、2s、4s...)"""
return client.chat.completions.create(
        model="deepseek-v4-flash", messages=messages
    )

3. 缓存:相同的请求别花两次钱。 用字典做最简单的缓存——相同的消息列表直接返回上次结果:

import json

cache = {}

defcached_chat(client, messages):
    key = json.dumps(messages, ensure_ascii=False)   # 消息列表转字符串当键
if key in cache:
        print("(命中缓存,未调用 API)")
return cache[key]
    resp = client.chat.completions.create(model="deepseek-v4-flash", messages=messages)
    cache[key] = resp.choices[0].message.content
return cache[key]

4. 成本控制三板斧:① 提示词精简——能 50 字说清的事别写 200 字,输入 token 直接省一半;② 利用缓存命中价——DeepSeek V4 Flash 缓存命中的输入价仅 $0.0028/M,约为未命中的 1/50(以官网为准),固定前缀(如 system 人设)尽量保持一致,命中缓存直接省钱;③ 批量任务放闲时跑——DeepSeek 2026 年 8 月起实行峰谷定价,非实时任务(日志分析、批量摘要)挪到晚上跑,成本立省一半。

5. API Key 安全加固:环境变量 + .gitignore 里加 .env + 定期轮换密钥。记住:密钥进了 Git 历史,就等于泄露了,只能作废重建。另外,如果项目要部署到服务器,别用 .env 文件,直接用平台的环境变量配置功能(Vercel、阿里云、AWS 都支持),更安全也更方便。

6. 日志与可观测性:生产环境里,每次 API 调用都要留痕——时间、模型、token 数、耗时、是否重试。别小看这件事,排查线上问题时,日志就是你的"黑匣子"。最简单的做法是每轮调用后打印一行结构化日志,或者用 Python 自带的 logging 模块输出到文件。等你开始写真正的 Agent 应用,会庆幸从第一天就养成了记日志的习惯。

下一步学习路径,按顺序走:

  1. FastAPI:把命令行助手包成 Web 服务,别人就能通过浏览器/接口访问你的 AI 应用——这是从"玩具"到"产品"的第一步。
  2. LangChain / LangGraph:学 Agent 编排。你已经在 07 节亲手实现了 tool 调用闭环,LangGraph 就是把这个循环工程化、可视化。
  3. RAG(检索增强):让 Agent 能"读"你自己的文档,回答基于你的知识库。
  4. 多模态:让 Agent 能看图、听声音。
  5. 微调:用你的业务数据定制模型行为——这是最后才需要碰的,前面的路还长。

给你的行动清单(建议一周内完成):

  • [ ] 跑通 04 节的第一个 LLM 程序,看到模型回复
  • [ ] 把 05 节多轮对话扩成"旅行助手",聊 10 轮以上
  • [ ] 把 06 节流式输出加进你的助手,体验打字机效果
  • [ ] 跑通 07 节 Function Calling 闭环,把 get_weather 换成真实天气 API
  • [ ] 完成 08 节命令行助手,并加上 /clear(清空历史)命令

完成这五步,你就不再是"看过教程的人",而是"跑通过真实 Agent 的人"。


结语:从"模拟"到"真实",只差一次 API 调用

回到开头那个"简易智能客服"。上篇我们用字典模拟 LLM,假装模型会回答;这篇你亲手调通了真实的大模型 API——从"模拟"到"真实",中间隔的只是一次 client.chat.completions.create() 调用,但跨过去之后,你看到的世界完全不同了。

你现在的工具箱里,有 HTTP 与 API 的底层认知、token 与成本的算账能力、多轮对话的记忆管理、流式输出的体验优化、结构化输出与工具调用的 Agent 核心机制,还有一个能跑的命令行 AI 助手。这套东西,就是 2026 年 AI Agent 开发者的入门标配。

最后送你一句话:学 API 调用,最忌讳"收藏了就等于会了"。今天这篇的所有代码,加起来约 400 行,但每一行都值得你亲手敲一遍。敲的过程中你会遇到报错、遇到 None、遇到状态码 429——这些"坑"才是真正的老师。把 08 节的助手跑通,再按动手建议加上一两个功能,你就完成了从"看教程的人"到"写代码的人"的转变,这个转变,谁也替不了你。

接下来,把 08 节的助手改造一下,加一个你自己的功能——比如让它读你的待办清单、帮你总结文章。改通的那一刻,你就不再是"学习者",而是"开发者"了。

跑通第一个程序了吗?评论区晒出你的运行结果


参考来源

  • DeepSeek 官方 API 文档(Chat Completions) - https://api-docs.deepseek.com/api/create-chat-completion
  • DeepSeek 官方 API 定价页 - https://api-docs.deepseek.com/quick_start/pricing
  • OpenAI Python SDK 官方文档 - https://github.com/openai/openai-python
  • 阿里云百炼:DeepSeek-V4 上线公告 - https://developer.aliyun.com/article/1731293
  • 2026 年主流 LLM API 价格对比(DevTk.AI / Neodrop AI,数据截至 2026 年 8 月,以官网为准) - https://devtk.ai/zh/news/llm-api-pricing-2026

注:文中价格与模型信息截至 2026 年 8 月,厂商调价频繁,请以各官网实时价格为准。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 22:52:00 HTTP/2.0 GET : https://f.mffb.com.cn/a/511619.html
  2. 运行时间 : 0.219732s [ 吞吐率:4.55req/s ] 内存消耗:4,852.73kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0bf1ac2913b301f06ccbf360b4a81305
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000898s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001460s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000706s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000686s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001325s ]
  6. SELECT * FROM `set` [ RunTime:0.000618s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001550s ]
  8. SELECT * FROM `article` WHERE `id` = 511619 LIMIT 1 [ RunTime:0.001323s ]
  9. UPDATE `article` SET `lasttime` = 1787323920 WHERE `id` = 511619 [ RunTime:0.015457s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000614s ]
  11. SELECT * FROM `article` WHERE `id` < 511619 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001223s ]
  12. SELECT * FROM `article` WHERE `id` > 511619 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001136s ]
  13. SELECT * FROM `article` WHERE `id` < 511619 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002904s ]
  14. SELECT * FROM `article` WHERE `id` < 511619 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001923s ]
  15. SELECT * FROM `article` WHERE `id` < 511619 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.028802s ]
0.223450s