1. 从零起步:为什么第一天不急着写代码
很多人对"AI应用开发"的第一反应是:得先学完Python基础、搞懂机器学习、啃完Transformer论文,然后才有资格碰大模型。这个路径不能说错,但它有个致命问题——周期太长,正反馈太慢,绝大多数人在第三周就放弃了。我带过不少零基础转过来的朋友,真正能坚持下来的,几乎都不是从"系统学习"开始的,而是从"跑通一个能看见结果的小东西"开始的。
所以Day1的目标非常明确:不追求理解原理,只追求亲手跑通一条从本地代码到大模型返回结果的完整链路。这条链路一旦打通,你后面学Python、学API、学工程化,都是在给一个"活的东西"添砖加瓦,而不是对着空气搭积木。
具体到这一天的产出,就是一个命令行聊天机器人:你在终端里敲一句话,程序把它发给大模型,大模型把回答打印回终端。听起来简单,但它包含了AI应用开发最核心的几个动作——环境搭建、依赖管理、API调用、请求构造、响应解析、异常处理。把这六个动作走一遍,你就已经跨过了90%新手卡住的那道门槛。
这篇文章适合三类人:完全没写过代码但想试试AI应用开发的小白、写过一点Python但没调过大模型API的初学者、以及想快速验证某个模型接口能不能用的开发者。我会把每一步为什么这么做讲清楚,包括那些教程里通常不写、但你一定会踩的坑。
2. 环境准备:Python安装与虚拟环境的那些细节
2.1 Python版本选择:别用最新的,用最稳的
打开Python官网下载页,你会看到一堆版本号。新手最容易犯的错就是"下最新的",结果装完发现某个库还不支持,报一堆编译错误。我的建议很直接:选3.10或3.11。这两个版本是目前生态兼容性最好的区间,主流库(requests、openai、httpx等)都有成熟的预编译包,不会让你在安装阶段就卡在编译C扩展上。
安装时有一个选项必须勾选:Add Python to PATH。Windows安装器第一屏底部就有这个复选框,很多人一路Next就跳过了,结果装完在命令行敲python提示"不是内部或外部命令"。如果你已经装完才发现没勾,不用重装,手动把Python安装目录和它的Scripts子目录加到系统环境变量Path里就行。
验证安装是否成功,打开终端(Windows用PowerShell或CMD,Mac用Terminal),输入:
python --version正常会输出类似Python 3.11.5。如果提示找不到命令,试试python3 --version,Mac和部分Linux系统默认只有python3这个命令名。
2.2 虚拟环境:一个项目一个坑,别共用
新手常问:"我直接pip install不就行了,为什么要搞虚拟环境?"答案是:因为你不可能只做一个项目。今天这个项目要requests 2.28,明天那个项目要requests 2.31,全局安装迟早会打架,而且一旦某个库升级破坏了依赖,你连回滚都困难。
创建虚拟环境的命令很简单,在项目文件夹里执行:
python -m venv venv这会在当前目录下生成一个venv文件夹,里面是一套独立的Python运行环境。激活它:
- Windows:
venv\Scripts\activate - Mac/Linux:
source venv/bin/activate
激活成功后,命令行前面会出现(venv)前缀。这时候你再用pip install,装的包只在这个环境里生效,跟系统Python完全隔离。用完想退出,敲deactivate即可。
提示:虚拟环境文件夹不要提交到代码仓库,也不要在里面放源码。它就是个一次性的运行沙箱,删了重建成本很低。
2.3 安装requests:一行命令背后的网络问题
我们这一天只需要一个第三方库:requests。它是Python里最常用的HTTP客户端,用来向大模型的API发请求。
pip install requests如果下载慢或者超时,可以指定国内镜像源:
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple装完验证一下:
python -c "import requests; print(requests.__version__)"能打印出版本号就说明环境没问题了。这一步看着简单,但它是后面所有工作的地基,地基不稳,后面全是玄学问题。
3. 拿到API凭证:调用大模型前的必要准备
3.1 API Key是什么,为什么不能写死在代码里
大模型的API不是随便谁都能调的,它需要一个身份凭证,也就是API Key。你可以把它理解成一把钥匙:谁拿着这把钥匙,谁就能消耗你账户里的额度。所以第一条铁律是——永远不要把API Key直接写在代码里,更不要提交到公开仓库。
我见过太多新手把Key硬编码在.py文件里,然后传到代码托管平台,几小时后收到账单预警。正确的做法是用环境变量:
- Windows PowerShell临时设置:
$env:DEEPSEEK_API_KEY="你的key" - Mac/Linux:
export DEEPSEEK_API_KEY="你的key"
然后在代码里用os.environ.get("DEEPSEEK_API_KEY")读取。这样代码可以随便分享,Key始终留在你自己的机器上。
3.2 接口地址与请求格式:先看懂再动手
调用大模型API,本质就是发一个HTTP POST请求。请求体是JSON,里面最关键的几个字段是:
| 字段 | 作用 | 常见取值 |
|---|---|---|
| model | 指定用哪个模型 | 如 deepseek-chat |
| messages | 对话历史列表 | 包含role和content |
| temperature | 回答的随机性 | 0到2之间,聊天常用0.7 |
| stream | 是否流式返回 | true或false |
messages这个字段值得多说一句。它是一个数组,每个元素有role和content两个属性。role有三种:system用来设定AI的人设和行为边界,user是用户说的话,assistant是AI之前的回复。多轮对话就是把历史消息按顺序全部塞进这个数组,模型据此理解上下文。
3.3 用curl先探路,再写代码
在写Python之前,我强烈建议先用curl手动发一次请求。这样做的好处是:如果出错,你能立刻分清是"网络/凭证问题"还是"代码问题"。
curl https://api.deepseek.com/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ -d '{ "model": "deepseek-chat", "messages": [{"role": "user", "content": "你好"}] }'如果返回一段包含choices字段的JSON,说明凭证和网络都没问题,可以放心写代码了。如果返回401,是Key不对;返回429,是请求太频繁;返回404,多半是接口地址写错了。这个排查顺序能帮你省下大量瞎猜的时间。
4. 写出第一个命令行聊天机器人
4.1 最小可用版本:20行代码跑通闭环
先上能跑的最小版本,把链路打通,再谈优化。
import os import requests API_KEY = os.environ.get("DEEPSEEK_API_KEY") URL = "https://api.deepseek.com/chat/completions" def chat(user_input): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}" } payload = { "model": "deepseek-chat", "messages": [{"role": "user", "content": user_input}] } resp = requests.post(URL, headers=headers, json=payload, timeout=30) data = resp.json() return data["choices"][0]["message"]["content"] if __name__ == "__main__": while True: text = input("你:") if text.strip() in ("exit", "quit"): break print("AI:" + chat(text))这段代码做了四件事:读Key、构造请求头、发POST、解析返回。timeout=30是必须加的,否则网络卡住时程序会一直挂着。resp.json()把返回的JSON字符串转成Python字典,然后按路径choices[0].message.content取出回答文本。
4.2 加上多轮记忆:让机器人记住上下文
上面这版有个明显问题:它没有记忆。你问"我叫小明",再问"我叫什么",它会一脸茫然。解决办法是维护一个messages列表,每轮把用户输入和AI回复都追加进去。
history = [{"role": "system", "content": "你是一个简洁友好的助手。"}] def chat(user_input): history.append({"role": "user", "content": user_input}) payload = {"model": "deepseek-chat", "messages": history} resp = requests.post(URL, headers=headers, json=payload, timeout=30) reply = resp.json()["choices"][0]["message"]["content"] history.append({"role": "assistant", "content": reply}) return reply这里有个隐藏的坑:上下文越长,消耗的token越多,费用越高,而且超过模型的最大上下文长度后会直接报错。所以真实项目里通常要做一个"截断"策略,比如只保留最近10轮对话,或者当消息总长度超过阈值时丢掉最早的历史。Day1可以先不做,但心里要有这根弦。
4.3 异常处理:429和超时才是常态
新手写的代码往往假设"请求一定成功",但现实是:网络会抖、接口会限流、Key会过期。最常见的两个错误是:
- 429 Too Many Requests:请求频率超了。解决方式是加退避重试,比如失败后等1秒、2秒、4秒再试。
- 超时:网络慢或服务端响应慢。解决方式是设置合理的timeout,并捕获
requests.exceptions.Timeout。
一个健壮一点的调用函数长这样:
import time def chat_with_retry(user_input, retries=3): for i in range(retries): try: resp = requests.post(URL, headers=headers, json=payload, timeout=30) if resp.status_code == 429: time.sleep(2 ** i) continue resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"] except requests.exceptions.RequestException as e: if i == retries - 1: return f"请求失败:{e}" time.sleep(2 ** i)2 ** i就是指数退避,第一次等1秒,第二次2秒,第三次4秒。这个模式在处理限流时非常有效,也是很多生产系统的标配。
5. 那些教程不写但你一定会踩的坑
5.1 编码问题:中文乱码从哪来
Windows的CMD默认编码可能是GBK,而API返回的是UTF-8。如果你在打印中文时看到一堆乱码,多半是终端编码不匹配。解决办法有两个:一是改用Windows Terminal或PowerShell,它们对UTF-8支持更好;二是在代码开头强制设置标准输出编码:
import sys sys.stdout.reconfigure(encoding="utf-8")Mac和Linux一般不会有这个问题,但跨平台代码加上这一行更保险。
5.2 代理与网络:请求发不出去怎么办
有些网络环境需要走代理才能访问外部接口。requests会自动读取系统的HTTP_PROXY和HTTPS_PROXY环境变量。如果你发现curl能通但Python不通,或者反过来,先检查这两个环境变量。另外,公司网络有时会拦截特定域名,这种情况下报错通常是连接超时或SSL错误,而不是401/429。
5.3 把Key写进代码后忘了删
这是新手最高频的安全事故。养成习惯:从第一天起就用环境变量。如果你已经不小心提交了Key,第一件事是去控制台把它吊销并重新生成,而不是删掉代码就完事——Git历史里还留着呢。
5.4 盲目复制网上的代码
网上很多示例代码是几年前的,接口地址、参数名、返回结构可能都变了。遇到报错时,第一反应应该是去看官方文档的最新示例,而不是继续在旧代码上打补丁。我一般会先用curl验证接口,确认返回结构后再写解析逻辑,这样能避免大量"字段不存在"的低级错误。
6. Day1之后:这条链路还能怎么长
跑通命令行机器人只是起点。基于这个最小闭环,你可以往几个方向自然延伸:
加流式输出。把stream设为true,用resp.iter_lines()逐行读取,实现打字机效果。这会让交互体验提升一个档次,也是很多聊天界面的基础。
换模型对比。把model字段换成别的模型名,同样的代码就能调用不同模型,方便你对比效果和成本。
加简单界面。用gradio或streamlit几行代码就能把命令行程序变成网页界面,这是从"能跑"到"能用"的关键一步。
做错误日志。把每次请求的状态码、耗时、token用量记到文件里,方便排查问题和估算成本。
我个人在带新手时的体会是:Day1最重要的不是学到多少知识,而是建立"我能让它动起来"的信心。环境、Key、请求、解析这四步走通之后,后面所有的复杂度都只是在这条主线上加分支。别急着追求完美,先让它跑起来,再让它跑得稳,最后才谈跑得快。
最后分享一个小技巧:把API Key、接口地址、模型名这三个东西抽到一个单独的配置文件或环境变量里,代码里只引用变量名。这样以后换模型、换服务商,你只需要改一处,不用满代码找硬编码的字符串。这个习惯从第一天养成,后面会省下你大量重构的时间。