Day 07 / 共 68 天 · 阶段 1 编程与工具地基

调你的第一个 API

昨天(Day 06)你学会了用命令行和 Git 管理代码;今天学怎么让你的程序"打电话"给别人的服务器——这就是"调 API"。搞懂 HTTP、JSON、requests 这三件套,明天(Day 08)就能理解"为什么调 API 要用异步",也为后面调大模型 API 打下地基。

📍 你在 68 天里的位置(阶段 1:编程与工具地基 · Day 3-9)
D03 Python 基础 D06 命令行&Git D07 调 API D08 异步 D09 FastAPI
💡 先用一个类比兜住今天(今天世界观:去餐厅点餐) 调 API 就像去餐厅点餐:你(你的程序)是顾客,服务器是后厨,中间的服务员就是 HTTP。你不用会做菜,只要按菜单(API 文档)下单(发请求),后厨就把菜(数据)端回来(返回响应)。今天所有名词都能塞进这个餐厅:菜单=文档、点单方式=GET/POST、"菜好了/卖光了/后厨着火了"=状态码、装菜的餐盒=JSON、会员卡=API Key。记住"点餐"这个画面,今天全通。
L01

为什么转行 Agent 要先学"调 API"

🤔 痛点你可能会想:我要做的是 AI Agent,为什么第一步是学这么"传统"的 HTTP?
💡 本质因为大模型就是一个"别人开的餐厅"——OpenAI、Claude、通义都把模型放在他们的服务器上,你唯一的用法就是"给它下单(发 API 请求)、拿回它的回答(响应)"。不会调 API,就一行大模型的代码都写不出来。

后面 60 天你几乎每天都在做同一件事:把数据打包成请求发出去、把返回的结果解析出来用。RAG 要调 embedding API,工具调用要调外部服务 API,部署后你的 Agent 本身也是一个 API。所以今天是整条路线的地基——它不难,但绕不过去。

👶 一句话“调 API” = 让你的程序去访问别人服务器上的一个功能,把结果拿回来。就像你用手机点外卖,不用自己开饭店。
L02

HTTP 是什么:一问一答的规矩

🤔 痛点浏览器、App、你的 Python 程序,凭什么能和世界上任意一台服务器"对上话"?
💡 本质因为大家都约定用同一套"说话规矩"——HTTP(超文本传输协议)。它规定:永远是客户端先发"请求(Request)",服务器回一个"响应(Response)",一问一答。就像点餐永远是顾客先开口,服务员再上菜,不会反过来。

一次请求里主要装 4 样东西:

  • 方法(Method):你想干嘛(取数据?提交数据?)——下节讲;
  • URL(网址):找哪家店、哪张桌 https://api.example.com/weather?city=北京
  • 请求头(Headers):附带说明,比如"我是谁"(身份/API Key)、"我要 JSON 格式";
  • 请求体(Body):要提交的具体内容(点单的详细内容),只有 POST 这类才有。
你的程序 客户端 / 顾客 服务器 后厨 ① 请求 Request(下单) ② 响应 Response(上菜) 永远客户端先问、服务器后答,一问一答
图注:HTTP 的全部世界观——客户端下单、服务器上菜,一来一回。
L03

常用方法:GET 取、POST 交

💡 本质HTTP 方法就是告诉服务器"我这次是来干什么的"。就像进餐厅你得说清楚:是来看菜单(GET),还是来下一单(POST)
方法用途(一句话)点餐类比
GET取数据,不改变服务器上的东西看菜单、查今天的菜价
POST提交数据,新建一条东西下一个新订单
PUT整体更新一条已有数据把整张订单改掉重下
DELETE删除一条数据取消订单

新手 90% 的时间只会用到 GETPOST。规律:只是"要点信息"用 GET,"提交内容让服务器处理"用 POST。调大模型属于后者——你把问题(内容)提交上去让它生成回答,所以是 POST

📝 例子:同一个天气服务,两种方法 查北京天气(只取信息)→ GET https://api.xxx.com/weather?city=北京,参数直接挂在网址 ? 后面。
提交一条留言(新建内容)→ POST https://api.xxx.com/comments,内容放在"请求体"里,网址上看不到。
L04

状态码:这一单成了没

💡 本质服务器上菜时会先甩给你一个3 位数字,一眼说明"这单啥情况"。看首位就够:2 开头=成功、4 开头=你点错了、5 开头=后厨出事了
状态码含义点餐类比
200 OK成功,数据在响应里菜做好了,端上来了
201 Created成功创建了一条新数据(常见于 POST)新订单已建好
400 Bad Request你的请求写错了(参数缺失/格式错)你点的菜没写清楚
401 Unauthorized没带身份 / API Key 错了没出示会员卡
404 Not Found网址指向的东西不存在菜单上没这道菜
429 Too Many Requests你请求太频繁,被限流了你一分钟催了 100 次,被叫停
500 Server Error服务器自己出错了(不怪你)后厨着火了

👶 小白:我调大模型报了 401,是我代码写错了吗?

👨‍🏫 老师:401 几乎总是"身份没对上"——要么忘了带 API Key,要么 Key 写错/过期/没充值。先别改逻辑,去 L07 检查 Key。反过来 500 大多不怪你,是服务器临时抽风,隔几秒重试常常就好;429 则是你太频繁,要放慢或加重试等待。先看状态码首位,能省你一半排查时间。

L05

JSON:数据的"标准餐盒"

🤔 痛点后厨把菜端出来,得用一个双方都认的餐盒装,你才好接。程序之间传数据也一样——用什么格式?
💡 本质答案几乎总是 JSON。它是一种人也读得懂、程序也好解析的文本格式,长得就像 Python 的字典和列表。JSON 就是数据的标准餐盒,全世界的 API 都用它装菜。

JSON 只有几种积木:对象 {...}(键值对,像字典)、数组 [...](列表)、字符串 "..."、数字、true/falsenull。看一眼就懂:

{
  "city": "北京",
  "temperature": 28,
  "is_sunny": true,
  "forecast": ["晴", "多云", "小雨"]
}

Python 里用内置的 json 模块互转(不过下一节的 requests 会帮你自动做):

import json                      # Python 自带,不用安装

# 字符串(JSON) → Python 字典,叫“解析/反序列化”
s = '{"city": "北京", "temp": 28}'
data = json.loads(s)             # loads = load string
print(data["city"])             # 北京   ← 像字典一样取值

# Python 字典 → 字符串(JSON),叫“序列化”,准备发出去
d = {"city": "上海", "temp": 30}
print(json.dumps(d, ensure_ascii=False))  # {"city": "上海", "temp": 30}
# ensure_ascii=False 让中文正常显示,而不是变成 \u...
记忆法loads 带 s = 从 String 读进来;dumps 带 s = 倒成 String 发出去。方向别搞反。
L06

requests:三行调通一个 API

💡 本质requests 是 Python 最常用的 HTTP 库——它就是你专属的那位服务员:你告诉它"去哪家店、点什么",它跑腿发请求、把菜端回来。先装它:pip install requests

GET 取数据(这里用一个公开的免费测试 API,能真的跑):

import requests                       # 上面 pip install 过

# 向一个公开测试服务要一条假数据(无需 Key,可直接跑)
resp = requests.get("https://httpbin.org/get", params={"city": "北京"})
#      └ 方法    └ 网址(URL)             └ params 会自动拼成 ?city=北京

print(resp.status_code)              # 200  ← 先看状态码(L04)
data = resp.json()                   # 自动把返回的 JSON 解析成字典(省了 json.loads)
print(data["args"])                  # {'city': '北京'}  ← 服务器把我们传的参数原样回显

POST 交数据(提交一段内容,最接近之后调大模型的写法):

import requests

payload = {"question": "你好,今天天气如何?"}   # 要提交的内容(字典)
resp = requests.post(
    "https://httpbin.org/post",
    json=payload,                    # 用 json= ,requests 自动序列化+设好请求头
    timeout=10,                      # 关键!最多等 10 秒,否则网络卡住会一直挂着
)
resp.raise_for_status()              # 若状态码是 4xx/5xx,这里直接抛错,提前发现问题
print(resp.json()["json"])           # {'question': '你好,今天天气如何?'}
三个必记习惯:① 一定加 timeout,不然请求可能永远卡住;② POST 传字典用 json=(不是 data=)最省事;③ 拿到响应先看 status_code 或用 raise_for_status(),别默认它一定成功。
📝 例子:一次完整的"点餐"对照 你写 requests.post(url, json={"question":"..."}) = 你对服务员说"我要下单,内容是这句话";
返回的 resp.status_code == 200 = 服务员说"做好了";
resp.json() = 你打开餐盒,把里面的菜(数据)拿出来吃。之后调 Claude / OpenAI,几乎就是这段的翻版,只是换个网址、加个 Key。
L07

API Key:会员卡,千万别泄露

💡 本质大多数正经 API(尤其大模型)要收费,所以要认人。API Key 就是你的会员卡号——服务器靠它知道"是你在用、该往你账上记多少钱"。通常放在请求头里带上去。
import requests

api_key = "sk-你的密钥"                       # 先别急,下面讲更安全的存法
headers = {"Authorization": f"Bearer {api_key}"}  # 放在请求头,Bearer 是常见格式

resp = requests.get("https://api.example.com/data", headers=headers, timeout=10)
# 如果 Key 没带对,服务器会回 401(还记得吗?= 没出示会员卡)

最重要的一条安全铁律:绝不把 Key 直接写死在代码里、更不能提交到 GitHub。泄露的 Key 会被别人盗刷,账单能到几千块。正确做法是放进环境变量,代码只去"读"它:

# 在终端里设置环境变量(临时,当前窗口有效)
export API_KEY="sk-你的真实密钥"

# 更常用:把它写进项目根目录的 .env 文件(记得把 .env 加进 .gitignore!)
import os

api_key = os.environ["API_KEY"]     # 从环境变量读取,代码里不出现真实密钥
# 或用 os.getenv("API_KEY") ——读不到时返回 None 而不报错,可自行判断

if not api_key:
    raise RuntimeError("没找到 API_KEY,请先 export 或写进 .env")

👶 小白:为什么不能图省事直接把 Key 写代码里?就我自己电脑跑而已。

👨‍🏫 老师:因为你迟早会 git push 到 GitHub。全网有机器人 7×24 小时扫描公开仓库里的 Key,泄露常在几分钟内就被盗刷。用环境变量后,代码可以随便分享,密钥留在你自己机器上——这是所有公司的标配习惯,面试也会问。养成"密钥永不进代码"的肌肉记忆。

L08

今日小结 + 动手 10 分钟

🧠 今天你应该能回答

  • 调 API 为什么是学大模型的第一步?(大模型就是别人服务器上的服务,只能通过 API 用)
  • HTTP 的基本模式是什么?(客户端发请求、服务器回响应,一问一答)
  • GET 和 POST 分别什么时候用?(取信息用 GET,提交内容用 POST;调大模型是 POST)
  • 状态码 200 / 401 / 404 / 429 / 500 各代表啥?(成功 / 没带对 Key / 不存在 / 太频繁 / 服务器出错)
  • 为什么 API Key 不能写进代码?该放哪?(会被盗刷,放环境变量/.env)

✋ 动手:10 分钟调通一个真实 API

下面这段无需任何 Key,直接能跑,帮你把今天全部概念串一遍:

# 1. 装库(只需一次)
pip install requests
# 2. 存成 first_api.py 后运行: python first_api.py
import requests

# —— GET:向公开测试服务要数据 ——
r = requests.get("https://httpbin.org/get",
                 params={"name": "转行第7天"}, timeout=10)
print("状态码:", r.status_code)              # 期望 200
print("服务器收到的参数:", r.json()["args"]) # {'name': '转行第7天'}

# —— POST:提交一段内容(模拟给大模型发问题)——
r2 = requests.post("https://httpbin.org/post",
                   json={"question": "HTTP 是什么?"}, timeout=10)
r2.raise_for_status()                        # 不是 2xx 就报错
print("服务器收到的内容:", r2.json()["json"])# {'question': 'HTTP 是什么?'}

进阶挑战:把网址故意改成 https://httpbin.org/status/404,再打印 status_code,亲眼看看 404 长什么样,然后想想它对应"点餐"里的哪一幕。

明天预告 · Day 08 异步入门:今天每个请求你都得干等服务器回话——1 个请求等 1 秒还好,但 Agent 常要同时问 10 个 API,串着等就要 10 秒!明天用"点餐 vs 排队打饭"的类比讲清 async/await,让 10 个请求几乎同时发出、一起等,把 10 秒压到 1 秒。这是 Agent 高效的关键。
← Day 06 命令行 & Git Day 08 · 异步与并发入门 →