# LangChain 天气助手从脚本到桌面应用:5 步打包你的第一个 AI Agent EXE
·
LangChain 天气助手从脚本到桌面应用:5 步打包你的第一个 AI Agent EXE
摘要:本文手把手教你从零构建一个基于 LangChain + DeepSeek 的天气查询智能体,涵盖命令行版本、GUI 可视化版本,最终通过 PyInstaller 打包为单文件 EXE,并创建桌面快捷方式。完整覆盖:环境准备、模型配置、工具定义、Agent 构建、tkinter GUI 开发、PyInstaller 打包、桌面快捷方式创建。每个步骤配有可直接运行的完整代码。
目录
- 一、项目概述
- 二、环境准备
- 三、构建命令行 Agent
- 3.1 定义工具
- 3.2 配置模型
- 3.3 创建 Agent
- 3.4 运行测试
- 四、升级为 GUI 版本
- 4.1 GUI 设计思路
- 4.2 完整 GUI 代码
- 4.3 GUI 运行效果
- 五、PyInstaller 打包为 EXE
- 5.1 安装 PyInstaller
- 5.2 打包命令详解
- 5.3 打包常见问题
- 六、创建桌面快捷方式
- 6.1 PowerShell 脚本创建
- 6.2 快捷方式属性配置
- 七、完整项目结构
- 八、使用说明
- 九、踩坑清单
- 十、总结
- 封面图提示词
一、项目概述
本项目基于 LangChain 官方开源的 langchain-skills 项目(https://github.com/langchain-ai/langchain-skills),实现了一个简单的天气查询 Agent,并最终打包为 Windows 桌面应用程序。
技术栈:
| 组件 | 版本 | 作用 |
|---|---|---|
| LangChain | 1.3.14 | Agent 框架 |
| langchain-openai | 1.4.1 | OpenAI 兼容接口 |
| DeepSeek API | deepseek-chat | 大语言模型 |
| tkinter | 内置 | GUI 界面 |
| PyInstaller | 6.21.0 | EXE 打包 |
最终产物:
- 命令行脚本:
weather_agent.py - GUI 脚本:
weather_agent_gui.py - 桌面应用:
Agent.exe - 桌面快捷方式:
Agent.lnk
二、环境准备
2.1 安装依赖
pip install langchain langchain-openai
国内镜像加速:
pip install langchain langchain-openai -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 配置 API Key
本项目使用 DeepSeek 的 OpenAI 兼容接口:
model = ChatOpenAI(
model="deepseek-chat",
api_key="sk-f3c7557e9fc54541802fd638de03bbeb",
base_url="https://api.deepseek.com",
temperature=0.7,
)
你也可以替换为其他兼容 OpenAI 格式的模型:
| 服务商 | base_url | model |
|---|---|---|
| DeepSeek | https://api.deepseek.com | deepseek-chat |
| OpenAI | https://api.openai.com/v1 | gpt-4o |
| 硅基流动 | https://api.siliconflow.cn/v1 | deepseek-ai/DeepSeek-V3 |
三、构建命令行 Agent
3.1 定义工具
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
关键点:
- 函数名即工具名
- docstring 描述工具用途(Agent 通过 docstring 判断何时调用)
- 参数类型标注帮助 Agent 正确传参
3.2 完整命令行版本代码
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
# 使用 DeepSeek (OpenAI兼容格式)
model = ChatOpenAI(
model="deepseek-chat",
api_key="sk-f3c7557e9fc54541802fd638de03bbeb",
base_url="https://api.deepseek.com",
temperature=0.7,
)
agent = create_agent(
model=model,
tools=[get_weather],
system_prompt="You are a helpful assistant",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]}
)
print(result["messages"][-1].content)
3.3 运行测试
python weather_agent.py
输出:
The weather in San Francisco is always sunny! ☀️
运行流程解析:
用户提问:"What's the weather in San Francisco?"
↓
Agent 分析:涉及"天气"和"城市"
↓
Agent 决定:调用 get_weather 工具,参数 city="San Francisco"
↓
工具执行:get_weather("San Francisco") → "It's always sunny in San Francisco!"
↓
Agent 整理:把工具返回整理成自然语言回答
↓
最终输出:"The weather in San Francisco is always sunny! ☀️"
四、升级为 GUI 版本
4.1 GUI 设计思路
使用 Python 内置的 tkinter 库,无需额外安装:
| 组件 | 功能 |
|---|---|
| 聊天记录区(ScrolledText) | 显示对话历史 |
| 输入框(Entry) | 用户输入问题 |
| 发送按钮 | 提交问题 |
| 快捷按钮 | 预设常用问题 |
| 状态栏 | 显示运行状态 |
4.2 完整 GUI 代码
import tkinter as tk
from tkinter import ttk, scrolledtext
import threading
from datetime import datetime
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
def get_current_time() -> str:
"""Get the current date and time."""
now = datetime.now()
return f"Current time: {now.strftime('%Y-%m-%d %H:%M:%S')}"
def calculate(expression: str) -> str:
"""Calculate a mathematical expression."""
try:
result = eval(expression)
return f"Result: {result}"
except:
return "Invalid expression"
class WeatherAgentApp:
def __init__(self, root):
self.root = root
self.root.title("AI 智能助手 - Weather Agent")
self.root.geometry("800x600")
self.root.minsize(700, 500)
# 初始化模型和Agent
self.init_agent()
# 创建UI
self.create_ui()
# 欢迎消息
self.add_system_message("AI 智能助手已启动!您可以问我:\n")
self.add_system_message("1. 天气查询:\"北京天气怎么样?\"\n")
self.add_system_message("2. 时间查询:\"现在几点了?\"\n")
self.add_system_message("3. 数学计算:\"计算 25 * 4 + 10\"\n")
def init_agent(self):
"""初始化AI模型和Agent"""
self.model = ChatOpenAI(
model="deepseek-chat",
api_key="sk-f3c7557e9fc54541802fd638de03bbeb",
base_url="https://api.deepseek.com",
temperature=0.7,
)
self.agent = create_agent(
model=self.model,
tools=[get_weather, get_current_time, calculate],
system_prompt="You are a helpful assistant.",
)
self.agent_ready = True
def create_ui(self):
"""创建用户界面"""
# 顶部标题
header_frame = ttk.Frame(self.root, padding="10")
header_frame.pack(fill=tk.X)
title_label = ttk.Label(
header_frame,
text="AI 智能助手",
font=("Microsoft YaHei", 18, "bold")
)
title_label.pack()
# 聊天显示区域
chat_frame = ttk.Frame(self.root, padding="10")
chat_frame.pack(fill=tk.BOTH, expand=True)
self.chat_display = scrolledtext.ScrolledText(
chat_frame,
wrap=tk.WORD,
font=("Microsoft YaHei", 11),
bg="#f5f5f5",
padx=10,
pady=10,
state=tk.DISABLED
)
self.chat_display.pack(fill=tk.BOTH, expand=True)
# 配置标签样式
self.chat_display.tag_configure("user", foreground="#0066cc", font=("Microsoft YaHei", 11, "bold"))
self.chat_display.tag_configure("assistant", foreground="#009933", font=("Microsoft YaHei", 11, "bold"))
self.chat_display.tag_configure("system", foreground="#666666", font=("Microsoft YaHei", 10))
# 输入区域
input_frame = ttk.Frame(self.root, padding="10")
input_frame.pack(fill=tk.X, side=tk.BOTTOM)
self.input_var = tk.StringVar()
self.input_entry = ttk.Entry(
input_frame,
textvariable=self.input_var,
font=("Microsoft YaHei", 12)
)
self.input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
self.input_entry.bind("<Return>", lambda e: self.send_message())
self.send_btn = ttk.Button(
input_frame,
text="发送",
command=self.send_message,
width=10
)
self.send_btn.pack(side=tk.RIGHT)
# 快捷按钮区域
quick_frame = ttk.Frame(self.root, padding="10")
quick_frame.pack(fill=tk.X, side=tk.BOTTOM)
quick_label = ttk.Label(quick_frame, text="快捷提问:")
quick_label.pack(side=tk.LEFT)
quick_questions = [
"北京天气怎么样?",
"现在几点了?",
"计算 25 * 4",
"你好"
]
for q in quick_questions:
btn = ttk.Button(
quick_frame,
text=q[:8] + "..." if len(q) > 8 else q,
command=lambda question=q: self.ask_quick(question)
)
btn.pack(side=tk.LEFT, padx=5)
# 状态栏
self.status_var = tk.StringVar(value="就绪")
status_bar = ttk.Label(
self.root,
textvariable=self.status_var,
font=("Microsoft YaHei", 9),
foreground="gray",
anchor=tk.W
)
status_bar.pack(fill=tk.X, side=tk.BOTTOM, padx=10, pady=2)
# 聚焦输入框
self.input_entry.focus()
def add_message(self, role, content):
"""添加消息到聊天显示区域"""
self.chat_display.config(state=tk.NORMAL)
timestamp = datetime.now().strftime("%H:%M:%S")
if role == "user":
self.chat_display.insert(tk.END, f"\n[{timestamp}] ", "system")
self.chat_display.insert(tk.END, "你:", "user")
self.chat_display.insert(tk.END, f" {content}\n", "")
elif role == "assistant":
self.chat_display.insert(tk.END, f"[{timestamp}] ", "system")
self.chat_display.insert(tk.END, "AI:", "assistant")
self.chat_display.insert(tk.END, f" {content}\n", "")
elif role == "system":
self.chat_display.insert(tk.END, f"[{timestamp}] ", "system")
self.chat_display.insert(tk.END, f"{content}\n", "system")
self.chat_display.see(tk.END)
self.chat_display.config(state=tk.DISABLED)
def add_system_message(self, content):
self.add_message("system", content)
def send_message(self):
"""发送用户消息"""
question = self.input_var.get().strip()
if not question:
return
self.input_var.set("")
self.add_message("user", question)
# 在后台线程中处理AI响应
self.status_var.set("AI思考中...")
self.send_btn.config(state=tk.DISABLED)
self.input_entry.config(state=tk.DISABLED)
thread = threading.Thread(target=self.process_ai_response, args=(question,))
thread.daemon = True
thread.start()
def process_ai_response(self, question):
"""在后台线程中处理AI响应"""
try:
result = self.agent.invoke(
{"messages": [{"role": "user", "content": question}]}
)
answer = result["messages"][-1].content
self.root.after(0, lambda: self.add_message("assistant", answer))
except Exception as e:
self.root.after(0, lambda: self.add_message("system", f"错误:{str(e)}"))
finally:
self.root.after(0, self.reset_ui_state)
def reset_ui_state(self):
"""重置UI状态"""
self.status_var.set("就绪")
self.send_btn.config(state=tk.NORMAL)
self.input_entry.config(state=tk.NORMAL)
self.input_entry.focus()
def ask_quick(self, question):
"""快捷提问"""
self.input_var.set(question)
self.send_message()
def main():
root = tk.Tk()
app = WeatherAgentApp(root)
root.mainloop()
if __name__ == "__main__":
main()
4.3 运行 GUI 版本
python weather_agent_gui.py
界面包含:
- 顶部标题栏
- 聊天记录区(带时间戳,区分用户和AI)
- 输入框 + 发送按钮
- 快捷提问按钮(预设4个常用问题)
- 状态栏显示运行状态
五、PyInstaller 打包为 EXE
5.1 安装 PyInstaller
pip install pyinstaller
5.2 打包命令详解
cd /f/hmc/wzsc
pyinstaller --onefile --windowed --name "Agent" --clean weather_agent_gui.py
参数说明:
| 参数 | 作用 |
|---|---|
--onefile |
打包为单文件 exe(所有依赖合并) |
--windowed |
不显示控制台窗口(GUI 应用必备) |
--name "Agent" |
生成的 exe 文件名 |
--clean |
清理临时文件 |
5.3 打包常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 打包后 exe 很大(50MB+) | 包含了 Python 解释器和所有依赖 | 正常,使用 --onefile 就是单文件 |
| 缺少 tkinter 支持 | PyInstaller 未正确打包 tkinter | 使用标准 Python 安装,不要用 Anaconda |
| 运行时报缺失 DLL | 系统依赖不完整 | 在相同系统环境打包,或用 --hidden-import |
| 启动很慢 | --onefile 需要先解压到临时目录 |
正常现象,可用 --onedir 替代 |
六、创建桌面快捷方式
6.1 PowerShell 脚本创建
创建快捷方式脚本 create_shortcut.ps1:
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut('C:\Users\WIT_User\Desktop\Agent.lnk')
$Shortcut.TargetPath = 'F:\hmc\wzsc\dist\Agent.exe'
$Shortcut.WorkingDirectory = 'F:\hmc\wzsc\dist'
$Shortcut.IconLocation = 'F:\hmc\wzsc\dist\Agent.exe,0'
$Shortcut.Description = 'AI Weather Agent'
$Shortcut.Save()
Write-Host 'Desktop shortcut created successfully'
运行脚本:
powershell -NoProfile -ExecutionPolicy Bypass -File create_shortcut.ps1
6.2 快捷方式属性配置
快捷方式创建后包含以下属性:
| 属性 | 值 | 说明 |
|---|---|---|
| 目标 | F:\hmc\wzsc\dist\Agent.exe |
EXE 可执行文件路径 |
| 起始位置 | F:\hmc\wzsc\dist |
工作目录 |
| 图标 | Agent.exe,0 |
使用 EXE 自带图标 |
| 描述 | AI Weather Agent |
鼠标悬停提示 |
七、完整项目结构
F:\hmc\wzsc\
├── weather_agent.py # 命令行版本
├── weather_agent_gui.py # GUI 版本
├── Agent.spec # PyInstaller 配置文件
├── create_shortcut.ps1 # 快捷方式创建脚本
├── dist\
│ └── Agent.exe # 打包后的桌面应用
└── build\ # PyInstaller 构建临时文件
八、使用说明
8.1 命令行使用
python weather_agent.py
修改问题:编辑 weather_agent.py 第 24 行的 content。
8.2 GUI 使用
python weather_agent_gui.py
- 在输入框输入问题,按回车发送
- 或点击快捷按钮快速提问
- 查看聊天记录中的 AI 回复
8.3 桌面应用使用
双击桌面的 Agent 快捷方式:
- 启动无需 Python 环境
- 自动连接 DeepSeek API
- 输入问题后等待 AI 回复
- 支持天气、时间、计算查询
九、踩坑清单
| 坑 | 描述 | 解决方案 |
|---|---|---|
| 网络不通 | 国内访问 DeepSeek 或 OpenAI 超时 | 换用国内镜像接口(硅基流动等) |
| API Key 过期 | DeepSeek Key 欠费或失效 | 检查 Key 状态,或替换为新 Key |
| tkinter 缺失 | Python 安装未包含 tkinter | 重新安装标准 Python(勾选 tcl/tk) |
| 打包后无图标 | PyInstaller 默认无图标 | 用 --icon=icon.ico 指定自定义图标 |
| 快捷方式路径错误 | 移动项目后快捷方式失效 | 右键快捷方式 → 属性 → 修改目标路径 |
| Agent 不调用工具 | docstring 写得不够清楚 | 详细描述工具用途和参数 |
| GUI 界面卡顿 | 模型调用在主线程阻塞 | 使用 threading.Thread 后台执行 |
| 打包后报缺失模块 | PyInstaller 未检测到隐式导入 | 用 --hidden-import module_name 补充 |
| 中文显示乱码 | tkinter 字体不支持中文 | 指定 "Microsoft YaHei" 字体 |
| 快捷方式名称不是 Agent | PowerShell 脚本写错路径 | 检查 $Shortcut.TargetPath 变量 |
十、总结
本文完整展示了从命令行脚本到桌面应用的完整流程:
- 命令行版本(20行代码):定义工具 → 配置模型 → 创建 Agent → 运行
- GUI 版本(200+行代码):添加 tkinter 界面、聊天记录、快捷按钮、状态栏
- EXE 打包(一行命令):PyInstaller
--onefile --windowed一键打包 - 桌面快捷方式(PowerShell 脚本):自动创建
Agent.lnk
核心原理回顾:
- Agent 通过 docstring 判断何时调用工具
create_agent只需要模型 + 工具列表 + 系统提示词- tkinter 是 Python 内置 GUI,无需额外安装
- PyInstaller
--onefile生成独立可执行文件
| 阶段 | 代码量 | 复杂度 | 适用场景 |
|---|---|---|---|
| 命令行 | ~30行 | 低 | 快速验证、自动化脚本 |
| GUI | ~200行 | 中 | 日常工具、内部使用 |
| EXE | 无代码 | 低 | 交付用户、无 Python 环境 |
如果觉得这篇文章对你有帮助,欢迎点赞收藏。你在打包 Python 应用时遇到了什么问题?评论区交流。
更多推荐


所有评论(0)