从零搭建你的第一个AI应用

从环境搭建到部署上线,手把手带你完成第一个 AI 聊天应用 Maurice | 灵阙学院

前置准备

  • Python 3.10+
  • 一个 OpenAI 或 Anthropic API Key
  • 基本的命令行操作能力

一、搭建 Python 开发环境

创建一个独立的虚拟环境,避免依赖冲突。

# 创建项目目录
mkdir my-first-ai-app && cd my-first-ai-app

# 创建虚拟环境
python3 -m venv .venv

# 激活虚拟环境
source .venv/bin/activate   # macOS/Linux
# .venv\Scripts\activate    # Windows

# 确认 Python 版本
python --version

预期输出:

Python 3.11.x

安装核心依赖:

pip install openai anthropic python-dotenv

创建环境变量文件,存放 API Key(不要提交到 Git):

echo "OPENAI_API_KEY=sk-your-key-here" > .env
echo "ANTHROPIC_API_KEY=sk-ant-your-key-here" >> .env
echo ".env" >> .gitignore

二、第一次调用 AI 模型

创建 chat_basic.py

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "你是一个友好的中文助手。"},
        {"role": "user", "content": "用一句话解释什么是大语言模型。"}
    ]
)

print(response.choices[0].message.content)

运行:

python chat_basic.py

预期输出:

大语言模型是一种通过海量文本数据训练的深度学习模型,能够理解和生成自然语言。

如果你使用 Anthropic Claude,替换为:

import os
from dotenv import load_dotenv
from anthropic import Anthropic

load_dotenv()

client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))

message = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "用一句话解释什么是大语言模型。"}
    ]
)

print(message.content[0].text)

三、添加流式输出

流式输出让用户看到"逐字生成"的效果,体验更好。创建 chat_stream.py

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "你是一个友好的中文助手。"},
        {"role": "user", "content": "简要介绍 Python 的三大特点。"}
    ],
    stream=True  # 关键参数
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

print()  # 结尾换行

预期输出(逐字显示):

Python 的三大特点:
1. **简洁易读**:语法接近自然语言,学习曲线平缓。
2. **生态丰富**:拥有超过 40 万个第三方包,覆盖几乎所有领域。
3. **跨平台**:同一份代码可在 Windows、macOS、Linux 上运行。

四、添加多轮对话历史

AI 模型本身无状态,需要我们手动维护对话历史。创建 chat_history.py

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 对话历史列表
history = [
    {"role": "system", "content": "你是一个友好的中文助手,名叫小灵。"}
]

def chat(user_input: str) -> str:
    history.append({"role": "user", "content": user_input})

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=history
    )

    assistant_msg = response.choices[0].message.content
    history.append({"role": "assistant", "content": assistant_msg})
    return assistant_msg

# 交互式对话循环
print("小灵已上线!输入 'quit' 退出。\n")
while True:
    user_input = input("你: ")
    if user_input.lower() in ("quit", "exit", "q"):
        print("再见!")
        break
    reply = chat(user_input)
    print(f"小灵: {reply}\n")

运行后的交互示例:

小灵已上线!输入 'quit' 退出。

你: 我叫张三
小灵: 你好张三!很高兴认识你,有什么我可以帮你的吗?

你: 你还记得我叫什么吗?
小灵: 当然记得,你叫张三!

你: quit
再见!

五、用 FastAPI 构建 API 服务

安装 FastAPI:

pip install fastapi uvicorn

创建 server.py

import os
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from openai import OpenAI

load_dotenv()

app = FastAPI(title="My First AI App")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

class ChatRequest(BaseModel):
    messages: list[dict]
    stream: bool = False

@app.post("/chat")
async def chat(req: ChatRequest):
    if req.stream:
        return StreamingResponse(
            stream_response(req.messages),
            media_type="text/event-stream"
        )

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=req.messages
    )
    return {"content": response.choices[0].message.content}

async def stream_response(messages: list[dict]):
    stream = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        stream=True
    )
    for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            yield f"data: {content}\n\n"
    yield "data: [DONE]\n\n"

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

启动服务:

python server.py

测试 API:

curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "你好"}]}'

预期输出:

{"content": "你好!有什么我可以帮你的吗?"}

六、添加简单 Web 前端

创建 index.html,放在项目根目录:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>我的第一个 AI 应用</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: system-ui, sans-serif; background: #f5f5f5; }
        .container { max-width: 640px; margin: 40px auto; padding: 20px; }
        h1 { text-align: center; margin-bottom: 24px; color: #333; }
        #chat-box {
            background: #fff; border-radius: 12px; padding: 20px;
            height: 400px; overflow-y: auto; margin-bottom: 16px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
        }
        .msg { margin-bottom: 12px; line-height: 1.6; }
        .msg.user { text-align: right; }
        .msg.user span { background: #007aff; color: #fff; }
        .msg.ai span { background: #e9e9eb; color: #333; }
        .msg span {
            display: inline-block; padding: 8px 14px;
            border-radius: 16px; max-width: 80%;
        }
        .input-row { display: flex; gap: 8px; }
        input {
            flex: 1; padding: 12px 16px; border-radius: 24px;
            border: 1px solid #ddd; font-size: 15px; outline: none;
        }
        button {
            padding: 12px 24px; border-radius: 24px; border: none;
            background: #007aff; color: #fff; font-size: 15px; cursor: pointer;
        }
        button:hover { background: #005ecb; }
    </style>
</head>
<body>
<div class="container">
    <h1>AI 对话助手</h1>
    <div id="chat-box"></div>
    <div class="input-row">
        <input id="input" placeholder="输入消息..."
               onkeydown="if(event.key==='Enter') send()" />
        <button onclick="send()">发送</button>
    </div>
</div>
<script>
const chatBox = document.getElementById('chat-box');
const input = document.getElementById('input');
const messages = [
    { role: 'system', content: '你是一个友好的中文助手。' }
];

function addMessage(role, text) {
    const div = document.createElement('div');
    div.className = `msg ${role === 'user' ? 'user' : 'ai'}`;
    div.innerHTML = `<span>${text}</span>`;
    chatBox.appendChild(div);
    chatBox.scrollTop = chatBox.scrollHeight;
}

async function send() {
    const text = input.value.trim();
    if (!text) return;
    input.value = '';
    addMessage('user', text);
    messages.push({ role: 'user', content: text });

    const res = await fetch('http://localhost:8000/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ messages })
    });
    const data = await res.json();
    addMessage('ai', data.content);
    messages.push({ role: 'assistant', content: data.content });
}
</script>
</body>
</html>

让 FastAPI 同时提供静态文件。在 server.py 中加入:

from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

# 在 app 定义之后添加
@app.get("/")
async def root():
    return FileResponse("index.html")

重启服务后,打开浏览器访问 http://localhost:8000,即可看到聊天界面。


七、项目结构总览

my-first-ai-app/
  .env                # API Key(不要提交)
  .gitignore
  .venv/              # 虚拟环境
  chat_basic.py       # 第一次调用
  chat_stream.py      # 流式输出
  chat_history.py     # 多轮对话
  server.py           # FastAPI 后端
  index.html          # 前端页面
  requirements.txt    # pip freeze > requirements.txt

生成依赖文件:

pip freeze > requirements.txt

常见问题

Q1: 报错 openai.AuthenticationError 检查 .env 中的 API Key 是否正确,确认 load_dotenv()OpenAI() 之前调用。

Q2: 流式输出在浏览器中不生效? 确保 server.pyStreamingResponse 使用了 text/event-stream 类型,前端使用 EventSourcefetchreader 来读取。

Q3: 对话越来越慢、越来越贵? 对话历史 messages 列表会不断增长。实际项目中需要做截断或摘要压缩,只保留最近 N 轮或用摘要替代早期对话。

Q4: 如何切换到 Claude 模型? 安装 anthropic SDK,使用 Anthropic 客户端替换 OpenAI 客户端,API 参数略有不同(max_tokens 为必填)。

Q5: 如何部署到服务器? 推荐使用 gunicorn + uvicorn worker 部署到 VPS,或者使用 Railway/Render/Fly.io 等 PaaS 平台一键部署。


Maurice | maurice_wen@proton.me