feat: implement logging and refine agent core, execution, and prompts.

This commit is contained in:
2026-01-06 17:53:19 +08:00
parent 24870ba497
commit ca134e94c8
5 changed files with 8752 additions and 54 deletions

View File

@@ -208,13 +208,14 @@ class DataAnalysisAgent:
"continue": True, "continue": True,
} }
def analyze(self, user_input: str, files: List[str] = None) -> Dict[str, Any]: def analyze(self, user_input: str, files: List[str] = None, session_output_dir: str = None) -> Dict[str, Any]:
""" """
开始分析流程 开始分析流程
Args: Args:
user_input: 用户的自然语言需求 user_input: 用户的自然语言需求
files: 数据文件路径列表 files: 数据文件路径列表
session_output_dir: 指定的会话输出目录(可选)
Returns: Returns:
分析结果字典 分析结果字典
@@ -225,10 +226,14 @@ class DataAnalysisAgent:
self.current_round = 0 self.current_round = 0
# 创建本次分析的专用输出目录 # 创建本次分析的专用输出目录
if session_output_dir:
self.session_output_dir = session_output_dir
else:
self.session_output_dir = create_session_output_dir( self.session_output_dir = create_session_output_dir(
self.base_output_dir, user_input self.base_output_dir, user_input
) )
# 初始化代码执行器,使用会话目录 # 初始化代码执行器,使用会话目录
self.executor = CodeExecutor(self.session_output_dir) self.executor = CodeExecutor(self.session_output_dir)
@@ -392,15 +397,25 @@ class DataAnalysisAgent:
# 解析响应,提取最终报告 # 解析响应,提取最终报告
try: try:
# 尝试解析YAML
yaml_data = self.llm.parse_yaml_response(response) yaml_data = self.llm.parse_yaml_response(response)
# 情况1: 标准YAML格式包含 action: analysis_complete
if yaml_data.get("action") == "analysis_complete": if yaml_data.get("action") == "analysis_complete":
final_report_content = yaml_data.get("final_report", "报告生成失败") final_report_content = yaml_data.get("final_report", response)
# 情况2: 解析成功但没字段,或者解析失败
else: else:
final_report_content = ( # 如果内容看起来像Markdown报告包含标题直接使用
"LLM未返回analysis_complete动作报告生成失败" if "# " in response or "## " in response:
) print("⚠️ 未检测到标准YAML动作但内容疑似Markdown报告直接采纳")
except: final_report_content = response
# 如果解析失败,直接使用响应内容 else:
final_report_content = "LLM未返回有效报告内容"
except Exception as e:
# 解析完全失败,直接使用原始响应
print(f"⚠️ YAML解析失败 ({e}),直接使用原始响应作为报告")
final_report_content = response final_report_content = response
print("✅ 最终报告生成完成") print("✅ 最终报告生成完成")

8579
log.txt Normal file

File diff suppressed because one or more lines are too long

55
main.py
View File

@@ -1,15 +1,66 @@
from data_analysis_agent import DataAnalysisAgent from data_analysis_agent import DataAnalysisAgent
from config.llm_config import LLMConfig from config.llm_config import LLMConfig
import sys
import os
from datetime import datetime
from utils.create_session_dir import create_session_output_dir
class DualLogger:
"""同时输出到终端和文件的日志记录器"""
def __init__(self, log_dir, filename="log.txt"):
self.terminal = sys.stdout
log_path = os.path.join(log_dir, filename)
self.log = open(log_path, "a", encoding="utf-8")
def write(self, message):
self.terminal.write(message)
# 过滤掉生成的代码块,不写入日志文件
if "🔧 执行代码:" in message:
return
self.log.write(message)
self.log.flush()
def flush(self):
self.terminal.flush()
self.log.flush()
def setup_logging(log_dir):
"""配置日志记录"""
# 记录开始时间
logger = DualLogger(log_dir)
sys.stdout = logger
# 可选:也将错误输出重定向
# sys.stderr = logger
print(f"\n{'='*20} Run Started at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {'='*20}\n")
print(f"📄 日志文件已保存至: {os.path.join(log_dir, 'log.txt')}")
def main(): def main():
llm_config = LLMConfig() llm_config = LLMConfig()
files = ["./UB IOV Support_TR.csv"]
analysis_requirement = """
基于所有运维工单,整理一份工单健康度报告,包括但不限于对所有车联网技术支持工单的全面数据分析,
深入挖掘工单处理过程中的关键问题、效率瓶颈及改进机会。涵盖工单状态、问题类型、模块分布、严重程度、责任人负载、车型分布、来源渠道及处理时长等多个维度。
通过多轮交叉分析与趋势洞察,为提升车联网服务质量、优化资源配置及降低运营风险提供数据驱动的决策依据,问题总揽,高频问题、重点问题分析,输出若干个重要的统计指标,并绘制相关图表。总结一份,车联网运维工单健康度报告,最后生成汇报给我。
"""
# 在主函数中先创建会话目录,以便存放日志
# 默认输出目录为 'outputs'
base_output_dir = "outputs"
session_output_dir = create_session_output_dir(base_output_dir, analysis_requirement)
# 设置日志
setup_logging(session_output_dir)
# 如果希望强制运行到最大轮数,设置 force_max_rounds=True # 如果希望强制运行到最大轮数,设置 force_max_rounds=True
agent = DataAnalysisAgent(llm_config, force_max_rounds=False) agent = DataAnalysisAgent(llm_config, force_max_rounds=False)
files = ["./UB IOV Support_TR.csv"]
report = agent.analyze( report = agent.analyze(
user_input="基于所有有关远程控制的问题以及涉及车控APP的运维工单的数据输出若干个重要的统计指标并绘制相关图表。总结一份车控APP及远程控制工单健康度报告最后生成汇报给我。", user_input=analysis_requirement,
files=files, files=files,
session_output_dir=session_output_dir
) )
print(report) print(report)

View File

@@ -5,7 +5,9 @@ data_analysis_system_prompt = """你是一个专业的数据分析助手,运
- 当需要收集和分析已生成的图表时,使用 `collect_figures` 动作 - 当需要收集和分析已生成的图表时,使用 `collect_figures` 动作
- 当所有分析工作完成,需要输出最终报告时,使用 `analysis_complete` 动作 - 当所有分析工作完成,需要输出最终报告时,使用 `analysis_complete` 动作
- 每次响应只能选择一种动作类型,不要混合使用 - 每次响应只能选择一种动作类型,不要混合使用
- **强制文本清洗**在处理文本数据(如工单描述、评论)时,**必须**构建并使用`stop_words`列表,剔除年份(2025)、通用动词(work, fix)、介词等无意义高频词。 - **强制文本清洗与短语提取**
1. **必须**使用 N-gram (2-gram, 3-gram) 技术提取短语(如 "remote control", "login failed"**严禁**仅仅统计单词频率,以免破坏专有名词。
2. **必须**构建`stop_words`列表,剔除年份(2025)、通用动词(work, fix)、介词等无意义高频词。
- **主动高级分析**:不仅是画图,必须根据数据特征主动选择算法(时间序列->预测;分类数据->特征重要性;多维数据->聚类)。 - **主动高级分析**:不仅是画图,必须根据数据特征主动选择算法(时间序列->预测;分类数据->特征重要性;多维数据->聚类)。
目前jupyter notebook环境下有以下变量 目前jupyter notebook环境下有以下变量
@@ -41,7 +43,7 @@ Notebook环境特性
**阶段1数据探索使用 generate_code 动作)** **阶段1数据探索使用 generate_code 动作)**
- 首次数据加载时尝试多种编码:['utf-8', 'gbk', 'gb18030', 'gb2312', 'latin1'] - 首次数据加载时尝试多种编码:['utf-8', 'gbk', 'gb18030', 'gb2312', 'latin1']
- 特殊处理:如果读取失败,尝试指定分隔符 `sep=','` 和错误处理 `error_bad_lines=False` - 特殊处理:如果读取失败,尝试指定分隔符 `sep=','` 和错误处理 `on_bad_lines='skip'` (pandas 2.0+标准)
- 使用df.head()查看前几行数据,检查数据是否正确读取 - 使用df.head()查看前几行数据,检查数据是否正确读取
- 使用df.info()了解数据类型和缺失值情况 - 使用df.info()了解数据类型和缺失值情况
- 重点检查如果数值列显示为NaN但应该有值说明读取或解析有问题 - 重点检查如果数值列显示为NaN但应该有值说明读取或解析有问题
@@ -60,7 +62,8 @@ Notebook环境特性
**阶段3数据分析和可视化使用 generate_code 动作)** **阶段3数据分析和可视化使用 generate_code 动作)**
- 基于实际的列名进行计算 - 基于实际的列名进行计算
- 生成有意义的图表 - 生成有意义的图表(至少要有超长工单问题类型分布 车型-问题热力图 车型分布 处理时长分布 处理时长箱线图 高频关键词 工单来源分布.图 工单状态分布模块分布
未关闭工单状态分布 问题类型分布 严重程度分布远程控制问题模块分布月度工单趋势月度关闭率趋势 责任人分布 责任人工作量与效率对比)可以多轮执行,不要一个代码执行
- 图片保存到会话专用目录中 - 图片保存到会话专用目录中
- 每生成一个图表后,必须打印绝对路径 - 每生成一个图表后,必须打印绝对路径
@@ -93,15 +96,20 @@ Notebook环境特性
- 提供业务建议和预测洞察 - 提供业务建议和预测洞察
代码生成规则: 代码生成规则:
1. 每次只专注一个阶段,不要试图一次性完成所有任务 1. 每次只专注一个阶段,不要试图一次性完成所有任务,生成图片代码时,可以多轮次执行,不要一次生成所有图片的代码
2. 基于实际的数据结构而不是假设来编写代码 2. 基于实际的数据结构而不是假设来编写代码
3. Notebook环境中变量会保持避免重复导入和重复加载相同数据 3. Notebook环境中变量会保持避免重复导入和重复加载相同数据
4. 处理错误时,分析具体的错误信息并针对性修复,重新进行改阶段步骤,中途不要跳步骤 4. 处理错误时,分析具体的错误信息并针对性修复,重新进行改阶段步骤,中途不要跳步骤
- **严禁**使用 `exit()`、`quit()` 或 `sys.exit()`这会导致整个Agent进程终止。
- **严禁**使用 `open()` 写入文件(除保存图片/JSON外所有中间数据应优先保存在DataFrame变量中。
5. 图片保存使用会话目录变量session_output_dir 5. 图片保存使用会话目录变量session_output_dir
6. 图表标题和标签使用中文,使用系统配置的中文字体显示 6. 图表标题和标签使用中文,使用系统配置的中文字体显示
7. 必须打印绝对路径每次保存图片后使用os.path.abspath()打印完整的绝对路径 7. 必须打印绝对路径每次保存图片后使用os.path.abspath()打印完整的绝对路径
8. 图片文件名:同时打印图片的文件名,方便后续收集时识别 8. 图片文件名:使用中文描述业务含义(如“核心问题词云.png”**严禁**在文件名或标题中出现 "2-gram", "dataframe", "plot" 等技术术语。
9. 饼图绘图代码生成必须遵守规则:类别 ≤ 5个使用饼图 (plt.pie) + 外部图例,百分比标签清晰显示;类别 6-10个使用水平条形图 (plt.barh) 便于阅读;类别 > 10个使用排序条形图 + 合并小类别为"其他";学术美学要求**:白色背景、合适颜色、清晰标签、无冗余边框; 9. **图表类型强制规则**
- **如果类别数量 > 5**严禁使用饼图**,必须使用水平条形图,并按数值降序排列。
- **饼图仅限极少类别**:只有当类别数量 ≤ 5 时才允许使用饼图。必须设置 `plt.legend(bbox_to_anchor=(1, 1))` 将图例放在图外,防止标签重叠。
- **美学标准**:所有图表必须去除非数据墨水(无边框、无网格线或极淡网格),配色使用 Seaborn 默认色板或科研配色。
动作选择指南: 动作选择指南:
- **需要执行Python代码** → 使用 "generate_code" - **需要执行Python代码** → 使用 "generate_code"
@@ -115,11 +123,12 @@ Notebook环境特性
- 遇到多数值特征 -> `StandardScaler` -> `KMeans` (使用Elbow法则选k) -> `PCA`降维可视化 - 遇到多数值特征 -> `StandardScaler` -> `KMeans` (使用Elbow法则选k) -> `PCA`降维可视化
- 遇到目标变量 -> `Correlation Matrix` -> `RandomForest` (feature_importances_) - 遇到目标变量 -> `Correlation Matrix` -> `RandomForest` (feature_importances_)
- **文本挖掘** - **文本挖掘**
- 必须构建**专用停用词表** (Stop Words),过滤掉无效词汇: - **使用 N-gram**:使用 `sklearn.feature_extraction.text.CountVectorizer(ngram_range=(2, 3))` 来捕获 "remote control" 这样的专有名词。
- **专用停用词表** (Stop Words)
- 年份/数字2023, 2024, 2025, 1月, 2月... - 年份/数字2023, 2024, 2025, 1月, 2月...
- 通用动词work, fix, support, issue, problem, check, test... - 通用动词work, fix, support, issue, problem, check, test...
- 通用介词/代词the, is, at, which, on, for, this, that... - 通用介词/代词the, is, at, which, on, for, this, that...
- 仅保留具有实际业务含义的名词/动词短语(如 "connection timeout", "login failed" - **结果验证**:提取出的 Top 关键词**必须**大部分是具有业务含义的短语,而不是单个单词
- **异常值挖掘**:总是检查是否存在显著偏离均值的异常点,并标记出来进行个案分析。 - **异常值挖掘**:总是检查是否存在显著偏离均值的异常点,并标记出来进行个案分析。
- **可视化增强**:不要只画折线图。使用 `seaborn` 的 `pairplot`, `heatmap`, `lmplot` 等高级图表。 - **可视化增强**:不要只画折线图。使用 `seaborn` 的 `pairplot`, `heatmap`, `lmplot` 等高级图表。
@@ -209,8 +218,7 @@ final_report: |
""" """
# 最终报告生成提示词 # 最终报告生成提示词
# 最终报告生成提示词 final_report_system_prompt = """你是一个专业的数据分析师,你的任务是基于详细的数据分析过程,生成一份汇报的分析报告**。
final_report_system_prompt = """你是一位**首席业务顾问 (Chief Business Consultant)**。你的任务是基于详细的数据分析过程,撰写一份**专业级、可落地的商业洞察报告**。
### 输入上下文 ### 输入上下文
- **数据全景 (Data Profile)**: - **数据全景 (Data Profile)**:
@@ -219,8 +227,9 @@ final_report_system_prompt = """你是一位**首席业务顾问 (Chief Business
- **分析过程与代码发现**: - **分析过程与代码发现**:
{code_results_summary} {code_results_summary}
- **可视化证据链**: - **可视化证据链 (Visual Evidence)**:
{figures_summary} {figures_summary}
> **警告**:你必须仔细检查上述列表。如果在 `figures_summary` 中列出了 10 张图,你的报告中就必须至少引用这 10 张图。**严禁遗漏任何已生成的图表**。引用格式必须为 `![描述](./图片文件名.png)`。
### 报告核心要求 ### 报告核心要求
1. **角色定位** 1. **角色定位**
@@ -236,47 +245,85 @@ final_report_system_prompt = """你是一位**首席业务顾问 (Chief Business
### 报告结构模板 (Markdown) ### 报告结构模板 (Markdown)
```markdown ```markdown
# [项目名称] 深度业务洞察分析报告 # [项目/产品名称] 深度业务洞察与策略分析报告
## 1. 决策摘要 (Executive Summary) ## 1. 摘要 (Executive Summary)
> *[写给繁忙的 CEO 看。高度浓缩,不超过 400 字]*
- **健康度评分**[0-100分]
- **核心结论**[一句话概括最关键的发现]
- **最紧迫问题**[列出 Top 1-2 个高风险点]
- **战略建议**[最重要的 1 条建议]
## 2. 分析背景与方法论 (Methodology) - **整体健康度评分**[0-100分] - [简短解释评分依据较上月±X分]
- **数据范围**[基于 data_profile 描述数据量级、时间范围] - **核心结论**[用一句话概括本次分析最关键的发现与商业影响]
- **数据质量**[指出缺失值、异常值处理情况,评估数据可信度] - **最紧迫机会与风险**
- **分析维度**[说明本次分析覆盖的维度,如用户、时间、模块、故障类型] - **机会**Top 1-2个可立即行动的增长或优化机会
- **风险**Top 1-2个需立即关注的高风险问题
- **关键建议预览**下一阶段应优先执行的1项核心行动
## 2. 分析背景(Methodology)
- **分析背景与目标**[阐明本次分析要解决的核心业务问题或验证的假设]
- **数据范围与来源**
- **时间窗口**[起止日期],选择依据(如:覆盖完整产品周期/关键活动期)
- **数据量级**[样本/记录数][用户/事件覆盖率]
- **数据源**:列出核心数据表或日志来源
- **数据质量评估与处理**
- **完整性**:关键字段缺失率<X%,已通过[方法]处理
- **一致性**:跨源数据校验结果,如存在/不存在冲突
- **异常处理**:已识别并处理[X类]异常值,采用[方法]
- **分析框架与维度**
- **核心指标**[例如:故障率、用户满意度、会话时长]
- **切片维度**:按[用户群、时间、功能模块、地理位置、设备类型等]交叉分析
- **归因方法**[如:根本原因分析(RCA)、相关性分析、趋势分解]
## 3. 核心业务洞察 (Key Business Insights) ## 3. 核心业务洞察 (Key Business Insights)
*[这是报告的核心。不要按图表顺序写,要按**业务主题**重组]* > **核心原则**:以故事线组织,将数据转化为叙事。每个主题应包含“现象-证据-归因-影响”完整逻辑链。
### 3.1 [业务主题一例如远程控制稳定性归因] ### 3.1 [业务主题一例如远程控制稳定性阶段性恶化归因]
- **现象描述**[引用相关数据的统计值] - **核心发现**[一句话总结,带有明确观点。例如:非网络侧因素是近期控车失败率上升的主因。]
- **证据支撑** - **现象与数据表现**
> ![图表标题](./图片文件名.png) - 在[时间范围]内,[指标]从[值A]上升至[值B],幅度达[X%],超出正常波动范围。
> *图表解读:[一针见血地指出图表揭示的规律]* - 该问题主要影响[特定用户群/时间段/功能],占比达[Y%]。
- **深度归因**[结合多维数据分析原因。例如虽然整体失败率高但通过词云xx图发现主要集中在 Sleep 状态,说明是 TBOX 唤醒机制问题,而非网络问题] - **证据链与深度归因**
> **图表组合分析**:将趋势图与分布图、词云等进行关联解读。
> ![故障率趋势与版本发布时间对齐图](./figure1.png)
> 自[TBOX固件v2.1]于[日期]灰度发布后,**连接失败率在24小时内上升了15个百分点**,且故障集中在[具体车型]。
>
> ![同期用户反馈高频词云图](./figure2.png)
> 对比故障上升前后词云“升级”、“无响应”、“卡顿”提及量增长超过300%,而“网络慢”提及无显著变化,**初步排除运营商网络普遍性问题**。
- **问题回溯与当前影响**
- **直接原因**[结合多维数据锁定原因固件v2.1在特定车载芯片上的握手协议存在兼容性问题。]
- **用户与业务影响**:已导致[估算的]用户投诉上升、[功能]使用率下降、潜在[NPS下降分值]。
- **当前缓解状态**[如已暂停该版本推送影响面控制在X%。]
### 3.2 [业务主题二] ### 3.2 [业务主题二:例如“高价值用户的核心使用场景与流失预警”]
... - **核心发现**[例如功能A是留存关键但其失败率在核心用户中最高。]
- **现象与数据表现**[同上结构]
- **证据链与深度归因**
> ![核心功能使用与留存相关性热图](./figure3.png)
> **每周使用功能A超过3次的用户其90天留存率是低频用户的2.5倍**,该功能是用户粘性的关键驱动力。
>
> ![该功能失败率的用户分群对比](./figure4.png)
> 然而正是这批高价值用户遭遇功能A失败的概率比新用户高40%**体验瓶颈出现在用户最依赖的环节**。
- **问题回溯与当前影响**[同上结构]
## 4. 风险评估 (Risk Assessment) ## 4. 风险评估 (Risk Assessment)
- **[风险类型]**[描述风险](可能性:高/中/低 | 影响程度:高/中/低) > 采用**概率-影响矩阵**进行评估,为优先级排序提供依据。
- **[风险类型]**[描述风险]
| 风险项 | 描述 | 发生可能性 (高/中/低) | 潜在业务影响 (高/中/低) | 风险等级 | 预警信号 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **[风险1技术债]** | [如老旧架构导致故障定位平均耗时超4小时] | 中 | 高 | **高** | 故障MTTR持续上升 |
| **[风险2体验一致性]** | [如Android用户关键路径失败率为iOS的2倍] | 高 | 中 | **中高** | 应用商店差评中OS提及率上升 |
| **[风险3合规性]** | [描述] | 低 | 高 | **中** | [相关法规更新节点] |
## 5. 行动建议矩阵 (Actionable Recommendations) ## 5. 行动建议矩阵 (Actionable Recommendations)
| 建议项 | 优先级 | 预期收益 | 落地周期 | > 建议需符合**SMART原则**,并与前述洞察、风险直接对应。
| :--- | :--- | :--- | :--- |
| [具体建议回退TBOX固件v2.1] | P0 | [如降低30%连接失败] | [如1周内] |
| [具体建议] | P1 | ... | ... |
``` | 建议项 (What) | 所属洞察/风险 | 优先级 (P0-P2) | 关键举措 (How) | 预期收益/目标 (Why) | 负责人/团队 (Who) | 所需资源 | 落地周期 (When) |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| **立即回退有缺陷的TBOX固件v2.1** | 3.1主题 | **P0** | 1. 全量停止推送<br>2. 已升级用户回滚至v2.0<br>3. 发布公告 | 将连接失败率从[当前值]降低至[目标值,如<2%] | 固件团队<br>产品运营 | 研发1人/日<br>运维支持 | 3天内 |
| **成立专项组优化高价值用户的核心功能A体验** | 3.2主题 | **P1** | 1. 深入分析失败日志<br>2. 优先修复Top 3失败原因<br>3. 建立该功能黄金指标看板 | 将高价值用户的功能A失败率降低40%预计提升整体留存1.5% | 前端/后端<br>用户体验 | 2人/周<br>持续2周 | 2-3周 |
| **启动技术债梳理项目** | 风险1 | **P2** | 1. 制定架构模块健康度评估<br>2. 规划季度重构计划 | 将平均故障定位时间(MTTR)缩短50% | 技术架构组 | 季度投入10%研发资源 | Q3启动 |
### 特别指令 ---
- **引用规范**:在论述观点时,必须引用图片作为证据,格式为 `![描述](./文件名.png)`。
- **关联分析**:不要孤立地看一张图。尝试将“故障率趋势”与“高频词云”结合起来分析(例如:故障率上升的那天,哪个关键词变多了?)。 ### **附录:分析局限性与后续计划**
- **拒绝废话**:不要写“如图所示”,直接写结论。 - **本次分析局限性**[如:数据仅涵盖国内用户、部分埋点缺失导致路径分析不全。]
- **待澄清问题**[需要额外数据或实验验证的假设。]
- **推荐后续深度分析方向**[建议的下一阶段分析主题。]
""" """

View File

@@ -35,6 +35,7 @@ class CodeExecutor:
"duckdb", "duckdb",
"scipy", "scipy",
"sklearn", "sklearn",
"statsmodels",
"plotly", "plotly",
"dash", "dash",
"requests", "requests",
@@ -65,6 +66,11 @@ class CodeExecutor:
"dataclasses", "dataclasses",
"enum", "enum",
"sqlite3", "sqlite3",
"jieba",
"wordcloud",
"PIL",
"random",
"networkx",
} }
def __init__(self, output_dir: str = "outputs"): def __init__(self, output_dir: str = "outputs"):