第 29 课 · 亲手构建 AI 应用
第一次API调用:从请求检查到保存真实回答
怎样证明程序实际收到并正确使用了一次模型回答?
学完这一课,你能做到什么
- 构造包含模型、消息与输出设置的完整请求。
- 使用标准库脚本执行一次真实API调用并保存结果,或准确记录未完成。
- 检查HTTP、响应结构、终止状态与原材料事实。
网站对应:第一次调用 API:30 行代码的聊天机器人。按网站显示顺序编为第 29 课。
先预测 · 操作材料 · 改变条件
检查请求与返回,先不联网
页面构建请求、解析给定响应;默认响应是人工样例。这里没有密钥输入,不会发送API请求。
真实API操作说明+本地请求构建和响应校验;本轮未付费调用
- 先读模型名与完整公开材料。
- 用默认人工响应观察解析路径。
- 只改HTTP状态或finish_reason;真实执行后的脚本记录也可粘贴核对。
先记录你的预测,再点击运行。每次结果都由本课程序根据当前输入生成。
本次过程与结果
对照上一次运行结果
留下自己的解释
文字只保留在当前页面,离开前可导出;工作纸还有本课的具体记录题。
教师可以怎样一步步讲
完整教师讲稿
第29课|第一次API调用:请求、返回和事实逐层核对
本课60分钟,使用一条公开原创通知完成最小模型调用。教师需要课前准备可用账户、有限API额度和Python环境,学生可以按完整步骤在自己的练习目录执行一次;本轮课程编写没有调用付费服务。浏览器中的本地实验负责构建请求与解析给定响应,不能代替实际发送。
课前把环境准备到哪一步
教师先打开原有标准库脚本api_client.py。保存副本到本课独立练习文件夹,例如classroom29,学生所有输出也放这里。不要修改旧课程原文件。脚本仅用Python标准库,最低Python3.9;教师提前在终端运行python3 --version确认。Windows通常使用py -3,以下命令中的python3可对应替换。
课前核对官方首次调用页和账户中可用的模型名称。2026-09-10核对到示例名称deepseek-flash,但实际上课仍查当期支持项,不把旧的名称当永久接口。API账户、密钥与余额应按供应商要求准备;能够在网页对话不证明API已经可用。今天不把开户、支付、排查所有网络问题挤进60分钟。
脚本默认不联网,只有加--live才发送一次。默认输出上限512,问题是短提取;如果服务模式或输出上限使最终文本为空,必须记录未完成并查原因,不能无限提高预算反复重试。浏览器实验没有密钥输入框,真实密钥也不应出现在讲义截图、聊天记录或提交作业里。
本课的明确材料与验收
原创材料:“周三15:00在图书馆二楼举行资料分享,报名方式未提供。”要求提取时间、地点、报名方式,缺失项写未提供。真实模型可以换表述,但不能改时间地点或编一个报名网址。正确语法、HTTP成功、拿到最终文本、文本符合材料,四层都要分开检查。
板书画“准备请求→发送一次→读取返回→检查终止→核对事实→保存记录”。浏览器按钮叫“构建请求并检查响应”;它只完成本地构建和解析。终端脚本才承担实际发送,二者的输出记录要分开标模式。
0—5分钟:聊天窗口后面发生了什么
教师说:“以前我们把问题打进输入框,界面帮我们把问题交给服务。今天自己组织这份请求,让一个程序把模型答案取回来。API不是另一个更聪明的模型,而是程序使用服务的接口方式。” 请学生在Q0画输入、服务、输出三框。问:“屏幕上看见一段示例答案,能证明我们实际请求过吗?”让他指出还缺发送与真实返回证据。今天材料可以用来练接口,也可以很容易由原文检查,不让复杂知识掩盖接线问题。
5—10分钟:请求里面的三个核心部分
打开本地页面,先读模型名称,再读完整材料。教师说:“model说明请求哪一个已提供的模型;messages是消息列表,里面区分角色与内容;stream设false,让本次示例先读取一个完整JSON响应,避免同时学习流式分片。” 再介绍max_tokens=512作为本样例限制。它不是本节实际费用,也不保证模型一定在这个上限内完成理想回答。请求还需要正确端点和认证,浏览器这里只展示内容,不携带密钥、更不发送。学生要先读出服务被要求做什么,而不是照着复制一堆字段。
10—15分钟:先解析一份人工响应
默认响应是人工构造:choices列表里第一项有message.content与finish_reason,usage给用量字段。教师先让学生从外层找到列表第一项,再进入message,最后到content,不直接念最终文本。 点击“构建请求并检查响应”,记录请求和解析到的文字。教师说:“现在我们证明的是程序能读这份给定材料,不是已经调用服务。默认用量也只是样例数字。”若学生只复制内容不写模式,让他补“人工响应校验”,避免把格式练习误报为API完成。
15—20分钟:HTTP成功与完整文字不是一回事
只把HTTP状态200改401,先预测再运行。页面会停止成功验收路径。教师说明先查实际错误与账号、服务条件,不凭经验直接重装Python。之后恢复200,再把响应content改成空字符串,观察仍无法取得最终文字。 接着给finish_reason为length的人工片段,学生看到有文字也可能未完成。不能把非空内容与完整回答混为一谈。HTTP状态是人工输入用来学习核验顺序,真实调用时要根据实际记录填写,不能为了让界面通过把错误状态改成200。
20—25分钟:进入练习目录,做一次不联网预演
学生把api_client.py副本放到自己的classroom29目录,打开这个目录里的终端。先用文件列表确认脚本存在,再运行:
python3 --version
python3 api_client.py --model deepseek-flash
模型名需替换为当期已核对名称。预期看到mode为dry_run_no_network以及endpoint和payload,不能把它记为模型回答。教师请学生在输出里找到公开材料、stream与max_tokens,和浏览器请求对照。 如果提示找不到文件,检查当前目录,不修改脚本内容;如果找不到python3,使用教师已核对的启动命令。此处是环境准备不足时的分流点:没有条件的同学继续本地校验并记录未完成真实调用,不以安装一堆软件填满课堂。
25—30分钟:准备一次发送,密钥不写进文件
学生在同一目录创建call_once.py,粘贴以下完整代码。它通过不回显输入取得密钥,只传给子进程的环境;文件里没有实际密钥值。代码接收模型名称,调用已有api_client.py一次,并把脚本输出保存到api-result.json。
from pathlib import Path
import getpass
import os
import subprocess
import sys
model_name = input("填写已核对的模型名:").strip()
question = "原创材料:周三15:00在图书馆二楼举行资料分享,报名方式未提供。请提取时间、地点、报名方式;没有的内容写未提供。"
api_key = getpass.getpass("输入API密钥(不回显):").strip()
if not api_key or not api_key.isascii() or any(ord(c) < 33 or ord(c) > 126 for c in api_key):
raise SystemExit("密钥格式不适合本示例,未发送请求;请按官方说明检查。")
child_env = os.environ.copy()
child_env["DEEPSEEK_API_KEY"] = api_key
child_env["PYTHONIOENCODING"] = "utf-8"
result = subprocess.run(
[sys.executable, "api_client.py", "--model", model_name,
"--question", question, "--live"],
env=child_env, capture_output=True, text=True, encoding="utf-8"
)
Path("api-result.json").write_text(result.stdout, encoding="utf-8")
Path("api-stderr.txt").write_text(result.stderr, encoding="utf-8")
print(result.stdout)
if result.returncode != 0:
print("本次未完成。检查api-result.json;若为空,再检查api-stderr.txt,不要循环重试。")
else:
print("脚本已返回。继续核对answer、finish_reason和材料事实。")
教师逐项指出sys.executable复用当前Python,--live表示实际发送,capture_output便于保存本次输出。学生不需要熟练编写这些语句,但必须找到发送开关、材料与保存位置。不能一边说“只是试一下”一边忘了命令已经联网。 如果账户没有准备好,不运行这一段,填写未完成原因即可。浏览器后备仍有独立学习目标,但不宣称替代真实接入技能。
30—35分钟:执行一次真实调用并等待结果
运行:
python3 call_once.py
按提示填已核对的模型名,再在终端输入自己的API密钥。密钥不回显是预期行为,不是键盘失灵。不要把屏幕或日志中的私密配置提交给老师。本次请求仅含课堂公开材料,不包含个人笔记或聊天记录。 程序只发送一次,等待期间不要连续再开几次命令。服务返回错误时保存它,不把失败藏起来。如果因账户、限额或网络没有结果,记录发生在哪层;修复后是否重新执行,应有明确改动与有限次数,不能靠循环重试碰运气。教师示范也一样遵守一次运行一份记录。
35—40分钟:打开保存文件,不以一句成功提示验收
打开api-result.json。成功路径的mode是live_api,answer里应有最终文字,finish_reason反映终止原因。脚本已经解析过原始choices,所以这是解析后的记录;不能称保存了完整HTTP原始包。若需要完整原始响应应另用经过核对的记录方式,本课不为凑证据编造字段。 将这份脚本记录粘到浏览器响应框,可以再检查非空文字和终止状态。页面会识别live_api模式并说明它是已解析记录。人工填的HTTP状态仍不能替代实际请求证据。若脚本报live_failed或failed,按实际状态记录,不把错误JSON改成成功样例。 学生需要区分“脚本正常退出”和“业务材料回答正确”。答案可能没写报名方式,可能新增网址,仍须下一步人工检查。保存文件让教师能够复核本次究竟发生什么,也便于以后比较改动效果。
40—45分钟:把回答放回通知原文
先将时间、地点、报名三项分列。教师问:“这项来自哪一句原文?是否增加了原文没有的细节?”允许模型写成表格或句子,只要事实一致。若把周三改周四,不因文字更漂亮就算通过。 再看缺失项:写未提供、材料未说明都可接受;凭空给二维码、网址或电话不合格。一个HTTP200加一个非空content,只证明通信和解析走到某个位置,还没有证明回答忠于材料。学生把接口状态与内容核验分栏,避免绿灯掩盖业务错误。
45—50分钟:独立处理新响应
Q4提供完整人工响应,内容是“时间:周五10:00;地点:实验楼一层;报名方式:未提供”,finish_reason为stop。对应材料也改为周五10:00实验楼一层,报名未提供。学生先标字段,再粘到本地页面检查。此任务不再发付费请求。 第二轮只把finish_reason改length,其他保持。教师只问“哪些证据没变、哪些状态变了”,不公布验收结果。要求保存原判断与实际检查文字,不为了消除提示把字段改回去。
50—55分钟:把格式正确但事实错误找出来
独立第三份响应仍为stop,内容却把地点写为实验楼三层。请学生说明本地结构检查能否发现、人工还要核对什么。页面不是通用事实判分器,不会因为一处中文地点错就自动知道;学生不能将“检查通过”误读为内容全对。 再问如果只有dry_run_no_network记录,能否填写已实际获取模型回答。用mode与是否带--live的操作证据解释。提交原答后再讲评,不给独立阶段提前背答案。
55—60分钟:交付一份有边界的运行记录
人工独立响应第一轮结构与终止符合样例;length轮有内容但截断,不能验收完整;三层地点错需要对照原材料才能发现;dry_run不代表实测。教师按这四处讲评,再看学生的真实运行记录或准确的未完成原因。 离堂题:从一条请求到可信交付,至少需要核对哪三层?学生可回答发送与状态、响应结构和终止、原文事实。若只能回答按按钮,说明还需回到字段与原文。分数是本课操作和核验,不是已会搭建所有AI应用。
常见问题的具体处理
“我网页能聊天,为何脚本不能用?”教师让学生确认是否为同一API账户、是否取得有效API密钥、模型名称是否支持。不要先重写所有代码。“我看见usage有数字,是否已经按这些数字收费?”真实用量与计费还需看实际服务账单口径;默认页面usage是人工材料,不能算真实消费。 “我改了提示,但答案没变,是不是没有发送?”先检查保存文件时间、实际命令材料与响应,不靠感觉判断。多次运行要各自保留,不能把两次最好的片段拼成从未发生过的一次结果。“我需要把密钥交给同学调试吗?”这份练习可以核对不含密钥的请求体、状态和字段,没有必要把密钥写进作业。
后备、扩展与来源
无真实账户或环境时,完成请求构建、正常/空内容/截断/错误状态四类材料检查,标“本地后备完成,真实API尚未完成”。教师可把真实接入另排补做,不能因后备得分高就把接入状态改成完成。文件保存与事实核验仍须完成。 扩展可以让程序处理第二条公开通知,或再加自己的字段检查,另计20分钟。不要第一课接口实验就加入文件上传、并发、复杂代理与多个SDK,让故障层叠难以定位。 来源为DeepSeek官方首次调用页,本轮读取2026-09-10;示例模型名称以后按当期控制台核对。本课使用既存教师原创标准库脚本,编写课程时只运行不联网预演与本地检查,未实际付费调用。
60 分钟安排
每行给出具体讲解、学生动作和可检查的结果。根据学生反应调整,尚未真人计时试教。
| 时间 | 教师讲解与操作 | 学生动作 | 检查依据 |
|---|---|---|---|
| 00:00— 01:00 | 读公开通知和本课目标。 | 写通知三个待提取项。 | API链路:写通知三个待提取项。可核查。 |
| 01:00— 02:00 | 画输入服务输出。 | 画三框。 | API链路:画三框。可核查。 |
| 02:00— 03:00 | 说明API是使用接口而非新模型。 | 记接口与模型区别。 | API链路:记接口与模型区别。可核查。 |
| 03:00— 04:00 | 问示例文字是否证明发送。 | 留初判。 | API链路:留初判。可核查。 |
| 04:00— 05:00 | 列必须保存的证据。 | 写真实返回证据需求。 | API链路:写真实返回证据需求。可核查。 |
| 05:00— 06:00 | 读model名称。 | 填已核对名称。 | 请求字段:填已核对名称。可核查。 |
| 06:00— 07:00 | 展开messages角色与内容。 | 圈用户材料。 | 请求字段:圈用户材料。可核查。 |
| 07:00— 08:00 | 说明stream=false。 | 写一次完整响应方式。 | 请求字段:写一次完整响应方式。可核查。 |
| 08:00— 09:00 | 说明max_tokens512是上限。 | 标不能保证实际完成。 | 请求字段:标不能保证实际完成。可核查。 |
| 09:00— 10:00 | 明确浏览器不带密钥不发送。 | 记录本地构建模式。 | 请求字段:记录本地构建模式。可核查。 |
| 10:00— 11:00 | 找choices列表。 | 画字段路径。 | 人工响应:画字段路径。可核查。 |
| 11:00— 12:00 | 进入首项message。 | 圈消息对象。 | 人工响应:圈消息对象。可核查。 |
| 12:00— 13:00 | 定位content和finish_reason。 | 记录文本与状态。 | 人工响应:记录文本与状态。可核查。 |
| 13:00— 14:00 | 点构建请求并检查响应。 | 运行默认材料。 | 人工响应:运行默认材料。可核查。 |
| 14:00— 15:00 | 说明usage是人工样例。 | 标未实际调用。 | 人工响应:标未实际调用。可核查。 |
| 15:00— 16:00 | 只改状态401。 | 预测再运行HTTP案例。 | 失败类型:预测再运行HTTP案例。可核查。 |
| 16:00— 17:00 | 恢复200并把content清空。 | 运行空内容案例。 | 失败类型:运行空内容案例。可核查。 |
| 17:00— 18:00 | 给length片段。 | 记录截断案例。 | 失败类型:记录截断案例。可核查。 |
| 18:00— 19:00 | 分别记录不能验收原因。 | 填写三类区别。 | 失败类型:填写三类区别。可核查。 |
| 19:00— 20:00 | 提醒人工状态不能改写真实事实。 | 保留原样例身份。 | 失败类型:保留原样例身份。可核查。 |
| 20:00— 21:00 | 将脚本副本放classroom29。 | 核对脚本位置。 | 干运行:核对脚本位置。可核查。 |
| 21:00— 22:00 | 在该目录确认Python版本。 | 保存版本信息。 | 干运行:保存版本信息。可核查。 |
| 22:00— 23:00 | 运行不含--live命令。 | 实际执行预演。 | 干运行:实际执行预演。可核查。 |
| 23:00— 24:00 | 读dry_run_no_network和payload。 | 圈端点和材料。 | 干运行:圈端点和材料。可核查。 |
| 24:00— 25:00 | 故障按目录/运行时定位。 | 记录未联网或环境缺口。 | 干运行:记录未联网或环境缺口。可核查。 |
| 25:00— 26:00 | 创建call_once.py。 | 保存自己的启动文件。 | 发送准备:保存自己的启动文件。可核查。 |
| 26:00— 27:00 | 粘贴完整代码。 | 核对代码完整。 | 发送准备:核对代码完整。可核查。 |
| 27:00— 28:00 | 找到--live与输入模型位置。 | 标发送开关。 | 发送准备:标发送开关。可核查。 |
| 28:00— 29:00 | 说明密钥不回显且不写文件。 | 不用真实密钥做截图。 | 发送准备:不用真实密钥做截图。可核查。 |
| 29:00— 30:00 | 指出api-result保存位置。 | 确认输出在练习目录。 | 发送准备:确认输出在练习目录。可核查。 |
| 30:00— 31:00 | 已准备者执行call_once.py。 | 运行或如实标未具备条件。 | 真实一次运行:运行或如实标未具备条件。可核查。 |
| 31:00— 32:00 | 填核对模型名。 | 记录模型名称与日期。 | 真实一次运行:记录模型名称与日期。可核查。 |
| 32:00— 33:00 | 终端不回显输入密钥。 | 本地输入凭据。 | 真实一次运行:本地输入凭据。可核查。 |
| 33:00— 34:00 | 等待一次结束,不多次启动。 | 保存这一轮结果。 | 真实一次运行:保存这一轮结果。可核查。 |
| 34:00— 35:00 | 失败者记录状态走后备。 | 记错误位置或后备模式。 | 真实一次运行:记错误位置或后备模式。可核查。 |
| 35:00— 36:00 | 打开api-result.json。 | 保存原文件。 | 保存结果:保存原文件。可核查。 |
| 36:00— 37:00 | 查mode与answer。 | 区分成功与错误mode。 | 保存结果:区分成功与错误mode。可核查。 |
| 37:00— 38:00 | 查finish_reason和usage。 | 记录结束状态。 | 保存结果:记录结束状态。可核查。 |
| 38:00— 39:00 | 说明是已解析记录非原始HTTP包。 | 准确命名证据。 | 保存结果:准确命名证据。可核查。 |
| 39:00— 40:00 | 粘到本地解析辅助检查。 | 比较本地检查与真实文件。 | 保存结果:比较本地检查与真实文件。可核查。 |
| 40:00— 41:00 | 把时间地点报名分三栏。 | 填三字段。 | 业务核对:填三字段。可核查。 |
| 41:00— 42:00 | 逐项追问原文出处。 | 引用公开材料。 | 业务核对:引用公开材料。可核查。 |
| 42:00— 43:00 | 缺报名不得造网址。 | 标新增或缺失。 | 业务核对:标新增或缺失。可核查。 |
| 43:00— 44:00 | 接口绿灯不等于事实正确。 | 写结构和事实两结论。 | 业务核对:写结构和事实两结论。可核查。 |
| 44:00— 45:00 | 完成真实或后备核验表。 | 保存本人核验。 | 业务核对:保存本人核验。可核查。 |
| 45:00— 46:00 | 发周五10点实验楼一层材料。 | 圈新事实。 | 独立新响应:圈新事实。可核查。 |
| 46:00— 47:00 | 先判断人工stop响应。 | 写初判。 | 独立新响应:写初判。可核查。 |
| 47:00— 48:00 | 粘贴运行。 | 记录结构状态。 | 独立新响应:记录结构状态。可核查。 |
| 48:00— 49:00 | 仅改length再运行。 | 比较单字段变化。 | 独立新响应:比较单字段变化。可核查。 |
| 49:00— 50:00 | 不提前给验收结果。 | 保留原答。 | 独立新响应:保留原答。可核查。 |
| 50:00— 51:00 | 恢复stop改响应三层。 | 修改人工响应。 | 独立事实错误:修改人工响应。可核查。 |
| 51:00— 52:00 | 问结构检查是否会知原文错。 | 说明自动检查范围。 | 独立事实错误:说明自动检查范围。可核查。 |
| 52:00— 53:00 | 让学生自己核对地点。 | 独立写事实判断。 | 独立事实错误:独立写事实判断。可核查。 |
| 53:00— 54:00 | 给只有dry_run记录的判断。 | 用mode解释是否真实调用。 | 独立事实错误:用mode解释是否真实调用。可核查。 |
| 54:00— 55:00 | 提交后再讲评。 | 锁定首次答案。 | 独立事实错误:锁定首次答案。可核查。 |
| 55:00— 56:00 | 核对stop/length两状态。 | 修订接口判断。 | 讲评交付:修订接口判断。可核查。 |
| 56:00— 57:00 | 说明三层与一层事实冲突。 | 修订业务判断。 | 讲评交付:修订业务判断。可核查。 |
| 57:00— 58:00 | 强调dry_run不冒充真实返回。 | 标实际完成范围。 | 讲评交付:标实际完成范围。可核查。 |
| 58:00— 59:00 | 离堂写三层检查。 | 完成Q5。 | 讲评交付:完成Q5。可核查。 |
| 59:00— 60:00 | 收脱敏记录和补做事项。 | 提交自己的证据。 | 讲评交付:提交自己的证据。可核查。 |
来源与使用范围
2026-09-10核对DeepSeek官方首次调用页;示例模型deepseek-flash,课堂前核对当前账户可用名称。脚本为既存教师原创标准库材料。
- DeepSeek官方首次API调用:端点、Bearer鉴权、消息与最终content字段;模型名需按当期列表核对。