# 使用 AI 构建聊天助手
还记得《星际迷航》里,船员们随意与飞船计算机聊天,提出复杂问题并得到深思熟虑的回答吗? 在20世纪60年代这似乎纯属科幻的场景,现在你可以使用已掌握的网页技术来实现。
在本课中,我们将使用 HTML、CSS、JavaScript 以及一些后端集成创建一个 AI 聊天助手。你将发现,所学技能如何连接到强大的 AI 服务,这些服务能够理解上下文并生成有意义的回答。
可以把 AI 想象成拥有访问一个庞大图书馆的能力,它不仅能查找信息,还能将信息综合成符合你特定问题的连贯答案。你无需翻阅成千上万页,只需获得直接且有上下文的回应。
这一集成通过熟悉的网页技术协同工作完成。HTML 创建聊天界面,CSS 处理视觉设计,JavaScript 管理用户交互,后端 API 连接 AI 服务。类似管弦乐队不同部门协作演奏交响乐。
我们实际上是在搭建一座桥梁,连接自然的人类交流和机器处理。你将学习 AI 服务集成的技术实现,也将掌握让交互感觉直观的设计模式。
到本课结束时,AI 集成不再神秘,而是另一个你可以操作的 API。你将理解 ChatGPT 和 Claude 等应用背后的基础模式,使用你一直学习的网页开发原理。
## ⚡ 接下来5分钟你能做什么
**为忙碌开发者准备的快速入门路径**
```mermaid
flowchart LR
A[⚡ 5 分钟] --> B[获取 GitHub 令牌]
B --> C[测试 AI 操作台]
C --> D[复制 Python 代码]
D --> E[查看 AI 响应]
```
- **第1分钟**:访问 [GitHub Models Playground](https://github.com/marketplace/models/azure-openai/gpt-4o-mini/playground),创建个人访问令牌
- **第2分钟**:在演示界面直接测试 AI 交互
- **第3分钟**:点击“Code”标签,复制 Python 代码片段
- **第4分钟**:携带你的令牌本地运行代码:`GITHUB_TOKEN=your_token python test.py`
- **第5分钟**:观看你的代码首次生成的 AI 回应
**快速测试代码**:
```python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://models.github.ai/inference",
api_key="your_token_here"
)
response = client.chat.completions.create(
messages=[{"role": "user", "content": "Hello AI!"}],
model="openai/gpt-4o-mini"
)
print(response.choices[0].message.content)
```
**为何重要**:5分钟内,你将体验编程式 AI 交互的神奇。这是支撑所有你使用的 AI 应用的基本构建模块。
这是你完成项目后的效果:

## 🗺️ 你的 AI 应用开发学习之旅
```mermaid
journey
title 从网页开发到AI集成
section 理解AI基础
发现生成型AI概念: 4: You
探索GitHub模型平台: 6: You
掌握AI参数和提示: 8: You
section 后端集成
构建Python API服务器: 5: You
实现AI函数调用: 7: You
处理异步操作: 8: You
section 前端开发
创建现代聊天界面: 6: You
掌握实时交互: 8: You
构建响应式用户体验: 9: You
section 专业应用
部署完整AI系统: 7: You
优化性能模式: 8: You
创建生产就绪应用: 9: You
```
**你的目标**:完成本课后,你将构建完整的 AI 驱动应用,使用驱动现代 AI 助手(如 ChatGPT、Claude 和 Google Bard)的相同技术和模式。
## 认识 AI:从神秘到精通
在深入代码前,先理解我们要处理的内容。若你用过 API,知道基本模式:发送请求,接收响应。
AI API 也遵循类似结构,但它不是从数据库读取预存数据,而是基于海量文本学习的模式生成全新回应。可以把它想像成图书馆目录系统和通晓多源信息的博学图书管理员之间的区别。
### 什么是真正的“生成式 AI”?
想象罗塞塔石碑如何帮助学者通过已知和未知语言间的关系理解埃及象形文字。AI 模型的工作方式类似——它们通过大量文本找到语言的规律,再用这些规律生成对新问题的恰当回答。
**简单类比说明:**
- **传统数据库**:就像索取你的出生证明文件——每次都是同一个文件
- **搜索引擎**:像请图书管理员帮你找关于猫的书——他们展示现有资料
- **生成式 AI**:像问一个博学的朋友关于猫的知识——他们用自己的话,针对你想知道的内容讲述有趣的知识
```mermaid
graph LR
A[你的问题] --> B[AI模型]
B --> C[模式识别]
C --> D[内容生成]
D --> E[上下文响应]
F[训练数据
书籍,文章,网络] --> B
```
### AI 模型如何学习(简化说明)
AI 模型通过接触包含书籍、文章和对话文本的巨大数据集学习。在此过程中,它们识别出:
- 思维在书面交流中的构造方式
- 通常一起出现的词汇
- 对话的典型流转
- 正式与非正式交流的上下文差异
**这类似考古学家解码古代语言**:他们分析数千个实例,理解语法、词汇和文化背景,最终能够运用学到的模式解读新文本。
### 为什么选用 GitHub Models?
我们使用 GitHub Models 非常实用——它让我们无需自行搭建 AI 基础设施(相信我,你现在不想这么做!)即可使用企业级 AI。就像使用天气 API 而不是自己到处布置气象站预测天气。
它基本上是“AI 即服务”,好处是可以免费开始尝试,不用担心产生高昂费用。
```mermaid
graph LR
A[前端聊天界面] --> B[您的后端API]
B --> C[GitHub模型API]
C --> D[AI模型处理]
D --> C
C --> B
B --> A
```
我们将用 GitHub Models 做后端集成,通过为开发者优化的界面访问专业级 AI 功能。 [GitHub Models Playground](https://github.com/marketplace/models/azure-openai/gpt-4o-mini/playground) 是测试环境,你可以尝试不同 AI 模型,理解其能力后再在代码中实现它们。
## 🧠 AI 应用开发生态系统
```mermaid
mindmap
root((AI 开发))
Understanding AI
Generative Models
Pattern Recognition
Content Generation
Context Understanding
Response Synthesis
AI Parameters
Temperature Control
Token Limits
Top-p Filtering
System Prompts
Backend Architecture
API Integration
GitHub Models
Authentication
Request Handling
Error Management
Python Infrastructure
FastAPI Framework
Async Operations
Environment Security
CORS Configuration
Frontend Experience
Chat Interface
Real-time Updates
Message History
User Feedback
Loading States
Modern Web Tech
ES6 Classes
Async/Await
DOM Manipulation
Event Handling
Professional Patterns
Security Best Practices
Token Management
Input Validation
XSS Prevention
Error Boundaries
Production Readiness
Performance Optimization
Responsive Design
Accessibility
Testing Strategies
```
**核心原则**:AI 应用开发融合传统网页开发技能与 AI 服务集成,创建感觉自然且响应迅速的智能应用。

**Playground 超有用的原因:**
- **试用** 不同 AI 模型如 GPT-4o-mini、Claude 等(全都免费!)
- **测试** 你的想法和提示,代码编写前先验
- **获取** 适用你喜好编程语言的示例代码
- **调整** 创造力水平和响应长度等设置,观察输出变化
玩过后,直接点“Code”标签选你要的编程语言,拿到实现代码。

## 配置 Python 后端集成
现在用 Python 实现 AI 集成。Python 因语法简单且库强大,非常适合 AI 应用。我们将从 GitHub Models playground 的代码开始,然后重构成可复用、适用于生产的函数。
### 理解基础实现
从 playground 获取的 Python 代码大致如下。别担心开始时代码看起来复杂——我们逐步讲解:
```python
"""Run this model in Python
> pip install openai
"""
import os
from openai import OpenAI
# 要与模型进行身份验证,您需要在GitHub设置中生成个人访问令牌(PAT)。
# 按照此处的说明创建您的PAT令牌:https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
client = OpenAI(
base_url="https://models.github.ai/inference",
api_key=os.environ["GITHUB_TOKEN"],
)
response = client.chat.completions.create(
messages=[
{
"role": "system",
"content": "",
},
{
"role": "user",
"content": "What is the capital of France?",
}
],
model="openai/gpt-4o-mini",
temperature=1,
max_tokens=4096,
top_p=1
)
print(response.choices[0].message.content)
```
**代码中的关键操作:**
- **导入** 需要的工具:`os` 用于读取环境变量,`OpenAI` 用于与 AI 通讯
- **设置** OpenAI 客户端,指向 GitHub 的 AI 服务器而非直接 OpenAI
- **验证** 采用特殊的 GitHub 令牌(稍后详述!)
- **构造** 多角色对话结构——可想象成为戏剧布置场景
- **发送** 请求至 AI,带有微调参数
- **提取** 返回数据中的实际回应文本
### 理解消息角色:AI 对话框架
AI 对话采用特定结构,包含不同“角色”以实现各自功能:
```python
messages=[
{
"role": "system",
"content": "You are a helpful assistant who explains things simply."
},
{
"role": "user",
"content": "What is machine learning?"
}
]
```
**就像导演排戏:**
- **System 角色**:给演员的舞台指导——告诉 AI 如何表现、具备什么个性和应答方式
- **User 角色**:使用者的问题或信息
- **Assistant 角色**:AI 的回答(你不会发送,出现在对话历史中)
**现实模拟**:想象你在派对介绍朋友:
- **系统消息**:“这是我的朋友 Sarah,她是位医生,擅长用简单语言解释医学概念”
- **用户消息**:“你能解释疫苗如何工作吗?”
- **助手回应**:Sarah 以友好的医生身份作答,而非律师或厨师身份
### 理解 AI 参数:微调应答行为
AI API 中的数值参数控制模型生成回答的方式。这些设置能让你根据不同场景调整 AI 行为:
#### Temperature(0.0 到 2.0):创造力旋钮
**作用**:控制 AI 回答的创造力或可预测性。
**想象一名爵士乐手的即兴水平:**
- **Temperature = 0.1**:每次演奏完全相同旋律(非常可预测)
- **Temperature = 0.7**:带有适度变化,既可识别又有创意(均衡创造力)
- **Temperature = 1.5**:完全实验性爵士,大量意外转折(高度不可预测)
```python
# 非常可预测的回答(适合事实性问题)
response = client.chat.completions.create(
messages=[{"role": "user", "content": "What is 2+2?"}],
temperature=0.1 # 几乎总是会说“4”
)
# 有创意的回答(适合头脑风暴)
response = client.chat.completions.create(
messages=[{"role": "user", "content": "Write a creative story opening"}],
temperature=1.2 # 会生成独特且出乎意料的故事
)
```
#### Max Tokens(1 到 4096+):回答长度控制器
**作用**:设置 AI 回答的最长长度限制。
**Token 大致等同于单词**(英文约1个 token = 0.75个词):
- **max_tokens=50**:简短回复(类似短信)
- **max_tokens=500**:一两段合适长度的段落
- **max_tokens=2000**:含示例的详细解释
```python
# 简短,精炼的回答
response = client.chat.completions.create(
messages=[{"role": "user", "content": "Explain JavaScript"}],
max_tokens=100 # 强制简短解释
)
# 详细,全面的回答
response = client.chat.completions.create(
messages=[{"role": "user", "content": "Explain JavaScript"}],
max_tokens=1500 # 允许详细的解释和示例
)
```
#### Top_p(0.0 到 1.0):专注度参数
**作用**:控制 AI 在生成回答时集中在概率最高的词汇程度。
**想象 AI 拥有庞大词汇表,按词出现概率排序:**
- **top_p=0.1**:只考虑概率最高的10%词汇(非常集中)
- **top_p=0.9**:考虑90%的可能词汇(更多创意)
- **top_p=1.0**:考虑所有词汇(最大多样性)
**举例**:你问 “天空通常是……”
- **低 top_p**:几乎肯定回答“蓝色”
- **高 top_p**:可能回答“蓝色”、“多云”、“辽阔”、“变化多端”、“美丽”等
### 综合应用:针对不同场景的参数组合
```python
# 用于提供事实性、一致性的答案(如文档机器人)
factual_params = {
"temperature": 0.2,
"max_tokens": 300,
"top_p": 0.3
}
# 用于创意写作辅助
creative_params = {
"temperature": 1.1,
"max_tokens": 1000,
"top_p": 0.9
}
# 用于对话式、有帮助的回复(平衡型)
conversational_params = {
"temperature": 0.7,
"max_tokens": 500,
"top_p": 0.8
}
```
```mermaid
quadrantChart
title AI 参数优化矩阵
x-axis 低创造力 --> 高创造力
y-axis 短响应 --> 长响应
quadrant-1 创意内容
quadrant-2 详细分析
quadrant-3 快速事实
quadrant-4 会话式 AI
Documentation Bot: [0.2, 0.3]
Customer Service: [0.4, 0.4]
General Assistant: [0.7, 0.5]
Creative Writer: [0.9, 0.9]
Brainstorming Tool: [0.8, 0.8]
```
**为什么参数重要**:不同应用需要不同类型的回答。例如客户服务机器人需要稳定且事实性强的回答(低 Temperature),而创意写作助手则需富有想象力(高 Temperature)。掌握这些参数让你能塑造 AI 个性和回应风格。
```
**Here's what's happening in this code:**
- **We import** the tools we need: `os` for reading environment variables and `OpenAI` for talking to the AI
- **We set up** the OpenAI client to point to GitHub's AI servers instead of OpenAI directly
- **We authenticate** using a special GitHub token (more on that in a minute!)
- **We structure** our conversation with different "roles" – think of it like setting the scene for a play
- **We send** our request to the AI with some fine-tuning parameters
- **We extract** the actual response text from all the data that comes back
> 🔐 **Security Note**: Never hardcode API keys in your source code! Always use environment variables to store sensitive credentials like your `GITHUB_TOKEN`.
### Creating a Reusable AI Function
Let's refactor this code into a clean, reusable function that we can easily integrate into our web application:
```python
import asyncio
from openai import AsyncOpenAI
# Use AsyncOpenAI for better performance
client = AsyncOpenAI(
base_url="https://models.github.ai/inference",
api_key=os.environ["GITHUB_TOKEN"],
)
async def call_llm_async(prompt: str, system_message: str = "You are a helpful assistant."):
"""
Sends a prompt to the AI model asynchronously and returns the response.
Args:
prompt: The user's question or message
system_message: Instructions that define the AI's behavior and personality
Returns:
str: The AI's response to the prompt
"""
try:
response = await client.chat.completions.create(
messages=[
{
"role": "system",
"content": system_message,
},
{
"role": "user",
"content": prompt,
}
],
model="openai/gpt-4o-mini",
temperature=1,
max_tokens=4096,
top_p=1
)
return response.choices[0].message.content
except Exception as e:
logger.error(f"AI API error: {str(e)}")
return "I'm sorry, I'm having trouble processing your request right now."
# Backward compatibility function for synchronous calls
def call_llm(prompt: str, system_message: str = "You are a helpful assistant."):
"""Synchronous wrapper for async AI calls."""
return asyncio.run(call_llm_async(prompt, system_message))
```
**理解此改进函数:**
- **接收**两个参数:用户提示和可选的系统消息
- **提供**默认系统消息以控制一般助理行为
- **使用**恰当的 Python 类型注解,增强代码可读性
- **包含**详细文档字符串说明函数目的及参数
- **只返回**响应内容,便于在 Web API 中使用
- **保持**模型参数不变,保证 AI 行为一致
### 系统提示的魔力:编程 AI 个性
如果参数控制 AI 如何“思考”,系统提示控制 AI “认为自己是谁”。这真是 AI 工作中最酷的部分之一——你实际上是在赋予 AI 完整个性、专业水平和沟通风格。
**把系统提示想象成为不同角色选角**:你不必只有一位通用助理,可创建适用于不同场合的专家。需要耐心老师?创造性头脑风暴伙伴?直截了当的商业顾问?只需更改系统提示即可!
#### 系统提示为何如此强大
有趣的是:AI 模型经过无数对话训练,人们在对话中扮演不同角色和展现不同专业水平。给 AI 明确的角色相当于开关,激活这些学习到的模式。
**这就像 AI 的方法派演技**:告诉演员“你是位睿智老教授”,观察他们如何自动调整姿态、用词和举止。AI 用语言模式做同样操作。
#### 打造有效系统提示的艺术与科学
**一个优质系统提示的结构:**
1. **角色/身份**:AI 是谁?
2. **专业领域**:懂什么?
3. **沟通风格**:如何说话?
4. **具体指令**:专注什么?
```python
# ❌ 模糊的系统提示
"You are helpful."
# ✅ 详细且有效的系统提示
"You are Dr. Sarah Chen, a senior software engineer with 15 years of experience at major tech companies. You explain programming concepts using real-world analogies and always provide practical examples. You're patient with beginners and enthusiastic about helping them understand complex topics."
```
#### 系统提示示例及上下文
看看不同系统提示如何塑造截然不同的 AI 个性:
```python
# 示例 1:耐心的老师
teacher_prompt = """
You are an experienced programming instructor who has taught thousands of students.
You break down complex concepts into simple steps, use analogies from everyday life,
and always check if the student understands before moving on. You're encouraging
and never make students feel bad for not knowing something.
"""
# 示例 2:富有创意的合作者
creative_prompt = """
You are a creative writing partner who loves brainstorming wild ideas. You're
enthusiastic, imaginative, and always build on the user's ideas rather than
replacing them. You ask thought-provoking questions to spark creativity and
offer unexpected perspectives that make stories more interesting.
"""
# 示例 3:战略商业顾问
business_prompt = """
You are a strategic business consultant with an MBA and 20 years of experience
helping startups scale. You think in frameworks, provide structured advice,
and always consider both short-term tactics and long-term strategy. You ask
probing questions to understand the full business context before giving advice.
"""
```
#### 观察系统提示的实际效果
用不同系统提示测试同一问题,感受区别:
**问题**:“我如何处理我的网页应用中的用户认证?”
```python
# 使用教师提示:
teacher_response = call_llm(
"How do I handle user authentication in my web app?",
teacher_prompt
)
# 典型回复:“好问题!让我们将身份验证拆解成简单的步骤。
# 把它想象成夜总会保安检查身份证……”
# 使用商业提示:
business_response = call_llm(
"How do I handle user authentication in my web app?",
business_prompt
)
# 典型回复:“从战略角度来看,身份验证对于用户
# 信任和合规性至关重要。让我概述一个考虑安全性、
# 用户体验和可扩展性的框架……”
```
#### 高级系统提示技巧
**1. 上下文设定**:给 AI 背景信息
```python
system_prompt = """
You are helping a junior developer who just started their first job at a startup.
They know basic HTML/CSS/JavaScript but are new to backend development and databases.
Be encouraging and explain things step-by-step without being condescending.
"""
```
**2. 输出格式**:告诉 AI 如何结构化响应
```python
system_prompt = """
You are a technical mentor. Always structure your responses as:
1. Quick Answer (1-2 sentences)
2. Detailed Explanation
3. Code Example
4. Common Pitfalls to Avoid
5. Next Steps for Learning
"""
```
**3. 约束设置**:定义 AI 不应该做什么
```python
system_prompt = """
You are a coding tutor focused on teaching best practices. Never write complete
solutions for the user - instead, guide them with hints and questions so they
learn by doing. Always explain the 'why' behind coding decisions.
"""
```
#### 为什么这对于您的聊天助手很重要
理解系统提示让您拥有强大能力,创建专业的 AI 助手:
- **客服机器人**:有帮助、耐心、了解政策
- **学习导师**:鼓励、循序渐进、检查理解情况
- **创意伙伴**:富有想象力、构建想法、提出“如果怎样?”
- **技术专家**:精准、详尽,注重安全
**关键洞见**:您不仅仅是在调用 AI API——您正在创建一个为您的特定用例服务的定制 AI 个性。这就是现代 AI 应用感觉定制且有用,而非泛泛之辈的原因。
### 🎯 教学检查:AI 个性编程
**暂停反思**:您刚刚学习了通过系统提示编程 AI 个性。这是现代 AI 应用开发的基本技能。
**快速自我评估**:
- 您能解释系统提示与普通用户消息有何不同吗?
- temperature 和 top_p 参数有何区别?
- 您如何为特定用例(如编程导师)创建系统提示?
**现实世界联系**:您学习的系统提示技术应用在所有主要的 AI 应用中——从 GitHub Copilot 的代码辅助到 ChatGPT 的对话界面。您正在掌握大型科技公司 AI 产品团队使用的相同模式。
**挑战问题**:您如何为不同用户类型(初学者 vs 专家)设计不同的 AI 个性?考虑如何通过提示工程让同一个底层 AI 模型服务不同受众。
## 使用 FastAPI 构建 Web API:您的高性能 AI 通信中枢
现在让我们构建连接前端与 AI 服务的后端。我们将使用 FastAPI,这是一款现代 Python 框架,擅长构建 AI 应用的 API。
FastAPI 为此类项目提供了多项优势:内置异步支持处理并发请求、自动生成 API 文档以及卓越性能。您的 FastAPI 服务器充当中介,接收来自前端的请求,调用 AI 服务,并返回格式化响应。
### 为什么选择 FastAPI 用于 AI 应用?
您可能会想:“我不是可以直接用前端 JavaScript 调用 AI 吗?”或“为什么用 FastAPI,而不是 Flask 或 Django?”好问题!
**FastAPI 之所以适合我们的构建,原因如下:**
- **默认异步**:可以同时处理多个 AI 请求,不会卡住
- **自动文档**:访问 `/docs` 即可免费获得漂亮且交互式的 API 文档页面
- **内置验证**:在出现问题前捕获错误
- **速度快**:Python 框架中速度最快之一
- **现代 Python**:利用所有最新最棒的 Python 特性
**而我们为什么需要后端:**
**安全**:您的 AI API 密钥就像密码——如果放在前端 JavaScript 中,任何查看您网站源码的人都能窃取并使用您的 AI 配额。后端可以保护秘密凭据。
**限流与控制**:后端允许您控制用户请求频率,实施用户认证,并添加日志记录以追踪使用情况。
**数据处理**:您可能想保存对话、过滤不当内容或组合多个 AI 服务。后端是这些逻辑所在。
**架构类似客户端-服务器模型:**
- **前端**:交互的用户界面层
- **后端 API**:请求处理和路由层
- **AI 服务**:外部计算与响应生成
- **环境变量**:安全配置和凭据存储
### 理解请求-响应流程
让我们追踪用户发送一条消息时的过程:
```mermaid
sequenceDiagram
participant User as 👤 用户
participant Frontend as 🌐 前端
participant API as 🔧 FastAPI 服务器
participant AI as 🤖 AI 服务
User->>Frontend: 输入 "你好 AI!"
Frontend->>API: POST /hello {"message": "你好 AI!"}
Note over API: 验证请求
添加系统提示
API->>AI: 发送格式化请求
AI->>API: 返回 AI 响应
Note over API: 处理响应
记录对话
API->>Frontend: {"response": "你好!我能帮您什么?"}
Frontend->>User: 显示 AI 消息
```
**理解每一步:**
1. **用户交互**:人在聊天界面输入
2. **前端处理**:JavaScript 捕获输入并格式化为 JSON
3. **API 验证**:FastAPI 使用 Pydantic 模型自动验证请求
4. **AI 集成**:后端添加上下文(系统提示)并调用 AI 服务
5. **响应处理**:API 接收 AI 响应并根据需要修改
6. **前端显示**:JavaScript 在聊天界面显示响应
### 理解 API 架构
```mermaid
sequenceDiagram
participant Frontend
participant FastAPI
participant AI Function
participant GitHub Models
Frontend->>FastAPI: POST /hello {"message": "你好 AI!"}
FastAPI->>AI Function: call_llm(message, system_prompt)
AI Function->>GitHub Models: API 请求
GitHub Models->>AI Function: AI 响应
AI Function->>FastAPI: 响应文本
FastAPI->>Frontend: {"response": "你好!我能帮你什么?"}
```
```mermaid
flowchart TD
A[用户输入] --> B[前端校验]
B --> C[HTTP POST 请求]
C --> D[FastAPI 路由]
D --> E[Pydantic 校验]
E --> F[AI 函数调用]
F --> G[GitHub 模型 API]
G --> H[响应处理]
H --> I[JSON 响应]
I --> J[前端更新]
subgraph "安全层"
K[CORS 中间件]
L[环境变量]
M[错误处理]
end
D --> K
F --> L
H --> M
```
### 创建 FastAPI 应用
让我们一步步构建 API。创建文件 `api.py`,内容如下:
```python
# api.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from llm import call_llm
import logging
# 配置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 创建FastAPI应用
app = FastAPI(
title="AI Chat API",
description="A high-performance API for AI-powered chat applications",
version="1.0.0"
)
# 配置跨域资源共享(CORS)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 为生产环境进行适当配置
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 用于请求/响应验证的Pydantic模型
class ChatMessage(BaseModel):
message: str
class ChatResponse(BaseModel):
response: str
@app.get("/")
async def root():
"""Root endpoint providing API information."""
return {
"message": "Welcome to the AI Chat API",
"docs": "/docs",
"health": "/health"
}
@app.get("/health")
async def health_check():
"""Health check endpoint."""
return {"status": "healthy", "service": "ai-chat-api"}
@app.post("/hello", response_model=ChatResponse)
async def chat_endpoint(chat_message: ChatMessage):
"""Main chat endpoint that processes messages and returns AI responses."""
try:
# 提取并验证消息
message = chat_message.message.strip()
if not message:
raise HTTPException(status_code=400, detail="Message cannot be empty")
logger.info(f"Processing message: {message[:50]}...")
# 调用AI服务(注意:call_llm应当异步调用以提升性能)
ai_response = await call_llm_async(message, "You are a helpful and friendly assistant.")
logger.info("AI response generated successfully")
return ChatResponse(response=ai_response)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error processing chat message: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5000, reload=True)
```
**理解 FastAPI 实现:**
- **导入** FastAPI 实现现代 Web 框架功能,和 Pydantic 用于数据验证
- **创建** 自动 API 文档(服务器运行时可在 `/docs` 访问)
- **启用** CORS 中间件允许来自不同来源的前端请求
- **定义** Pydantic 模型,实现自动请求/响应验证与文档生成
- **使用** 异步端点以提升并发请求的性能
- **实现** 正确的 HTTP 状态码和错误处理,使用 HTTPException
- **包含** 结构化日志以便监控和调试
- **提供** 健康检查端点用于监控服务状态
**FastAPI 相比传统框架的优势:**
- **自动验证**:Pydantic 模型保证处理前数据完整性
- **交互文档**:访问 `/docs` 获取自动生成的、可测试 API 文档
- **类型安全**:Python 类型提示防止运行时错误,提高代码质量
- **异步支持**:无阻塞地同时处理多个 AI 请求
- **性能优越**:实时应用请求处理速度显著提升
### 理解 CORS:网络的安全守护
CORS(跨源资源共享)就像建筑物门口的保安,检查访客是否获准入内。让我们了解其重要性以及如何影响应用。
#### 什么是 CORS 及其存在原因?
**问题**:假设任何网站都能无许可代表您向银行网站发请求,那将是安全灾难!浏览器默认通过 “同源策略” 来防止这种情况。
**同源策略**:浏览器只允许网页向加载该页面的相同域、端口和协议发请求。
**形象比喻**:这就像公寓楼保安——默认只有住户(同源)能进入楼内。若您想让朋友(异源)访问,需明确告知保安。
#### 开发环境中的 CORS
开发时前后端运行在不同端口:
- 前端:`http://localhost:3000`(或者直接打开 HTML 是 file://)
- 后端:`http://localhost:5000`
即使都在同一台电脑上,仍被视为“不同来源”!
```python
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(__name__)
CORS(app) # 这告诉浏览器:“允许其他来源对这个API发起请求”
```
**CORS 配置的实际作用:**
- **添加** 特殊 HTTP 头,告知浏览器“此跨源请求被允许”
- **处理** “预检”请求(浏览器有时先核查权限再发正式请求)
- **防止** 浏览器控制台出现可恶的 “被 CORS 策略阻止” 错误
#### CORS 安全性:开发 vs 生产
```python
# 🚨 开发:允许所有来源(方便但不安全)
CORS(app)
# ✅ 生产:仅允许您的特定前端域名
CORS(app, origins=["https://yourdomain.com", "https://www.yourdomain.com"])
# 🔒 高级:不同环境不同来源
if app.debug: # 开发模式
CORS(app, origins=["http://localhost:3000", "http://127.0.0.1:3000"])
else: # 生产模式
CORS(app, origins=["https://yourdomain.com"])
```
**重要性说明**:开发阶段,`CORS(app)` 就像没锁前门——方便但不安全。生产环境应明确指定允许访问 API 的网站。
#### 常见 CORS 场景与解决方案
| 场景 | 问题 | 解决方案 |
|--------------------|----------------------------|------------------------------|
| **本地开发** | 前端无法访问后端 | 为 FastAPI 添加 CORSMiddleware |
| **GitHub Pages + Heroku** | 部署的前端无法访问 API | 将 GitHub Pages URL 添加到 CORS origins |
| **自定义域名** | 生产环境出现 CORS 错误 | 更新 CORS origins 以匹配域名 |
| **移动应用** | 应用无法访问 Web API | 添加应用域名,或谨慎使用 `*` |
**专家提示**:您可以在浏览器开发者工具的网络 (Network) 标签页检查 CORS 头。查看响应中的 `Access-Control-Allow-Origin` 等头信息。
### 错误处理和验证
注意我们的 API 包含了完整的错误处理:
```python
# 验证我们是否收到了消息
if not message:
return jsonify({"error": "Message field is required"}), 400
```
**关键验证原则:**
- **检查** 请求中的必填字段
- **返回** 有意义的错误信息(JSON 格式)
- **使用** 合适的 HTTP 状态码(400 表示请求错误)
- **提供** 清晰反馈,帮助前端开发调试问题
## 设置和运行您的后端
现在 AI 集成和 FastAPI 服务器都准备好了,让我们启动一切。设置过程包括安装 Python 依赖、配置环境变量和启动开发服务器。
### Python 环境设置
让我们搭建 Python 开发环境。虚拟环境就像曼哈顿计划中的隔离工作——每个项目拥有独立空间,使用特定工具和依赖,避免不同项目间冲突。
```bash
# 导航到你的后端目录
cd backend
# 创建一个虚拟环境(就像为你的项目创建一个洁净室)
python -m venv venv
# 激活它(Linux/Mac)
source ./venv/bin/activate
# 在Windows上,使用:
# venv\Scripts\activate
# 安装好东西
pip install openai fastapi uvicorn python-dotenv
```
**我们刚做了什么:**
- **创建** 自己的小 Python “泡泡”,安装包不会影响其他环境
- **激活** 该环境,让终端知道使用的是这个隔离空间
- **安装** 必要依赖:OpenAI 让 AI 变魔法,FastAPI 搭建网页 API,Uvicorn 运行服务器,python-dotenv 处理安全密钥
**核心依赖解释:**
- **FastAPI**:现代快速的 Web 框架,自动生成 API 文档
- **Uvicorn**:超快的 ASGI 服务器,运行 FastAPI 应用
- **OpenAI**:官方库,集成 GitHub 模型及 OpenAI API
- **python-dotenv**:从 .env 文件安全加载环境变量
### 环境配置:保护您的秘密
在启动 API 之前,有个最重要的课程:如何真正保护您的秘密。环境变量就像保险箱,只有您的应用能访问。
#### 什么是环境变量?
**把环境变量想象成保险箱**——您把贵重物品放进去,只有您(和应用)有钥匙取出。与把敏感信息直接写在代码(任何人都能看到)不同,环境变量可以安全存储。
**区别在于:**
- **错误的做法**:把密码写在便利贴贴在显示器上
- **正确的做法**:用密码管理器安全保管,只有您能访问
#### 为什么环境变量很重要
```python
# 🚨 绝不要这样做 - API密钥对所有人可见
client = OpenAI(
api_key="ghp_1234567890abcdef...", # 任何人都可以窃取它!
base_url="https://models.github.ai/inference"
)
# ✅ 这样做 - API密钥安全存储
client = OpenAI(
api_key=os.environ["GITHUB_TOKEN"], # 只有您的应用可以访问它
base_url="https://models.github.ai/inference"
)
```
**硬编码密钥的后果:**
1. **版本控制泄露**:有权访问 Git 仓库的人都看到您的 API 密钥
2. **公开仓库风险**:推送到 GitHub,密钥对全网公开
3. **团队共享问题**:其他开发者获得您的个人密钥
4. **安全漏洞**:密钥被盗后,别人滥用您的 AI 配额
#### 设置您的环境文件
在后端目录中创建 `.env` 文件,存放本地密钥:
```bash
# .env 文件 - 绝不应提交到 Git
GITHUB_TOKEN=your_github_personal_access_token_here
FASTAPI_DEBUG=True
ENVIRONMENT=development
```
**.env 文件说明:**
- **每行一个密钥**,格式为 `KEY=value`
- **等号两边无空格**
- **数值通常不需要引号**
- **注释以 `#` 开始**
#### 创建 GitHub 个人访问令牌
GitHub 令牌就像特殊密码,允许您的应用使用 GitHub 的 AI 服务:
**创建令牌步骤:**
1. 进入 GitHub 设置 → 开发者设置 → 个人访问令牌 → 经典令牌
2. 点击“生成新令牌(经典)”
3. 设置到期时间(测试用 30 天,生产环境可更长)
4. 选择权限范围:勾选 “repo” 及需要的其他权限
5. 生成令牌并立即复制(之后无法再次查看)
6. 粘贴到您的 `.env` 文件中
```bash
# 你的令牌样例(这是假的!)
GITHUB_TOKEN=ghp_1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R
```
#### 在 Python 中加载环境变量
```python
import os
from dotenv import load_dotenv
# 从.env文件加载环境变量
load_dotenv()
# 现在你可以安全地访问它们
api_key = os.environ.get("GITHUB_TOKEN")
if not api_key:
raise ValueError("GITHUB_TOKEN not found in environment variables!")
client = OpenAI(
api_key=api_key,
base_url="https://models.github.ai/inference"
)
```
**这段代码做了什么:**
- **加载** .env 文件,并让变量可用
- **检查** 必需的令牌是否存在(良好错误管理)
- **缺失时抛出明确错误**
- **安全使用令牌,不暴露在代码里**
#### Git 安全:.gitignore 文件
`.gitignore` 文件告诉 Git 哪些文件永远不做跟踪或上传:
```bash
# .gitignore - 添加这些行
.env
*.env
.env.local
.env.production
__pycache__/
venv/
.vscode/
```
**重要性**:加 `.env` 到 `.gitignore`,Git 会忽略您的环境文件,防止意外把秘密上传到 GitHub。
#### 不同环境,不同密钥
专业应用在不同环境使用不同 API 密钥:
```bash
# .env.development
GITHUB_TOKEN=your_development_token
DEBUG=True
# .env.production
GITHUB_TOKEN=your_production_token
DEBUG=False
```
**意义**:避免开发实验影响生产配额,并为不同环境设定不同安全等级。
### 启动开发服务器:让您的 FastAPI 运行起来
现在到了令人激动的时刻——启动你的 FastAPI 开发服务器,看到你的 AI 集成活灵活现地展现出来!FastAPI 使用 Uvicorn,这是一款专为异步 Python 应用设计的超快速 ASGI 服务器。
#### 了解 FastAPI 服务器启动过程
```bash
# 方法1:直接使用Python执行(包括自动重载)
python api.py
# 方法2:直接使用Uvicorn(更多控制)
uvicorn api:app --host 0.0.0.0 --port 5000 --reload
```
当你运行此命令时,后台发生的事情如下:
**1. Python 加载你的 FastAPI 应用**:
- 导入所有必需的库(FastAPI、Pydantic、OpenAI 等)
- 从 `.env` 文件加载环境变量
- 创建带有自动文档的 FastAPI 应用实例
**2. Uvicorn 配置 ASGI 服务器**:
- 绑定到 5000 端口,支持异步请求处理
- 设置请求路由并自动验证
- 开启热重载功能(文件更改时自动重启)
- 生成交互式 API 文档
**3. 服务器开始监听**:
- 终端显示:`INFO: Uvicorn running on http://0.0.0.0:5000`
- 服务器可处理多个并发 AI 请求
- 你的 API 准备就绪,自动文档地址为 `http://localhost:5000/docs`
#### 正常工作时你应该看到的内容
```bash
$ python api.py
INFO: Will watch for changes in these directories: ['/your/project/path']
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
INFO: Started reloader process [12345] using WatchFiles
INFO: Started server process [12346]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
**理解 FastAPI 输出:**
- **正在监听更改**:开发模式下启用自动重载
- **Uvicorn 运行中**:高性能 ASGI 服务器处于活动状态
- **已启动重载进程**:文件监视器自动重启
- **应用启动完成**:FastAPI 应用成功初始化
- **交互文档可用**:访问 `/docs` 查看自动生成的 API 文档
#### 测试你的 FastAPI:多种强大方式
FastAPI 提供了多种便捷的测试 API 的方法,包括自动生成的交互式文档:
**方法 1:交互式 API 文档(推荐)**
1. 打开浏览器,访问 `http://localhost:5000/docs`
2. 你会看到 Swagger UI,所有端点文档齐全
3. 点击 `/hello` → “Try it out” → 输入测试消息 → “Execute”
4. 在浏览器中直接看到格式化良好的响应
**方法 2:基础浏览器测试**
1. 访问 `http://localhost:5000` 查看根端点
2. 访问 `http://localhost:5000/health` 检查服务器健康状态
3. 以此确认 FastAPI 服务器运行正常
**方法 2:命令行测试(高级)**
```bash
# 使用 curl 进行测试(如果可用)
curl -X POST http://localhost:5000/hello \
-H "Content-Type: application/json" \
-d '{"message": "Hello AI!"}'
# 预期响应:
# {"response": "您好!我是您的 AI 助手。今天我能帮您什么忙?"}
```
**方法 3:Python 测试脚本**
```python
# test_api.py - 创建此文件以测试您的API
import requests
import json
# 测试API端点
url = "http://localhost:5000/hello"
data = {"message": "Tell me a joke about programming"}
response = requests.post(url, json=data)
if response.status_code == 200:
result = response.json()
print("AI Response:", result['response'])
else:
print("Error:", response.status_code, response.text)
```
#### 常见启动问题排查
| 错误信息 | 含义 | 解决方法 |
|---------------|---------------|------------|
| `ModuleNotFoundError: No module named 'fastapi'` | FastAPI 未安装 | 在虚拟环境中运行 `pip install fastapi uvicorn` |
| `ModuleNotFoundError: No module named 'uvicorn'` | ASGI 服务器未安装 | 在虚拟环境中运行 `pip install uvicorn` |
| `KeyError: 'GITHUB_TOKEN'` | 环境变量未找到 | 检查 `.env` 文件和 `load_dotenv()` 调用 |
| `Address already in use` | 端口 5000 被占用 | 终止占用端口的进程或更改端口号 |
| `ValidationError` | 请求数据与 Pydantic 模型不符 | 检查请求格式是否符合预期模式 |
| `HTTPException 422` | 无法处理的实体 | 请求验证失败,检查 `/docs` 的正确格式 |
| `OpenAI API error` | AI 服务认证失败 | 确认 GitHub 令牌正确且权限充足 |
#### 开发最佳实践
**热重载**:FastAPI 配合 Uvicorn 可在你保存 Python 文件后自动重载,无需手动重启,方便快速测试代码修改。
```python
# 明确启用热重载
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True) # debug=True 启用热重载
```
**开发时添加日志**:
```python
import logging
# 设置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route("/hello", methods=["POST"])
def hello():
data = request.get_json()
message = data.get("message", "")
logger.info(f"Received message: {message}")
if not message:
logger.warning("Empty message received")
return jsonify({"error": "Message field is required"}), 400
try:
response = call_llm(message, "You are a helpful and friendly assistant.")
logger.info(f"AI response generated successfully")
return jsonify({"response": response})
except Exception as e:
logger.error(f"AI API error: {str(e)}")
return jsonify({"error": "AI service temporarily unavailable"}), 500
```
**日志的重要性**:在开发过程中,你可以清晰看到接收到的请求、AI 的响应及错误位置,极大加快调试效率。
### 配置 GitHub Codespaces:轻松实现云端开发
GitHub Codespaces 就像是云上强大的开发电脑,任何浏览器都能访问。如果你在 Codespaces 中开发,需要做一些额外配置以让后端对前端可访问。
#### 了解 Codespaces 网络环境
本地开发环境中,一切都在同一台电脑运行:
- 后端:`http://localhost:5000`
- 前端:`http://localhost:3000`(或 file://)
而在 Codespaces 中,开发环境运行在 GitHub 服务器上,“localhost” 含义不同。GitHub 会自动创建服务的公共 URL,但你需要正确配置它们。
#### Codespaces 配置步骤
**1. 启动你的后端服务器**:
```bash
cd backend
python api.py
```
你会看到熟悉的 FastAPI/Uvicorn 启动信息,但它是在 Codespace 环境内运行。
**2. 配置端口可见性**:
- 在 VS Code 底部面板找到“Ports”标签
- 在列表中找到 5000 端口
- 右键点击 5000 端口
- 选择“Port Visibility” → “Public”
**为什么要公开端口?** 默认情况下,Codespace 端口是私有的(仅自己访问)。公开端口后,你的前端(运行于浏览器)才能与后端通信。
**3. 获取公共 URL**:
公开端口后,看到一个类似这样的 URL:
```
https://your-codespace-name-5000.app.github.dev
```
**4. 更新前端配置**:
```javascript
// 在你的前端 app.js 中,更新 BASE_URL:
this.BASE_URL = "https://your-codespace-name-5000.app.github.dev";
```
#### 了解 Codespace URL
Codespace URL 遵循可预测的格式:
```
https://[codespace-name]-[port].app.github.dev
```
**解析如下:**
- `codespace-name`:你的 Codespace 唯一标识(一般含用户名)
- `port`:服务运行的端口(本例中是 5000)
- `app.github.dev`:GitHub Codespace 应用的域名
#### 测试你的 Codespace 配置
**1. 直接测试后端**:
在新浏览器标签打开公共 URL,你应该看到:
```
Welcome to the AI Chat API. Send POST requests to /hello with JSON payload containing 'message' field.
```
**2. 使用浏览器开发工具测试**:
```javascript
// 打开浏览器控制台并测试您的API
fetch('https://your-codespace-name-5000.app.github.dev/hello', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: 'Hello from Codespaces!'})
})
.then(response => response.json())
.then(data => console.log(data));
```
#### Codespaces 与本地开发对比
| 方面 | 本地开发 | GitHub Codespaces |
|--------|-------------------|-------------------|
| **配置时间** | 较长(安装 Python、依赖等) | 即刻可用(预配置环境) |
| **URL 访问** | `http://localhost:5000` | `https://xyz-5000.app.github.dev` |
| **端口配置** | 自动 | 手动(需公开端口) |
| **文件持久化** | 本地机器 | GitHub 仓库 |
| **协作** | 共享环境困难 | 轻松分享 Codespace 链接 |
| **网络依赖** | 仅 AI API 调用 | 一切操作均需联网 |
#### Codespace 开发小贴士
**Codespaces 中的环境变量**:
你的 `.env` 文件在 Codespaces 中同样生效,也可以直接在 Codespace 设置环境变量:
```bash
# 为当前会话设置环境变量
export GITHUB_TOKEN="your_token_here"
# 或添加到你的 .bashrc 以保持永久有效
echo 'export GITHUB_TOKEN="your_token_here"' >> ~/.bashrc
```
**端口管理**:
- Codespaces 自动检测你的应用监听的端口
- 可同时转发多个端口(未来添加数据库时很有用)
- 只要 Codespace 运行,端口就持续可用
**开发流程**:
1. 在 VS Code 修改代码
2. FastAPI 自动重载(依赖 Uvicorn 重载模式)
3. 通过公共 URL 即时测试更改
4. 准备好后提交并推送代码
> 💡 **小贴士**:开发期间收藏你的 Codespace 后端 URL。Codespace 名称稳定,URL 只要不换 Codespace 就不会变。
## 创建前端聊天界面:人与 AI 的交汇点
现在开始构建用户界面——决定人与 AI 助理如何交互的部分。就像最初 iPhone 界面设计一样,我们专注于让复杂技术变得直观自然。
### 了解现代前端架构
我们的聊天界面将是所谓的“单页应用”(SPA)。与传统每次点击都会加载新页面不同,我们的应用更新流畅且即时:
**旧网站**:像读纸质书——翻到全新页面
**我们的聊天应用**:像用手机——内容流畅更新无阻
```mermaid
graph TD
A[用户输入消息] --> B[JavaScript 捕获输入]
B --> C[验证并格式化数据]
C --> D[发送到后端 API]
D --> E[显示加载状态]
E --> F[接收 AI 响应]
F --> G[更新聊天界面]
G --> H[准备接收下一条消息]
```
```mermaid
classDiagram
class ChatApp {
+messages: HTMLElement
+form: HTMLElement
+input: HTMLElement
+sendButton: HTMLElement
+BASE_URL: string
+API_ENDPOINT: string
+constructor()
+initializeEventListeners()
+handleSubmit(event)
+callAPI(message)
+appendMessage(text, role)
+escapeHtml(text)
+scrollToBottom()
+setLoading(isLoading)
}
ChatApp --> DOM : 操作
ChatApp --> FastAPI : 发送请求
```
### 前端开发的三大支柱
每个前端应用——从简单网站到像 Discord 或 Slack 这样复杂的应用——都基于三大核心技术。可以把它们看作你看到并互动的所有网页的基础:
**HTML(结构)**:这是你的基础
- 决定页面有哪些元素(按钮、文本框、容器等)
- 赋予内容含义(这是标题、这是表单等)
- 创建所有其他部分构筑的基本结构
**CSS(表现)**:这是你的室内设计师
- 让一切美观(颜色、字体、布局)
- 适配不同屏幕(手机、笔记本、平板)
- 制造平滑动画和视觉反馈
**JavaScript(行为)**:这是你的大脑
- 响应用户操作(点击、输入、滚动)
- 与后端通信并更新页面
- 让一切变得互动和动态
**可以把它们想象成建筑设计:**
- **HTML**:结构蓝图(定义空间和关系)
- **CSS**:美学及环境设计(视觉风格和用户体验)
- **JavaScript**:机械系统(功能和交互)
### 现代 JavaScript 架构的重要性
我们的聊天应用将使用你在专业应用中会见到的现代 JavaScript 模式。理解这些概念将帮助你成长为开发者:
**基于类的架构**:我们会将代码组织成类,类似于对象蓝图
**Async/Await**:处理耗时操作(如 API 调用)的现代方法
**事件驱动编程**:应用响应用户操作(点击、按键),而非持续循环运行
**DOM 操作**:根据用户交互和 API 响应动态更新网页内容
### 项目结构设置
创建一个 frontend 目录,结构如下有序组织:
```text
frontend/
├── index.html # Main HTML structure
├── app.js # JavaScript functionality
└── styles.css # Visual styling
```
**架构说明:**
- **分离**结构(HTML)、行为(JavaScript)和表现(CSS)
- **维护**简单易读的文件结构,易于导航和修改
- **遵循**网页开发最佳实践,增强组织性和可维护性
### 构建 HTML 基础:语义结构与可访问性
先从 HTML 结构开始。现代网页开发强调“语义 HTML”——使用明确定义用途的元素,而非仅仅控制外观。这使得应用更易被屏幕阅读器、搜索引擎等工具识别。
**为什么语义 HTML 重要**:想象你跟别人电话描述你的聊天应用,你会说“有个带标题的头部,一个显示对话的主体区域,底部是输入消息的表单”。语义 HTML 用的正是对应这种自然描述的元素。
创建 `index.html`,内容采用以下深思熟虑的语义标记:
```html
Ask me anything!