
1. 从零起步为什么第一天不急着写代码很多人对AI应用开发这四个字的第一反应是得先学完机器学习、深度学习、Transformer架构把数学基础打牢然后才有资格碰。这个想法不能说错但它把顺序搞反了。我见过太多人卡在准备阶段——买了一堆课看了三章线性代数然后就没有然后了。真正能让人坚持下去的路径恰恰相反先用最小的成本跑通一个能看见效果的完整链路拿到正反馈再回头补底层原理。Day1的目标不是学会AI开发而是让一个AI应用在你自己的电脑上跑起来。这个区别很关键。前者是一个需要几个月甚至几年的长期工程后者是一个两小时内可以完成的具体任务。我们要做的就是后者。具体做什么一个命令行聊天机器人。你在终端里输入一句话它调用大模型API返回回答打印在屏幕上。没有界面没有数据库没有部署就是最纯粹的输入-请求-输出闭环。为什么选这个作为起点因为它把AI应用开发中最核心的三个环节压缩到了最短路径上环境搭建、API调用、结果处理。这三个环节在后续无论你做多复杂的应用——RAG知识库、Agent工作流、多模态应用——本质上都是在这条链路上做加法。还有一个现实原因命令行程序排错最简单。没有前端渲染问题没有跨域没有浏览器缓存出错了就是Python报错堆栈信息一目了然。对于零基础的人来说第一天最怕的就是不知道哪里错了命令行能把这个变量降到最低。这篇文章适合谁如果你满足以下任意一条接下来的内容就是为你写的完全没写过Python但想试试AI开发写过一点Python但不知道怎么接大模型API接过API但总是遇到各种报错不知道怎么排查。我会把每一步的操作意图、可能踩的坑、以及我实际踩过的坑都写清楚你照着做就行。提示Day1不需要安装GPU驱动、不需要CUDA、不需要本地跑模型。所有推理都在云端完成你的电脑只要能上网、能跑Python就行。这一点很重要别被本地部署大模型的教程带偏了。2. Python环境搭建少装一个东西都可能卡你两小时2.1 版本选择与安装路径的坑Python安装这件事看起来简单实际上是我见过新手翻车最多的地方。先说版本选3.10或3.11。为什么不选最新的3.12或3.13因为部分第三方库的预编译包wheel还没跟上最新版本你可能会遇到pip install成功但import报错的诡异情况。3.10和3.11是目前生态兼容性最好的两个版本主流库全部支持。下载渠道只有一个Python官网python.org。不要去各种软件管家或者第三方下载站那些捆绑安装包会给你塞一堆没用的东西还可能改你的系统环境变量。官网下载页面会根据你的操作系统自动推荐对应版本Windows用户直接点Download Python 3.11.x那个大按钮就行。安装时有一个必须注意的选项Windows安装界面底部有一个Add Python to PATH的勾选框一定要勾上。这个选项的作用是把Python的可执行文件路径写入系统环境变量不勾的话你在命令行里敲python会提示不是内部或外部命令。如果你已经装完了才发现没勾不用重装手动添加环境变量也行但那个操作对新手来说更麻烦不如卸了重装一遍来得快。macOS用户相对省心官网下载pkg包双击安装即可PATH会自动配置。但要注意macOS系统自带的Python版本可能比较老比如2.7或者3.9你安装的新版本会以python3命令存在而不是python。这个区别在后面调用的时候要注意。安装完成后打开终端Windows是cmd或PowerShellmacOS是Terminal输入python --version如果显示Python 3.11.x说明安装成功。macOS用户如果python没反应试python3 --version。2.2 pip与虚拟环境为什么不能直接全局安装pip是Python的包管理工具安装Python时会自动带上。验证一下pip --version接下来是一个很多人会跳过但非常重要的步骤创建虚拟环境。虚拟环境的作用是给每个项目一个独立的包安装空间项目A装的库和项目B装的库互不干扰。不用的后果是什么你今天装了一个库的1.0版本明天另一个项目需要2.0版本升级之后第一个项目就跑不起来了。这种问题排查起来非常痛苦因为报错信息往往不会直接告诉你是版本冲突。创建虚拟环境的命令python -m venv mybot这会在当前目录下创建一个叫mybot的文件夹里面包含了一个独立的Python环境。然后激活它# Windows mybot\Scripts\activate # macOS / Linux source mybot/bin/activate激活成功后你的命令行提示符前面会出现(mybot)字样。之后所有pip安装的库都会装在这个虚拟环境里不会污染系统环境。用完想退出输入deactivate即可。注意每次重新打开终端要使用这个项目时都需要重新激活虚拟环境。这不是bug是设计如此。忘了激活就直接pip install库会装到全局环境里然后你运行代码时可能报ModuleNotFoundError因为虚拟环境里没有。2.3 安装requests唯一需要的第三方库Day1我们只需要一个第三方库requests。它是Python里最常用的HTTP请求库用来向大模型API发送请求。安装命令pip install requests就这一条。不需要装openai SDK不需要装langchain不需要装任何框架。为什么因为我想让你看清楚API调用的本质——无非就是构造一个HTTP POST请求把JSON数据发出去再把JSON响应解析回来。框架帮你封装了这些步骤但如果你不知道底层发生了什么出了问题你根本不知道怎么排查。安装完成后验证一下python -c import requests; print(requests.__version__)能打印出版本号就说明没问题。如果报错大概率是虚拟环境没激活或者pip版本太老需要先pip install --upgrade pip。3. 大模型API调用从构造请求到解析响应3.1 API Key的获取与安全存放要调用大模型API你需要一个API Key。以DeepSeek为例注册账号后在控制台的API管理页面可以创建一个Key。这个Key是一串长字符串相当于你的身份凭证每次请求都要带上它。关于API Key的安全有一条铁律绝对不要硬编码在代码里更不要提交到代码仓库。我见过有人把Key直接写在.py文件里然后传到公开仓库几个小时后收到账单才发现被人盗刷了。正确的做法是存在环境变量里代码从环境变量读取。Windows设置环境变量setx DEEPSEEK_API_KEY 你的keymacOS/Linuxexport DEEPSEEK_API_KEY你的keysetx设置后需要重新打开终端才生效。macOS的export只对当前终端会话有效想永久生效需要写进~/.bashrc或~/.zshrc。代码里这样读取import os api_key os.environ.get(DEEPSEEK_API_KEY) if not api_key: raise ValueError(请先设置 DEEPSEEK_API_KEY 环境变量)这个检查看起来多余但实际上能帮你省很多时间。如果Key没设置好程序会立刻告诉你环境变量没找到而不是发一个请求出去然后收到一个模糊的401错误。3.2 请求体构造每个参数到底在控制什么DeepSeek的API接口和OpenAI的格式兼容请求地址是https://api.deepseek.com/chat/completions。一个最简的请求体长这样import requests import os url https://api.deepseek.com/chat/completions headers { Authorization: fBearer {os.environ.get(DEEPSEEK_API_KEY)}, Content-Type: application/json } data { model: deepseek-chat, messages: [ {role: system, content: 你是一个简洁的助手回答控制在三句话以内。}, {role: user, content: 什么是API} ], temperature: 0.7, max_tokens: 500 } response requests.post(url, headersheaders, jsondata, timeout30) result response.json() print(result[choices][0][message][content])逐项拆解这些参数的含义因为不理解它们你就没法调优model指定用哪个模型。DeepSeek目前有deepseek-chat通用对话和deepseek-reasoner推理增强两个主要选项。Day1用deepseek-chat就够了响应快、价格低。messages是一个列表包含对话历史。每条消息有role和content两个字段。role有三种system用来设定AI的行为风格user是用户的输入assistant是AI之前的回复。多轮对话的原理就是把这个列表越拼越长每次把完整历史发给API。这一点很关键——API本身是无状态的它不记得你上一句说了什么所谓记忆完全是客户端在维护。temperature控制输出的随机性范围0到2。值越低输出越确定、越保守值越高越有创造性但也越容易跑偏。写代码、做翻译这类任务建议0.2到0.5创意写作可以0.8到1.2。Day1聊天机器人设0.7是个中庸的选择。max_tokens限制回复的最大长度。注意这不是字数而是token数。一个中文字大约对应1到2个token英文一个单词大约1到1.5个token。设500大概能容纳300到400个中文字。设太小会导致回复被截断设太大浪费额度虽然不一定会用完但有些API会按最大值预留计费。3.3 响应解析与错误处理API返回的是一个JSON对象结构大致如下{ choices: [ { message: { role: assistant, content: API是应用程序编程接口... }, finish_reason: stop } ], usage: { prompt_tokens: 25, completion_tokens: 48, total_tokens: 73 } }取回复内容就是result[choices][0][message][content]。usage字段告诉你这次请求消耗了多少token方便你估算成本。但实际运行中你不会每次都拿到正常响应。最常见的错误是429——请求频率超限。这个错误的完整信息通常是exceeded retry limit, last status: 429 too many requests。意思是你在短时间内发了太多请求触发了服务端的限流。解决办法有两个一是降低请求频率在每次请求之间加time.sleep(1)二是实现重试逻辑收到429后等几秒再试。import time def chat(messages, retries3): for i in range(retries): try: response requests.post(url, headersheaders, json{ model: deepseek-chat, messages: messages, temperature: 0.7 }, timeout30) if response.status_code 429: wait 2 ** i print(f请求太频繁等待{wait}秒后重试...) time.sleep(wait) continue response.raise_for_status() return response.json()[choices][0][message][content] except requests.exceptions.Timeout: print(f请求超时第{i1}次重试...) time.sleep(2) except requests.exceptions.RequestException as e: print(f请求出错{e}) return None return None这段重试逻辑用了指数退避——第一次等1秒第二次等2秒第三次等4秒。为什么用指数而不是固定间隔因为如果是服务端整体拥堵固定间隔重试只会加剧拥堵指数退避给服务端恢复的时间也提高你重试成功的概率。4. 命令行聊天机器人把零件组装成完整程序4.1 对话循环的设计思路有了API调用的基础聊天机器人的逻辑就很直接了一个无限循环每次读取用户输入追加到消息列表发送请求打印回复然后等下一次输入。import os import time import requests API_KEY os.environ.get(DEEPSEEK_API_KEY) URL https://api.deepseek.com/chat/completions HEADERS { Authorization: fBearer {API_KEY}, Content-Type: application/json } def chat(messages): data { model: deepseek-chat, messages: messages, temperature: 0.7, max_tokens: 1000 } for i in range(3): try: resp requests.post(URL, headersHEADERS, jsondata, timeout30) if resp.status_code 429: time.sleep(2 ** i) continue resp.raise_for_status() return resp.json()[choices][0][message][content] except requests.exceptions.Timeout: print(超时重试中...) time.sleep(2) return 抱歉请求失败了请稍后再试。 def main(): messages [ {role: system, content: 你是一个友好的助手用简洁的中文回答问题。} ] print(聊天机器人已启动输入 quit 退出。) while True: user_input input(\n你).strip() if not user_input: continue if user_input.lower() in (quit, exit, q): print(再见) break messages.append({role: user, content: user_input}) reply chat(messages) print(f\nAI{reply}) messages.append({role: assistant, content: reply}) if __name__ __main__: main()这段代码有几个设计决策值得说明。为什么把system消息放在循环外面因为system消息只需要设置一次它定义了AI的全局行为。如果放在循环里每次都重新构造虽然效果一样但代码更啰嗦。为什么要把AI的回复也追加到messages里这就是多轮对话的实现方式。每次请求都把完整的对话历史发过去AI才能看到之前聊了什么。代价是token消耗会随着对话轮次增加而增长聊得越久每次请求越贵。Day1不用管这个但心里要有数。为什么用strip()处理输入用户可能不小心多敲了空格或回车strip()去掉首尾空白字符。如果输入为空就跳过避免发一个空消息给API浪费额度。4.2 实测中会遇到的问题第一次跑起来大概率不会一帆风顺。以下是我实际遇到过的情况和解决办法。中文乱码。Windows的cmd默认编码可能是GBK而API返回的是UTF-8打印中文时可能出现乱码。解决办法是在代码开头加import sys sys.stdout.reconfigure(encodingutf-8)或者在运行前执行chcp 65001切换cmd的编码页。响应特别慢。大模型生成回复需要时间尤其是回复较长时等十几秒是正常的。timeout30设的是30秒超时如果网络状况不好可能需要调到60。但注意超时时间设太长也不好——如果请求真的卡死了你要等很久才知道。我的经验是30到60秒比较合理。Key无效或余额不足。如果返回401检查Key是否正确、是否有多余的空格。如果返回402或类似错误检查账户余额。这些错误信息API都会返回在response.json()里能看到具体的错误描述调试时把完整的响应打印出来看。对话越来越慢。聊了十几轮之后每次请求的token数会显著增加响应变慢、费用上升。Day1的简单实现没有做历史截断实际项目中需要限制历史消息的数量或者做摘要压缩。这是后续要解决的问题第一天知道有这回事就行。4.3 从能跑到好用几个立竿见影的改进程序跑通之后可以做一些小改进让它更好用。流式输出。默认情况下AI生成完整回复后才一次性返回你要盯着空屏幕等好几秒。流式输出可以让文字像打字机一样逐字出现体验好很多。实现方式是在请求体里加stream: True然后逐行读取响应def chat_stream(messages): data { model: deepseek-chat, messages: messages, temperature: 0.7, stream: True } resp requests.post(URL, headersHEADERS, jsondata, streamTrue, timeout60) full_reply for line in resp.iter_lines(): if not line: continue line line.decode(utf-8) if line.startswith(data: ): payload line[6:] if payload [DONE]: break import json chunk json.loads(payload) delta chunk[choices][0][delta].get(content, ) print(delta, end, flushTrue) full_reply delta print() return full_reply流式输出的原理是服务端每生成一小段文字就推送给客户端而不是等全部生成完再发。iter_lines()逐行读取响应每行以data:开头最后以data: [DONE]结束。这个格式和OpenAI的流式接口一致。保存对话记录。把messages列表在退出时写入一个JSON文件下次启动时读回来就能实现跨会话的记忆。这个功能实现简单但很实用import json def save_history(messages, filenamehistory.json): with open(filename, w, encodingutf-8) as f: json.dump(messages, f, ensure_asciiFalse, indent2) def load_history(filenamehistory.json): try: with open(filename, r, encodingutf-8) as f: return json.load(f) except FileNotFoundError: return [{role: system, content: 你是一个友好的助手。}]加一个清空历史的命令。在循环里判断如果用户输入/clear就把messages重置为只含system消息的初始状态。这样聊到一半想换话题时不用重启程序。5. 第一天之后这条路接下来怎么走Day1跑通了一个命令行聊天机器人代码量不到一百行但它包含了一个AI应用的全部核心要素身份认证、请求构造、响应解析、错误处理、多轮对话管理。后面无论你做什么方向的应用都是在这个骨架上长肉。如果你想继续深入几个自然的下一步给机器人加上工具调用能力让它能查天气、算数学、搜索网页把命令行换成Web界面用Flask或FastAPI做一个简单的聊天页面接入向量数据库做RAG让机器人能回答你私有文档里的问题。每一条路都建立在你今天搭好的这个基础之上。我在带新人的时候经常说一句话第一天最重要的不是学会了什么而是确认了我能让它跑起来。这个确认本身就是最大的收获。很多人卡在第一步不是因为技术难而是因为不确定自己走的方向对不对。跑通一个最小闭环你就有了一个可以不断迭代的起点。最后分享一个我自己的习惯每次开始一个新项目第一件事就是写一个能跑通全链路的最小版本哪怕它只输出hello world。这个版本可能丑、可能慢、可能功能残缺但它能跑。有了能跑的版本后面所有的改进都有参照物你知道改坏了可以退回来。没有能跑的版本你就是在黑暗中摸索每一步都不确定对不对。Day1的聊天机器人就是这样一个最小可运行版本把它保存好后面你会经常回来参考它。