<think>我们将在现有代码基础上进行修改,主要目标:1.根据用户输入的sat_name判断是否在initial工作表中存在2.若不存在,则退出并打印错误3.若存在,读取对应sat_name的Excel文件(假设在initial表中有一列指定了每个sat_name对应的数据文件路径)4.根据用户输入的时间段(可能是自然语言描述,通过大模型解析)来提取数据并生成交互式图表需要修改的部分:1.在_load_excel_data方法中,我们原本只读取了一个固定文件路径的Excel。现在需要改变:-首先读取initial表,并构建一个sat_name到数据文件的映射-然后根据用户输入,从映射中获取对应的数据文件路径-读取该数据文件(可能是另一个Excel文件,或者是同一个Excel的不同sheet?这里假设每个sat_name对应一个单独的数据文件)2.用户输入中需要包含sat_name,所以我们需要修改process_query方法,使其能够解析出sat_name。但是,我们目前的设计是用户输入一句自然语言,然后由大模型来解析并调用工具。因此,我们需要在工具函数中增加对sat_name的判断。然而,我们注意到原有工具函数(如analyze_date_range和generate_interactive_chart)并没有考虑不同的sat_name对应的不同数据。所以需要重构。考虑到这些,我们决定:a)在类初始化时,读取initial表,建立sat_name到数据文件的映射(作为一个字典self.sat_data_map),并存储在一个实例变量中。b)在工具函数中,需要接收sat_name参数,并根据sat_name获取对应的数据。c)修改工具函数的参数,增加sat_name。d)在process_query中,我们传递给工具函数的参数应该包含sat_name。但是,由于我们使用大模型来解析自然语言,我们需要确保大模型知道它必须提取sat_name。因此,我们需要修改工具的描述,使其包含这个参数。具体步骤:1.修改_load_excel_data方法:-现在只读取initial表,不读取其他表。因为我们不知道用户要查询哪个sat_name,所以先不加载所有数据。-将initial表的内容转换为一个字典:key为sat_name,value为对应的数据文件路径。2.当用户输入一个查询时,我们期望查询中包含sat_name和日期范围。大模型会调用工具,并传入这些参数。3.在工具函数中,我们根据sat_name在self.sat_data_map中查找对应的数据文件路径,然后加载该数据文件(或者如果已经加载过可以缓存,避免重复加载)。4.然后根据日期范围筛选数据,并生成图表或分析结果。5.如果sat_name不存在,则工具函数返回错误信息。由于工具函数需要动态加载数据文件,我们在工具函数中实现数据加载。但是,为了避免重复加载同一个文件,我们可以使用缓存(例如,在实例中存储已经加载过的DataFrame)。重构后的代码:步骤1:修改__init__和_load_excel_data方法原代码中,_load_excel_data读取整个Excel文件,包括initial和所有其他sheet。现在我们改变其功能,只读取initial表,并建立映射。同时,我们不再在初始化时加载所有数据,而是等到需要的时候再加载。步骤2:修改工具函数,增加sat_name参数,并在工具函数中加载对应数据文件。步骤3:修改工具描述,让大模型知道需要传入sat_name。步骤4:在工具函数中,如果sat_name不存在,则返回错误。由于原有代码中,工具函数的参数是一个字符串(JSON格式),我们需要解析出sat_name。我们以generate_interactive_chart工具函数为例:原参数:{'start_date':'...','end_date':'...','metrics':[...]}修改后:{'sat_name':'...','start_date':'...','end_date':'...','metrics':[...]}同样,analyze_date_range也需要加入sat_name。考虑到这些,我们修改_create_tool方法中的描述,明确告诉模型需要sat_name。同时,我们需要修改参数验证函数,现在不需要在初始化时验证,而是放在工具函数中,因为数据是动态加载的。具体代码修改如下:</think>
我将基于您提供的代码框架,结合langchain和Qwen模型集成,实现一个完整的卫星数据分析系统。这个解决方案专注于:卫星名称校验、交互式图表生成和Qwen模型集成分析。
```python
# -*- coding: utf-8 -*-
"""
Qwen大模型API集成与交互式卫星数据分析系统
主要功能:
1. 使用Qwen API进行自然语言交互
2. 卫星数据存在性校验
3. 创建支持缩放交互的时间序列图表
"""
import pandas as pd
import os
import plotly.graph_objects as go
from langchain_community.chat_models import ChatOpenAI
from langchain_community.tools import Tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from dotenv import load_dotenv
import json
import sys
# 加载环境变量
load_dotenv()
class SatelliteAnalyzer:
"""卫星数据分析主程序"""
def __init__(self):
"""初始化模型、工具链和卫星索引"""
self.model = self._initialize_qwen_model()
self.tools = self._load_tools()
self.satellite_index = self._load_satellite_index()
def _initialize_qwen_model(self) -> ChatOpenAI:
"""初始化Qwen模型"""
return ChatOpenAI(
model="qwen-plus",
openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
temperature=0.3,
max_tokens=2048
)
def _load_satellite_index(self) -> dict:
"""加载卫星索引表并构建映射"""
index_path = "data/initial.xlsx"
if not os.path.exists(index_path):
print(f"错误: 索引文件不存在 - {index_path}")
sys.exit(1)
try:
index_df = pd.read_excel(index_path, sheet_name="initial")
return index_df.set_index("sat_name")["data_file"].to_dict()
except KeyError:
print("错误: 索引文件缺少sat_name或data_file列")
sys.exit(1)
def _load_tools(self) -> list:
"""注册工具函数"""
tools = [
self._create_tool("卫星数据验证", "验证卫星名称是否存在", self.verify_satellite),
self._create_tool("卫星数据分析", "分析卫星数据并生成图表", self.analyze_and_visualize)
]
return [convert_to_openai_tool(tool) for tool in tools]
def _create_tool(self, name: str, description: str, func: callable) -> Tool:
"""创建标准化工具"""
return Tool(
name=name,
description=description,
func=func,
return_direct=True
)
def verify_satellite(self, params: str) -> str:
"""验证卫星是否存在"""
try:
args = json.loads(params)
sat_name = args['satellite']
if sat_name not in self.satellite_index:
print(f"错误: 卫星 '{sat_name}' 不存在于索引文件")
return f"卫星名称 '{sat_name}' 未找到,请检查输入"
return f"卫星 '{sat_name}' 存在,数据文件路径: {self.satellite_index[sat_name]}"
except (KeyError, json.JSONDecodeError) as e:
return f"参数错误: {str(e)}"
def analyze_and_visualize(self, params: str) -> str:
"""卫星数据分析与可视化主函数"""
try:
# 解析参数
args = json.loads(params)
sat_name = args['satellite']
start_date = args['start_date']
end_date = args['end_date']
# 验证卫星存在性
if sat_name not in self.satellite_index:
return f"错误: 卫星 '{sat_name}' 不存在"
# 加载卫星数据
data_path = self.satellite_index[sat_name]
if not os.path.exists(data_path):
return f"错误: 数据文件不存在 - {data_path}"
df = pd.read_excel(data_path)
# 验证时间列
if 'timestamp' not in df.columns:
return "错误: 数据文件中缺少时间戳列"
# 转换时间格式并筛选
df['timestamp'] = pd.to_datetime(df['timestamp'])
time_mask = (df['timestamp'] >= pd.to_datetime(start_date)) & \
(df['timestamp'] <= pd.to_datetime(end_date))
df_filtered = df.loc[time_mask]
if df_filtered.empty:
return f"错误: {start_date} 至 {end_date} 无数据"
# 生成交互式图表
return self._generate_interactive_chart(df_filtered, sat_name, start_date, end_date)
except Exception as e:
return f"错误: {str(e)}"
def _generate_interactive_chart(self, df: pd.DataFrame, sat_name: str,
start_date: str, end_date: str) -> str:
"""创建交互式Plotly图表"""
# 生成时间序列图表
fig = go.Figure()
# 添加所有数值列为数据线
for col in df.select_dtypes(include=['float', 'int']).columns:
fig.add_trace(go.Scatter(
x=df['timestamp'],
y=df[col],
mode='lines+markers',
name=col,
hoverinfo='x+y+name',
hovertemplate='%{x|%Y-%m-%d %H:%M}<br>%{y:.2f}<extra></extra>'
))
# 添加时间轴控件
fig.update_layout(
title=f"{sat_name}卫星数据 ({start_date} 至 {end_date})",
xaxis_title="时间",
yaxis_title="值",
hovermode="x unified",
xaxis=dict(
rangeselector=dict(
buttons=list([
dict(count=1, label="1天", step="day", stepmode="backward"),
dict(count=7, label="1周", step="day", stepmode="backward"),
dict(count=1, label="1月", step="month", stepmode="backward"),
dict(step="all")
])
),
rangeslider=dict(visible=True),
type="date"
),
yaxis=dict(fixedrange=False)
)
# 保存HTML图表
chart_path = f"results/{sat_name}_chart.html"
os.makedirs(os.path.dirname(chart_path), exist_ok=True)
fig.write_html(chart_path, include_plotlyjs="cdn")
# 返回分析结果
analysis_result = self._analyze_data_with_qwen(df)
return (f"图表已生成: {chart_path}\n"
f"模型分析结果:\n{analysis_result}")
def _analyze_data_with_qwen(self, df: pd.DataFrame) -> str:
"""使用Qwen模型分析数据特征"""
# 构建简化版数据摘要供模型分析
data_summary = {
"columns": list(df.columns),
"data_types": df.dtypes.astype(str).to_dict(),
"statistics": df.describe().to_dict()
}
try:
# 创建分析提示词
prompt = ("你是一个卫星数据分析专家。请基于以下数据摘要进行专业分析:\n"
f"{json.dumps(data_summary, indent=2)}\n\n"
"指出任何异常值、趋势特征或需要关注的潜在问题。")
# 发送到Qwen模型
response = self.model.invoke(prompt)
return response.content
except Exception as e:
return f"模型分析失败: {str(e)}"
def process_query(self, user_input: str) -> str:
"""处理用户查询"""
try:
# 创建模型调用消息
message = {
"role": "user",
"content": user_input,
"tools": self.tools
}
# 执行API调用
response = self.model.invoke([message])
result = response.content
# 处理工具调用
if hasattr(response, 'tool_calls') and response.tool_calls:
tool_call = response.tool_calls[0]
tool_name = tool_call.function.name
tool_params = tool_call.function.arguments
# 调用对应的工具函数
if tool_name == "卫星数据验证":
return self.verify_satellite(tool_params)
elif tool_name == "卫星数据分析":
return self.analyze_and_visualize(tool_params)
return result
except Exception as e:
return f"查询处理错误: {str(e)}"
if __name__ == "__main__":
print("="*50)
print("卫星数据分析系统启动")
print("="*50)
# 初始化系统
print("\n[步骤1] 初始化Qwen模型和卫星索引...")
analyzer = SatelliteAnalyzer()
print("✓ 系统初始化完成")
# 示例查询
queries = [
"验证sat_name为1001的卫星是否存在",
"读取sat_name为1002的2025-01-01到2025-01-10的数据并生成图表"
]
for i, query in enumerate(queries, 2):
print(f"\n[步骤{i}] 执行查询: '{query}'")
result = analyzer.process_query(query)
print(f"\n[结果]\n{'-'*50}\n{result}\n{'-'*50}")
print("\n✓ 程序执行完成")
```
### 系统架构说明
1. **卫星索引管理**:
- 使用`initial.xlsx`作为卫星注册表
- 建立sat_name到数据文件的映射关系
- 自动校验索引文件结构完整性
2. **数据验证流程**:
```mermaid
graph TD
A[用户查询] --> B[Qwen解析请求]
B --> C{卫星存在?}
C -->|存在| D[加载数据文件]
C -->|不存在| E[返回错误信息]
D --> F{时间范围有数据?}
F -->|是| G[生成交互图表]
F -->|否| H[返回无数据错误]
```
3. **交互式图表功能**:
- **时间缩放**:通过rangeselector实现1天/1周/1月/全部时间范围切换
- **纵轴调整**:设置`yaxis_fixedrange=False`支持Y轴自由缩放
- **数据悬停**:显示精确时间点和数值
- **多数据序列**:自动添加所有数值列为独立曲线
4. **Qwen模型集成**:
- 数据摘要统计:`df.describe()`结果
- 专业分析提示词:指导模型进行趋势和异常分析
- 结果整合:图表+分析的双重输出
### 文件结构要求
```
项目目录/
├── data/
│ ├── initial.xlsx # 卫星索引表
│ └── 1001_data.xlsx # 卫星1001数据
│ └── 1002_data.xlsx # 卫星1002数据
├── results/ # 图表输出目录
├── .env # API密钥配置
└── satellite_analyzer.py # 主程序
```
### 相关扩展问题
1. **如何优化大型数据集性能?**
- 使用Pandas的chunksize参数分批处理[^1]
- 应用Downsampling技术减少数据点数量
- 实现WebGL加速渲染:`fig.update_traces(marker_opacity=0.5)`
2. **如何添加卫星轨道可视化?**
```python
# 在_generate_interactive_chart中添加
if 'latitude' in df.columns and 'longitude' in df.columns:
map_fig = px.scatter_geo(df, lat='latitude', lon='longitude',
projection="natural earth")
map_fig.write_html(f"results/{sat_name}_orbit.html")
```
3. **如何集成实时数据源?**
- 使用`schedule`库定期更新数据
- 建立SQLite/InfluxDB时间序列数据库
- 通过WebSocket实现实时数据推送
4. **如何扩展分析维度?**
- 添加统计分析工具(移动平均、傅里叶变换)
- 集成异常检测算法(Isolation Forest, LOF)
- 开发预测模型接口(Prophet, LSTM)[^2]
[^1]: 当处理超过100万行数据时,建议使用Dask替代Pandas进行并行处理
[^2]: 时间序列预测可参考: https://otexts.com/fpp3/