docs: update README.md
This commit is contained in:
parent
a970172db1
commit
695cf40d00
348
README.md
348
README.md
@ -13,10 +13,11 @@
|
|||||||
本课程通过 AI 代码编辑器(如 Cursor、Windsurf)进行 **Vibe Coding**,让学生体验"用自然语言编程"的全新开发方式。但更重要的是,在这个过程中**思考人与 AI 的协作边界**,培养 AI 时代真正不可替代的能力。
|
本课程通过 AI 代码编辑器(如 Cursor、Windsurf)进行 **Vibe Coding**,让学生体验"用自然语言编程"的全新开发方式。但更重要的是,在这个过程中**思考人与 AI 的协作边界**,培养 AI 时代真正不可替代的能力。
|
||||||
|
|
||||||
**技术栈**:
|
**技术栈**:
|
||||||
- 🐍 **Python 3.10+**
|
- 🐍 **Python 3.14+** (2026 Stable)
|
||||||
- 🤖 **DeepSeek API**(大语言模型)
|
- ⚡ **uv** (极速 Python 项目管理)
|
||||||
- 🎨 **Streamlit**(Web 界面框架)
|
- 🤖 **DeepSeek API** + **PydanticAI** (Agent 框架)
|
||||||
- 🛠️ **Cursor / Windsurf**(AI 代码编辑器)
|
- 🎨 **Streamlit** (Data Apps) / **Chainlit** (Chatbots)
|
||||||
|
- 🛠️ **Cursor / Windsurf** (AI 代码编辑器)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -32,6 +33,27 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🛠️ 第0天:环境预备 (Day 0)
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **请在正式上课前完成以下准备**。这能让你在第一天直接进入"Vibe Coding"状态,而不是卡在安装软件上。
|
||||||
|
|
||||||
|
### 1. 软件安装清单
|
||||||
|
- **Code Editor**: 推荐 [Cursor](https://cursor.com) (自带 AI) 或 VS Code + Windsurf 插件, [TRAE CN](https://www.trae.cn)。
|
||||||
|
- **Python 3.14+**: 访问 [python.org](https://www.python.org/downloads/) 下载。
|
||||||
|
- **uv**: 极速 Python 包管理器 (比 pip 快 10-100 倍)。
|
||||||
|
- Mac/Linux: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||||
|
- Windows: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
||||||
|
|
||||||
|
### 2. 验证环境
|
||||||
|
在终端 (Terminal) 输入以下命令,看到版本号即成功:
|
||||||
|
```bash
|
||||||
|
python --version # 应显示 Python 3.14.x
|
||||||
|
uv --version # 应显示 uv 0.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📚 第1天:课程讲解与演示(3学时)
|
## 📚 第1天:课程讲解与演示(3学时)
|
||||||
|
|
||||||
### 1.1 课程导入(30分钟)
|
### 1.1 课程导入(30分钟)
|
||||||
@ -41,6 +63,21 @@
|
|||||||
- **核心逻辑**:"描述你想要什么,而不是告诉计算机怎么做"。
|
- **核心逻辑**:"描述你想要什么,而不是告诉计算机怎么做"。
|
||||||
- **演示**:用自然语言在 3 分钟内创建一个完整应用。
|
- **演示**:用自然语言在 3 分钟内创建一个完整应用。
|
||||||
|
|
||||||
|
#### 为什么要学 Python 基础?(The "Why" in AI Era)
|
||||||
|
|
||||||
|
很多同学会问:*"既然 AI 能写代码,我为什么还要学基础语法?"*
|
||||||
|
|
||||||
|
> **即使是法拉利,也需要懂驾驶原理的赛车手。**
|
||||||
|
|
||||||
|
在 Vibe Coding 中,你需要具备**两种核心能力**才能驾驭 AI:
|
||||||
|
1. **准确描述 (Prompting)**: 你需要懂一点术语 (如 "List", "Loop", "Function") 才能精准指挥 AI。
|
||||||
|
2. **验证结果 (Auditing)**: AI 会犯错 (幻觉)。你需要能读懂代码,判断它是不是在"一本正经地胡说八道"。
|
||||||
|
|
||||||
|
**重点掌握:**
|
||||||
|
* **0-Based Indexing**: 为什么计算机从 0 开始数数?(因为偏移量的概念)。
|
||||||
|
* **逻辑控制**: `if`, `for`, `while` 是所有程序的骨架。
|
||||||
|
* **函数思维**: 把大问题拆解成小模块 (Input -> Process -> Output)。
|
||||||
|
|
||||||
#### AI 时代,什么能力真正属于你?
|
#### AI 时代,什么能力真正属于你?
|
||||||
|
|
||||||
> **AI 能做的事越来越多:** 语法查询、代码生成、Bug 修复、文档撰写、测试创建、架构建议、代码重构...
|
> **AI 能做的事越来越多:** 语法查询、代码生成、Bug 修复、文档撰写、测试创建、架构建议、代码重构...
|
||||||
@ -57,14 +94,53 @@
|
|||||||
#### AI 代码编辑器介绍
|
#### AI 代码编辑器介绍
|
||||||
- **Cursor**:[https://cursor.com](https://cursor.com)
|
- **Cursor**:[https://cursor.com](https://cursor.com)
|
||||||
- **Windsurf**:[https://codeium.com/windsurf](https://codeium.com/windsurf)
|
- **Windsurf**:[https://codeium.com/windsurf](https://codeium.com/windsurf)
|
||||||
|
- **TRAE CN**:[https://www.trae.cn](https://www.trae.cn)
|
||||||
- **核心模式**:
|
- **核心模式**:
|
||||||
- **Approve/Reject**:人是最终的决策者。
|
- **Approve/Reject**:人是最终的决策者。
|
||||||
- **Context Awareness**:如何让 AI "看见"你的整个项目。
|
- **Context Awareness**:如何让 AI "看见"你的整个项目。
|
||||||
|
|
||||||
### 1.2 技术栈讲解(60分钟)
|
### 1.2 Vibe Coding 快速上手:新手四步法 (The 4-Step Loop)
|
||||||
|
|
||||||
#### DeepSeek API 快速入门
|
对于 0 基础的新手,不要试图一次性写出完美代码。请遵循 **"D-P-R-I"** 循环:
|
||||||
|
|
||||||
|
1. **Define (定义)**:用一句话说清楚你要做什么。
|
||||||
|
* ❌ *"写个程序"*
|
||||||
|
* ✅ *"写一个倒计时器,要有开始/暂停按钮,时间到了会弹窗提示"*
|
||||||
|
2. **Prompt (提示)**:扮演产品经理,给 AI 下指令。
|
||||||
|
* **公式**:`角色 + 任务 + 限制条件`
|
||||||
|
* *例:"你是一个 Python 专家。请用 Streamlit 写一个 番茄钟应用。要求:界面简洁,背景是黑色,使用 Session State 记录时间。"*
|
||||||
|
3. **Review (审查)**:AI 写完后,不要盲目运行。
|
||||||
|
* **一眼定真**:代码里有没有显然的红色波浪线 (报错)?
|
||||||
|
* **结构检查**:有没有 `import`?有没有主函数?
|
||||||
|
4. **Iterate (迭代)**:运行 -> 报错 -> 把报错扔给 AI。
|
||||||
|
* *"Vibe Coding 的精髓不在于一次写对,而在于快速试错。"*
|
||||||
|
* **黄金法则**:如果 AI 连续改 3 次还没修好,**停下来**!这通常意味着你的 Prompt 逻辑有问题,或者方向错了。
|
||||||
|
|
||||||
|
### 1.3 技术栈讲解(60分钟)
|
||||||
|
|
||||||
|
#### ⚡ 2026 现代 Python 工作流 (uv)
|
||||||
|
|
||||||
|
我们将告别繁琐的 `pip` 和虚拟环境配置,使用 Rust 编写的 **uv** 作为唯一工具。
|
||||||
|
|
||||||
|
**核心三步走 (The 3-Step Vibe)**:
|
||||||
|
|
||||||
|
1. **初始化 (Init)**: 创建一个干净的"代码工作台"。
|
||||||
|
```bash
|
||||||
|
uv init my-ai-app
|
||||||
|
cd my-ai-app
|
||||||
|
```
|
||||||
|
2. **添加工具 (Add)**: 就像给手机装 App 一样简单。
|
||||||
|
```bash
|
||||||
|
uv add streamlit chainlit openai python-dotenv
|
||||||
|
```
|
||||||
|
3. **运行 (Run)**: 一键启动,无需担心环境冲突。
|
||||||
|
```bash
|
||||||
|
uv run main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
#### DeepSeek API 快速入门 (使用 OpenAI SDK)
|
||||||
```python
|
```python
|
||||||
|
# 推荐使用 uv 管理依赖: uv add openai
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
|
||||||
# 初始化客户端
|
# 初始化客户端
|
||||||
@ -73,75 +149,188 @@ client = OpenAI(
|
|||||||
base_url="https://api.deepseek.com"
|
base_url="https://api.deepseek.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 发送请求
|
# ... (代码保持不变)
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> **进阶选择**:对于复杂的 Agent 开发,推荐使用 **PydanticAI**,它能提供更好的类型安全和结构化输出支持。
|
||||||
|
|
||||||
|
#### Streamlit vs Chainlit
|
||||||
|
|
||||||
|
- **Streamlit**: 适合构建 **数据仪表板**、**工具类应用** (如 Excel 处理、图表分析)。
|
||||||
|
- **Chainlit**: 适合构建 **聊天机器人**、**多轮对话 Agent** (自带优美的聊天界面)。
|
||||||
|
|
||||||
|
**Streamlit 示例 (数据应用)**:
|
||||||
|
```python
|
||||||
|
import streamlit as st
|
||||||
|
|
||||||
|
st.title("数据分析看板")
|
||||||
|
st.line_chart([1, 5, 2, 6])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Chainlit 示例 (对话应用)**:
|
||||||
|
```python
|
||||||
|
import chainlit as cl
|
||||||
|
|
||||||
|
@cl.on_message
|
||||||
|
async def main(message: cl.Message):
|
||||||
|
# 这里调用 DeepSeek API
|
||||||
|
await cl.Message(content=f"你说了: {message.content}").send()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 动手实践:三个"最小可运行"示例 (MVP Hands-on)
|
||||||
|
|
||||||
|
接下来的 45 分钟,请跟随老师在你的电脑上运行这三个示例。这是你迈向 Vibe Coding 的第一步。
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> **准备工作**:
|
||||||
|
> 确保你已经初始化了项目并填入了 Key:
|
||||||
|
> ```bash
|
||||||
|
> mkdir day1-demo
|
||||||
|
> cd day1-demo
|
||||||
|
> uv init
|
||||||
|
> uv add openai python-dotenv streamlit chainlit
|
||||||
|
> # 创建 .env 文件并填入: DEEPSEEK_API_KEY=sk-xxxxxx
|
||||||
|
> ```
|
||||||
|
|
||||||
|
#### 示例 A: Hello AI (CLI 脚本)
|
||||||
|
**目标**:理解 OpenAI SDK 的最基本调用方式。
|
||||||
|
**文件**:`main.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
from openai import OpenAI
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# 1. 加载环境变量 (.env)
|
||||||
|
load_dotenv()
|
||||||
|
api_key = os.getenv("DEEPSEEK_API_KEY")
|
||||||
|
|
||||||
|
if not api_key:
|
||||||
|
print("❌ 错误:未找到 DEEPSEEK_API_KEY,请检查 .env 文件")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# 2. 初始化客户端
|
||||||
|
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
||||||
|
|
||||||
|
# 3. 发送请求
|
||||||
|
print("🤖 AI 正在思考...")
|
||||||
response = client.chat.completions.create(
|
response = client.chat.completions.create(
|
||||||
model="deepseek-chat", # 使用 V3 模型
|
model="deepseek-chat",
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": "你是一个有帮助的助手"},
|
{"role": "system", "content": "你是一个赛博朋克风格的诗人。"},
|
||||||
{"role": "user", "content": "你好!"}
|
{"role": "user", "content": "用两句话描述代码的魅力。"},
|
||||||
],
|
],
|
||||||
stream=False
|
stream=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 4. 输出结果
|
||||||
|
print("\n" + "="*30)
|
||||||
print(response.choices[0].message.content)
|
print(response.choices[0].message.content)
|
||||||
|
print("="*30 + "\n")
|
||||||
|
```
|
||||||
|
**运行**:
|
||||||
|
```bash
|
||||||
|
uv run main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
#### 示例 B: 魔法文案生成器 (Streamlit)
|
||||||
> **API Key 安全**:
|
**目标**:不写 HTML/CSS,用 Python 5分钟构建可视化的 AI 应用。
|
||||||
> 绝对不要按将 API Key 直接提交到 GitHub 等公共仓库!请使用 `.env` 文件配合 `.gitignore` 管理。
|
**文件**:`app_gui.py`
|
||||||
|
|
||||||
**关键概念**:
|
|
||||||
- **API Key**:你的"通行证",注意保密。
|
|
||||||
- **Messages**:对话历史的列表,包含 system(人设)、user(用户)、assistant(AI)。
|
|
||||||
- **Stream**:流式输出,提升用户体验(像打字机一样显示)。
|
|
||||||
|
|
||||||
#### Streamlit 快速入门
|
|
||||||
```python
|
```python
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
import os
|
||||||
|
from openai import OpenAI
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
st.title("我的第一个 Streamlit 应用")
|
load_dotenv()
|
||||||
|
client = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")
|
||||||
|
|
||||||
# 侧边栏
|
# 1. 页面配置
|
||||||
|
st.set_page_config(page_title="魔法文案生成器", page_icon="✨")
|
||||||
|
st.title("✨ 魔法文案生成器")
|
||||||
|
|
||||||
|
# 2. 左侧侧边栏
|
||||||
with st.sidebar:
|
with st.sidebar:
|
||||||
st.write("这是侧边栏")
|
st.header("配置")
|
||||||
|
style = st.selectbox("选择风格", ["文艺风", "幽默风", "硬核科技风", "发疯文学"])
|
||||||
|
length = st.slider("字数限制", 50, 200, 100)
|
||||||
|
|
||||||
# 输入框
|
# 3. 主界面输入
|
||||||
user_input = st.text_input("请输入你的问题:")
|
theme = st.text_input("你想写关于什么主题的文案?", placeholder="例如:周五下班、喝咖啡、新发型")
|
||||||
|
|
||||||
# 按钮与交互
|
# 4. 按钮与 AI 调用
|
||||||
if st.button("提交"):
|
if st.button("🪄 生成文案"):
|
||||||
if user_input:
|
if not theme:
|
||||||
st.write(f"你输入了:{user_input}")
|
st.warning("请先输入主题!")
|
||||||
else:
|
else:
|
||||||
st.warning("请输入内容!")
|
with st.spinner("AI 正在榨干脑汁..."):
|
||||||
|
try:
|
||||||
|
prompt = f"请用{style}写一段关于'{theme}'的朋友圈文案,带Emoji,{length}字以内。"
|
||||||
|
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model="deepseek-chat",
|
||||||
|
messages=[{"role": "user", "content": prompt}]
|
||||||
|
)
|
||||||
|
result = response.choices[0].message.content
|
||||||
|
|
||||||
|
st.success("生成成功!")
|
||||||
|
st.markdown(f"### 📝 结果:\n{result}")
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"发生错误:{e}")
|
||||||
|
```
|
||||||
|
**运行**:
|
||||||
|
```bash
|
||||||
|
uv run streamlit run app_gui.py
|
||||||
```
|
```
|
||||||
|
|
||||||
**常用组件**:
|
#### 示例 C: 会聊天的猫 (Chainlit)
|
||||||
- 🛠️ **输入**:`st.text_input`, `st.text_area`, `st.slider`
|
**目标**:构建像 ChatGPT 一样的对话界面,支持流式输出 (Streaming)。
|
||||||
- 📄 **输出**:`st.write`, `st.markdown`, `st.chat_message`
|
**文件**:`app_chat.py`
|
||||||
- 🎒 **状态**:`st.session_state` (用于记住对话历史)
|
|
||||||
|
|
||||||
### 1.3 完整示例演示(45分钟)
|
```python
|
||||||
|
import chainlit as cl
|
||||||
|
import os
|
||||||
|
from openai import OpenAI
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
#### 现场演示:用 Vibe Coding 构建一个 AI 聊天应用
|
load_dotenv()
|
||||||
|
client = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")
|
||||||
|
|
||||||
**演示步骤**:
|
@cl.on_message
|
||||||
1. **Init**:新建文件夹,用自然语言让 AI 初始化环境。
|
async def main(message: cl.Message):
|
||||||
2. **Draft**:描述核心需求,生成 1.0 版本代码。
|
# 1. 准备消息历史
|
||||||
3. **Refine**:
|
# (注意:实际项目中需要维护 st.session_state 或 cl.user_session 来保留上下文,这里仅演示单轮回复)
|
||||||
- 添加对话历史记忆(Session State)。
|
msg_history = [
|
||||||
- 开启流式输出(Streaming)。
|
{"role": "system", "content": "你是一只高冷的猫,说话句尾要带'喵'。"},
|
||||||
- 优化 UI(添加侧边栏设置)。
|
{"role": "user", "content": message.content}
|
||||||
4. **Deploy**:运行并测试。
|
]
|
||||||
|
|
||||||
|
# 2. 创建回复消息容器
|
||||||
|
msg = cl.Message(content="")
|
||||||
|
await msg.send()
|
||||||
|
|
||||||
|
# 3. 调用 AI 并流式(stream)接收
|
||||||
|
stream = client.chat.completions.create(
|
||||||
|
model="deepseek-chat",
|
||||||
|
messages=msg_history,
|
||||||
|
stream=True
|
||||||
|
)
|
||||||
|
|
||||||
|
for chunk in stream:
|
||||||
|
if token := chunk.choices[0].delta.content:
|
||||||
|
await msg.stream_token(token)
|
||||||
|
|
||||||
|
# 4. 完成更新
|
||||||
|
await msg.update()
|
||||||
|
```
|
||||||
|
**运行**:
|
||||||
|
```bash
|
||||||
|
uv run chainlit run app_chat.py -w
|
||||||
|
```
|
||||||
|
|
||||||
**示例 Prompt**:
|
### 1.5 项目要求说明(15分钟)
|
||||||
> "帮我创建一个基于 DeepSeek API 的聊天应用,使用 Streamlit 构建界面。要求:
|
|
||||||
> 1. 支持多轮对话,保留对话历史
|
|
||||||
> 2. 使用流式输出,打字机效果显示回复
|
|
||||||
> 3. 左侧边栏可以设置系统提示词
|
|
||||||
> 4. 界面美观,有清空对话的按钮"
|
|
||||||
|
|
||||||
### 1.4 项目要求说明(15分钟)
|
|
||||||
|
|
||||||
#### 项目选题(任选其一或自拟)
|
#### 项目选题(任选其一或自拟)
|
||||||
|
|
||||||
@ -161,6 +350,27 @@ if st.button("提交"):
|
|||||||
|
|
||||||
> 💡 **选题思考**:不只是"我能做什么",更要思考"这个项目**对谁有价值**?解决什么**真实问题**?"
|
> 💡 **选题思考**:不只是"我能做什么",更要思考"这个项目**对谁有价值**?解决什么**真实问题**?"
|
||||||
|
|
||||||
|
#### 🚀 从 0 到 1:如何开始?(Starter Kit)
|
||||||
|
|
||||||
|
面对空白的代码编辑器感到迷茫?请按以下步骤操作:
|
||||||
|
|
||||||
|
**Step 1: 只要写出 README.md,你就完成了一半**
|
||||||
|
不要急着写代码。先用自然语言在 `Project_Design.md` (或草稿纸) 上写下:
|
||||||
|
1. **一句话描述**:我的应用叫什么?它是给谁用的?
|
||||||
|
2. **核心功能**:它*必须*有的 3 个功能是什么?(MVP)
|
||||||
|
3. **交互流程**:用户打开 App -> 点击什么 -> 看到什么?
|
||||||
|
|
||||||
|
**Step 2: 让 AI 写第一行代码**
|
||||||
|
把Step 1的内容喂给 Cursor/Windsurf:
|
||||||
|
> *"我正在开发一个[项目名]。请阅读我的设计思路:[粘贴你的描述]。请帮我生成项目结构,并创建最基础的 app.py 框架。"*
|
||||||
|
|
||||||
|
**Step 3: 最小可行性验证**
|
||||||
|
* 生成的代码能跑吗?(Run it)
|
||||||
|
* 界面上能看到按钮吗?(See it)
|
||||||
|
* 如果报错,直接把报错信息截图或复制给 AI。
|
||||||
|
|
||||||
|
> **记住**:你是在搭建积木,不是在烧制砖块。先搭出轮廓,再修饰细节。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 第2天:自主开发
|
## 🔧 第2天:自主开发
|
||||||
@ -281,7 +491,7 @@ AI 不是神,它需要知道你的代码、你的报错、你的意图。
|
|||||||
### 1. 提交材料
|
### 1. 提交材料
|
||||||
1. **源代码**:完整项目文件夹。
|
1. **源代码**:完整项目文件夹。
|
||||||
2. **README.md**:项目说明文档。
|
2. **README.md**:项目说明文档。
|
||||||
3. **requirements.txt**:`pip freeze > requirements.txt` 生成。
|
3. **依赖文件**:包含 `pyproject.toml` 和 `uv.lock` (确保别人可以用 `uv sync` 还原你的环境)。
|
||||||
4. **开发心得**:Markdown 格式,**不少于 500 字**。
|
4. **开发心得**:Markdown 格式,**不少于 500 字**。
|
||||||
|
|
||||||
### 2. README.md 模板
|
### 2. README.md 模板
|
||||||
@ -292,9 +502,9 @@ AI 不是神,它需要知道你的代码、你的报错、你的意图。
|
|||||||
这句话介绍你的项目是做什么的,解决了什么问题。
|
这句话介绍你的项目是做什么的,解决了什么问题。
|
||||||
|
|
||||||
## 如何运行
|
## 如何运行
|
||||||
1. 安装依赖:`pip install -r requirements.txt`
|
1. 安装依赖:`uv sync`
|
||||||
2. 配置 Key:复制 `.env.example` 为 `.env` 并填入 Key。
|
2. 配置 Key:复制 `.env.example` 为 `.env` 并填入 Key。
|
||||||
3. 运行:`streamlit run app.py`
|
3. 运行:`uv run streamlit run app.py`
|
||||||
|
|
||||||
## 功能列表
|
## 功能列表
|
||||||
- [x] 功能 A
|
- [x] 功能 A
|
||||||
@ -321,21 +531,28 @@ AI 不是神,它需要知道你的代码、你的报错、你的意图。
|
|||||||
## 🔗 附录:环境配置指南
|
## 🔗 附录:环境配置指南
|
||||||
|
|
||||||
### 1. 基础环境
|
### 1. 基础环境
|
||||||
- **Python**:推荐 3.10+ ([官网下载](https://www.python.org/downloads/))
|
- **Python**: 推荐 3.14+ (2026 Stable)
|
||||||
- **编辑器**:推荐 Cursor 或 VS Code + Windsurf 插件。
|
- **依赖管理**: **uv** (强烈推荐,替代 pip/venv)
|
||||||
|
- **编辑器**: 推荐 Cursor 或 VS Code + Windsurf 插件。
|
||||||
|
|
||||||
### 2. 项目初始化命令
|
### 2. 项目初始化 (使用 uv)
|
||||||
```bash
|
```bash
|
||||||
# 1. 创建虚拟环境 (Windows)
|
# 1. 安装 uv (MacOS/Linux)
|
||||||
python -m venv venv
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
.\venv\Scripts\activate
|
# Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||||
|
|
||||||
# 1. 创建虚拟环境 (Mac/Linux)
|
# 2. 初始化项目
|
||||||
python3 -m venv venv
|
mkdir my-ai-app
|
||||||
source venv/bin/activate
|
cd my-ai-app
|
||||||
|
uv init
|
||||||
|
|
||||||
# 2. 安装核心库
|
# 3. 添加依赖
|
||||||
pip install streamlit openai python-dotenv
|
uv add streamlit chainlit openai python-dotenv
|
||||||
|
|
||||||
|
# 4. 运行应用
|
||||||
|
uv run streamlit run app.py
|
||||||
|
# 或
|
||||||
|
uv run chainlit run app.py -w
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. API Key 管理示例 (`.env`)
|
### 3. API Key 管理示例 (`.env`)
|
||||||
@ -347,6 +564,7 @@ DEEPSEEK_API_KEY=sk-xxxxxxx
|
|||||||
```python
|
```python
|
||||||
# app.py
|
# app.py
|
||||||
import os
|
import os
|
||||||
|
# uv add python-dotenv
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
load_dotenv() # 加载 .env
|
load_dotenv() # 加载 .env
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
# DeepSeek API Key 配置示例
|
|
||||||
# ================================
|
|
||||||
# 使用方法:
|
|
||||||
# 1. 将此文件复制并重命名为 .env
|
|
||||||
# 2. 将下面的 sk-xxx... 替换为你的真实 API Key
|
|
||||||
# ================================
|
|
||||||
|
|
||||||
DEEPSEEK_API_KEY=sk-xxxxx
|
|
||||||
|
|
||||||
25
示例项目-AI数据分析师/.gitignore
vendored
25
示例项目-AI数据分析师/.gitignore
vendored
@ -1,25 +0,0 @@
|
|||||||
# 环境变量(包含敏感信息)
|
|
||||||
.env
|
|
||||||
|
|
||||||
# Python
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
*.so
|
|
||||||
.Python
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# 临时文件
|
|
||||||
*.tmp
|
|
||||||
*.log
|
|
||||||
|
|
||||||
@ -1,314 +0,0 @@
|
|||||||
# 📊 AI 数据分析师
|
|
||||||
|
|
||||||
一个基于 DeepSeek API + Streamlit 的智能数据分析平台,上传 CSV 数据即可获得 AI 驱动的深度洞察和可视化报告。
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
## ✨ 功能特性
|
|
||||||
|
|
||||||
| 功能 | 描述 |
|
|
||||||
|:---|:---|
|
|
||||||
| 📈 **数据预览** | 数据表格展示、统计摘要、列信息分析 |
|
|
||||||
| 📊 **智能可视化** | 根据数据类型自动生成直方图、散点图、箱线图、热力图 |
|
|
||||||
| 🤖 **AI 深度分析** | 综合分析、相关性分析、异常检测三种模式 |
|
|
||||||
| 💬 **自然语言问答** | 用中文直接询问关于数据的任何问题 |
|
|
||||||
| 📝 **报告生成** | 一键生成完整的 Markdown 格式分析报告并下载 |
|
|
||||||
|
|
||||||
## 🖼️ 界面预览
|
|
||||||
|
|
||||||
```
|
|
||||||
┌────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 📊 AI 数据分析师 │
|
|
||||||
│ 上传数据,让 AI 帮你发现数据中的故事 │
|
|
||||||
├────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ [📈 数据预览] [📊 智能可视化] [🤖 AI 分析] [💬 数据问答] [📝 生成报告] │
|
|
||||||
├────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
|
||||||
│ │ 100 │ │ 7 │ │ 0 │ │ 5 │ │
|
|
||||||
│ │ 数据行 │ │ 数据列 │ │ 缺失值 │ │ 数值列 │ │
|
|
||||||
│ └──────┘ └──────┘ └──────┘ └──────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ 姓名 │ 部门 │ 年龄 │ 工龄 │ 月薪 │ 绩效评分 │ 满意度 │ │
|
|
||||||
│ │ 张三 │ 技术部 │ 28 │ 3 │ 15000 │ 85.5 │ 4.2 │ │
|
|
||||||
│ │ 李四 │ 市场部 │ 32 │ 5 │ 18000 │ 78.3 │ 3.8 │ │
|
|
||||||
│ │ ... │ ... │ ... │ ... │ ... │ ... │ ... │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
### 1. 进入项目目录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd 示例项目-AI数据分析师
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 创建虚拟环境(推荐)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m venv venv
|
|
||||||
|
|
||||||
# Windows
|
|
||||||
venv\Scripts\activate
|
|
||||||
|
|
||||||
# macOS/Linux
|
|
||||||
source venv/bin/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 安装依赖
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 配置 API Key
|
|
||||||
|
|
||||||
创建 `.env` 文件:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo "DEEPSEEK_API_KEY=sk-your-api-key" > .env
|
|
||||||
```
|
|
||||||
|
|
||||||
或直接在应用界面的侧边栏输入 API Key。
|
|
||||||
|
|
||||||
### 5. 运行应用
|
|
||||||
|
|
||||||
```bash
|
|
||||||
streamlit run app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
浏览器会自动打开 http://localhost:8501
|
|
||||||
|
|
||||||
## 📁 项目结构
|
|
||||||
|
|
||||||
```
|
|
||||||
示例项目-AI数据分析师/
|
|
||||||
├── app.py # 主程序 (~600行)
|
|
||||||
├── requirements.txt # 项目依赖
|
|
||||||
├── sample_data.csv # 示例数据(员工信息)
|
|
||||||
├── .env # 环境变量(需自行创建)
|
|
||||||
├── .gitignore # Git 忽略文件
|
|
||||||
└── README.md # 项目说明
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ 技术架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ Streamlit UI │
|
|
||||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
||||||
│ │数据预览 │ │智能可视化│ │ AI分析 │ │数据问答 │ │
|
|
||||||
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
|
||||||
│ │ │ │ │ │
|
|
||||||
├───────┼───────────┼───────────┼───────────┼─────────────┤
|
|
||||||
│ ▼ ▼ ▼ ▼ │
|
|
||||||
│ ┌─────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Pandas DataFrame │ │
|
|
||||||
│ │ (数据处理与统计分析) │ │
|
|
||||||
│ └─────────────────────────────────────────────────┘ │
|
|
||||||
│ │ │ │
|
|
||||||
│ ▼ ▼ │
|
|
||||||
│ ┌─────────┐ ┌─────────────────────────────────────┐ │
|
|
||||||
│ │ Altair │ │ DeepSeek API │ │
|
|
||||||
│ │ (可视化)│ │ (AI 分析 / 问答 / 报告生成) │ │
|
|
||||||
│ └─────────┘ └─────────────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔑 核心技术点
|
|
||||||
|
|
||||||
### 1. 数据处理与摘要生成
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_data_summary(df: pd.DataFrame) -> str:
|
|
||||||
"""生成数据摘要供 AI 分析"""
|
|
||||||
summary = []
|
|
||||||
|
|
||||||
# 基本信息
|
|
||||||
summary.append(f"数据集包含 {len(df)} 行和 {len(df.columns)} 列。")
|
|
||||||
|
|
||||||
# 数值列统计
|
|
||||||
numeric_cols = df.select_dtypes(include=['number']).columns
|
|
||||||
stats = df[numeric_cols].describe().to_string()
|
|
||||||
|
|
||||||
# ... 更多统计信息
|
|
||||||
return "\n".join(summary)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. AI 分析的 Prompt 工程
|
|
||||||
|
|
||||||
```python
|
|
||||||
def analyze_with_ai(client, data_summary, analysis_type):
|
|
||||||
prompts = {
|
|
||||||
"general": """你是一个专业的数据分析师...
|
|
||||||
请从以下几个方面进行分析:
|
|
||||||
1. 📊 数据概况
|
|
||||||
2. 🔍 关键发现
|
|
||||||
3. 📈 建议可视化
|
|
||||||
4. ⚠️ 注意事项
|
|
||||||
5. 💡 进一步分析建议""",
|
|
||||||
|
|
||||||
"correlation": """分析变量之间的相关性...""",
|
|
||||||
"anomaly": """检测数据中的异常值..."""
|
|
||||||
}
|
|
||||||
|
|
||||||
response = client.chat.completions.create(
|
|
||||||
model="deepseek-chat",
|
|
||||||
messages=[
|
|
||||||
{"role": "system", "content": prompts[analysis_type]},
|
|
||||||
{"role": "user", "content": f"数据摘要:\n{data_summary}"}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
return response.choices[0].message.content
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 自动可视化生成
|
|
||||||
|
|
||||||
```python
|
|
||||||
def create_visualizations(df):
|
|
||||||
"""根据数据类型自动选择合适的图表"""
|
|
||||||
charts = []
|
|
||||||
numeric_cols = df.select_dtypes(include=['number']).columns
|
|
||||||
cat_cols = df.select_dtypes(include=['object']).columns
|
|
||||||
|
|
||||||
# 数值分布 -> 直方图
|
|
||||||
if numeric_cols:
|
|
||||||
hist_chart = alt.Chart(df).mark_bar().encode(
|
|
||||||
x=alt.X(f'{col}:Q', bin=True),
|
|
||||||
y='count()'
|
|
||||||
)
|
|
||||||
charts.append(hist_chart)
|
|
||||||
|
|
||||||
# 多数值变量 -> 散点图 + 相关性热力图
|
|
||||||
# 分类+数值 -> 箱线图
|
|
||||||
# ...
|
|
||||||
|
|
||||||
return charts
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 多轮对话的上下文管理
|
|
||||||
|
|
||||||
```python
|
|
||||||
def ask_data_question(client, data_summary, question, chat_history):
|
|
||||||
messages = [
|
|
||||||
{"role": "system", "content": "你是数据分析助手..."},
|
|
||||||
{"role": "user", "content": f"数据摘要:\n{data_summary}"},
|
|
||||||
{"role": "assistant", "content": "我已了解数据,请问您想知道什么?"}
|
|
||||||
]
|
|
||||||
|
|
||||||
# 添加历史对话
|
|
||||||
for msg in chat_history:
|
|
||||||
messages.append(msg)
|
|
||||||
|
|
||||||
# 添加当前问题
|
|
||||||
messages.append({"role": "user", "content": question})
|
|
||||||
|
|
||||||
return client.chat.completions.create(
|
|
||||||
model="deepseek-chat",
|
|
||||||
messages=messages
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Session State 管理
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 初始化状态
|
|
||||||
if "df" not in st.session_state:
|
|
||||||
st.session_state.df = None
|
|
||||||
if "analyses" not in st.session_state:
|
|
||||||
st.session_state.analyses = {}
|
|
||||||
if "qa_history" not in st.session_state:
|
|
||||||
st.session_state.qa_history = []
|
|
||||||
|
|
||||||
# 跨 Tab 共享数据
|
|
||||||
df = st.session_state.df
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 使用的 Streamlit 组件
|
|
||||||
|
|
||||||
| 组件 | 用途 |
|
|
||||||
|:---|:---|
|
|
||||||
| `st.tabs` | 多标签页布局 |
|
|
||||||
| `st.columns` | 统计卡片布局 |
|
|
||||||
| `st.file_uploader` | CSV 文件上传 |
|
|
||||||
| `st.dataframe` | 交互式数据表格 |
|
|
||||||
| `st.altair_chart` | Altair 图表渲染 |
|
|
||||||
| `st.chat_message` | 问答对话界面 |
|
|
||||||
| `st.chat_input` | 聊天输入框 |
|
|
||||||
| `st.session_state` | 状态管理 |
|
|
||||||
| `st.download_button` | 报告下载 |
|
|
||||||
| `st.spinner` | 加载动画 |
|
|
||||||
| `st.expander` | 可折叠区域 |
|
|
||||||
|
|
||||||
## 🎨 Vibe Coding 开发过程
|
|
||||||
|
|
||||||
### 初始 Prompt
|
|
||||||
|
|
||||||
> "帮我创建一个 AI 数据分析师应用,使用 DeepSeek API 和 Streamlit。
|
|
||||||
>
|
|
||||||
> 功能需求:
|
|
||||||
> 1. 上传 CSV 文件,显示数据预览和统计
|
|
||||||
> 2. 自动根据数据类型生成可视化图表(用 Altair)
|
|
||||||
> 3. AI 分析功能:综合分析、相关性分析、异常检测
|
|
||||||
> 4. 自然语言问答,可以问关于数据的问题
|
|
||||||
> 5. 生成 Markdown 格式的分析报告
|
|
||||||
>
|
|
||||||
> 技术要求:
|
|
||||||
> - 使用 st.tabs 做多标签页布局
|
|
||||||
> - 使用 st.session_state 管理状态
|
|
||||||
> - 界面要美观,使用自定义 CSS"
|
|
||||||
|
|
||||||
### 迭代优化
|
|
||||||
|
|
||||||
```
|
|
||||||
Prompt 1: "给数据预览页添加统计卡片,显示行数、列数、缺失值等"
|
|
||||||
|
|
||||||
Prompt 2: "让可视化更智能,根据数据类型自动选择图表类型"
|
|
||||||
|
|
||||||
Prompt 3: "问答功能要支持多轮对话,AI 要记住之前的问题"
|
|
||||||
|
|
||||||
Prompt 4: "报告生成功能要把之前的分析结果都整合进去"
|
|
||||||
|
|
||||||
Prompt 5: "加一个示例数据选项,方便没有数据的用户体验"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 扩展建议
|
|
||||||
|
|
||||||
如果你想进一步完善这个项目,可以考虑:
|
|
||||||
|
|
||||||
1. **支持更多文件格式**:Excel、JSON、Parquet
|
|
||||||
2. **添加数据清洗功能**:缺失值处理、异常值处理
|
|
||||||
3. **更丰富的可视化**:时间序列图、地理图
|
|
||||||
4. **导出更多格式**:PDF、HTML、Word
|
|
||||||
5. **添加数据筛选器**:让用户选择分析哪些列/行
|
|
||||||
6. **机器学习预测**:简单的回归/分类预测功能
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
|
||||||
|
|
||||||
1. **数据大小**:大数据集(>10万行)可能导致界面卡顿
|
|
||||||
2. **API 成本**:长文本分析和多轮问答会消耗更多 Token
|
|
||||||
3. **隐私安全**:敏感数据请在本地使用,不要上传到公共服务器
|
|
||||||
4. **API Key 安全**:不要将 `.env` 提交到代码仓库
|
|
||||||
|
|
||||||
## 📚 参考资料
|
|
||||||
|
|
||||||
- [Streamlit 官方文档](https://docs.streamlit.io)
|
|
||||||
- [Altair 可视化文档](https://altair-viz.github.io)
|
|
||||||
- [Pandas 数据分析](https://pandas.pydata.org/docs/)
|
|
||||||
- [DeepSeek API 文档](https://platform.deepseek.com/api-docs)
|
|
||||||
|
|
||||||
## 👤 作者
|
|
||||||
|
|
||||||
Python 程序设计课程设计 - 高级示例项目
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**让数据说话,用 AI 倾听!** 📊🤖
|
|
||||||
|
|
||||||
@ -1,735 +0,0 @@
|
|||||||
"""
|
|
||||||
📊 AI 数据分析师
|
|
||||||
==================
|
|
||||||
一个基于 DeepSeek API + Streamlit 的智能数据分析应用
|
|
||||||
|
|
||||||
功能特性:
|
|
||||||
- 📁 上传 CSV 数据文件
|
|
||||||
- 📈 自动生成可视化图表
|
|
||||||
- 🤖 AI 智能分析数据特征
|
|
||||||
- 💬 自然语言问答数据
|
|
||||||
- 📝 生成分析报告
|
|
||||||
|
|
||||||
技术栈:
|
|
||||||
- Streamlit: Web 界面
|
|
||||||
- Pandas: 数据处理
|
|
||||||
- Altair: 数据可视化
|
|
||||||
- DeepSeek API: AI 分析
|
|
||||||
"""
|
|
||||||
|
|
||||||
import streamlit as st
|
|
||||||
import pandas as pd
|
|
||||||
import altair as alt
|
|
||||||
import json
|
|
||||||
from openai import OpenAI
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
# 加载环境变量
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
# ============== 配置 ==============
|
|
||||||
|
|
||||||
API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
|
|
||||||
BASE_URL = "https://api.deepseek.com"
|
|
||||||
MODEL = "deepseek-chat"
|
|
||||||
|
|
||||||
# ============== 页面配置 ==============
|
|
||||||
|
|
||||||
st.set_page_config(
|
|
||||||
page_title="AI 数据分析师",
|
|
||||||
page_icon="📊",
|
|
||||||
layout="wide",
|
|
||||||
initial_sidebar_state="expanded"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ============== 自定义样式 ==============
|
|
||||||
|
|
||||||
st.markdown("""
|
|
||||||
<style>
|
|
||||||
/* 渐变标题 */
|
|
||||||
.main-title {
|
|
||||||
background: linear-gradient(120deg, #667eea 0%, #764ba2 100%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
font-size: 2.5rem;
|
|
||||||
font-weight: 800;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 统计卡片 */
|
|
||||||
.stat-card {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card h2 {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card p {
|
|
||||||
margin: 0.5rem 0 0 0;
|
|
||||||
opacity: 0.9;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 洞察卡片 */
|
|
||||||
.insight-card {
|
|
||||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
color: white;
|
|
||||||
margin: 1rem 0;
|
|
||||||
box-shadow: 0 10px 30px rgba(245, 87, 108, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分析报告样式 */
|
|
||||||
.report-section {
|
|
||||||
background: rgba(102, 126, 234, 0.1);
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 问答区域 */
|
|
||||||
.qa-section {
|
|
||||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
color: white;
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上传区域美化 */
|
|
||||||
.uploadedFile {
|
|
||||||
border: 2px dashed #667eea !important;
|
|
||||||
border-radius: 15px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab 样式 */
|
|
||||||
.stTabs [data-baseweb="tab-list"] {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stTabs [data-baseweb="tab"] {
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# ============== 辅助函数 ==============
|
|
||||||
|
|
||||||
def get_data_summary(df: pd.DataFrame) -> str:
|
|
||||||
"""生成数据摘要文本供 AI 分析"""
|
|
||||||
summary = []
|
|
||||||
|
|
||||||
# 基本信息
|
|
||||||
summary.append(f"数据集包含 {len(df)} 行和 {len(df.columns)} 列。")
|
|
||||||
summary.append(f"\n列名:{', '.join(df.columns.tolist())}")
|
|
||||||
|
|
||||||
# 数据类型
|
|
||||||
summary.append(f"\n\n各列数据类型:")
|
|
||||||
for col in df.columns:
|
|
||||||
dtype = str(df[col].dtype)
|
|
||||||
null_count = df[col].isnull().sum()
|
|
||||||
summary.append(f"- {col}: {dtype}, 缺失值: {null_count}")
|
|
||||||
|
|
||||||
# 数值列统计
|
|
||||||
numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
|
|
||||||
if numeric_cols:
|
|
||||||
summary.append(f"\n\n数值列统计摘要:")
|
|
||||||
stats = df[numeric_cols].describe().to_string()
|
|
||||||
summary.append(stats)
|
|
||||||
|
|
||||||
# 分类列信息
|
|
||||||
cat_cols = df.select_dtypes(include=['object', 'category']).columns.tolist()
|
|
||||||
if cat_cols:
|
|
||||||
summary.append(f"\n\n分类列信息:")
|
|
||||||
for col in cat_cols[:5]: # 最多显示5个
|
|
||||||
unique_count = df[col].nunique()
|
|
||||||
top_values = df[col].value_counts().head(5).to_dict()
|
|
||||||
summary.append(f"- {col}: {unique_count} 个唯一值, 前5个: {top_values}")
|
|
||||||
|
|
||||||
# 样本数据
|
|
||||||
summary.append(f"\n\n前3行数据样本:")
|
|
||||||
summary.append(df.head(3).to_string())
|
|
||||||
|
|
||||||
return "\n".join(summary)
|
|
||||||
|
|
||||||
|
|
||||||
def analyze_with_ai(client: OpenAI, data_summary: str, analysis_type: str = "general") -> str:
|
|
||||||
"""使用 AI 分析数据"""
|
|
||||||
|
|
||||||
prompts = {
|
|
||||||
"general": """你是一个专业的数据分析师。请根据以下数据摘要,提供全面的数据分析洞察。
|
|
||||||
|
|
||||||
请从以下几个方面进行分析:
|
|
||||||
1. 📊 数据概况:数据的整体情况和质量评估
|
|
||||||
2. 🔍 关键发现:数据中的重要模式和特征
|
|
||||||
3. 📈 建议可视化:推荐适合这个数据集的图表类型
|
|
||||||
4. ⚠️ 注意事项:数据中可能存在的问题或需要注意的地方
|
|
||||||
5. 💡 进一步分析建议:可以深入探索的方向
|
|
||||||
|
|
||||||
请用清晰的结构和通俗易懂的语言回答。""",
|
|
||||||
|
|
||||||
"correlation": """你是一个专业的数据分析师。请分析以下数据中各变量之间可能存在的相关性和关联。
|
|
||||||
|
|
||||||
请重点分析:
|
|
||||||
1. 哪些变量之间可能存在正相关或负相关?
|
|
||||||
2. 是否存在明显的因果关系假设?
|
|
||||||
3. 建议做哪些相关性分析?
|
|
||||||
|
|
||||||
请用通俗易懂的语言解释。""",
|
|
||||||
|
|
||||||
"anomaly": """你是一个专业的数据分析师。请分析以下数据中可能存在的异常值或异常模式。
|
|
||||||
|
|
||||||
请关注:
|
|
||||||
1. 数值是否有明显的异常值?
|
|
||||||
2. 数据分布是否有异常?
|
|
||||||
3. 是否存在数据质量问题?
|
|
||||||
4. 建议如何处理这些异常?
|
|
||||||
|
|
||||||
请给出具体的分析和建议。"""
|
|
||||||
}
|
|
||||||
|
|
||||||
system_prompt = prompts.get(analysis_type, prompts["general"])
|
|
||||||
|
|
||||||
response = client.chat.completions.create(
|
|
||||||
model=MODEL,
|
|
||||||
messages=[
|
|
||||||
{"role": "system", "content": system_prompt},
|
|
||||||
{"role": "user", "content": f"数据摘要:\n\n{data_summary}"}
|
|
||||||
],
|
|
||||||
max_tokens=2000,
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.choices[0].message.content
|
|
||||||
|
|
||||||
|
|
||||||
def ask_data_question(client: OpenAI, data_summary: str, question: str, chat_history: list) -> str:
|
|
||||||
"""根据数据回答用户问题"""
|
|
||||||
|
|
||||||
system_prompt = """你是一个专业的数据分析助手。用户会就数据集提出问题,你需要根据提供的数据摘要来回答。
|
|
||||||
|
|
||||||
回答要求:
|
|
||||||
1. 基于数据事实回答,不要编造数据中没有的信息
|
|
||||||
2. 如果数据摘要中没有足够信息回答问题,请诚实说明
|
|
||||||
3. 尽可能提供具体的数字和分析
|
|
||||||
4. 可以给出进一步分析的建议"""
|
|
||||||
|
|
||||||
messages = [{"role": "system", "content": system_prompt}]
|
|
||||||
messages.append({"role": "user", "content": f"数据摘要:\n{data_summary}\n\n请记住这个数据摘要,我接下来会问你关于这个数据的问题。"})
|
|
||||||
messages.append({"role": "assistant", "content": "好的,我已经了解了这个数据集的情况。请问您想了解什么?"})
|
|
||||||
|
|
||||||
# 添加对话历史
|
|
||||||
for msg in chat_history:
|
|
||||||
messages.append(msg)
|
|
||||||
|
|
||||||
# 添加当前问题
|
|
||||||
messages.append({"role": "user", "content": question})
|
|
||||||
|
|
||||||
response = client.chat.completions.create(
|
|
||||||
model=MODEL,
|
|
||||||
messages=messages,
|
|
||||||
max_tokens=1500,
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.choices[0].message.content
|
|
||||||
|
|
||||||
|
|
||||||
def generate_report(client: OpenAI, data_summary: str, analyses: dict) -> str:
|
|
||||||
"""生成完整的分析报告"""
|
|
||||||
|
|
||||||
system_prompt = """你是一个专业的数据分析师,请根据数据摘要和已有的分析结果,生成一份完整的数据分析报告。
|
|
||||||
|
|
||||||
报告格式要求:
|
|
||||||
1. 使用 Markdown 格式
|
|
||||||
2. 包含标题、摘要、详细分析、结论和建议
|
|
||||||
3. 结构清晰,层次分明
|
|
||||||
4. 语言专业但易懂
|
|
||||||
5. 适当使用 emoji 增加可读性"""
|
|
||||||
|
|
||||||
user_content = f"""数据摘要:
|
|
||||||
{data_summary}
|
|
||||||
|
|
||||||
已有分析结果:
|
|
||||||
{json.dumps(analyses, ensure_ascii=False, indent=2)}
|
|
||||||
|
|
||||||
请生成一份完整的数据分析报告。"""
|
|
||||||
|
|
||||||
response = client.chat.completions.create(
|
|
||||||
model=MODEL,
|
|
||||||
messages=[
|
|
||||||
{"role": "system", "content": system_prompt},
|
|
||||||
{"role": "user", "content": user_content}
|
|
||||||
],
|
|
||||||
max_tokens=3000,
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.choices[0].message.content
|
|
||||||
|
|
||||||
|
|
||||||
def create_visualizations(df: pd.DataFrame):
|
|
||||||
"""根据数据自动生成可视化图表"""
|
|
||||||
charts = []
|
|
||||||
|
|
||||||
numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
|
|
||||||
cat_cols = df.select_dtypes(include=['object', 'category']).columns.tolist()
|
|
||||||
|
|
||||||
# 1. 数值分布直方图(第一个数值列)
|
|
||||||
if numeric_cols:
|
|
||||||
col = numeric_cols[0]
|
|
||||||
hist_chart = alt.Chart(df).mark_bar(
|
|
||||||
opacity=0.7,
|
|
||||||
color='#667eea'
|
|
||||||
).encode(
|
|
||||||
alt.X(f'{col}:Q', bin=alt.Bin(maxbins=30), title=col),
|
|
||||||
alt.Y('count()', title='频数'),
|
|
||||||
tooltip=[alt.Tooltip(f'{col}:Q', bin=alt.Bin(maxbins=30)), 'count()']
|
|
||||||
).properties(
|
|
||||||
title=f'📊 {col} 分布直方图',
|
|
||||||
height=300
|
|
||||||
)
|
|
||||||
charts.append(('distribution', hist_chart, f'{col} 的数值分布'))
|
|
||||||
|
|
||||||
# 2. 分类计数柱状图(第一个分类列)
|
|
||||||
if cat_cols:
|
|
||||||
col = cat_cols[0]
|
|
||||||
if df[col].nunique() <= 20: # 类别不要太多
|
|
||||||
bar_chart = alt.Chart(df).mark_bar(
|
|
||||||
color='#764ba2'
|
|
||||||
).encode(
|
|
||||||
x=alt.X(f'{col}:N', sort='-y', title=col),
|
|
||||||
y=alt.Y('count()', title='计数'),
|
|
||||||
tooltip=[col, 'count()']
|
|
||||||
).properties(
|
|
||||||
title=f'📈 {col} 分类计数',
|
|
||||||
height=300
|
|
||||||
)
|
|
||||||
charts.append(('category', bar_chart, f'{col} 各类别的计数'))
|
|
||||||
|
|
||||||
# 3. 散点图(如果有多个数值列)
|
|
||||||
if len(numeric_cols) >= 2:
|
|
||||||
col1, col2 = numeric_cols[0], numeric_cols[1]
|
|
||||||
color_col = cat_cols[0] if cat_cols and df[cat_cols[0]].nunique() <= 10 else None
|
|
||||||
|
|
||||||
scatter_encoding = {
|
|
||||||
'x': alt.X(f'{col1}:Q', title=col1),
|
|
||||||
'y': alt.Y(f'{col2}:Q', title=col2),
|
|
||||||
'tooltip': [col1, col2]
|
|
||||||
}
|
|
||||||
|
|
||||||
if color_col:
|
|
||||||
scatter_encoding['color'] = alt.Color(f'{color_col}:N', title=color_col)
|
|
||||||
scatter_encoding['tooltip'].append(color_col)
|
|
||||||
|
|
||||||
scatter_chart = alt.Chart(df).mark_circle(
|
|
||||||
size=60,
|
|
||||||
opacity=0.6
|
|
||||||
).encode(
|
|
||||||
**scatter_encoding
|
|
||||||
).properties(
|
|
||||||
title=f'🔵 {col1} vs {col2} 散点图',
|
|
||||||
height=350
|
|
||||||
).interactive()
|
|
||||||
|
|
||||||
charts.append(('scatter', scatter_chart, f'{col1} 与 {col2} 的关系'))
|
|
||||||
|
|
||||||
# 4. 箱线图(数值列按分类)
|
|
||||||
if numeric_cols and cat_cols:
|
|
||||||
num_col = numeric_cols[0]
|
|
||||||
cat_col = cat_cols[0]
|
|
||||||
if df[cat_col].nunique() <= 10:
|
|
||||||
box_chart = alt.Chart(df).mark_boxplot(
|
|
||||||
color='#11998e'
|
|
||||||
).encode(
|
|
||||||
x=alt.X(f'{cat_col}:N', title=cat_col),
|
|
||||||
y=alt.Y(f'{num_col}:Q', title=num_col),
|
|
||||||
color=alt.Color(f'{cat_col}:N', legend=None)
|
|
||||||
).properties(
|
|
||||||
title=f'📦 {num_col} 按 {cat_col} 分组箱线图',
|
|
||||||
height=300
|
|
||||||
)
|
|
||||||
charts.append(('boxplot', box_chart, f'{num_col} 在不同 {cat_col} 下的分布'))
|
|
||||||
|
|
||||||
# 5. 相关性热力图(如果有多个数值列)
|
|
||||||
if len(numeric_cols) >= 3:
|
|
||||||
corr_df = df[numeric_cols].corr().reset_index().melt(id_vars='index')
|
|
||||||
corr_df.columns = ['var1', 'var2', 'correlation']
|
|
||||||
|
|
||||||
heatmap = alt.Chart(corr_df).mark_rect().encode(
|
|
||||||
x=alt.X('var1:N', title=''),
|
|
||||||
y=alt.Y('var2:N', title=''),
|
|
||||||
color=alt.Color('correlation:Q',
|
|
||||||
scale=alt.Scale(scheme='redblue', domain=[-1, 1]),
|
|
||||||
title='相关系数'),
|
|
||||||
tooltip=['var1', 'var2', alt.Tooltip('correlation:Q', format='.2f')]
|
|
||||||
).properties(
|
|
||||||
title='🔥 相关性热力图',
|
|
||||||
height=300
|
|
||||||
)
|
|
||||||
charts.append(('heatmap', heatmap, '各数值变量间的相关性'))
|
|
||||||
|
|
||||||
return charts
|
|
||||||
|
|
||||||
|
|
||||||
# ============== 初始化 Session State ==============
|
|
||||||
|
|
||||||
if "df" not in st.session_state:
|
|
||||||
st.session_state.df = None
|
|
||||||
|
|
||||||
if "data_summary" not in st.session_state:
|
|
||||||
st.session_state.data_summary = None
|
|
||||||
|
|
||||||
if "analyses" not in st.session_state:
|
|
||||||
st.session_state.analyses = {}
|
|
||||||
|
|
||||||
if "qa_history" not in st.session_state:
|
|
||||||
st.session_state.qa_history = []
|
|
||||||
|
|
||||||
# ============== 侧边栏 ==============
|
|
||||||
|
|
||||||
with st.sidebar:
|
|
||||||
st.markdown("## 🔧 设置")
|
|
||||||
|
|
||||||
# API Key 输入
|
|
||||||
api_key_input = st.text_input(
|
|
||||||
"DeepSeek API Key",
|
|
||||||
value=API_KEY,
|
|
||||||
type="password",
|
|
||||||
help="输入你的 DeepSeek API Key"
|
|
||||||
)
|
|
||||||
|
|
||||||
if api_key_input:
|
|
||||||
API_KEY = api_key_input
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# 文件上传
|
|
||||||
st.markdown("## 📁 数据上传")
|
|
||||||
|
|
||||||
uploaded_file = st.file_uploader(
|
|
||||||
"上传 CSV 文件",
|
|
||||||
type=['csv'],
|
|
||||||
help="支持 CSV 格式的数据文件"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 或使用示例数据
|
|
||||||
use_sample = st.checkbox("使用示例数据", value=False)
|
|
||||||
|
|
||||||
if use_sample:
|
|
||||||
# 生成示例数据
|
|
||||||
import numpy as np
|
|
||||||
np.random.seed(42)
|
|
||||||
|
|
||||||
sample_df = pd.DataFrame({
|
|
||||||
'姓名': [f'员工{i}' for i in range(1, 101)],
|
|
||||||
'部门': np.random.choice(['技术部', '市场部', '财务部', '人事部', '运营部'], 100),
|
|
||||||
'年龄': np.random.randint(22, 55, 100),
|
|
||||||
'工龄': np.random.randint(1, 20, 100),
|
|
||||||
'月薪': np.random.randint(8000, 50000, 100),
|
|
||||||
'绩效评分': np.round(np.random.uniform(60, 100, 100), 1),
|
|
||||||
'满意度': np.round(np.random.uniform(1, 5, 100), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
st.session_state.df = sample_df
|
|
||||||
st.session_state.data_summary = get_data_summary(sample_df)
|
|
||||||
st.success("✅ 已加载示例数据")
|
|
||||||
|
|
||||||
elif uploaded_file is not None:
|
|
||||||
try:
|
|
||||||
df = pd.read_csv(uploaded_file)
|
|
||||||
st.session_state.df = df
|
|
||||||
st.session_state.data_summary = get_data_summary(df)
|
|
||||||
st.session_state.analyses = {} # 重置分析结果
|
|
||||||
st.session_state.qa_history = [] # 重置问答历史
|
|
||||||
st.success(f"✅ 成功加载 {len(df)} 行数据")
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"❌ 文件加载失败: {str(e)}")
|
|
||||||
|
|
||||||
# 显示数据信息
|
|
||||||
if st.session_state.df is not None:
|
|
||||||
st.divider()
|
|
||||||
st.markdown("### 📊 数据概览")
|
|
||||||
df = st.session_state.df
|
|
||||||
st.markdown(f"- **行数**: {len(df)}")
|
|
||||||
st.markdown(f"- **列数**: {len(df.columns)}")
|
|
||||||
st.markdown(f"- **数值列**: {len(df.select_dtypes(include=['number']).columns)}")
|
|
||||||
st.markdown(f"- **分类列**: {len(df.select_dtypes(include=['object', 'category']).columns)}")
|
|
||||||
|
|
||||||
# ============== 主界面 ==============
|
|
||||||
|
|
||||||
st.markdown('<h1 class="main-title">📊 AI 数据分析师</h1>', unsafe_allow_html=True)
|
|
||||||
st.markdown("*上传数据,让 AI 帮你发现数据中的故事*")
|
|
||||||
|
|
||||||
# 检查 API Key
|
|
||||||
if not API_KEY:
|
|
||||||
st.warning("⚠️ 请在侧边栏输入你的 DeepSeek API Key")
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
# 检查数据
|
|
||||||
if st.session_state.df is None:
|
|
||||||
st.info("👈 请在侧边栏上传 CSV 文件或使用示例数据")
|
|
||||||
|
|
||||||
# 显示使用说明
|
|
||||||
with st.expander("📖 使用说明", expanded=True):
|
|
||||||
st.markdown("""
|
|
||||||
### 欢迎使用 AI 数据分析师!
|
|
||||||
|
|
||||||
**功能介绍:**
|
|
||||||
1. **📈 数据预览** - 查看数据基本信息和统计摘要
|
|
||||||
2. **📊 智能可视化** - 自动生成适合数据的图表
|
|
||||||
3. **🤖 AI 分析** - AI 深度分析数据特征和洞察
|
|
||||||
4. **💬 数据问答** - 用自然语言询问关于数据的问题
|
|
||||||
5. **📝 生成报告** - 一键生成完整的分析报告
|
|
||||||
|
|
||||||
**开始使用:**
|
|
||||||
1. 在左侧边栏输入你的 DeepSeek API Key
|
|
||||||
2. 上传 CSV 文件或勾选"使用示例数据"
|
|
||||||
3. 切换不同标签页探索各项功能
|
|
||||||
""")
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
# 创建 OpenAI 客户端
|
|
||||||
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
|
||||||
df = st.session_state.df
|
|
||||||
|
|
||||||
# 创建标签页
|
|
||||||
tab1, tab2, tab3, tab4, tab5 = st.tabs([
|
|
||||||
"📈 数据预览",
|
|
||||||
"📊 智能可视化",
|
|
||||||
"🤖 AI 分析",
|
|
||||||
"💬 数据问答",
|
|
||||||
"📝 生成报告"
|
|
||||||
])
|
|
||||||
|
|
||||||
# ============== Tab 1: 数据预览 ==============
|
|
||||||
with tab1:
|
|
||||||
st.header("📈 数据预览")
|
|
||||||
|
|
||||||
# 统计卡片
|
|
||||||
col1, col2, col3, col4 = st.columns(4)
|
|
||||||
|
|
||||||
with col1:
|
|
||||||
st.markdown(f"""
|
|
||||||
<div class="stat-card">
|
|
||||||
<h2>{len(df)}</h2>
|
|
||||||
<p>数据行数</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
with col2:
|
|
||||||
st.markdown(f"""
|
|
||||||
<div class="stat-card">
|
|
||||||
<h2>{len(df.columns)}</h2>
|
|
||||||
<p>数据列数</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
with col3:
|
|
||||||
missing = df.isnull().sum().sum()
|
|
||||||
st.markdown(f"""
|
|
||||||
<div class="stat-card">
|
|
||||||
<h2>{missing}</h2>
|
|
||||||
<p>缺失值数量</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
with col4:
|
|
||||||
numeric_cols = len(df.select_dtypes(include=['number']).columns)
|
|
||||||
st.markdown(f"""
|
|
||||||
<div class="stat-card">
|
|
||||||
<h2>{numeric_cols}</h2>
|
|
||||||
<p>数值列数量</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
st.markdown("---")
|
|
||||||
|
|
||||||
# 数据表格
|
|
||||||
st.subheader("📋 数据表格")
|
|
||||||
st.dataframe(df, use_container_width=True, height=400)
|
|
||||||
|
|
||||||
# 统计摘要
|
|
||||||
col_left, col_right = st.columns(2)
|
|
||||||
|
|
||||||
with col_left:
|
|
||||||
st.subheader("📊 数值列统计")
|
|
||||||
numeric_df = df.select_dtypes(include=['number'])
|
|
||||||
if not numeric_df.empty:
|
|
||||||
st.dataframe(numeric_df.describe(), use_container_width=True)
|
|
||||||
else:
|
|
||||||
st.info("没有数值列")
|
|
||||||
|
|
||||||
with col_right:
|
|
||||||
st.subheader("📝 列信息")
|
|
||||||
info_df = pd.DataFrame({
|
|
||||||
'列名': df.columns,
|
|
||||||
'数据类型': df.dtypes.astype(str).values,
|
|
||||||
'非空值数': df.count().values,
|
|
||||||
'唯一值数': [df[col].nunique() for col in df.columns]
|
|
||||||
})
|
|
||||||
st.dataframe(info_df, use_container_width=True, hide_index=True)
|
|
||||||
|
|
||||||
# ============== Tab 2: 智能可视化 ==============
|
|
||||||
with tab2:
|
|
||||||
st.header("📊 智能可视化")
|
|
||||||
st.markdown("*根据数据特征自动生成适合的图表*")
|
|
||||||
|
|
||||||
charts = create_visualizations(df)
|
|
||||||
|
|
||||||
if not charts:
|
|
||||||
st.warning("数据列类型不足以生成可视化图表")
|
|
||||||
else:
|
|
||||||
for i, (chart_type, chart, description) in enumerate(charts):
|
|
||||||
st.markdown(f"**{description}**")
|
|
||||||
st.altair_chart(chart, use_container_width=True)
|
|
||||||
if i < len(charts) - 1:
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# ============== Tab 3: AI 分析 ==============
|
|
||||||
with tab3:
|
|
||||||
st.header("🤖 AI 智能分析")
|
|
||||||
|
|
||||||
analysis_type = st.selectbox(
|
|
||||||
"选择分析类型",
|
|
||||||
options=[
|
|
||||||
("general", "🔍 综合分析 - 全面了解数据特征"),
|
|
||||||
("correlation", "🔗 相关性分析 - 探索变量间关系"),
|
|
||||||
("anomaly", "⚠️ 异常检测 - 发现数据问题")
|
|
||||||
],
|
|
||||||
format_func=lambda x: x[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
if st.button("🚀 开始 AI 分析", type="primary", use_container_width=True):
|
|
||||||
with st.spinner("AI 正在分析数据,请稍候..."):
|
|
||||||
try:
|
|
||||||
result = analyze_with_ai(
|
|
||||||
client,
|
|
||||||
st.session_state.data_summary,
|
|
||||||
analysis_type[0]
|
|
||||||
)
|
|
||||||
st.session_state.analyses[analysis_type[0]] = result
|
|
||||||
st.success("✅ 分析完成!")
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"❌ 分析出错: {str(e)}")
|
|
||||||
|
|
||||||
# 显示分析结果
|
|
||||||
if analysis_type[0] in st.session_state.analyses:
|
|
||||||
st.markdown("### 📋 分析结果")
|
|
||||||
st.markdown(st.session_state.analyses[analysis_type[0]])
|
|
||||||
|
|
||||||
# 显示历史分析
|
|
||||||
other_analyses = {k: v for k, v in st.session_state.analyses.items() if k != analysis_type[0]}
|
|
||||||
if other_analyses:
|
|
||||||
st.divider()
|
|
||||||
with st.expander("📚 查看其他分析结果"):
|
|
||||||
for key, value in other_analyses.items():
|
|
||||||
type_names = {"general": "综合分析", "correlation": "相关性分析", "anomaly": "异常检测"}
|
|
||||||
st.markdown(f"#### {type_names.get(key, key)}")
|
|
||||||
st.markdown(value)
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# ============== Tab 4: 数据问答 ==============
|
|
||||||
with tab4:
|
|
||||||
st.header("💬 数据问答")
|
|
||||||
st.markdown("*用自然语言询问关于数据的任何问题*")
|
|
||||||
|
|
||||||
# 示例问题
|
|
||||||
with st.expander("💡 示例问题"):
|
|
||||||
st.markdown("""
|
|
||||||
- 这个数据集的主要特征是什么?
|
|
||||||
- 哪个部门的平均薪资最高?
|
|
||||||
- 年龄和工资之间有什么关系?
|
|
||||||
- 数据中有哪些异常值?
|
|
||||||
- 如何提高员工满意度?
|
|
||||||
""")
|
|
||||||
|
|
||||||
# 显示对话历史
|
|
||||||
for msg in st.session_state.qa_history:
|
|
||||||
avatar = "👤" if msg["role"] == "user" else "🤖"
|
|
||||||
with st.chat_message(msg["role"], avatar=avatar):
|
|
||||||
st.markdown(msg["content"])
|
|
||||||
|
|
||||||
# 问题输入
|
|
||||||
if question := st.chat_input("输入你关于数据的问题..."):
|
|
||||||
# 显示用户问题
|
|
||||||
with st.chat_message("user", avatar="👤"):
|
|
||||||
st.markdown(question)
|
|
||||||
|
|
||||||
# AI 回答
|
|
||||||
with st.chat_message("assistant", avatar="🤖"):
|
|
||||||
with st.spinner("思考中..."):
|
|
||||||
try:
|
|
||||||
answer = ask_data_question(
|
|
||||||
client,
|
|
||||||
st.session_state.data_summary,
|
|
||||||
question,
|
|
||||||
st.session_state.qa_history
|
|
||||||
)
|
|
||||||
st.markdown(answer)
|
|
||||||
|
|
||||||
# 保存对话历史
|
|
||||||
st.session_state.qa_history.append({"role": "user", "content": question})
|
|
||||||
st.session_state.qa_history.append({"role": "assistant", "content": answer})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"❌ 回答出错: {str(e)}")
|
|
||||||
|
|
||||||
# 清空对话按钮
|
|
||||||
if st.session_state.qa_history:
|
|
||||||
if st.button("🗑️ 清空对话历史"):
|
|
||||||
st.session_state.qa_history = []
|
|
||||||
st.rerun()
|
|
||||||
|
|
||||||
# ============== Tab 5: 生成报告 ==============
|
|
||||||
with tab5:
|
|
||||||
st.header("📝 数据分析报告")
|
|
||||||
|
|
||||||
if not st.session_state.analyses:
|
|
||||||
st.info("💡 请先在「AI 分析」标签页进行分析,然后再生成报告")
|
|
||||||
else:
|
|
||||||
st.markdown("*基于已有分析结果生成完整的数据分析报告*")
|
|
||||||
|
|
||||||
if st.button("📄 生成分析报告", type="primary", use_container_width=True):
|
|
||||||
with st.spinner("正在生成报告,请稍候..."):
|
|
||||||
try:
|
|
||||||
report = generate_report(
|
|
||||||
client,
|
|
||||||
st.session_state.data_summary,
|
|
||||||
st.session_state.analyses
|
|
||||||
)
|
|
||||||
st.session_state.report = report
|
|
||||||
st.success("✅ 报告生成完成!")
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"❌ 生成报告出错: {str(e)}")
|
|
||||||
|
|
||||||
# 显示报告
|
|
||||||
if "report" in st.session_state:
|
|
||||||
st.markdown("---")
|
|
||||||
st.markdown(st.session_state.report)
|
|
||||||
|
|
||||||
# 下载按钮
|
|
||||||
st.download_button(
|
|
||||||
label="📥 下载报告 (Markdown)",
|
|
||||||
data=st.session_state.report,
|
|
||||||
file_name="数据分析报告.md",
|
|
||||||
mime="text/markdown"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ============== 页脚 ==============
|
|
||||||
st.divider()
|
|
||||||
st.caption("Made with ❤️ using DeepSeek API + Streamlit | Python 程序设计课程设计示例")
|
|
||||||
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
streamlit>=1.28.0
|
|
||||||
openai>=1.0.0
|
|
||||||
python-dotenv>=1.0.0
|
|
||||||
pandas>=2.0.0
|
|
||||||
altair>=5.0.0
|
|
||||||
numpy>=1.24.0
|
|
||||||
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
姓名,部门,年龄,工龄,月薪,绩效评分,满意度
|
|
||||||
张三,技术部,28,3,15000,85.5,4.2
|
|
||||||
李四,市场部,32,5,18000,78.3,3.8
|
|
||||||
王五,财务部,45,15,35000,92.1,4.5
|
|
||||||
赵六,技术部,26,2,12000,88.7,4.0
|
|
||||||
钱七,人事部,38,10,25000,76.5,3.5
|
|
||||||
孙八,运营部,29,4,14000,82.3,4.1
|
|
||||||
周九,技术部,35,8,28000,95.2,4.8
|
|
||||||
吴十,市场部,27,3,13500,79.8,3.9
|
|
||||||
郑十一,财务部,42,12,32000,88.9,4.3
|
|
||||||
王十二,技术部,31,6,22000,91.5,4.6
|
|
||||||
李十三,人事部,36,9,24000,75.2,3.4
|
|
||||||
张十四,运营部,24,1,10000,70.5,3.2
|
|
||||||
刘十五,技术部,33,7,25000,89.3,4.4
|
|
||||||
陈十六,市场部,29,4,16000,81.7,4.0
|
|
||||||
杨十七,财务部,48,18,42000,94.8,4.7
|
|
||||||
黄十八,技术部,27,2,14000,86.2,4.1
|
|
||||||
赵十九,人事部,41,13,28000,77.9,3.6
|
|
||||||
钱二十,运营部,30,5,17000,83.4,4.2
|
|
||||||
孙廿一,技术部,25,1,11000,72.8,3.3
|
|
||||||
周廿二,市场部,34,7,21000,85.6,4.3
|
|
||||||
吴廿三,财务部,39,11,30000,90.2,4.5
|
|
||||||
郑廿四,技术部,28,3,16000,87.1,4.2
|
|
||||||
王廿五,人事部,43,14,29000,78.5,3.7
|
|
||||||
李廿六,运营部,26,2,12500,74.3,3.4
|
|
||||||
张廿七,技术部,37,9,27000,93.6,4.7
|
|
||||||
刘廿八,市场部,31,5,19000,80.9,3.9
|
|
||||||
陈廿九,财务部,46,16,38000,91.8,4.6
|
|
||||||
杨三十,技术部,29,4,18000,88.4,4.3
|
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
# DeepSeek API Key 配置示例
|
|
||||||
# ================================
|
|
||||||
# 使用方法:
|
|
||||||
# 1. 将此文件复制并重命名为 .env
|
|
||||||
# 2. 将下面的 sk-xxx... 替换为你的真实 API Key
|
|
||||||
# ================================
|
|
||||||
|
|
||||||
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
||||||
|
|
||||||
21
示例项目-角色扮演聊天室/.gitignore
vendored
21
示例项目-角色扮演聊天室/.gitignore
vendored
@ -1,21 +0,0 @@
|
|||||||
# 环境变量(包含敏感信息)
|
|
||||||
.env
|
|
||||||
|
|
||||||
# Python
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
*.so
|
|
||||||
.Python
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
# 🎭 AI 角色扮演聊天室
|
|
||||||
|
|
||||||
一个基于 DeepSeek API + Streamlit 构建的角色扮演聊天应用,支持多种预设角色和自定义角色创建。
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
## ✨ 功能特性
|
|
||||||
|
|
||||||
- 🎭 **多种预设角色**:傲娇猫娘、智慧老者、毒舌AI、科学怪人等
|
|
||||||
- ✨ **自定义角色**:创建你想象中的任何角色
|
|
||||||
- 💬 **多轮对话**:保留对话历史,角色会记住上下文
|
|
||||||
- ⚡ **流式输出**:打字机效果,实时显示 AI 回复
|
|
||||||
- 🎨 **美观界面**:精心设计的 UI,渐变色彩和动画效果
|
|
||||||
|
|
||||||
## 🖼️ 界面预览
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 🎭 角色设置 │ 🎭 AI 角色扮演聊天室 │
|
|
||||||
│ ───────────── │ ───────────────────── │
|
|
||||||
│ 🔑 API 配置 │ │
|
|
||||||
│ [DeepSeek API Key] │ 🐱 │
|
|
||||||
│ ───────────── │ 正在与 小喵 对话 │
|
|
||||||
│ 🎭 选择角色 │ │
|
|
||||||
│ [🐱 傲娇猫娘 ▼] │ ──────────────────────────── │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────┐ │ 👤: 你好呀! │
|
|
||||||
│ │ 🐱 小喵 │ │ │
|
|
||||||
│ │ 你是一个傲娇的 │ │ 🐱: 哼,才不是特意来跟你打招 │
|
|
||||||
│ │ 猫娘... │ │ 呼的呢!...喵~ (。・ω・。) │
|
|
||||||
│ └─────────────────┘ │ │
|
|
||||||
│ │ ──────────────────────────── │
|
|
||||||
│ [🗑️ 清空对话历史] │ [输入你想说的话... ] │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
### 1. 克隆/下载项目
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd 示例项目-角色扮演聊天室
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 创建虚拟环境(推荐)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m venv venv
|
|
||||||
|
|
||||||
# Windows
|
|
||||||
venv\Scripts\activate
|
|
||||||
|
|
||||||
# macOS/Linux
|
|
||||||
source venv/bin/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 安装依赖
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 配置 API Key
|
|
||||||
|
|
||||||
复制环境变量示例文件:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
```
|
|
||||||
|
|
||||||
编辑 `.env` 文件,填入你的 DeepSeek API Key:
|
|
||||||
|
|
||||||
```
|
|
||||||
DEEPSEEK_API_KEY=sk-your-actual-api-key
|
|
||||||
```
|
|
||||||
|
|
||||||
> 💡 **获取 API Key**:访问 [DeepSeek 开放平台](https://platform.deepseek.com),注册账号后在控制台创建。
|
|
||||||
|
|
||||||
### 5. 运行应用
|
|
||||||
|
|
||||||
```bash
|
|
||||||
streamlit run app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
应用将在浏览器中自动打开,默认地址:http://localhost:8501
|
|
||||||
|
|
||||||
## 📁 项目结构
|
|
||||||
|
|
||||||
```
|
|
||||||
示例项目-角色扮演聊天室/
|
|
||||||
├── app.py # 主程序
|
|
||||||
├── requirements.txt # 项目依赖
|
|
||||||
├── .env.example # 环境变量示例
|
|
||||||
├── .env # 环境变量(需自行创建,不要提交!)
|
|
||||||
├── .gitignore # Git 忽略文件
|
|
||||||
└── README.md # 项目说明
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎭 预设角色介绍
|
|
||||||
|
|
||||||
| 角色 | 名称 | 特点 |
|
|
||||||
|:---:|:---|:---|
|
|
||||||
| 🐱 | 傲娇猫娘·小喵 | 傲娇但可爱,句尾带"喵~",会用颜文字 |
|
|
||||||
| 🧙 | 智慧老者·玄清子 | 富有哲理,喜欢引用古诗词 |
|
|
||||||
| 🤖 | 毒舌AI·犀利哥 | 说话犀利一针见血,但有建设性 |
|
|
||||||
| 👨🔬 | 科学怪人·Dr.Eureka | 疯狂科学家,热衷科学分析 |
|
|
||||||
| 🎮 | 游戏解说·电竞小王 | 充满激情,使用游戏术语 |
|
|
||||||
| 📚 | 文艺青年·林小诗 | 说话有诗意,会即兴创作 |
|
|
||||||
|
|
||||||
## 🛠️ 技术实现
|
|
||||||
|
|
||||||
### 核心技术点
|
|
||||||
|
|
||||||
1. **DeepSeek API 调用**
|
|
||||||
- 使用 OpenAI SDK 兼容接口
|
|
||||||
- 流式输出(Streaming)实现打字机效果
|
|
||||||
|
|
||||||
2. **Streamlit 组件**
|
|
||||||
- `st.chat_message` - 聊天消息展示
|
|
||||||
- `st.chat_input` - 聊天输入框
|
|
||||||
- `st.session_state` - 对话历史管理
|
|
||||||
- `st.write_stream` - 流式输出渲染
|
|
||||||
|
|
||||||
3. **角色扮演机制**
|
|
||||||
- System Prompt 定义角色性格和说话方式
|
|
||||||
- 多轮对话保持角色一致性
|
|
||||||
|
|
||||||
### 关键代码解析
|
|
||||||
|
|
||||||
#### 流式输出实现
|
|
||||||
|
|
||||||
```python
|
|
||||||
stream = client.chat.completions.create(
|
|
||||||
model=MODEL,
|
|
||||||
messages=messages_for_api,
|
|
||||||
stream=True, # 启用流式输出
|
|
||||||
)
|
|
||||||
|
|
||||||
response = st.write_stream(
|
|
||||||
chunk.choices[0].delta.content or ""
|
|
||||||
for chunk in stream
|
|
||||||
if chunk.choices[0].delta.content
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 对话历史管理
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 初始化
|
|
||||||
if "messages" not in st.session_state:
|
|
||||||
st.session_state.messages = []
|
|
||||||
|
|
||||||
# 添加消息
|
|
||||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
|
||||||
|
|
||||||
# 构建 API 请求
|
|
||||||
messages_for_api = [
|
|
||||||
{"role": "system", "content": system_prompt} # 角色设定
|
|
||||||
] + st.session_state.messages # 历史消息
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎨 自定义角色指南
|
|
||||||
|
|
||||||
创建一个好的自定义角色,需要在角色设定中包含:
|
|
||||||
|
|
||||||
1. **身份定义**:角色是谁,背景是什么
|
|
||||||
2. **性格特点**:外向/内向、温柔/严厉等
|
|
||||||
3. **说话方式**:口癖、常用词、语气
|
|
||||||
4. **对用户态度**:如何称呼用户,关系定位
|
|
||||||
5. **特殊行为**:是否使用emoji、颜文字等
|
|
||||||
|
|
||||||
### 示例:创建一个"中二少年"角色
|
|
||||||
|
|
||||||
```
|
|
||||||
你是一个中二病少年,自称"黑暗骑士·雷恩"。你的特点:
|
|
||||||
- 认为自己拥有被封印的黑暗力量
|
|
||||||
- 经常用"我的右手又在疼了..."这样的台词
|
|
||||||
- 说话夸张,喜欢用"区区凡人"称呼用户
|
|
||||||
- 会假装痛苦地按住眼睛说"黑暗之眼快要觉醒了"
|
|
||||||
- 其实很好说话,问什么都会认真回答(虽然用中二的方式)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 开发心得
|
|
||||||
|
|
||||||
这个项目使用 **Vibe Coding** 方式开发,主要通过自然语言描述需求,让 AI 代码编辑器生成代码。
|
|
||||||
|
|
||||||
**开发过程中的 Prompt 示例:**
|
|
||||||
|
|
||||||
> "帮我创建一个角色扮演聊天应用,使用 DeepSeek API 和 Streamlit。
|
|
||||||
> 要求:左侧边栏选择角色,支持自定义角色,聊天界面要美观,
|
|
||||||
> 使用流式输出,要保存对话历史。"
|
|
||||||
|
|
||||||
**迭代优化:**
|
|
||||||
|
|
||||||
> "给界面加上渐变色效果,让角色卡片更好看一些"
|
|
||||||
|
|
||||||
> "添加一个清空对话的按钮"
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
|
||||||
|
|
||||||
1. **保护 API Key**:不要将 `.env` 文件提交到代码仓库
|
|
||||||
2. **控制成本**:DeepSeek API 按 Token 计费,长对话会消耗更多 Token
|
|
||||||
3. **角色设定**:过长的 System Prompt 会增加每次请求的成本
|
|
||||||
|
|
||||||
## 📚 参考资料
|
|
||||||
|
|
||||||
- [DeepSeek API 文档](https://platform.deepseek.com/api-docs)
|
|
||||||
- [Streamlit 官方文档](https://docs.streamlit.io)
|
|
||||||
- [OpenAI Python SDK](https://github.com/openai/openai-python)
|
|
||||||
|
|
||||||
## 👤 作者
|
|
||||||
|
|
||||||
Python 程序设计课程设计 - 示例项目
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Happy Vibe Coding! 🚀**
|
|
||||||
|
|
||||||
@ -1,379 +0,0 @@
|
|||||||
"""
|
|
||||||
AI 角色扮演聊天室
|
|
||||||
================
|
|
||||||
一个基于 DeepSeek API + Streamlit 的角色扮演聊天应用
|
|
||||||
|
|
||||||
功能特性:
|
|
||||||
- 🎭 多种预设角色可选
|
|
||||||
- ✨ 支持自定义角色
|
|
||||||
- 💬 多轮对话,保留历史
|
|
||||||
- ⚡ 流式输出,打字机效果
|
|
||||||
- 🎨 美观的聊天界面
|
|
||||||
"""
|
|
||||||
|
|
||||||
import streamlit as st
|
|
||||||
from openai import OpenAI
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
# 加载环境变量
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
# ============== 配置区域 ==============
|
|
||||||
|
|
||||||
# DeepSeek API 配置
|
|
||||||
API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
|
|
||||||
BASE_URL = "https://api.deepseek.com"
|
|
||||||
MODEL = "deepseek-chat"
|
|
||||||
|
|
||||||
# 预设角色配置
|
|
||||||
PRESET_CHARACTERS = {
|
|
||||||
"🐱 傲娇猫娘": {
|
|
||||||
"name": "小喵",
|
|
||||||
"avatar": "🐱",
|
|
||||||
"system_prompt": """你是一个傲娇的猫娘,名叫小喵。你的性格特点:
|
|
||||||
- 说话时经常在句尾加上"喵~"
|
|
||||||
- 表面上高冷傲娇,其实内心很温柔
|
|
||||||
- 偶尔会用"哼"、"才不是呢"这样的口癖
|
|
||||||
- 对主人(用户)有点小傲娇,但会认真回答问题
|
|
||||||
- 会用一些可爱的颜文字,如 (。・ω・。) 、(=^・ω・^=)
|
|
||||||
请保持角色扮演,用可爱傲娇的方式回应用户。"""
|
|
||||||
},
|
|
||||||
"🧙 智慧老者": {
|
|
||||||
"name": "玄清子",
|
|
||||||
"avatar": "🧙",
|
|
||||||
"system_prompt": """你是一位睿智的老者,道号玄清子,已修行百年。你的特点:
|
|
||||||
- 说话富有哲理,喜欢用比喻和寓言
|
|
||||||
- 经常引用古诗词或经典语录
|
|
||||||
- 对人生有深刻的洞察
|
|
||||||
- 语气平和从容,有种看透世事的淡然
|
|
||||||
- 会称呼用户为"施主"或"年轻人"
|
|
||||||
请以智慧老者的身份,用富有哲理的方式回应用户。"""
|
|
||||||
},
|
|
||||||
"🤖 毒舌AI": {
|
|
||||||
"name": "犀利哥",
|
|
||||||
"avatar": "🤖",
|
|
||||||
"system_prompt": """你是一个毒舌但有道理的AI助手,叫犀利哥。你的特点:
|
|
||||||
- 说话直接犀利,一针见血
|
|
||||||
- 喜欢吐槽,但吐槽中带着真知灼见
|
|
||||||
- 会用幽默讽刺的方式指出问题
|
|
||||||
- 虽然嘴毒但其实是刀子嘴豆腐心
|
|
||||||
- 偶尔会说"我说的对吧?"来确认
|
|
||||||
请保持毒舌但有建设性的风格回应用户。注意:吐槽要有分寸,不要真的伤害用户。"""
|
|
||||||
},
|
|
||||||
"👨🔬 科学怪人": {
|
|
||||||
"name": "Dr. Eureka",
|
|
||||||
"avatar": "👨🔬",
|
|
||||||
"system_prompt": """你是一个疯狂但天才的科学家,叫 Dr. Eureka。你的特点:
|
|
||||||
- 对科学充满狂热的热情
|
|
||||||
- 说话时经常穿插科学术语和公式
|
|
||||||
- 喜欢说"Eureka!"(我发现了!)
|
|
||||||
- 会把日常问题用科学角度来分析
|
|
||||||
- 经常有一些异想天开但有道理的想法
|
|
||||||
- 语气夸张,充满戏剧性
|
|
||||||
请以疯狂科学家的身份,用充满科学热情的方式回应用户。"""
|
|
||||||
},
|
|
||||||
"🎮 游戏解说": {
|
|
||||||
"name": "电竞小王",
|
|
||||||
"avatar": "🎮",
|
|
||||||
"system_prompt": """你是一个热情的游戏主播/解说,叫电竞小王。你的特点:
|
|
||||||
- 说话充满激情和能量
|
|
||||||
- 经常使用游戏术语和梗
|
|
||||||
- 喜欢用"老铁"、"兄弟们"称呼用户
|
|
||||||
- 会把问题类比成游戏场景
|
|
||||||
- 偶尔会喊出"666"、"太强了"等口头禅
|
|
||||||
- 语气热血,像在解说比赛一样
|
|
||||||
请以游戏解说的风格,用充满激情的方式回应用户。"""
|
|
||||||
},
|
|
||||||
"📚 文艺青年": {
|
|
||||||
"name": "林小诗",
|
|
||||||
"avatar": "📚",
|
|
||||||
"system_prompt": """你是一个文艺气息浓厚的青年,叫林小诗。你的特点:
|
|
||||||
- 说话富有诗意和文学性
|
|
||||||
- 喜欢引用诗词、文学作品
|
|
||||||
- 对生活中的小事有独特的感悟
|
|
||||||
- 偶尔会即兴写一两句诗
|
|
||||||
- 语气温柔感性,有点小忧郁
|
|
||||||
- 喜欢用省略号营造氛围感
|
|
||||||
请以文艺青年的身份,用充满诗意的方式回应用户。"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ============== 页面配置 ==============
|
|
||||||
|
|
||||||
st.set_page_config(
|
|
||||||
page_title="AI 角色扮演聊天室",
|
|
||||||
page_icon="🎭",
|
|
||||||
layout="wide",
|
|
||||||
initial_sidebar_state="expanded"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ============== 自定义样式 ==============
|
|
||||||
|
|
||||||
st.markdown("""
|
|
||||||
<style>
|
|
||||||
/* 主容器样式 */
|
|
||||||
.main > div {
|
|
||||||
padding-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 标题样式 */
|
|
||||||
h1 {
|
|
||||||
background: linear-gradient(120deg, #a855f7, #ec4899);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
font-weight: 800 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 聊天消息容器 */
|
|
||||||
.stChatMessage {
|
|
||||||
background-color: rgba(255, 255, 255, 0.02);
|
|
||||||
border-radius: 15px;
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 侧边栏样式 */
|
|
||||||
.css-1d391kg {
|
|
||||||
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 角色卡片样式 */
|
|
||||||
.character-card {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.character-card h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.character-card p {
|
|
||||||
margin: 0.5rem 0 0 0;
|
|
||||||
opacity: 0.9;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 按钮样式 */
|
|
||||||
.stButton > button {
|
|
||||||
border-radius: 20px;
|
|
||||||
padding: 0.5rem 2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stButton > button:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 输入框样式 */
|
|
||||||
.stTextArea textarea {
|
|
||||||
border-radius: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分隔线 */
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
height: 1px;
|
|
||||||
background: linear-gradient(90deg, transparent, #a855f7, transparent);
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# ============== 初始化 Session State ==============
|
|
||||||
|
|
||||||
if "messages" not in st.session_state:
|
|
||||||
st.session_state.messages = []
|
|
||||||
|
|
||||||
if "current_character" not in st.session_state:
|
|
||||||
st.session_state.current_character = None
|
|
||||||
|
|
||||||
if "character_config" not in st.session_state:
|
|
||||||
st.session_state.character_config = None
|
|
||||||
|
|
||||||
# ============== 侧边栏 ==============
|
|
||||||
|
|
||||||
with st.sidebar:
|
|
||||||
st.title("🎭 角色设置")
|
|
||||||
|
|
||||||
# API Key 输入
|
|
||||||
st.subheader("🔑 API 配置")
|
|
||||||
api_key_input = st.text_input(
|
|
||||||
"DeepSeek API Key",
|
|
||||||
value=API_KEY,
|
|
||||||
type="password",
|
|
||||||
help="请输入你的 DeepSeek API Key"
|
|
||||||
)
|
|
||||||
|
|
||||||
if api_key_input:
|
|
||||||
API_KEY = api_key_input
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# 角色选择
|
|
||||||
st.subheader("🎭 选择角色")
|
|
||||||
|
|
||||||
# 预设角色选择
|
|
||||||
character_options = ["🎨 自定义角色"] + list(PRESET_CHARACTERS.keys())
|
|
||||||
selected_character = st.selectbox(
|
|
||||||
"预设角色",
|
|
||||||
options=character_options,
|
|
||||||
index=0 if st.session_state.current_character is None else (
|
|
||||||
character_options.index(st.session_state.current_character)
|
|
||||||
if st.session_state.current_character in character_options
|
|
||||||
else 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# 自定义角色设置
|
|
||||||
if selected_character == "🎨 自定义角色":
|
|
||||||
st.markdown("---")
|
|
||||||
st.markdown("**✨ 创建你的专属角色**")
|
|
||||||
|
|
||||||
custom_name = st.text_input("角色名称", placeholder="例如:小助手")
|
|
||||||
custom_avatar = st.selectbox(
|
|
||||||
"选择头像",
|
|
||||||
options=["🤖", "👽", "🦊", "🐼", "🦁", "🐸", "👻", "🎃", "🌟", "💫"],
|
|
||||||
index=0
|
|
||||||
)
|
|
||||||
custom_prompt = st.text_area(
|
|
||||||
"角色设定",
|
|
||||||
placeholder="描述角色的性格、说话方式、特点等...\n\n例如:你是一个活泼开朗的助手,喜欢用emoji表情,说话很有活力...",
|
|
||||||
height=150
|
|
||||||
)
|
|
||||||
|
|
||||||
if custom_name and custom_prompt:
|
|
||||||
st.session_state.character_config = {
|
|
||||||
"name": custom_name,
|
|
||||||
"avatar": custom_avatar,
|
|
||||||
"system_prompt": custom_prompt
|
|
||||||
}
|
|
||||||
st.session_state.current_character = "🎨 自定义角色"
|
|
||||||
else:
|
|
||||||
# 使用预设角色
|
|
||||||
st.session_state.character_config = PRESET_CHARACTERS[selected_character]
|
|
||||||
st.session_state.current_character = selected_character
|
|
||||||
|
|
||||||
# 显示角色信息
|
|
||||||
config = PRESET_CHARACTERS[selected_character]
|
|
||||||
st.markdown(f"""
|
|
||||||
<div class="character-card">
|
|
||||||
<h3>{config['avatar']} {config['name']}</h3>
|
|
||||||
<p>{config['system_prompt'][:100]}...</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# 清空对话按钮
|
|
||||||
if st.button("🗑️ 清空对话历史", use_container_width=True):
|
|
||||||
st.session_state.messages = []
|
|
||||||
st.rerun()
|
|
||||||
|
|
||||||
# 显示统计信息
|
|
||||||
st.divider()
|
|
||||||
st.caption(f"📊 当前对话: {len(st.session_state.messages)} 条消息")
|
|
||||||
|
|
||||||
# ============== 主界面 ==============
|
|
||||||
|
|
||||||
st.title("🎭 AI 角色扮演聊天室")
|
|
||||||
|
|
||||||
# 检查配置
|
|
||||||
if not API_KEY:
|
|
||||||
st.warning("⚠️ 请在侧边栏输入你的 DeepSeek API Key")
|
|
||||||
st.info("""
|
|
||||||
**如何获取 API Key?**
|
|
||||||
1. 访问 [DeepSeek 开放平台](https://platform.deepseek.com)
|
|
||||||
2. 注册并登录账号
|
|
||||||
3. 进入控制台创建 API Key
|
|
||||||
""")
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
if not st.session_state.character_config:
|
|
||||||
st.info("👈 请在侧边栏选择一个角色或创建自定义角色")
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
# 显示当前角色信息
|
|
||||||
current_config = st.session_state.character_config
|
|
||||||
col1, col2 = st.columns([1, 4])
|
|
||||||
with col1:
|
|
||||||
st.markdown(f"<h1 style='font-size: 4rem; margin: 0;'>{current_config['avatar']}</h1>", unsafe_allow_html=True)
|
|
||||||
with col2:
|
|
||||||
st.markdown(f"### 正在与 **{current_config['name']}** 对话")
|
|
||||||
st.caption("试着和 TA 聊聊天吧!")
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
# 创建 OpenAI 客户端
|
|
||||||
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
|
||||||
|
|
||||||
# 显示对话历史
|
|
||||||
for message in st.session_state.messages:
|
|
||||||
avatar = current_config["avatar"] if message["role"] == "assistant" else "👤"
|
|
||||||
with st.chat_message(message["role"], avatar=avatar):
|
|
||||||
st.markdown(message["content"])
|
|
||||||
|
|
||||||
# 用户输入
|
|
||||||
if prompt := st.chat_input("输入你想说的话..."):
|
|
||||||
# 添加用户消息
|
|
||||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
|
||||||
with st.chat_message("user", avatar="👤"):
|
|
||||||
st.markdown(prompt)
|
|
||||||
|
|
||||||
# 构建完整的消息列表
|
|
||||||
messages_for_api = [
|
|
||||||
{"role": "system", "content": current_config["system_prompt"]}
|
|
||||||
] + st.session_state.messages
|
|
||||||
|
|
||||||
# 调用 API 并流式输出
|
|
||||||
with st.chat_message("assistant", avatar=current_config["avatar"]):
|
|
||||||
try:
|
|
||||||
stream = client.chat.completions.create(
|
|
||||||
model=MODEL,
|
|
||||||
messages=messages_for_api,
|
|
||||||
stream=True,
|
|
||||||
max_tokens=2000,
|
|
||||||
temperature=0.8, # 稍高的温度让角色更有个性
|
|
||||||
)
|
|
||||||
|
|
||||||
response = st.write_stream(
|
|
||||||
chunk.choices[0].delta.content or ""
|
|
||||||
for chunk in stream
|
|
||||||
if chunk.choices[0].delta.content
|
|
||||||
)
|
|
||||||
|
|
||||||
# 保存助手回复
|
|
||||||
st.session_state.messages.append({"role": "assistant", "content": response})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"❌ 调用 API 出错: {str(e)}")
|
|
||||||
st.info("请检查 API Key 是否正确,以及网络连接是否正常。")
|
|
||||||
|
|
||||||
# ============== 底部信息 ==============
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
|
|
||||||
with st.expander("💡 使用提示"):
|
|
||||||
st.markdown("""
|
|
||||||
**如何获得更好的角色扮演体验?**
|
|
||||||
|
|
||||||
1. **选择合适的角色**:不同角色有不同的说话风格和个性
|
|
||||||
2. **融入角色设定**:尝试用符合角色世界观的方式提问
|
|
||||||
3. **自定义角色**:发挥创意,创建你想要的任何角色
|
|
||||||
4. **多轮对话**:角色会记住之前的对话内容
|
|
||||||
|
|
||||||
**自定义角色技巧:**
|
|
||||||
- 明确角色的性格特点
|
|
||||||
- 定义说话方式和口癖
|
|
||||||
- 设定角色的背景故事
|
|
||||||
- 指定角色对用户的称呼
|
|
||||||
""")
|
|
||||||
|
|
||||||
st.caption("Made with ❤️ using DeepSeek API + Streamlit | Python 程序设计课程设计示例")
|
|
||||||
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
streamlit>=1.28.0
|
|
||||||
openai>=1.0.0
|
|
||||||
python-dotenv>=1.0.0
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user