From b7a27b12bdbd3c5d14e1ef5d5dfa64283484651d Mon Sep 17 00:00:00 2001 From: Jeason <1710884619@qq.com> Date: Fri, 20 Mar 2026 13:20:31 +0800 Subject: [PATCH] =?UTF-8?q?SQLite=20=E6=8C=81=E4=B9=85=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=20=E2=80=94=20sandbox=20=E4=B8=8D=E5=86=8D=E6=AF=8F=E6=AC=A1?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=BC=80=E5=85=B3=E8=BF=9E=E6=8E=A5=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20=5F=5Finit=5F=5F=20=E6=97=B6=E5=BB=BA?= =?UTF-8?q?=E8=BF=9E=E3=80=81close()=20=E6=97=B6=E9=87=8A=E6=94=BE=20Explo?= =?UTF-8?q?rer=20=E7=9A=84=20system=20prompt=20=E6=98=8E=E7=A1=AE=E5=91=8A?= =?UTF-8?q?=E7=9F=A5=20sandbox=20=E8=A7=84=E5=88=99=20=E2=80=94=20"?= =?UTF-8?q?=E6=AF=8F=E6=9D=A1=20SQL=20=E5=BF=85=E9=A1=BB=E5=8C=85=E5=90=AB?= =?UTF-8?q?=E8=81=9A=E5=90=88=E5=87=BD=E6=95=B0=E6=88=96=20LIMIT"=EF=BC=8C?= =?UTF-8?q?=E5=87=8F=E5=B0=91=20LLM=20=E7=94=9F=E6=88=90=E8=BF=9D=E8=A7=84?= =?UTF-8?q?=20SQL=20=E6=B5=AA=E8=B4=B9=E8=BD=AE=E6=AC=A1=20LLM=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E5=8D=95=E4=BE=8B=20=E2=80=94=20=E6=89=80?= =?UTF-8?q?=E6=9C=89=E7=BB=84=E4=BB=B6=E5=85=B1=E4=BA=AB=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=20openai.OpenAI=20=E5=AE=9E=E4=BE=8B=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=90=84=E5=BB=BA=E5=90=84=E7=9A=84=20sanitize=20?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E4=BF=AE=E5=A4=8D=20=E2=80=94=20=E5=B0=8F?= =?UTF-8?q?=E6=A0=B7=E6=9C=AC=E6=8A=91=E5=88=B6=E6=94=BE=E5=9C=A8=20float?= =?UTF-8?q?=20round=20=E4=B9=8B=E5=89=8D=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E8=A2=AB=20round=20=E5=B9=B2=E6=89=B0=20quick=5Fdetect=20?= =?UTF-8?q?=E4=BB=8E=20O(n=C2=B2)=20=E6=94=B9=E4=B8=BA=20O(n)=20=E2=80=94?= =?UTF-8?q?=20=E6=8C=89=E5=88=97=E8=81=9A=E5=90=88=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=EF=BC=8C=E5=8A=A0=E5=8E=BB=E9=87=8D=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=AF=B9=E6=AF=8F=E8=A1=8C=E9=87=8D=E5=A4=8D=E7=AE=97=E6=95=B4?= =?UTF-8?q?=E5=88=97=E7=BB=9F=E8=AE=A1=20=E5=8E=86=E5=8F=B2=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E5=AE=9E=E9=99=85=E7=94=9F=E6=95=88=20?= =?UTF-8?q?=E2=80=94=20get=5Fcontext=5Ffor=20=E7=9A=84=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E4=BC=9A=E6=B3=A8=E5=85=A5=E5=88=B0=20Explor?= =?UTF-8?q?er=20=E7=9A=84=E5=88=9D=E5=A7=8B=20prompt=20=E9=87=8C=EF=BC=8C?= =?UTF-8?q?=E5=A4=9A=E8=BD=AE=E5=88=86=E6=9E=90=E6=97=B6=20LLM=20=E8=83=BD?= =?UTF-8?q?=E7=9C=8B=E5=88=B0=E4=B9=8B=E5=89=8D=E7=9A=84=E5=8F=91=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 83 ++- agent.py | 192 ++--- charts/chart_1.png | Bin 0 -> 47221 bytes charts/chart_2.png | Bin 0 -> 38114 bytes charts/chart_3.png | Bin 0 -> 64144 bytes charts/chart_4.png | Bin 0 -> 47174 bytes charts/chart_5.png | Bin 0 -> 44646 bytes cleaned_data.csv | 849 +++++++++++++++++++++++ cli.py | 102 +-- config.py | 36 - context.py | 134 ---- core/__init__.py | 22 + core/config.py | 38 + sandbox_executor.py => core/sandbox.py | 102 +-- schema_extractor.py => core/schema.py | 48 +- core/utils.py | 93 +++ demo.py | 5 +- explorer.py | 238 ------- import_csv.py | 103 +++ insights.py | 239 ------- layers/__init__.py | 1 + layers/context.py | 80 +++ layers/explorer.py | 224 ++++++ layers/insights.py | 148 ++++ layers/planner.py | 74 ++ layers/playbook.py | 179 +++++ output/__init__.py | 1 + output/chart.py | 247 +++++++ output/consolidator.py | 84 +++ output/reporter.py | 84 +++ planner.py | 129 ---- playbooks/auto_1_工单处理效率分析.json | 22 + playbooks/auto_2_工单来源与状态分布.json | 22 + playbooks/auto_3_责任人绩效分析.json | 22 + playbooks/auto_4_车型与问题关联分析.json | 22 + playbooks/auto_5_时间趋势分析.json | 22 + reporter.py | 110 --- requirements.txt | 1 + test_run.py | 14 + 39 files changed, 2637 insertions(+), 1133 deletions(-) create mode 100644 charts/chart_1.png create mode 100644 charts/chart_2.png create mode 100644 charts/chart_3.png create mode 100644 charts/chart_4.png create mode 100644 charts/chart_5.png create mode 100644 cleaned_data.csv delete mode 100644 config.py delete mode 100644 context.py create mode 100644 core/__init__.py create mode 100644 core/config.py rename sandbox_executor.py => core/sandbox.py (52%) rename schema_extractor.py => core/schema.py (74%) create mode 100644 core/utils.py delete mode 100644 explorer.py create mode 100644 import_csv.py delete mode 100644 insights.py create mode 100644 layers/__init__.py create mode 100644 layers/context.py create mode 100644 layers/explorer.py create mode 100644 layers/insights.py create mode 100644 layers/planner.py create mode 100644 layers/playbook.py create mode 100644 output/__init__.py create mode 100644 output/chart.py create mode 100644 output/consolidator.py create mode 100644 output/reporter.py delete mode 100644 planner.py create mode 100644 playbooks/auto_1_工单处理效率分析.json create mode 100644 playbooks/auto_2_工单来源与状态分布.json create mode 100644 playbooks/auto_3_责任人绩效分析.json create mode 100644 playbooks/auto_4_车型与问题关联分析.json create mode 100644 playbooks/auto_5_时间趋势分析.json delete mode 100644 reporter.py create mode 100644 test_run.py diff --git a/README.md b/README.md index 2b3d117..322e4cd 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,21 @@ ┌─────────────────────────────────────────────────────────┐ │ Agent (编排层) │ │ 接收问题 → 调度四层 → 输出报告 │ -└──────┬──────────┬──────────┬──────────┬─────────────────┘ - │ │ │ │ - ┌────▼────┐ ┌──▼─────┐ ┌─▼──────┐ ┌▼─────────┐ - │ Planner │ │Explorer│ │Insight │ │ Context │ - │ 意图规划 │ │探索循环 │ │异常检测 │ │ 上下文记忆 │ - └─────────┘ └────────┘ └────────┘ └──────────┘ +└──┬──────────┬──────────┬──────────┬──────────┬──────────┘ + │ │ │ │ │ +┌──▼───┐ ┌───▼────┐ ┌───▼───┐ ┌───▼────┐ ┌───▼─────┐ +│Planner│ │Playbook│ │Explorer│ │Insight │ │ Context │ +│意图规划│ │预设匹配 │ │探索循环 │ │异常检测 │ │上下文记忆│ +└──────┘ └────────┘ └───────┘ └────────┘ └─────────┘ ``` -### 四层分工 +### 分层分工 | 层 | 组件 | 职责 | |---|---|---| | L1 | **Planner** | 理解用户意图,生成结构化分析计划(类型、维度、指标) | -| L2 | **Explorer** | 基于计划多轮迭代探索,每轮根据上一轮结果决定下一步 | +| L1.5 | **Playbook** | 匹配预设分析剧本,提供确定性查询保底 | +| L2 | **Explorer** | 先执行预设查询(如有),再基于结果多轮迭代发散探索 | | L3 | **InsightEngine** | 从探索结果中检测异常、趋势、关联,输出主动洞察 | | L4 | **ContextManager** | 管理多轮对话历史,后续问题可引用之前的分析 | @@ -77,34 +78,78 @@ python3 cli.py /path/to/your.db ├── schema_extractor.py # Schema 提取器(只提取结构) ├── sandbox_executor.py # 沙箱执行器(SQL 验证 + 结果脱敏) ├── planner.py # [L1] 意图规划器 -├── explorer.py # [L2] 自适应探索器 +├── playbook.py # [L1.5] 预设分析剧本匹配 +├── explorer.py # [L2] 自适应探索器(预设 + 发散) ├── insights.py # [L3] 洞察引擎(异常检测) ├── context.py # [L4] 上下文管理器 ├── reporter.py # 报告生成器 ├── agent.py # Agent 编排层 ├── demo.py # 一键演示 ├── cli.py # 交互式 CLI +├── playbooks/ # 预设分析剧本 +│ ├── regional_sales.json # 地区销售分析 +│ ├── category_analysis.json # 商品类别分析 +│ └── order_health.json # 订单健康度分析 ├── requirements.txt # 依赖 └── README.md ``` -## 对比预制脚本 +## 预设分析剧本(Playbook) -| | 预制脚本 / 模板 | 本方案(四层架构) | -|---|---|---| -| SQL 生成 | 模板拼接 | LLM 动态生成 | -| 查询数量 | 固定 | 1-6 轮,AI 自适应 | -| 后续追问 | 无 | AI 看到结果后判断是否深挖 | -| 异常发现 | 无 | 主动检测 + 主动输出 | -| 多轮对话 | 无 | 上下文记忆,可引用历史分析 | -| 适用场景 | 已知分析模式 | 探索性分析、开放性问题 | +Playbook 是"确定性保底 + AI 自由发散"的结合: + +- 为常见分析场景预定义一组确定性 SQL 查询 +- Planner 生成计划后,LLM 自动判断是否匹配某个 Playbook +- 匹配到:先执行预设 SQL(结果可控),再让 Explorer 基于结果自由发散 +- 没匹配到:走纯自适应路径(和之前一样) + +### 创建 Playbook + +在 `playbooks/` 目录下创建 `.json` 文件: + +```json +{ + "name": "地区销售分析", + "description": "按地区维度分析销售额、订单量、客单价", + "tags": ["地区", "区域", "销售"], + "preset_queries": [ + { + "purpose": "各地区销售总额", + "sql": "SELECT {{region_column}} AS region, ROUND(SUM({{amount_column}}), 2) AS total FROM {{table}} GROUP BY {{region_column}} ORDER BY total DESC" + } + ], + "exploration_hints": "请关注地区间的增长差异和客单价异常", + "placeholders": { + "table": "orders", + "region_column": "region", + "amount_column": "amount" + } +} +``` + +- `preset_queries`: 确定性 SQL,用 `{{占位符}}` 标记可变部分 +- `placeholders`: 默认值,LLM 匹配时会根据实际 Schema 自动替换 +- `exploration_hints`: 给 Explorer 的提示,引导发散方向 +- `tags`: 帮助 LLM 判断是否匹配 + +## 对比 + +| | 预制脚本 / 模板 | 纯自适应 | 本方案(Playbook + 自适应) | +|---|---|---|---| +| SQL 生成 | 模板拼接 | LLM 动态生成 | 预设保底 + LLM 发散 | +| 结果可控性 | 高 | 低 | 核心数据确定,发散部分灵活 | +| 查询数量 | 固定 | 1-6 轮 | 预设 N 条 + 自适应 M 轮 | +| 后续追问 | 无 | AI 自主判断 | 基于预设结果深挖 | +| 异常发现 | 无 | 主动检测 | 预设结果 + 主动检测 | +| 适用场景 | 已知分析模式 | 开放性问题 | 两者兼顾 | ## CLI 命令 ``` -📊 > 帮我分析各地区的销售表现 # 分析问题 +📊 > 帮我分析各地区的销售表现 # 分析问题(自动匹配 Playbook) 📊 > rounds=3 最近的趋势怎么样 # 限制探索轮数 📊 > schema # 查看数据库 Schema +📊 > playbooks # 查看已加载的预设剧本 📊 > history # 查看分析历史 📊 > audit # 查看 SQL 审计日志 📊 > clear # 清空历史 diff --git a/agent.py b/agent.py index 4a288a1..8c35a47 100644 --- a/agent.py +++ b/agent.py @@ -1,139 +1,175 @@ """ Agent 编排层 —— 调度四层架构完成分析 -Layer 1: Planner 意图规划 -Layer 2: Explorer 自适应探索 -Layer 3: Insight 异常洞察 -Layer 4: Context 上下文记忆 +Layer 1: Planner 意图规划 +Layer 1.5: Playbook 预设匹配 +Layer 2: Explorer 自适应探索 +Layer 3: Insight 异常洞察(与图表并行) +Layer 4: Context 上下文记忆 +Output: Reporter + Chart + Consolidator """ +import os from typing import Optional +from concurrent.futures import ThreadPoolExecutor, as_completed -from config import DB_PATH, MAX_EXPLORATION_ROUNDS -from schema_extractor import extract_schema, schema_to_text -from sandbox_executor import SandboxExecutor -from planner import Planner -from explorer import Explorer -from insights import InsightEngine, quick_detect -from reporter import ReportGenerator -from context import ContextManager +from core.config import DB_PATH, MAX_EXPLORATION_ROUNDS, PLAYBOOK_DIR, CHARTS_DIR +from core.schema import extract_schema, schema_to_text +from core.sandbox import SandboxExecutor +from layers.planner import Planner +from layers.explorer import Explorer +from layers.insights import InsightEngine, quick_detect +from layers.context import ContextManager +from layers.playbook import PlaybookManager +from output.reporter import ReportGenerator +from output.chart import ChartGenerator +from output.consolidator import ReportConsolidator class DataAnalysisAgent: - """ - 数据分析 Agent + """数据分析 Agent —— 四层架构编排""" - 四层架构: - 1. Planner - 理解用户意图,生成分析计划 - 2. Explorer - 基于计划多轮迭代探索 - 3. Insights - 从结果中检测异常、输出主动洞察 - 4. Context - 管理多轮对话上下文 - - Agent 负责编排这四层,从问题到报告。 - """ - - def __init__(self, db_path: str): - # 数据层 + def __init__(self, db_path: str = DB_PATH): self.db_path = db_path self.schema = extract_schema(db_path) self.schema_text = schema_to_text(self.schema) self.executor = SandboxExecutor(db_path) - # 四层组件 + # 各层组件 self.planner = Planner() self.explorer = Explorer(self.executor) self.insight_engine = InsightEngine() self.reporter = ReportGenerator() self.context = ContextManager() + self.playbook_mgr = PlaybookManager(PLAYBOOK_DIR) + self.chart_gen = ChartGenerator(output_dir=CHARTS_DIR) + self.consolidator = ReportConsolidator() + + # 累积图表 + self._all_charts: list[dict] = [] + + # 自动生成 Playbook + if not self.playbook_mgr.playbooks: + print("\n🤖 [Playbook] 未发现预设剧本,AI 自动生成中...") + generated = self.playbook_mgr.auto_generate(self.schema_text, save_dir=PLAYBOOK_DIR) + if generated: + print(f" ✅ 自动生成 {len(generated)} 个剧本:") + for pb in generated: + print(f" 📋 {pb.name} — {pb.description}") + else: + print(" ⚠️ 自动生成失败,将使用纯自适应模式") def analyze(self, question: str, max_rounds: Optional[int] = None) -> str: - """ - 完整分析流程 - - Args: - question: 用户分析问题 - max_rounds: 最大探索轮数(默认用配置值) - - Returns: - 格式化的分析报告 - """ + """完整分析流程""" max_rounds = max_rounds or MAX_EXPLORATION_ROUNDS print(f"\n{'='*60}") print(f"📊 {question}") print(f"{'='*60}") - # ── Layer 0: 检查上下文 ────────────────────────── - prev_context = self.context.get_context_for(question) - if prev_context: - print("📎 发现历史分析上下文,将结合之前的发现") + # Layer 0: 上下文 + prev = self.context.get_context_for(question) + if prev: + print("📎 发现历史上下文") - # ── Layer 1: 意图规划 ──────────────────────────── + # Layer 1: 意图规划 print("\n🎯 [Layer 1] 意图规划...") plan = self.planner.plan(question, self.schema_text) - - analysis_type = plan.get("analysis_type", "unknown") - dimensions = plan.get("dimensions", []) + # 注入历史上下文到 plan 中,让 Explorer 能看到 + if prev: + plan["_prev_context"] = prev + print(f" 类型: {plan.get('analysis_type', 'unknown')}") + print(f" 维度: {', '.join(plan.get('dimensions', [])) or '自动发现'}") rationale = plan.get("rationale", "") - print(f" 类型: {analysis_type}") - print(f" 维度: {', '.join(dimensions) if dimensions else '自动发现'}") print(f" 理由: {rationale[:80]}{'...' if len(rationale) > 80 else ''}") - # ── Layer 2: 自适应探索 ────────────────────────── - print(f"\n🔍 [Layer 2] 自适应探索 (最多 {max_rounds} 轮)...") - steps = self.explorer.explore(plan, self.schema_text, max_rounds=max_rounds) + # Layer 1.5: Playbook 匹配 + playbook_result = None + if self.playbook_mgr.playbooks: + print(f"\n📋 [Playbook] 匹配预设剧本 ({len(self.playbook_mgr.playbooks)} 个可用)...") + playbook_result = self.playbook_mgr.match(plan, self.schema_text) + if playbook_result: + n = len(playbook_result.get("preset_queries", [])) + print(f" ✅ 匹配: {playbook_result['playbook_name']} ({n} 条预设查询)") + else: + print(" ❌ 无匹配,走纯自适应路径") + # Layer 2: 自适应探索 + print(f"\n🔍 [Layer 2] 自适应探索 (最多 {max_rounds} 轮)...") + steps = self.explorer.explore(plan, self.schema_text, max_rounds=max_rounds, playbook_result=playbook_result) successful = sum(1 for s in steps if s.success) print(f"\n 完成: {len(steps)} 轮, {successful} 条成功查询") - # ── Layer 3: 异常洞察 ──────────────────────────── - print("\n🔎 [Layer 3] 异常洞察...") - - # 先做规则检测 + # Layer 3 + Charts: 并行执行洞察和图表生成 + print("\n🔎 [Layer 3] 异常洞察 + 📊 图表生成(并行)...") rule_alerts = quick_detect(steps) for alert in rule_alerts: print(f" {alert}") - # 再做 LLM 深度分析 - insights = self.insight_engine.analyze(steps, question) - if insights: - print(f" 发现 {len(insights)} 条洞察") - for insight in insights: - print(f" {insight}") - else: - print(" 未发现异常") + insights = [] + charts = [] - # ── 生成报告 ──────────────────────────────────── + with ThreadPoolExecutor(max_workers=2) as pool: + future_insights = pool.submit(self.insight_engine.analyze, steps, question) + future_charts = pool.submit(self.chart_gen.generate, steps, question) + + for future in as_completed([future_insights, future_charts]): + try: + result = future.result() + if future == future_insights: + insights = result + if insights: + print(f" 🔎 发现 {len(insights)} 条洞察") + else: + print(" 🔎 未发现异常") + else: + charts = result + if charts: + print(f" 📊 生成 {len(charts)} 张图表:") + for c in charts: + print(f" 🖼️ {c['title']} → {c['path']}") + else: + print(" 📊 无需生成图表") + except Exception as e: + print(f" ⚠️ 并行任务出错: {e}") + + if charts: + self._all_charts.extend(charts) + + # 生成报告 print("\n📝 正在生成报告...") - report = self.reporter.generate(question, plan, steps, insights) + report = self.reporter.generate(question, plan, steps, insights, charts=charts) - # 追加主动洞察 if insights: - insight_text = self.insight_engine.format_insights(insights) - report += f"\n\n---\n\n{insight_text}" + report += f"\n\n---\n\n{self.insight_engine.format_insights(insights)}" - # ── Layer 4: 记录上下文 ────────────────────────── - self.context.add_session( - question=question, - plan=plan, - steps=steps, - insights=insights, - report=report, - ) + # Layer 4: 记录上下文 + self.context.add_session(question=question, plan=plan, steps=steps, insights=insights, report=report) return report + def full_report(self, question: str = "") -> str: + """整合所有历史分析为综合报告""" + sessions = self.context.sessions + if not sessions: + return "(还没有分析记录,请先执行分析)" + print(f"\n📑 整合 {len(sessions)} 次分析为综合报告...") + report = self.consolidator.consolidate(sessions=sessions, question=question, charts=self._all_charts or None) + print(" ✅ 综合报告生成完成") + return report + def get_schema(self) -> str: - """获取 Schema 文本""" return self.schema_text def get_history(self) -> str: - """获取分析历史摘要""" return self.context.get_history_summary() def get_audit(self) -> str: - """获取执行审计日志""" return self.executor.get_execution_summary() def clear_history(self): - """清空分析历史""" self.context.clear() + self._all_charts.clear() + + def close(self): + """释放资源""" + self.executor.close() diff --git a/charts/chart_1.png b/charts/chart_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7d40e022dee56ccf47d807d6f6f1cc001c85d5a3 GIT binary patch literal 47221 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≺0?OOIYf^zXrjPHjB>12tCd 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 JDZ8xC{V$5^UeW*n literal 0 HcmV?d00001 diff --git a/charts/chart_2.png b/charts/chart_2.png new file mode 100644 index 0000000000000000000000000000000000000000..06a8fe830b4ec2efc03efd234f56060f5252380b GIT binary patch literal 38114 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 F{{_)>Sk3?d literal 0 HcmV?d00001 diff --git a/charts/chart_3.png b/charts/chart_3.png new file mode 100644 index 0000000000000000000000000000000000000000..02979b9cdab22debb2bd589f8abc2987d0743b77 GIT binary patch literal 64144 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 z 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 Y+l#AOGyAJ6eV&-*;b@!TEz-nUJy^ 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 TR closed +1704: Source version for OTA not right +03/04: there is still no connection to the server. Wait for downloading. +march 06:waiting ota +08/01: TSP : the apn1 &apn2 don't work +02/01: + 1. The status and location of the car is not displayed correctly in the application. +2. The remote startup function does not work +3. Navigation does not work on the GU",Low,close,TBOX,Evgeniy,24/04/2025,EXEED RX(T22),LVTDD24BXRG023494,,,,Evgeniy,112.0,2025-01-02,2025-04-24 +TR342,Telegram bot,02/01/2025,Remote control ,The customer can't use remote control,"0211:长时间未等到用户反馈,默认关闭,新问题则提交新工单进行处理 +没有问题发生详细时间","Feb 11:If user feedback is not received for a long period of time, it will be closed by default, and new issues will be submitted to a new work order for processing. +08/01 TSP has not recieved the remote command at the time of picture on 02.01 . Collect the customer’s operating scenario, operating time after reproducing the remote control problem",Low,close,local O&M,Evgeniy,11/02/2025,EXEED VX FL(M36T),LVTDD24B9RD030308,,,"image.png,image.png",Evgeniy,40.0,2025-01-02,2025-02-11 +TR343,Telegram bot,02/01/2025,PKI problem,The customer can't use navi. ,"0218 关闭问题,IHUID修改问题 +0211:专题群讨论","11/02:make a group to anlayse the reason +07/01: please change the hu-sn on tsp. replace it using the hu-sn in the ihu-system +02/01: After checking I found problems with PKI ",Low,close,local O&M,Evgeniy,18/02/2025,EXEED RX(T22),LVTDD24B1RG031497,,,"image.png,image.png,image.png",Evgeniy,47.0,2025-01-02,2025-02-18 +TR344,Telegram bot,02/01/2025,OTA,"There is impossible to put settings in Navi after OTA update. After customers put ""settings"" - application crashed. ",,"02/11 该问题已经SOTA修复,问题关闭 +02/06 项目定的方案走主机OTA更新,座舱的应用包已提交给主机集成,@待会后少龙更新OTA时间计划 +01/21 东软正在排查OTA系统是否修改Jar包混肴逻辑 +02/05 已更换东软提供的新jar包以及jar包的集成方式,测试验证后OTA更新",Low,close,DMC,张少龙,11/02/2025,EXEED VX FL(M36T),"LVTDD24B2RD089359 +LVTDD24B3PD625958 +LVTDD24B6RD032517 +LVTDD24B7PD635067 +LVTDD24B3PD626379 +LVTDD24B7PD635067",,"TGR0000145, TGR0000159, TGR0000205",,Evgeniy,40.0,2025-01-02,2025-02-11 +TR345,Telegram bot,02/01/2025,Remote control ,The customer can't use remote control,0319:等待OTA0108:图片显示时间,还未收到远控指令,等待收集信息,"13/05: upgrade success- close +24/04: upgrade downloaded. Still wait for upgrade. +1704: downloaded but not installed +03/04: OTA upgrade downloaded. Wait for upgrading. +08/01 TSP: TSP has not recieved the remote command at the time of picture. Collect the customer’s operating scenario, operating time after reproducing the remote control problem",Low,close,local O&M,Evgeniy,13/05/2025,EXEED RX(T22),LVTDD24B6RG020639,,,image.png,Evgeniy,131.0,2025-01-02,2025-05-13 +TR346,Telegram bot,02/01/2025,Remote control ,The customer can't use remote control,"0201:Tbox日志无法下载 +0108:唤醒失败,Tbox在1月2日无注册记录","24/04: upgrade TBOX SW OTA. No issues since the upgrade => TR closed +08/01: wake up failed. tbox has no login record on 01.02. +02/01: Opetation date/time: 02.01.2025 16:26. TBOX LOG from tsp can't download",Low,close,local O&M,Evgeniy,24/04/2025,EXEED RX(T22),LVTDD24B7RG033223,,,image.png,Evgeniy,112.0,2025-01-02,2025-04-24 +TR358,Telegram bot,03/01/2025,APN 2 problem,"After 1st of January the customer can't use any applications in car, but with WIFI everything ok.",,,Low,close,local O&M,Evgeniy,21/01/2025,EXEED VX FL(M36T),LVTDD24B9PD578824,,,"img_v3_02i6_fc34b5b6-46d9-4812-b442-b89b5469d6hu.jpg,img_v3_02i6_1ec7ce4b-4beb-4693-908b-ff2f75c80dhu.jpg",Evgeniy,18.0,2025-01-03,2025-01-21 +TR359,Telegram bot,03/01/2025,doesn't exist on TSP,Local produced car don't exist on TSP platrom. ,,"Feb 27:waiting infromation +03/01: Asked autosales team to invite customers for collecting TBOX info",Low,temporary close,local O&M,Evgeniy,27/02/2025,EXEED VX FL(M36T),"XEYDD14B1RA006337, +XEYDD14B1RA004158",,,,Evgeniy,55.0,2025-01-03,2025-02-27 +TR360,Telegram bot,03/01/2025,Navi,Navi doesn't work ,"0107:Cabin-team has fixed it , pls ask the customer to retry",03/01: Operation date 03.01 / time on the screen,Medium,close,local O&M,Evgeniy,26/02/2025,JAECOO J7(T1EJ),LVVDD21B6RC065115,,,"image.png,image.png",Evgeniy,54.0,2025-01-03,2025-02-26 +TR361,Telegram bot,06/01/2025,Navi,Navi doesn't work ,,0107:Cabin-team has fixed it ,Medium,close,生态/ecologically,何韬,08/01/2025,EXEED VX FL(M36T),LVTDD24B5RD075097,,,b5c001dd-c234-4610-a809-1169d5205350.jpeg,Evgeniy,2.0,2025-01-06,2025-01-08 +TR362,Telegram bot,06/01/2025,Navi,Navi doesn't work ,,0107:Cabin-team has fixed it ,Medium,close,生态/ecologically,何韬,10/01/2025,JAECOO J7(T1EJ),LVVDD21B4RC077618,,,,Evgeniy,4.0,2025-01-06,2025-01-10 +TR363,Telegram bot,06/01/2025,Navi,Navi doesn't work ,,0107:Cabin-team has fixed it ,Medium,close,生态/ecologically,何韬,10/01/2025,JAECOO J7(T1EJ),LVVDD21B9RC053170,,,image.png,Evgeniy,4.0,2025-01-06,2025-01-10 +TR364,Telegram bot,06/01/2025,Navi,Navi doesn't work ,,0107:Cabin-team has fixed it ,Medium,close,生态/ecologically,何韬,10/01/2025,JAECOO J7(T1EJ),LVVDB21B6RC071017,,,,,4.0,2025-01-06,2025-01-10 +TR365,Mail,06/01/2025,Network,"VK services such as music, video, Navi etc. do not work because of the internet connection issue. However, the internet traffic is available and OK in MNO. Status in Simba is also OK. Customer was checking the apps operation using the internet shared via hot spot from a smartphone and confirmed that everything worked well.",,"14/01: customer's feed-back ""Cheked it out. VK services started working"". TR closed. +09/01 :According to TBOX LOG ,TBOX feedback device were assigned two IP address,based on the info provived by TBOX already raised a ticket to MTS for further checking. +10/01:As per MTS feedback as below: +The technicians have found out that your device is running two active sessions in the same APN at the same time. Two static IP addresses are assigned at once. This is a feature of the device itself, the MTS network is not related to this error. +10/01:As per the requirements from PDC, SIMBA had reset the SIM card, pls contact the owner to retry it . +13/01:The traffic record is normal on MNO platform .Need to ask for the latest feedback from the customer,if customer feedback have no issue on network, SIMBA recommand to close this ticket",Low,close,local O&M,,14/01/2025,JAECOO J7(T1EJ),LVVDD21B8RC053161,,ohrana737@mail.ru,"LOG_LVVDD21B8RC05316120250110051751.tar,Screenshot_error_message.jpg,Simba_status.JPG",Vsevolod,8.0,2025-01-06,2025-01-14 +TR366,Telegram bot,06/01/2025,Remote control ,"Commands are not executed in remote control app. Last apn1&2 on Jan, 6th. However, there was no more apn2 since Jan, 6th although SIMBA is OK.",,"14/01: customer's feed-back remote control started working again after updating of firmware. +08/01: Operation time&date: Jan, 8th at 16:41 (Moscow time), error message attached. Tbox log will be provided as soon as it's available. + +10/01:Please confirm with the car owner whether the location has MTS network coverage?The sim card state and package are all vailable. + +13/01:The traffic record is normal on MNO platform .Need to ask for the latest feedback from the customer,if customer feedback have no issue on network, SIMBA recommand to close this ticket",Low,close,MNO,胡纯静,14/01/2025,EXEED RX(T22),LVTDD24B1RG023450,,TGR0000107,"LOG_LVTDD24B1RG023450_27_01_2025.tar,Simba_status.JPG,Picture_3.jpg",Vsevolod,8.0,2025-01-06,2025-01-14 +TR367,Telegram bot,08/01/2025,Application,"VK video app stopped working. A message ""check your network connection"" comes up. VK video app is up to date. Others apps such as VK music, weather, Navi etc. work well. Customer was trying to launch the app with an internet newtwork shared via smartphone - it does not work either. MNO and SIMBA are OK.",09/01 视频账号需重新登陆,"16/10: on the request to relogin in VK app, the issue has been fixed.",Low,close,生态/ecologically,颜廷晓,16/01/2025,EXEED VX FL(M36T),LVTDD24B5RD069476,,TGR0000131,,Vsevolod,8.0,2025-01-08,2025-01-16 +TR370,Telegram bot,10/01/2025,Network,"VK apps do not work. Message ""Check network connection"". Customer was checking with network shared from smartphone - all the apps work well. SIMBA is OK. MNO: apn2 activated but there is no apn2 available.","0311:下次例会分享进度 +0227:需要属地运维抓取日志","11/03:Sharing progress at next regular meeting +05/03: customer was asked to log out of VK video app and re-log in. Wait for feedback if app started working. +Feb 27:need logs +10/01: tbox log attached +10/01:Pls collect the latest tbox log +13/01:The traffic record is normal on MNO platform , if customer feedback have no issue on network, SIMBA recommand to close this ticket",Medium,close,local O&M,Vsevolod,18/03/2025,JAECOO J7(T1EJ),LVVDD21B8RC054021,,TGR0000129,"LOG_LVVDD21B8RC054021_10_01_2025.tar,Picture_1.jpg,Picture_2.jpg",Vsevolod,67.0,2025-01-10,2025-03-18 +TR371,Mail,10/01/2025,Remote control ,Remote control doesn't work + bad status,,"26/02: solved +14/01 Collect the customer’s operating scenario, operating time @Evgeniy",Low,close,local O&M,Evgeniy,26/02/2025,EXEED RX(T22),LVTDD24B6RG023539,,+79044989820 bound,"LVTDD24B6RG023539.tar,image.png,image.png",Evgeniy,47.0,2025-01-10,2025-02-26 +TR372,Telegram bot,10/01/2025,Network,No any TBOX connect since 06.01.2025,0211:无信息更新,"13.02 Solved by SW updated at dealer centre +11/02:no anyinformation upadated +Waiting for feedback +01/14 anything is ok on tsp +Collecting data from customer",Low,close,local O&M,Kostya,13/02/2025,EXEED RX(T22),LVTDD24B1RG023450,,TGR0000016,,Kostya,34.0,2025-01-10,2025-02-13 +TR374,Mail,13/01/2025,Remote control ,The customer can't use remote control since 05.01.2025,"0319:等待OTA0226:问题依旧未解决 +0114:收集客户的操作场景、重现问题的操作时间,收集客户数据","13/05: downloaded, not installed - temporary close +1704: downloaded, not installed +03/04: OTA upgrade downloaded. Wait for upgrading. +Feb 26: still doesn't work +14/01 Collect the customer’s operating scenario, operating time after reproducing the problem +Collecting data from customer",Low,temporary close,local O&M,Evgeniy,13/05/2025,EXEED RX(T22),LVTDD24B1RG019902,,,image.png,Evgeniy,120.0,2025-01-13,2025-05-13 +TR375,Mail,13/01/2025,doesn't exist on TSP,"Vehicle is not in the TSP platform however it has VK services available in IHU. Thus, customer can't activate remote control as well as VK services",,"13/02: Vehicle had manually been imported in the TSP platform with VIN XEYDD14B1RA004189. +20/01: the vehicle had been produced in China. +01/14 this car doesnt exist on TSP& MES, can you confirm whether it was produced in Russian",Low,close,local O&M,,13/02/2025,EXEED VX FL(M36T),"LVTDD24B9RDB34557 +XEYDD14B1RA004189",,,"Picture_3.JPG,Picture_2.JPG,Picture_1.JPG",Vsevolod,31.0,2025-01-13,2025-02-13 +TR376,Mail,13/01/2025,Remote control ,"Remote control stopped working. A message ""Time for respond from server has expired. Please try again later"" comes up when calling an operation. TSP is OK: last login and frequency data on 13/01. MNO is also OK.",,"13/02: customer's feedback: remote control started working without any porblems. TR closed. +14/01 Collect the customer’s operating scenario, operating time after reproducing the problem +13/01: tbox log is attached.",Low,close,local O&M,Vsevolod,13/02/2025,EXEED RX(T22),LVTDD24B5RG023693,,Customer phone number: +79251338221,"LOG_LVTDD24B5RG023693_13_01_2025.tar,Picture_1.JPG",Vsevolod,31.0,2025-01-13,2025-02-13 +TR377,Telegram bot,13/01/2025,Activation SIM,Failed relogin HU via App QR scan,,21.01: Customer feedback - everything works now,Low,close,生态/ecologically,冯时光,21/01/2025,EXEED VX FL(M36T),LVTDD24B4PD577306,,TGR0000183,e5486d85-ef3a-4b9a-8ad9-5f02e93aff03.png,Kostya,8.0,2025-01-13,2025-01-21 +TR378,Mail,13/01/2025,Remote control ,"Remote control stopped working. There were no more apn1 available since Jan, 1st. ",0319:等待OTA0224:在41辆车计划清单中,等待用户进站升级。,"24/04: no issues => TR closed +03/04: OTA upgrade completed successfully on March, 31th - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +05/03: wait for OTA update to be released in march. +24/02: customer is in the list of 41 car to be upgraded with new TBOX SW at dealer. +11/02: should we wait for the new OTA in march to be applied to fix the remote control issue? @喻起航 +16/01:The cause of the problem is network congestion. Please send the user OTA upgrade to resolve the problem。 +14/01: operation date&time - on 14/01 at 13:15, command name - engine start. Tbox log attached + +MNO Investigation conclusion(15/01):There are no prohibitions or restrictions on the use of APN1 from MTS side.The device establishes a session through APN1 on Jan 14, however, the device does not actively use APN1, we recommend that the device side continue to check. You can see the detailed investigation results in the attachment.",Low,close,local O&M,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24B3RG016340,,Customer phone number: +79315051488,"image.png,LOG_LVTDD24B3RG016340_14_01_2025.tar,Picture_2.jpg,Picture_1.jpg",Vsevolod,101.0,2025-01-13,2025-04-24 +TR379,Telegram bot,13/01/2025,Remote control ,"The customer can't use remote control, no any data about car in app +MNO - OK, both APNs are ok +TSP Low frequency Data - Not since 11 jan +TSP high frequency Data - Not since 11 jan +Last Tbox connect - 11 jan",,"12.02 No feedback after asking -> closed +14/01 TSP: No login tsp records on 13 &14 Jan, all remote command on 13&14 Jan fialed Because TBOX wake-up failed. real-data reported normally. +Perhaps the customer's parking location is poor, or it could be an old issue with T22 that requires an OTA upgrade @Kostya",Low,close,local O&M,Kostya,13/02/2025,EXEED RX(T22),LVTDD24B7RG023517,,TGR0000178,,Kostya,31.0,2025-01-13,2025-02-13 +TR380,Mail,13/01/2025,Remote control ,The customer can't use remote control since 11.01.2025 + wrong status of car ,0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。,"Feb 26: problem solved +14/01 TSP: tsp hasnot recieved remote command records on 11 Jan, all remote command 13 Jan fialed Because TBOX wake-up failed. real-data reported normally. +Perhaps the customer's parking location is poor, or it could be an old issue with T22 that requires an OTA upgrade @Evgeniy",Low,close,local O&M,Evgeniy,26/02/2025,EXEED RX(T22),LVTDD24B4RG032014,,bound +79255388020,,Evgeniy,44.0,2025-01-13,2025-02-26 +TR381,Mail,13/01/2025,Remote control ,Vehicle data is not reflected in remote control app. MNO is OK. Last tbox login is on 13/01 as well as frequency data.,,"17/02: customer's feedback: remote control work well. No issue found => TR closed. +13/02: customer asked if the issue is still valid. Wait for a feedback. +20/01: what are the next steps to fix the customer's issue? MNO status - the only apn available is apn1 for the reason of the traffic used up. +14/01 TSP: tsp has not recieved the reflected request on 13 Jan @Vsevolod +13/01: tbox log is attached to the TR",Low,close,local O&M,Vsevolod,17/02/2025,EXEED RX(T22),LVTDD24B1RG013498,,Customer phone number: +79803429000,"LOG_LVTDD24B1RG013498_14_01_2025.tar,Picture_1.JPG",Vsevolod,35.0,2025-01-13,2025-02-17 +TR387,Mail,14/01/2025,Remote control ,The customer can't use remote control,"0506:TSP未查询1月至5月远控记录,建议用户重新尝试 +0427:等待属地与客户联系确认是否可用 +0425:等待属地与客户联系确认是否可用 +0424:等待属地与客户联系确认是否可用 +0423:平台查询日志上传为1月14,建议用户重新尝试后,如不可用远控,再提供远控操作及日志 +0421:TSP核实车控时间后,转TBOX分析日志 +0417:添加0226的截图。 +0415:等待用户反馈 +0410:等待用户反馈 +0325:继续等待 +0320:继续等待 +0318:等待最新日志 +0312:提供新抓取日志发生时间 @赵杰 +0311:转国内分析 @刘海丰 +0304;需要前方运维抓取日志 +0226:用户反馈始终无法正常运行 +0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。","13/05: customer can't use any functions. he installed another alarm security system with remote control -temporary close +06/05:TSP did not query remote control records from January to May, users are advised to try again.@Evgeniy +27/04:Waiting for the territory to contact the customer to confirm availability +25/04:Waiting for the territory to contact the client to confirm availability +24/04:Waiting for location to contact customer to confirm availability. +23/04:The platform query log was uploaded on January 14th. It is recommended that users try again and provide remote control operations and logs if remote control is not available. +21/04: TSP verifies car control time and then transfers to TBOX to analyse logs +17/04: added screenshot from 26.02. +10/04:Still Waiting. +01/04:Still Waiting. +25/03:Still waiting +20/03:Still waiting +18/03:Provide the remote control occurrence time in the newly captured logs.@Evgeniy +12/03:Provide the remote control occurrence time in the newly captured logs.@Evgeniy +11/03 : turn to analysis. +04/03 :need tbox log +26 /02: still doesn't work +14/01 TSP: tsp waited feedback timeout Because of waking up TBox spends too much time +14/01: operation data/time - 14.01 8:04",Low,temporary close,local O&M,Evgeniy,13/05/2025,JAECOO J7(T1EJ),LVVDD21B3RC077416,,bound 79606323647,"image.png,LVVDD21B3RC077416.tar,Screenshot_20250110_080433_com.ru.chery.od.myOmoda.jpg",Evgeniy,119.0,2025-01-14,2025-05-13 +TR388,Telegram bot,14/01/2025,Remote control ,Remote control issue - commands are not exucuted. TSP is OK as well as MNO - apn1 is OK.,"0429:等待用户进站 +0427:等待用户进站 +0425:等待用户进站 +0424:等待用户进站 +0422:等待属地确认 +0421: +4月16日的两次空调控制都是车辆不在线,唤醒短信下发成功后,tbox无响应,35s超时 +4月17日的三次空调控制都是车辆不在线,唤醒短信下发成功后,tbox无响应,35s超时 +4月18日的空调控制成功了,4月18和4月19日和4月21日都发生了,没有下发远控发动机的指令,但是tbox上报了远控发动机执行成功的指令。建议告知用户近几次远控地,信号稳定性不佳,建议提供远控地经纬度信息供排查 +0417:等待用户反馈相关远控信息 +0410:等待用户反馈 +0407:已告知用户执行发动机启动并提供相应数据。等待反馈。 +0401:建议用户在信号好的地方,尝试重启车辆后,重试远控 +0327: TSP显示近三天TBOX有登录记录,仅查询到一条空调远控,但提示超时。 +0326:客户回来说远控仍然不能用。 +0325:再次询问客户问题是否仍然存在。等待反馈。 +0224:再次询问客户问题是否仍然存在。 +0217 计划暂时关闭此问题","29/04:waiting customer go to dealer +27/04:waiting customer go to dealer +25/04:waiting customer go to dealer +24/04:waiting customer go to dealer +22/04: Awaiting feedback on progress. +21/04: +On the 16th o+f April two air conditioning control are vehicle is not online, wake up text message sent successfully, tbox no response, 35s timeout +On the 17th of April, the vehicle was not online on all three occasions of air conditioning control, and after the wake-up message was sent successfully, the tbox did not respond and the 35s timeout was exceeded. +The air conditioning control on 18 April was successful, it happened on 18 April and 19 April and 21 April, there was no remote engine command issued, but tbox reported a successful remote engine execution. It is recommended that the user be informed of the last few remote control locations where the signal stability is poor, and it is recommended that latitude and longitude information of the remote control locations be provided for troubleshooting. + +17/04: repeat request sent to customer to call a command and provide then the respective data.@Vsevolod Tsoi +10/04:Still waiting +07/04: customer was asked to execute the engine start and provide the respective data. Wait for feedback. +01/04:Users are advised to try to retry the remote control after restarting the vehicle in a place with a good signal.@Vsevolod Tsoi +27/03:TSP shows that TBOX has logged in records in the past three days, and only one air-conditioning remote control was queried, but it prompted timeout. +26/03: customer is back saying that remote control still doesn't work. +25/03: customer is asked again it the problem still takes place. Wait for feedback. +24/02: repeat request sent to customer whether the issue is still valid. +13/02: customer asked if the issue is still valid. Wait for a feedback. +21/01: operation date&time - on 17/01 at 7:23; picture of an error message attached. +14/01: customer is asked to provide the needed data for analysis",Low,temporary close,local O&M,Vsevolod,13/05/2025,CHERY TIGGO 9 (T28)),LVTDD24B3RG087859,,"TGR0000191, TGR00001013 ",file_327.jpg,Vsevolod,119.0,2025-01-14,2025-05-13 +TR392,Telegram bot,15/01/2025,Network,"11/02:still waiting +20/01 Normal on platforms waiting for feedback from customer @Константин +19/01 :the traffic record of this sim is normal, we recommand to close this ticket +17/01 :The Vehicle is in hiberation mode and will not be attached to the network. As per our observed the SIM card was offline.Pls double check with the owner the vehicle status currently.thanks +16/01 (SIMBA):pls ask customer to try it again ,network should be normal now.(this car was active at 2025-01-07 13:21:30 , at that time ,MTS network suffered DDOS attack,So the package was delayed for two days) +The customer can't use remote control -> No any network data on MNO platform, only 2g network on HU, No Tbox Login after 2024 11 12 (december) -> +was activated 16:22:57 09-01-2025 according MNO +2025-01-07 13:21:30 according TSP/Simba +PKI tbox certificate creating time 2024-11-27 08:53:43 +""Vehicle is in hiberation mode"" - Can't collerct tbox log",0211:等待客户反馈,"13.02 Solved, waited for feedback - time out - closed",Low,close,local O&M,Kostya,13/02/2025,CHERY TIGGO 9 (T28)),LVTDD24B0RG090671 sim:79863995436,79863995436,"TGR0000142 +TGR0000121 +TGR0000162 +TGR0000161",,Kostya,29.0,2025-01-15,2025-02-13 +TR395,Mail,16/01/2025,Remote control ,"Remote control issue - commands are not executed. Following the MNO investigation the status is as follow: apn2 had been desactivated on 07/01 for the reason of exeeding traffic limit but on other side we see well that apn2 continued working since this date. Last tbox login - 2025-01-16 10:11:39; last frequency data - 2025-01-16 10:11:35. Operation date&time - 13/01/2025 at 23:22 Moscow time, command - engine start, screenshot of the error message attached as well as tbox log.","0506:TSP见用户近期远控解锁已成功,建议关闭此问题 +0429:等待用户反馈 +0427:等待用户反馈 +0425:等待用户反馈 +0424:等待用户反馈有效信息 +0421:等待用户反馈 +0417:等待用户反馈信息 +0415:等待用户反馈 +0410:等待用户反馈 +0408: TSP分析无异常,TBOX登录记录正常,请提供具体远控操作及时间转TBOX分析 +0401:等待中 +0325:等待执行操作的反馈,然后提供相应的数据。 +0320:等待客户反馈操作时间及具体操作 +0318:等待取新日志@Vsevolod Tsoi +0312:重新获取日志并提供时间点@Vsevolod +0306 转国内分析 +0217 计划暂时关闭此问题","05/05:TSP sees that the user's recent remote unlocking has been successful, it is recommended to close this issue.@Vsevolod Tsoi +29/04:Awaiting feedback on progress. +27/04:Awaiting feedback on progress. +25/04:Awaiting feedback on progress. +24/04:Awaiting feedback on progress. +21/04: Awaiting feedback on progress. +17/04: customer has been asked for some details and executing of an operation with respective data.@Vsevolod Tsoi +10/04:waiting for feedback. +08/04:TSP analysis shows no abnormalities, TBOX login records are normal. Please provide specific remote control operations and time for TBOX analysis +07/04: command - unlock the car at 6:08. TBOX log attached. +01/04:Still Waiting. +25/03: wait for feedback on the executing of an operation and providing then the respective data. +20/03:Waiting for customer feedback on operation time and specific operation. +19/03: customer is asked to execute an operation as well as providing the respective data for investigation. Wait for feedback. +12/03: Retrieve logs and provide point-in-time. @Vsevolod +12/03: Retrieve logs and provide point-in-time. @Vsevolod +06/03: customer's feedback: the issue is still valid. Client is asked to reproduce the problem and then provied the needed input data for investigation. +05/03: repeat request to customer to provide feedaback. +0227 Plan to close the issue temporarily +13/02: customer is asked to provide a feedback if the problem is still valid. Wait for a feedback. +20/01: customer's feed-back: mobile network is OK. Customer was rebooting the network setting on our request. +17/01 Tsp: tsp has not recieved the remote control command at 13/01/2025 at 23:22 Moscow time, pls check the network of his mobile",Low,temporary close,TBOX,Vsevolod,13/05/2025,JAECOO J7(T1EJ),LVVDD21B9RC053203,,voyt056@gmail.com,"LOG_LVVDD21B9RC053203_07_04_2025.tar,LOG_LVVDD21B9RC053203_16_01_2025.tar,Error_message.jpeg,Picture_2.JPG,Simba_status.JPG,Picture_1.JPG",Vsevolod,117.0,2025-01-16,2025-05-13 +TR396,Mail,16/01/2025,Remote control ,"Remote control issue - commands are not executed. MNO investigation: apn1&2 are available. Last tbox login - 2025-01-16 12:44:44; last frequency data - 2025-01-16 11:23:49. Operation date&time - 03/01/2025 at 14:30 Moscow time, command - engine start, screenshot of the error message and tbox log are attached.","0311:等待顾客回复 +0225:向用户询问问题是否依旧存在 +0217 计划暂时关闭此问题","20/30: customer's feedback: the problem is solved. +19/03: customer is asked again to provide a feedback. +25/02: repeat request sent to customer wether the issue is still valid. +13/02: customer asked if the problem is still valid. Wait for feedback. +23/01: Input data such as the operation time, date etc. was on 13/01 at 14:30 and not on 13/01 at 23:52 on which you provided the feed-back. Please check it again @林小辉 +017/01 Tsp: tsp has not recieved the remote control command at 13/01/2025 at 23:22 Moscow time, pls check the network of his mobile",Low,close,local O&M,Vsevolod,20/03/2025,JAECOO J7(T1EJ),LVVDB21B0RC080599,,"lelya71@inbox.ru, phone number 79276589759","LOG_LVVDB21B0RC080599_16_01_2025.tar,Picture_1.jpeg",Vsevolod,63.0,2025-01-16,2025-03-20 +TR402,Mail,17/01/2025,Remote control ,Remote control issue - commands are not executed. MNO status: apn1&2 are availabel. Last tbox login on 17/01; last frequency data on 17/01. Operation date&time 03/01/2025 at 9:28. Pictures are attached as well as tbox log from 16/01.,0227 计划暂时关闭此问题,"19/03: customer is asked again wether the issue still exists. Wait for feedback. +05/03: repeat request to customer whether the issue is valid. +0227 Plan to close the issue temporarily +13/02: customer is asked for providing the info whether the issue is still valid. Wasit for feedback. +17/01 tep waited response timeout, waking up TBox spends too much time at 03/01/2025 at 9:28 +but excuting remote control command successfully at Jan 3, 2025 17:38",Low,temporary close,local O&M,Vsevolod,27/02/2025,JAECOO J7(T1EJ),LVVDB21B1RC075802,,"zdorov_sergey@mail.ru, phone number 79081505465","LOG_LVVDB21B1RC075802_16_01_2025.tar,Picture_3.JPG,Picture_4.jpeg,Picture_1.JPG,Picture_2.JPG",Vsevolod,41.0,2025-01-17,2025-02-27 +TR403,Telegram bot,17/01/2025,Problem with auth in member center,"Issue with coming up of QR-code to login in HU when entering to personal account. An error message ""QR code is not valid"". Customer was trying with internet network shared from smartphone => same result. No data exchange is available in PKI neither tbox or DMC login.",@胡纯静 apn1& apn2 流量都无法使用,T平台显示已激活,"20/01: customer's feed-back: after firmware update issue with QR-code has been fixed. +19/01 :the traffic record of this sim is normal, we recommand to close this ticket +18/01 apn1& apn2 don't work",Low,close,local O&M,胡纯静,20/01/2025,EXEED RX(T22),LVTDD24B4RG013561,,TGR0000154,"file_300.jpg,file_216.jpg,file_302.jpg,file_301.jpg,file_215.jpg",Vsevolod,3.0,2025-01-17,2025-01-20 +TR405,Mail,20/01/2025,Network,"There is bad connection of network no network/3g.When connecting wifi everything is ok. Network indication. Constantly jumps from 3G to no network. At the same time the network on other devices in the same place works without problems. No errors, T-BOX and multimedia updated to the latest versions. Registration is passed. ",,"23/01: customer's feed-back - internet network started working again - problem is fixed. TR is closed. +1/21:Please ask customer to try it again and feedback the latest status .as monitor from backend the sims two APNs are available now.",Low,close,local O&M,Evgeniy,23/01/2025,EXEED VX FL(M36T),LVTDD24B4RD062289,,,"LOG_LVTDD24B4RD06228920250120141741.tar,photo_2025-01-18_18-44-40.jpg,photo_2025-01-18_18-44-35.jpg,photo_2025-01-18_18-44-06.jpg",Evgeniy,3.0,2025-01-20,2025-01-23 +TR406,Mail,20/01/2025,Remote control ,"2/2 pls upgrade ota @Evgeniy +23/01:软件版本为旧版本,建议点对点OTA升级解决问题 +TR406- T22-tBOX 版本18.14.01_22.39.00 +The customer can't use remote control",0319:等待OTA0217 建议属地运维进行OTA升级操作,"Apr 17: after OTA works well- solved +03/04: OTA upgrade completed successfully on April, 1st - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +Feb 17 Recommended OTA upgrade operation by local O&M @Evgeniy +22/01: operation time 19/01 22:26 +20/01: waiting for screenshot+operation time",Low,close,OTA,Evgeniy,17/04/2025,EXEED RX(T22),LVTDD24BXRG012480 ,,bound +79164620901,"image.png,LOG_LVTDD24BXRG01248020250120144246.tar",Evgeniy,87.0,2025-01-20,2025-04-17 +TR407,Mail,20/01/2025,Remote control ,Remote control issue - commands are not executed in the remote control app,"0305:客户未接听电话 +0225:如果问题仍然有效,将在本周内与客户联系。 +0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。","13/03: TR closed. +05/03: customer doesn't respond on the calls. +25/02: we will get in contact with customer within this week if the issue is still valid. +Feb 17:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +21/01: what are the next steps to do? Another execution of an operation and then collecting the respective data for investigation? +21/01: tsp waited feedback timeout Because of waking up TBox spends too much time 【longer than 35 second】 +20/01: operation date&time - 29/12 at 22:06, tbox log attached as well as the screeenshot of an error message",Low,close,local O&M,Vsevolod,13/03/2025,JAECOO J7(T1EJ),LVVDD21B3RC077562,,,"image.png,LOG_LVVDD21B3RC077562_20_01_2025.tar,Picture_1.jpeg",Vsevolod,52.0,2025-01-20,2025-03-13 +TR408,Telegram channel,21/01/2025,Problem with auth in member center,"Issue with log in the HU: after scannning of QR-code by customer to log in IHU, he received a notification in mobile remote control app on his smartphone that the autentification in member center has successefully been completed. However, there was any autentification in the IHU (see pictures attached). All the apps such as VK services, Navi and weather work well.",,"23/01: issue has been fixed. Cause was an additional space being present in the end of IHU ID. +23/01:Please provide relevant logs. I initially suspect that it is caused by network issues. Can you ask the user to click the refresh button to re-obtain the QR code to see if this can solve the problem?",Medium,close,生态/ecologically,刘康男,23/01/2025,JAECOO J7(T1EJ),LVVDD21B9RC076643,,TG: @gav_n_o,"Picture_1.JPG,Picture_2.JPG",Vsevolod,2.0,2025-01-21,2025-01-23 +TR409,Mail,21/01/2025,Remote control ,Remote control issue - commands are not executed. Last tbox log in 2025-01-21 13:30:54; frequency data 2025-01-21 13:30:48; Both apn1&2 are active.,,"14/02: customer's feed-back: remote control started working. Problem is fixed. +13/02: customer asked whether the issue still tales place. Wait for feedback. +21/01: required data is asked for the investigation",Low,close,local O&M,Vsevolod,14/02/2025,EXEED RX(T22),LVTDD24B8RG019153,,,Picture_1.jpg,Vsevolod,24.0,2025-01-21,2025-02-14 +TR410,Mail,21/01/2025,Remote control ,Remote control issue - vehicle data is not displayed in real time in the remote control app. Last login 2025-01-21 14:31:11; frequency data 2025-01-21 14:32:53. Apn1&2 are available and operational in MNO.,0217 计划暂时关闭此问题,"17/02: following customer's feedback ""there is no longer issue with remote control"". TR closed. +13/02: status in TSP: last high frequency data 2025-02-13 06:46:26; last tbox login 2025-02-13 06:38:40. MNO: apn1&2 are available and active. Customer asked if the issue is still valid. Wait for feedback. +21/01: required data is asked for the investigation",Low,close,local O&M,Vsevolod,17/02/2025,EXEED RX(T22),LVTDD24B0RG009099,,,"Picture_2.jpg,Picture_1.JPG",Vsevolod,27.0,2025-01-21,2025-02-17 +TR411,Mail,21/01/2025,Remote control ,Remote control issue: vehicle data is not displayed in remote control app as well as commands are not executed. Last tbox log 2025-01-21 06:34:17; apn1 is available and active in MNO.,"0319:等待OTA +0217 计划暂时关闭此问题 +0225:2月19日16:12启动发动机,附图,APP端显示车门打开,但车辆实际已锁定","24/04: no issue since upgrade => TR closed. +03/04: OTA upgrade completed successfully on April, 1st - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +06/03: log upload is still in process. +25/02: command name - engine start on 19/02 at 16:12. Picture attached. Wrong vehicle condition in the app: door is shown as opened however the car is locked. +13/02: customer asked for the execution of remote engine start command providing then the required data for investigation. Wait for feedback. +22/01 tsp waited feedback timeout Because of waking up TBox spends too much time pls check the tbox-log @何文国 +21/01: operation date&time - on 21/01 at 17:19 - engine start, at 17:22 - AC activation Pictures attached. tbox log is still in process",Low,close,TBOX,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24BXRG033460,,,"Picture_4.JPG,Picture_1.jpg,Picture_3.jpg,Picture_2.jpg",Vsevolod,93.0,2025-01-21,2025-04-24 +TR412,Mail,21/01/2025,Remote control ,21/01 TSP:客户反馈的时间点tbox没有登录记录 请查下TBOX日志 @何文国,"0319:等待OTA +0217 计划暂时关闭此问题","13/05: solved- close +24/04: upgrade successfylly completed on April, 22th. If no issues after one week, close TR. +17/04: downloaded, but no installed +03/04: OTA upgraded downloaded. Wait for upgrading. +23/01:21 only 18:11 (Beijing time) there is an air conditioning remote control, the execution was successful, before did not receive remote control instructions +21/01 no login records at that time pls check tbox-log @何文国 +21/01: customer can't use remote control + doesn't see any info about car. Operation time 21.01 12:22",Low,close,local O&M,Evgeniy,13/05/2025,EXEED RX(T22),LVTDD24B6RG019748,,bound +79179060320,"LOG_LVTDD24B6RG01974820250121161220.tar,image.png",Evgeniy,112.0,2025-01-21,2025-05-13 +TR413,Mail,21/01/2025,VK ,The customer can't enter to VK video,,"Feb 26: solved +11/02: waiting customer's feedback +10/02L: cus +02/07 @Evgeniyhave done cloud processing,Now users can check whether the problem is solved and whether VK video is normal and can be used +02/06 pls ask the owner to relogin his VK-account on iHU @Evgeniy + collect the hu-log ",Low,close,local O&M,Evgeniy,26/02/2025,EXEED VX FL(M36T),LVTDD24B0RD065593,,bound +79883182020,image.png,Evgeniy,36.0,2025-01-21,2025-02-26 +TR414,Mail,22/01/2025,Remote control ,Remote control issue: vehicle data is not displayed in remote control app as well as commands are not executed in the remotre control app. Last tbox log 2025-01-22 07:32:46; no apn1 available since 16/01/2025.,"0319:等待OTA +0217 计划暂时关闭此问题 +0205:请提供问题日志 +辛巴已经找运营商看过了,APN1没做任何限制,请@何文国排查TBOX日志 +0225:日志上传中","24/04: no claimes since upgrade => TR closed +03/04: OTA upgrade completed successfully on March, 31st - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +06/03: log upload is still in process. +10/03: recent tbox log attached. +05/03: upload of logs is in progress. +25/02: logs is in process of uploading. +06/02 collect the tbox-log pls @Vsevolod +23/01 APN1 doesn't work @胡纯静 +23/01: time&date - at 18:02 on 22/01, command name - open the trunk, screenshot of the error message attached. +22/01: required data is requested for investigation (date&operation time etc.)",Low,close,TBOX,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24BXRG021292,,,"LOG_LVTDD24BXRG021292_10_03_2025.tar,Picture_4.jpg,Picture_3.jpg,Picture_2.jpg,Picture_1.jpg",Vsevolod,92.0,2025-01-22,2025-04-24 +TR415,Mail,22/01/2025,Remote control ,Wrong vehicle data is displayed in the remote control app. No commands are executed.,"0319:等待OTA +0217 计划暂时关闭此问题 +0205:请提供问题日志 +辛巴已经找运营商看过了,APN1没做任何限制,请@何文国排查TBOX日志 +0225:车辆处于休眠状态达3 周。请求经销商以收集日志。","24/04: no claimes since upgrade = TR closed +03/04: OTA upgrade completed successfully on March, 31st - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +24/03: customer's feedback: everythng works well besides Navi that is not able to detect right positon of the car - see picture attached +13/03: logs attached and customer is also asked wehther the issue is still valid. +25/02: vehicle is in the hibernation mode for 3 weeks. Request to send to dealer for collecting the log. +06/02 collect the tbox-log pls @Vsevolod +23/01 apn1 doesnt work @胡纯静 +23/01: commands name - open the door/engine start; date&time on 22/01 at 19:50. Screenshots of the error message attached. +22/01: required data is requested for investigation (date&operation time etc.)",Low,close,TBOX,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24B9RG013653/89701010050605376664,,13/02: vehicle is still in the hibernation mode.,"Picture_4.jpg,LOG_LVTDD24B9RG013653_13_03_2025.tar,Picture_2.jpg,Picture_3.jpg,Picture_1.jpg",Vsevolod,92.0,2025-01-22,2025-04-24 +TR416,Mail,23/01/2025,Remote control ,The customer can't see status of car and can't use remote control.,"0401:TSP尝试远程抓取日志失败,提示车辆已进入深度睡眠。建议使用物理钥匙启动发动机后,等待一会再尝试远控. +0318:等待日志 +0311:使用物理仍无法使用,取Tbox日志 +0304:建议用户使用物理钥匙进行启动发动机,再次进行远程操作尝试 +0228:TBox 进入深度睡眠模式,唤醒失败 +0217:无进展 +0211:会后更新进展 +1/24 MNO已分析流量没有限制,请TBOX分析日志","Apr 17: no feedback ->temporary close +01/04: TSP attempts to remotely capture logs failed, indicating that the vehicle has entered deep sleep. It is recommended to use the physical key to start the engine and wait for a 15 minutes before attempting remote control. +25/03: waiting invitation to the dealer for log +18/03:Need to catch new logs and detail time.@Evgeniy +Mar 11:need the tbox logs +March 04: The user is advised to use the physical key to start the engine and try the remote operation again +Feb 28: TBox enters deep sleep mode, fail to wake up. +Feb 26: still has problem +23.01: operation date 23.01 time - see video ",Low,temporary close,TBOX,Evgeniy,17/04/2025,EXEED VX FL(M36T),LVTDD24BXPD619459/89701010064499968536,,,"image.png,image.png,tboxlog.tar,IMG_7650.MP4",Evgeniy,84.0,2025-01-23,2025-04-17 +TR417,Mail,23/01/2025,Remote control ,Remote control issue - customer can't start the engine remotely via app.,"0325:等待反馈 +0320:等待反馈 +0318:等待反馈 +0311:等待经销商反馈 +0225:经销商至今没有反馈。 +0213:要求经销商写入 TboxPIN码。 +0206: 要求经销商使用诊断工具重写 TBOX 的 PIN 码,确保与 EMS PEPS的 PIN 码一致。 +密码:26506882 +0124:TPS:tsp 在 14/01 16:04 未收到遥控器,请客户尝试,收集客户的操作场景、重现问题后的操作时间 +2025 年 1 月 14 日 @ 16:12:27 启动发动机,但返回 “EMS 验证失败”。 +0123:最后一次 tbox 登录 2025-01-23 10:38:40;最后一次高频数据 2025-01-23 10:45:02;apn1&2 在 MNO 中可用。操作时间和日期 - 14/01 16:04;截图和 tbox 日志附后","25/03: still no feedback. +20/03: still no feedback. +18/03: still no feedback.@Vsevolod Tsoi +05/03: still no feedback. +25/02: no feedback from dealer so far. +13/02: request sent to dealer for writing the pin-code for Tbox. +11/02: still procesing +06/02 ask the dealer to Use the diagnostic tool to rewrite the PIN code for TBOX, ensuring it matches the PIN code with EMS PEPS @Vsevolod +pin-code:26506882 +24/01: TPS: tsp has not recieved the remote control at 16:04 on 14/01, pls ask the customer try, Collect the customer’s operating scenario, operating time after reproducing the problem @Vsevolod +and the starting engine has been excuted at Jan 14, 2025 @ 16:12:27, but return “EMS Authentication failed ” +23/01: last tbox login 2025-01-23 10:38:40; last high frequency data 2025-01-23 10:45:02; apn1&2 are available in MNO. Operation time&date - at 16:04 on 14/01; screenshot attached as well as tbox log",Low,temporary close,local O&M,Vsevolod,01/04/2025,EXEED VX FL(M36T),LVTDD24B0PD584060,,,"iChery20250206-162300.mp4,LOG_LVTDD24B0PD584060_23_01_2025.tar,Picture_2.jpg,Picture_1.JPG",Vsevolod,68.0,2025-01-23,2025-04-01 +TR422,Telegram bot,23/01/2025,Remote control ,"Remote control: commands are not executed via remote control app. An error message ""time for a response from the server has expired. Please try again later"" comes up when executing the command.","0422:14天未反馈,暂时关闭该问题 +0421:等待用户反馈 +0417:等待用户反馈 +0415:等待用户反馈 +0410:等待用户反馈 +0407:等待用户反馈 +0403:请提供用户远控区域的位置及经纬度,方便排查地区真实网络环境 +0403:TBOX分析,未收到唤醒短信,16:20车辆已点火唤醒,TSP见16:20TBOX登录记录,未见16:10登录记录,MNO反馈唤醒短信发送失败,短信延迟发送,后续短信功能正常,建议用户重新启动车辆后重新尝试远控。@Vsevolod Tsoi +0401:远控时间为3月18日16:10,TSP已见该时间段有TBOX登录记录,远控发动机失败(16:12),获取车辆位置失败(16:12),已查SIM卡及流量状态正常,等待TBOX分析结果。 +0320:转Tbox分析。远控时间为3月18日16:10,日志已附,请走合规流程申请日志并分析。@王桂玲 +0318:等待用户进站抓取日志 +0311:等待用户反馈 +0304:转TSP分析,tsp 查询35s超时 +0211:tbox反馈需sim排查 +0127:远控等待TBOX响应超时,请王桂玲分析TBOX日志","22/04:No feedback for 14 days, issue temporarily closed +17/04: no feedback so far. +15/04: still waiting for feedback +10/04: still waiting for feedback +07/04: still waiting for feedback from customer on operation and their respective data. +03/04:Please provide the location and latitude/longitude of the user's remote control area to facilitate the investigation of the real network environment in the area.@Vsevolod Tsoi +03/04:TBOX analysis, did not receive wake-up SMS, 16:20 vehicle has ignition wake-up, TSP has seen 16:20 TBOX login record, did not see 16:10 login record, MNO feedback wake-up SMS sending failure, SMS delayed sending, subsequent SMS function is normal, suggest the user to restart the vehicle and then re-try to remote control.@Vsevolod Tsoi +01/04: remote control time is 16:10 on 18 March, TSP has seen TBOX login record in this time period, remote control engine failed (16:12), get vehicle position failed (16:12), have checked SIM card and traffic status is normal, waiting for TBOX analysis results. +20/03:Turn to Tbox analysis +19/03: command name - engine start on March, 18th at 16:10. Tbox log attached. +18/03: Waiting customer go to the dealer for checking. +06/03: customer asked again to execute several commands in different areas of his region. Wait for feedback. +0306:suggest customer try again +11/02:need simba to analyse +27/01 tsp waited feedback timeout Because of waking up TBox spends too much time. +23/01: last tbox login 2025-01-23 16:38:23; high frequency data +2025-01-19 16:22:29; apn1&2 are availabel and active in MNO. Operation time&date - on 23/01 at 16:37, command - engine start, screenshots attached as well as tbox log.Please provide the location and latitude/longitude of the user's remote control area to facilitate the investigation of the real network environment in the area.",Medium,temporary close,MNO,林兆国,22/04/2025,EXEED VX FL(M36T),LVTDD24B5PD638355,,,"LOG_LVTDD24B5PD638355_19_03_2025.tar,Picture_2.jpg,Permitions.JPG",Vsevolod,89.0,2025-01-23,2025-04-22 +TR423,Mail,23/01/2025,Remote control ,The customer can't use remote control + wrong status of car + bad location ,"0217 计划关闭此问题 +0205:请提供问题日志 +MNO侧和运营商网络都没有对APN1进行过任何操作。需要TBOX侧进一步排查分析。@何文国","26 Feb: solved +06/02 collect the tbox-log pls @Evgeniy +27/01 tsp: apn1 didnt work since 19.01 .pls check on MNO side @胡纯静",Low,close,TBOX,Evgeniy,26/02/2025,EXEED RX(T22),LVTDD24B9RG011756,,bound +79536011490,"image.png,image.png",Evgeniy,34.0,2025-01-23,2025-02-26 +TR424,Mail,24/01/2025,Remote control ,"Remote control: commands are not executed via remote control app. An error message ""Response from server was not successful. Time for response has expired"" comes up when executing the command. TSP status: last tbox log - 2025-01-24 17:02:25; high frequency data - 2025-01-24 17:02:23. Apn1&2 are available and active in MNO.","0306;等待oj反馈关闭问题 +0227:计划 28 日讨论此问题进行关闭 +0217 计划关闭此问题","06/03: feedback from O&J team: the issue is solved. +06/03: request sent to O&J team to figure out the status. +Feb 27:a talk with oj team in Feb 28's meeting +17/02: a request sent to O&J app team to check. Wait for a feedaback. +27/01 tsp hasnot recieved remote command at that time, pls ask app-team to check on app side +24/01: operation date&time - on 23/01 at 21:21; command name - engine start, screenshot attached as well as tbox log.",Low,close,local O&M,Vsevolod,06/03/2025,JAECOO J7(T1EJ),LVVDB21B7RC070328,,,"LOG_LVVDB21B7RC070328_24_01_2025.tar,Picture_1.jpeg",Vsevolod,41.0,2025-01-24,2025-03-06 +TR425,Mail,24/01/2025,Remote control ,"Remote control: commands are not executed via remote control app. An error message ""Response from server was not successful. Time for response has expired"" comes up when executing the command. TSP status: last tbox log - 2025-01-24 17:02:15; high frequency data - +2025-01-24 17:12:57. Apn1&2 are available and active in MNO.",0217 计划关闭此问题,"25/02: O&J app's feedback: there was no any feedback from customer more than 7 days. Issue us codsidered as closed. +25/02: status of request in O&J - done. Wait for feedback from O&J team if the issue can be closed. +17/02: request sent to O&J app for investigation. +27/01 tsp hasnot recieved remote command at that time, pls ask app-team to check on app side +24/01: operation date&time - on 23/01 at 22:41; command name - engine start, screenshot attached as well as tbox log.",Low,close,local O&M,Vsevolod,25/02/2025,JAECOO J7(T1EJ),LVVDD21BXRC054389,,,"LOG_LVVDD21BXRC054389_24_01_2025.tar,Picture_3.jpg,Picture_1.jpg,Picture_2.jpg",Vsevolod,32.0,2025-01-24,2025-02-25 +TR426,Mail,27/01/2025,Remote control ,Remote control doesn't work + wrong status of car ,"0217 计划关闭此问题 +0205:app下发车控,tsp未收到车控指令,请APP和TSP排查 +MNO侧和运营商网络都没有对APN1进行过任何操作。需要TBOX侧进一步排查分析。@何文国","Feb 26: solved +06/02 collect the tbox-log pls @Evgeniy +6/2 MNO:MNO and MTS side have no restriction and issue on APN1 , need device side to further check +27/01: tsp: tsp didnt recieved the remote command at that time, and apn1 didnt work since 23.01 .pls check on MNO side @胡纯静 +27/01: operation date&time - on 27/01 at 00:09",Low,close,TBOX,林小辉,26/02/2025,EXEED RX(T22),LVTDD24B8RG019850,,bound +79131489742,"LOG_LVTDD24B8RG01985020250127114825.tar,image.png,image.png",Evgeniy,30.0,2025-01-27,2025-02-26 +TR427,Telegram channel,27/01/2025,Remote control ,"Remote control: remote engine start command is not executed via remote control app. An error message ""Operation failed. Please make a request on the issue via feed-back form"" comes up when executing the command. TSP status: last tbox log - 2025-01-27 09:15:26; high frequency data - +2025-01-27 09:15:24. Apn1&2 are available and active in MNO.",0217 计划关闭此问题,"25/02: remote control started working. Wait for feedback from customer if the issue can be closed. +17/02: customer asked if the issue stiil valid. If so, required data will be provided for investigation. +27/01 pls collect the Operation time of the problem",Low,close,local O&M,Vsevolod,27/02/2025,EXEED VX FL(M36T),LVTDD24B7PD606426,,,Video_1.mp4,Vsevolod,31.0,2025-01-27,2025-02-27 +TR429,Mail,28/01/2025,Remote control ,Remote control issue: remote engine start does not work via app. Last tbox log - 2025-01-28 06:40:09; high frequency data - 2025-01-28 06:40:00; Apn1&2 are OK in MNO.,0217 计划关闭此问题,"07/04: still wait for feedback. +25/03: repeat request to customer to execute an operation and provide then the respective data for investigation. Wait for feedback. +19/03: client is asked for feedback whether the problem is still valid. If so, required data for investigation will be requested. +Feb 27:still waiting customer's feedbcak +03/02: customer is asked again to provide the needed input data. Wait for a feed-back (by email). +2/2 TSP :Tsp has not received the command from app at that time .PLS Collect the customer’s operating scenario, operating time after reproducing the problem @Vsevolod +28/01: command name - engine start, time&date - on 26/01 at 15:34, screenshot attached. Tbox log is in process.",Low,temporary close,local O&M,Vsevolod,27/02/2025,JAECOO J7(T1EJ),LVVDB21B0RC071563,,dasha-0215@mail.ru,"LOG_LVVDB21B0RC071563_28_01_2025.tar,Picture_1.jpeg",Vsevolod,30.0,2025-01-28,2025-02-27 +TR430,Mail,28/01/2025,Remote control ,"Remote control issue - engine is not started remotly via app => error message ""respond from the server was not successful. Time for response has expired"". TSP stauts: last tbox login 2025-01-29 10:40:05, high frequency data - 2025-01-29 10:42:37. MNO status: apn1&2 are available and active.","0422:属地会后确认 +0421:等待日会结果反馈,无异议后关闭 +0417:一致协商确定下次日会没有反馈,关闭该问题。 +0416:用户反馈在别处测试,远控生效 +0410:等待用户反馈 +0407:等待用户反馈 +0401:会后联系用户确认状态及详细位置信息 +0327: TBOX日志分析 短信延迟 9:40收到短信唤醒.建议用户在信号好的地方重启车机,并尝试远控.建议提供具体的住址信息。 +0325:会后转TBOX分析@刘海丰 +0325:客户又来询问何时能解决问题。因此,问题仍然存在。远控时间:3月25日 9:24 。新的相关日志附后。 +0226: +经TBOX日志查询,TBOX没有收到短信唤醒 +0225:转tbox进行分析@刘海丰 +0218 转国内分析","22/04:after meeting check +21/04: Awaiting feedback on the outcome of the day session, to be closed without objection. +17/04:Unanimous consultation determined that there would be no feedback the following day to close the issue.@Vsevolod Tsoi +16/04: customer's feedback: tried to execute the engine start having the car in another place => execution completed successfully. +10/04: still wait for feedback. +07/04: still wait for feedback. +01/04:Contact the user after the meeting to confirm the status and detailed location information +27/03:TBOX log analysis: SMS delayed 9:40 received SMS.Users are advised to restart the car in a place with good signal and try remote control. +25/03: engine start was executed once again on March, 25th at 12:18. tbox log attached. +25/03: customer is back asking when the problem is going to be solved. So, problem is still valid. Command name - engine start on March, 25th at 9:24. New respective log attached. +18/02: command name - start of the engine on 18/02 at 10:36 (8:36 Moscow time), screenshot attached as well as tbox log. +17/02: customer asked if the issue is still valid. If so, the required data fro investigation will be requested +2/2 Tsp has not received the command from app at that time .PLS Collect the customer’s operating scenario, operating time after reproducing the problem @Vsevolod +29/01: command - engine start, date&time - 28/01 at 12:20 (Moscow), screent shot of an error message attached as well as tbox log",Low,temporary close,local O&M,Vsevolod,24/04/2025,JAECOO J7(T1EJ),LVVDD21B5RC053182,,,"LOG_LVVDD21B5RC053182_25_03_2025_V2.tar,LOG_LVVDD21B5RC053182_25_03_2025.tar,LOG_LVVDD21B5RC053182_18_02_2025.tar,Picture_2.JPG,LOG_LVVDD21B5RC053182_29_01_2025.tar,Picture_1.jpg",Vsevolod,86.0,2025-01-28,2025-04-24 +TR431,Mail,29/01/2025,Problem with auth in member center,"Customer can't log in member center with QR code coming up in IHU. Error message ""Found QR code is not conform"". Customer was reseting IHU for factory settings => no result. Desactivating remote control (unbinding the vehicle) in the remote control app => no result. IHU reboot => no result. Automatic synchronization of time and date is ON. Vehicle status in the TSP: last tbox login - 2025-01-29 13:47:16; high frequency data - 2025-01-29 13:47:06. MNO status: apn1&2 are active.","0401:继续等待 +0325:等待OJ反馈 +0320:等待OJ团队反馈 +0318:等待属地运维反馈错误提示 +0311:周五跟进 +0225:O&J 反馈无法支持 +0217:同TR433 ,待属地运维修改数据后恢复正常即可关闭 +0211:等待属地app运维反馈app报错原因","08/04: remote control started working => solved. +07/03: customer's feedback: he had wrong VIN number in his registration document that ofcourse had been used for remote control => this has been corrected. Right VIN is LVVDD21B3RC053732. So, current VIN needs to be deleted in the mobile app and then new one should be bound. Wait for feedback. +27/03:Still waiting. +25/03: stiill wait for the feedback on error code from O&J team. +20/03:Waiting for feedback from O&J on error code. +1803:Waiting for feedback from local O&M on error code. +0304:Discussion at Friday's meeting +25/02: feedabck from O&J team: they cannot support in the issue from their side. +11/02:Waiting for feedback from territorial app ops on why the app is reporting errors +06/02: current status - sim card acitivated, all apps work well. The only issue is that the customer tries to to enter to the member center with QR code coming up when opening the personal account in IHU. When scanning QR code an error message comes up ""Found QR code is not conform"". But actually log in had already been done. +06/02 CABIN: pls ask O&J-app-team to check the err-message on the first picture. +2/2 TSP: APN1& APN2 work well, and the car was bound on tsp. pls check the reason ",Low,close,O&J-APP,Vadim,08/04/2025,JAECOO J7(T1EJ),LVVDD21B2RC052622 => right one LVVDD21B3RC053732,,,"IMG_5032.MP4,Picture_3.JPG,Picture_1.jpeg,Picture_2.jpeg",Vsevolod,69.0,2025-01-29,2025-04-08 +TR432,Mail,30/01/2025,Remote control ,Remote control issue - engine is not started remotly via remote app. TSP status: last tbox login - 2025-01-24 09:27:48; frequency data - 2025-01-30 11:14:53. MNO: apn1 - OK.,0319:等待OTA0217 计划关闭此问题,"24/04: no issues since the upgrade => TR closed +03/04: OTA upgrade completed successfully on April, 2nd - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +06/03: no apn1 available since Feb, 4th. Would ti mean that this car needs to be updated with new OTA in march ? +06/02 collect the tbox-log pls @Vsevolod +5/2 MNO:MNO and MTS side have no restriction and issue on APN1 , need device side to further check +2/2 tsp: the APN1 didnt work, pls check on MNO side @胡纯静 +30/01: command name - engine start, date&time - 30/01 at 7:05 Khabarovsk time (14:05 Moscow). Screenshot attached. tbox log upload is in process.",Low,close,MNO,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24B3RG019934,,,Picture_1.jpg,Vsevolod,84.0,2025-01-30,2025-04-24 +TR433,Telegram bot,03/02/2025,Remote control ,"Customer can't log in member center with QR code - ""Bad connection"" error - QR is not loading. +Tried with Wi-Fi - result the same +In PKI platform IHU check ""The user does not exist""","0227:该问题已经修复,等待用户反馈 +0218:等待属地运维询问问题是否关闭 +0217:同TR436,计划关闭此问题 +0211: +运维反馈该问题仍然存在","March11. Solved -> closed after customer feedback +feb 27:need local om confrim this issue exist or not +11/02:this issue still exist +PLS add info which data we should collect",Low,close,PKI,Kostya,11/03/2025,JAECOO J7(T1EJ),LVVDB21B1RC071488,,,"image.png,image.png",Kostya,36.0,2025-02-03,2025-03-11 +TR434,Mail,04/02/2025,Remote control ,"Engine start command is not executed remotly via app. Error message ""Response from server was not successful. Time for response has expired"". Vehicle status in the TSP: last tbox login +2025-02-04 13:23:56, high frequenct data +2025-02-04 13:26:27. MNO: apn1&2 are available and operational.","0227:等待用户反馈 +0217:计划关闭此问题 +0211:联系用户确认该问题是否仍然存在,不存在就关闭问题 +07/02 经过日志分析,TBOX没有接到短信 请转平台端排查 +UTC时间,换算成KM时间,最后一条短信接收时间是2月3日早上8点53分37秒,这之后就没收到过信息","March 6: Omoda team asked us to close the problem +Feb 27:waiting feedbcak +17/02: customer asked if the issue is still valid. If so, reproducing of a problem will be requested with rpoviding then all the respective input data. +11/02: Contact the user to confirm that the issue still exists and close the issue if it doesn't +06/02 TSP: waking up TBox spends too much time,pls check the tbox-log @王浩博 +04/02: operation time&date - 03/02/2025 at 9:43, command - engine start, screenshot/tbox log attached.",Low,close,local O&M,Vsevolod,06/03/2025,JAECOO J7(T1EJ),LVVDD21B2RC052717,,,"LOG_LVVDD21B2RC052717_04_02_2025.tar,Picture_1.PNG",Vsevolod,30.0,2025-02-04,2025-03-06 +TR435,Telegram bot,04/02/2025,Remote control ,"Remote control issue: none of the commands is not executed via remote control app. Car status in TSP: last tbox login 2025-02-04 11:12:37, last high frequency data 2025-02-04 11:23:31. MNO status: both apn1&2 are available and active.",0319:等待OTA0217:询问属地运维情况,"03/04: upgrade complete on March, 24th. No any issues since update. +0217: Inquiries about territorial O&M +06/02 tsp: Tsp has received the command from app at 17:44 Russia time but failed, tsp waited tbox feedback timeout , waking up TBox spends too much time +but no stating engine command at 16:45 (Moscow time) +06/02 command - engine start, time&date on 05/02 at 16:45 (Moscow time). Screenshots attached.",Low,close,local O&M,Vsevolod,03/04/2025,EXEED RX(T22),LVTDD24B7RG032153,,TGR0000480,"image.png,Picture_4.jpg,Picture_3.jpg",Vsevolod,58.0,2025-02-04,2025-04-03 +TR436,Telegram bot,07/02/2025,VK ,"Customer can't use VK video on HU. According to customer everything else works fine. +No any abnormal data on platforms","0214: 个别历史遗留数据受影响,IHUID需属地运维手动修改,后续版本不会出现此问题 +0211:专题群讨论","0218: Solved +0214: Individual historical legacy data is affected, IHUID needs to be manually modified by local O&M, this issue will not occur in subsequent versions. +0211: Thematic group discussion +07/02 VK license issue in VK video App +Short HU sn Pki -> Long HU sn",Low,close,PKI,喻起航,18/02/2025,EXEED RX(T22),LVTDD24B8RG019864,,,image.png,Kostya,11.0,2025-02-07,2025-02-18 +TR437,Mail,12/02/2025,Remote control ,The customer can't use remote control,"0318: 等待顾客反馈 +0312:最近一次远控当地时间2025-03-09 10:24:26.956,显示远控成功 +0311:顾客反馈仍然无法使用,重新抓取日志 +0227:等待用户反馈该问题是否仍然存在,若恢复正常,则关闭该问题 +0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。 +0213: 当地时间11:11对应UTC时间8:11,日志上看11:11TBOX处于Sleep状态,TBOX没有收到短信唤醒,日志上也没有收到短信记录,与TSP失去连接,因此收不到车控指令。13分27秒才被上电唤醒; 没收到短信唤醒,转平台处理 +0213:转tbox进行分析 ","March 12: The most recent remote control was successful on 2025-03-09 10:24:26.956 local time. @Evgeniy +Feb 27: ask customer to try again about remote control this function,if ok we can close this issue +Feb 17:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +Feb 14: +0213: Local time 11:11 corresponds to UTC time 8:11, logs show that TBOX is in Sleep state at 11:11, TBOX did not receive SMS wakeup, logs also did not receive SMS records, and TSP lost connection, so it could not receive car control commands. 13 minutes 27 seconds before being woken up by the power supply; did not receive the SMS wakeup, transferred to the platform to handle +0213: transfer tbox for analysis +12/02 : Operation time: 11/02/2025 at 11:11",Low,temporary close,local O&M,Evgeniy,18/03/2025,JAECOO J7(T1EJ),LVVDD21B9RC053184,,bound 79272197355,"img_v3_02jf_5b7f937f-49f8-43fc-8b8c-b4d4d1c0b3ag.jpg,LOG_LVVDD21B9RC05318420250212131105.tar,image.png",Evgeniy,34.0,2025-02-12,2025-03-18 +TR438,Mail,13/02/2025,Remote control ,"After remote start of the engine, after switching on the seat heating, the keyless entry settings are reset, and if I switch on the mirror heating, the projection is switched off.","0318:等待抓取日志 +0227:取得日志 +0213:需要日志进行分析,请提供DMC和tbox日志","25/03: temporary close +18/03:Need to catch new logs and detail time.@Evgeniy +Feb 27:get log +0213:Need logs for analysis, please provide DMC and tbox logs ",Low,temporary close,TBOX,Evgeniy,25/03/2025,EXEED VX FL(M36T),LVTDD24B6RD030427,,,,Evgeniy,40.0,2025-02-13,2025-03-25 +TR439,Telegram bot,14/02/2025,Remote control ,The customer can't use remote control/doesn't see status of car.,"0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。 +0214:转开发分析TSP平台日志中,车辆当时处于不在线状态,TSP平台发送唤醒短信成功,tbox未成功上线,超时35S,转tbox分析 ","Feb 26: Solved +Feb 17:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +0214: turn development analysis TSP platform logs, the vehicle was in the state of not online, the TSP platform to send wake-up SMS successful, tbox unsuccessful online, timeout 35S, turn tbox analysis +14/02: Operation date/time 13.02 at 19:37",Low,close,TBOX,Evgeniy,26/02/2025,CHERY TIGGO 9 (T28)),LVTDD24B3RG087103,,bound 79197687091,image.png,Evgeniy,12.0,2025-02-14,2025-02-26 +TR443,Telegram bot,10/02/2025,Remote control ,"Remote control issue: wrong data is displayed, no commans are executed in app. Vehicle status in TSP: last tbox login 2025-02-10 06:19:34, high frequency data - 2025-02-10 06:51:53. Status in MNO: apn1&2 are available and active.","0515:暂时关闭该问题 +0515:平台查询到用户近日多条远控记录,可关闭该问题 +0424:已下载升级,但尚未应用。等待升级。 +0304:等待用户反馈 +0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。 +0214:转开发分析TSP平台日志中,车辆当时处于不在线状态,TSP平台发送唤醒短信成功,tbox未成功上线,超时35S,转tbox分析","15/05:Suggest temporarily shutting it down. If the user finds that there are still issues, restart the problem +15/05:The platform has found multiple remote control records of the user in recent days, and this issue can be closed. +24/04: upgrade downloaded but not applied yet. Wait for upgrade. +March 4:need customer feedback +Feb 17:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +Feb 14: turn development analysis TSP platform logs, the vehicle was in the state of not online, the TSP platform to send wake-up SMS successful, tbox unsuccessful online, timeout 35S, turn tbox analysis +10/02: customer opened the mobile app on 10/02 at 11:02, vehicle engine is shown as started however vehicle is parked and actually the engine is stopped. Then the customer tried to stop the engine clicking on button ""stop"", some time has passed and then there was an error message ""Time for the response from server has expired. Please try again later"". ",Medium,temporary close,TBOX,Vsevolod,15/05/2025,EXEED RX(T22),XEYDD14B3RA002651,,TGR0000509,"Picture_3.jpg,Picture_2.jpg",Vsevolod,94.0,2025-02-10,2025-05-15 +TR447,Mail,14/02/2025,Remote control ,"The application concluded. indicates that the driver's door is open and the engine is running, the mileage is old. the commands don't work, the update doesn't help. do something about it. I can't warm up the car, it all started about two weeks ago.",0319:等待OTA0217:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。,"13/05: upgrade success-problem solved +24/04: still wait for upgrade. +17/04: downloaded but not installed +03/04: downloaded +17/04:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +14/02: Operation date 14.02 - operation time-see screenshots",Low,close,车控APP(Car control),Evgeniy,13/05/2025,EXEED RX(T22),LVTDD24B3RG031453,,,"image.png,image.png,image.png",Evgeniy,88.0,2025-02-14,2025-05-13 +TR448,Telegram bot,14/02/2025,Application,"Customer can't use VK video app. An error message comes up ""License activation was not successful"" - see screenshot attached. Others apps work well.","0227:问题解决 ,等待用户反馈。 +0224:该问题已经修复,等待属地运维咨询用户是否问题解决,进而进行问题关闭。 +0221: +1.问题原因: +由于VK Video的新版本更新,用户从旧版本通过应用市场升级到新版本导致了在雄狮的存储中有两个相同的应用ID从而用户无法使用软件 +2.临时解决方案: +由雄狮老师对该用户的车辆应用ID修改,后续我们这边与雄狮老师确定更好的方案,目前VK也针对此问题做出对应的修改,即使在用户无网络状态下只要激活过,下次也可以校验通过减少出现激活失败的情况。 +3.遇到此类问题的排查方向: +VK在激活失败页面新增了报错提示,通过用户的提示可以更加直观分析出问题的发生情况,比如激活校验失败,PKI失败等等 +4.后续避免方案: +后续VK优化激活逻辑版本应用迭代可以减少此类问题发生,其次正在与雄狮的老师商讨出现问题时自动上报日志的可行性,敲定后此类问题即使出现也是小概率时间,出现后解决问题的方向也会更加明确 +0217:转信源分析@张鹏飞","0227:issue fixed ,waiting customer feedback +0221. +1: +Due to the new version update of VK Video, users upgrading from the old version to the new version through the app market have two identical app IDs in Lion's storage and thus users are unable to use the software. +2.Temporary solution: +By the Lion teacher to the user's vehicle application ID modification, followed by our side and the Lion teacher to determine a better solution, the current VK also for this problem to make the corresponding changes, even if the user no network state as long as the activation of the next time can be verified through the activation to reduce the failure of the situation. +3. The direction of troubleshooting this kind of problem: +VK has added an error message on the activation failure page, which can be used to analyze more intuitively the occurrence of the problem, such as activation verification failure, PKI failure and so on. +4. Subsequent avoidance program: +Subsequent VK optimization activation logic version of the application iteration can reduce the occurrence of such problems, and secondly, we are discussing with Lion's teachers the feasibility of automatically reporting logs when a problem occurs, after finalizing such problems even if they occur is a small probability of time, after the emergence of a solution to the problem will also be more clear in the direction of the problem. +0217:Transfer source analysis Pengfei Zhang",Low,close,生态/ecologically,张鹏飞,06/03/2025,CHERY TIGGO 9 (T28)),LVTDD24BXRG087843,,TGR0000593,Picture_1.jpg,Vsevolod,20.0,2025-02-14,2025-03-06 +TR451,Mail,17/02/2025,Remote control ,"Remote control doesn't work. An error message comes up ""command is not executed. Please try again on the engine started "" when tying to execute the command activation air conditionning. TSP status: last tbox login 2025-02-09 11:26:08; high frequency data on 2025-02-09 11:03:56. MNO: no apn1 working since Jan, 9th.","0218: +1.转辛巴分析中,流量使用正常, +2.转TSP分析,车辆于2月15日处于深度睡眠模式,建议用户钥匙启动车辆后再次尝试远控","24/02: customer's feedback: remote control started working again. The issue is solved. +18/02: after getting in contact with customer at 15:38 vehicle was running and using. No apn1 available leads to no executing of a remote command. Customer still can't use remote control. +Feb 18: +1. In the trans-Simba analysis, the flow rate is used normally, the +2. to TSP analysis, the vehicle was in deep sleep mode on February 15, suggesting that the user key start the vehicle and then try remote control again +17/02: command name - air conditionning on 17/02 at 13:14 (12:14 Moscow time). Screenshot attahed.",Low,close,TBOX,Vsevolod,24/02/2025,EXEED RX(T22),LVTDD24B7RG031441,89701010050664705050,,Picture_1.jpg,Vsevolod,7.0,2025-02-17,2025-02-24 +TR452,Mail,17/02/2025,Remote control ,Remote control doesn't work + wrong status of car.,"0529:等待更新 +0520:等待刷新 +0515:等待用户进站换件 +0513: 所有远控操作仍然失败。需要更换tbox。已要求ASD团队启动tbox更换程序 +0429:会上建议用户进站,解除深度睡眠,如所有操作都无法解决,建议换件 +0427:等待属地与客户确认 +0424:等待属地与客户确认 +0421:TBOX侧反馈是否换件可以解决问题,明日日会,确认问题状态,是无法解除深度睡眠,或其他情况 +0417:建议用户进站换件 +0415:等待用户反馈是否可用 +0410:等待用户反馈状态 +0407:继续等待用户反馈 +0401:TSP尝试远程抓取日志失败,提示车辆已进入深度睡眠。建议使用物理钥匙启动发动机后,等待一会再尝试远控. +0325:尝试使用物理密钥启动 -> 结果相同 +0325:会后检查 +0320:本周车辆未启动,建议用户使用物理钥匙启动车辆,并重新尝试远控 +0318:会后检查远控 +0227:未取得日志转入分析 +0218:转TSP分析,车辆于2月15日处于深度睡眠模式,建议用户钥匙启动车辆后再次尝试远控","05/06: tbox was changed, wating for result +15/05:Waiting change tbox +13/05 all operation still failed. need to change tbox. already asked ASD team to launch procedure of changing tbox +29/04:At the meeting, the user was advised to come in and release the deep sleep, and if all operations failed to solve the problem, it was recommended to change the TBOX. +27/04: Waiting for confirmation between the locality and the customer +24/04: Waiting for confirmation between the locality and the customer +21/04:TBOX side feedback on whether changing parts can solve the problem, tomorrow day meeting, to confirm the status of the problem, is not able to lift the deep sleep, or other circumstances. +17/04: asked ASD team invite the customer for replace TBOX +15/04: Waiting for user feedback on availability +10/04: Waiting for user feedback status +07/04: Continue to wait for user feedback +01/04:TSP attempts to remotely capture logs failed, indicating that the vehicle has entered deep sleep. It is recommended to use the physical key to start the engine and wait for a 15 minutes before attempting remote control. +25/03:after meeting check +25/03: tried to start with physical key -> result is the same +20/03:The vehicle did not start this week and the user is advised to start the vehicle with the physical key and retry the remote control. +Mar 18/03: Post-conference inspection of remote control.@赵杰 +27/02:get log and waiting analysis +18/02: The customer has already started car and nothing happend-> the car still stay in deep sleep mode +18/02: Turning to TSP analysis, the vehicle was in deep sleep mode on February 15, suggesting that the user key start the vehicle and try remote control again. @Evgeniy +17/02: Operation date :15.02 at 12:42",Low,close,车控APP(Car control),Evgeniy,05/06/2025,EXEED VX FL(M36T),LVTDD24B2PD384362,,,"image.png,image.png,image.png",Evgeniy,108.0,2025-02-17,2025-06-05 +TR453,Mail,17/02/2025,Remote control ,Remote control doesn't work ,0319:等待OTA0218:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。,"Apr17: solved after OTA +03/04: OTA upgrade completed successfully on April, 1st - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +Feb 18:After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. @Evgeniy +17:02: 14.02 at 22:38",Low,close,车控APP(Car control),Evgeniy,17/04/2025,EXEED RX(T22),LVTDD24B0RG031393,,,image.png,Evgeniy,59.0,2025-02-17,2025-04-17 +TR454,Mail,17/02/2025,doesn't exist on TSP,TE1J with IOV but doesn't exist on TSP platform ,"0226:无异常数据,该问题关闭 +0220:需运维筛查数据,如无历史数据则关闭; +0218: +1.转pdc分析,该车辆存在于TSP 正式环境,但是无TBOX信息, +2.详细TBOX信息见Note +3.具体车辆缺失信息原因排查中,涉及车辆数量排查中","0218: +1. to pdc analysis, the vehicle exists in the TSP official environment, but no TBOX information, the +2.Detailed TBOX information see Note +3. The reasons for missing information on specific vehicles are being investigated, and the number of vehicles involved is being investigated.",Low,close,TSP,喻起航,26/02/2025,JAECOO J7(T1EJ),LVVDB21B8RC107189,,"SN:FCOT1EEE527M069# +ICCID:89701010050664784857","Телематика Фев 15 2025 (002).jpg,Телематика Скриншот Фев 15 2025.jpg",Evgeniy,9.0,2025-02-17,2025-02-26 +TR455,Telegram bot,18/02/2025,Remote control ,"Remote control issue: vehicle data is not displayed in the app, commands are not executed. Vehicle status in the TSP: last tbox login 2025-02-15 07:28:43, high frequency data 2025-02-10 07:24:38. Status in the MNO: no apn1 available and active since Feb, 10th.","0605:问题暂时关闭,待抓到日志或问题再次出现开启 +0603: 建议问题关闭,5月15日已经提出日志收集请求,超过20天 +0529:等待更新 +0520:等待更新 +0519:已超过两周未回复,暂时关闭 +0513:协商一致,暂时关闭 +0429:等待用户进站 +0427:等待用户进站 +0425:等待用户进站 +0424:等待用户进站 +0422:等待属地运维确定 +0421:等待进度反馈 +0417:建议客户进站取TBOX日志 +0410:客户使用实体钥匙启动发动机,等待了 20 分钟,但车辆没有从休眠模式中醒来 - 请参见附图。在手机应用程序中无法执行命令。这是否意味着需要更换 TBOX ECU?最后一次 TBOX 登录 2025-02-15 07:28:43,高频数据 2025-02-10 07:24:38。 +0408:等待用户反馈 +0402:问题重新启用,TSP检查车辆已进入深度睡眠,建议用户使用物理钥匙进入车辆并启动车辆,重新尝试远控,如远控异常,请记录异常操作、异常时间点,及截图方便后续排查 +0328:客户依然遇到远控问题,远控指令失效。 +0319:等待顾客反馈已超过两周,如依然无反馈,建议暂时关闭 +0318: 等待用户反馈 +0311:等待顾客反馈 +0220:经后台查询,因用户车辆当时处于信号不好的地点,车辆未收到远控指令,后续查看功能可正常使用,建议用户再观察使用,若还有问题,建议再次反馈且提供相关日志。 +Feb 19:转国内TSP工程师分析","0605:Issue temporarily closed, to be caught in the log or the issue reappears open +05/06: wait for feedback. +03/06: still wait for +29/05: still wait for tbox from dealer. +19/05: no close possible as request to collect TBOX log has only been sent on May, 15th. +0519: More than two weeks without reply, temporarily closed +15/05: request for getting TBOX log has been sent. Wait for feed-back from dealer. +29/04:waiting customer go to dealer +27/04:waiting customer go to dealer +25/04:waiting customer go to dealer +24/04:waiting customer go to dealer +22/04: Awaiting feedback on progress. +21/04: Awaiting feedback on progress. +17/04:Suggest customer go to dealer for tbox logs.@Vsevolod Tsoi +10/04: customer started the engine wiht physical key, waited for 20 minutes but vehicle did not come out of hibernation mode - see picture attached. No commans possible to execute in the mobile app. Does it mean TBOX ECU needs to be replaced? Last tbox login 2025-02-15 07:28:43, high frequency data 2025-02-10 07:24:38. +April 07: Waiting for customer feedback +April 02:The problem is re-enabled, the TSP checks that the vehicle has entered deep sleep, and suggests that the user use the physical key to enter the vehicle and start the vehicle, and retry the remote control, such as the remote control is abnormal, please record the abnormal operation, the abnormal time point, and the screenshot for the convenience of subsequent troubleshooting. +March 28: customer still has the issue with remote control - commandes are not executed. +March 19:Waiting for customer feedback for more than two weeks, if there is still no feedback, recommend temporary closure.@Vsevolod Tsoi +March 18: Still Waiting. @Vsevolod Tsoi +March 11: Waiting for customer feedback +Feb 20After the background inquiry, because the user's vehicle was in a bad signal location, the vehicle did not receive the remote control command, the follow-up view function can be used normally, it is recommended that the user again to observe the use, if there are still problems, it is recommended to feedback and provide relevant logs again. +18/02: customer asked for reproducing the problem with futher providing the required input data.",Low,temporary close,车控APP(Car control),Vsevolod,05/06/2025,CHERY TIGGO 9 (T28)),LVTDD24B5RG091332,,TGR0000627,"Hibernation_mode.JPG,Picture_1.jpg",Vsevolod,107.0,2025-02-18,2025-06-05 +TR458,Mail,19/02/2025,Remote control ,Remote control doesn't work +wrong status,"0319:等待OTA0220:已知TBOX网络阻塞问题,待后续OTA或者进站软件升级 +0219: +1.转 TSP 国内分析 TSP 端日志@张石树 +2.TBOX log 已抓取,转专业分析@何文国","03/04: OTA upgrade completed successfully. No any issues since ugrade. +Feb 20: Known TBOX network blocking issue, pending subsequent OTA or inbound software upgrade +19/02: operation time 18/02 at 17:57",Low,close,TBOX,Evgeniy,03/04/2025,EXEED RX(T22),LVTDD24B7RG032105,,,"LOG_LVTDD24B7RG03210520250219115848.tar,image.png",Evgeniy,43.0,2025-02-19,2025-04-03 +TR460,Telegram bot,20/02/2025,Remote control ,"Remote control issue: vehicle is shown in the remote control app as unlocked and started but in fact it is locked and the engine stopped. When trying to stop the engine in the app an error message comes up ""Time for the response has expired. Please try again later"" - see attached.","0319:等待OTA0225:2月20日13:22发出停止发动机指令,用户尝试熄火但实际已熄火了,附图 +0224:属地运维已经提交USB刷写tbox清单,刷写后若问题解决,则问题关闭。","24/04: no issues since upgrade => TR closed +03/04: OTA upgrade completed successfully on March, 31th - under monitoring Wait for 1 week. If there is no any negative feedback, the issue will be closed. +25/02: command name - engine stop on 20/02 at 13:22. Customer tried to stop the engine although in fact it was already stopped. Picture attached. +24/02: this car was not included into the list of 41 cars for TBOX SW upgrade. Should it be in? +Feb 24:If the issue is resolved after USB refresh, the issue is closed. +20/02: vehicle status in the TSP: last tbox login 2025-02-19 17:18:30, high frequency data 2025-02-19 18:31:08. Status in the MNO: acces to the network is granted. Apn1&2 are available and active.",Low,close,TBOX,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24B2RG032562,,TGR0000654,"Picture_3.jpg,Picture_2.jpg,Picture_1.jpg",Vsevolod,63.0,2025-02-20,2025-04-24 +TR461,Mail,20/02/2025,Application,The customer can't bound the car in APP. Error : А07913,0224:该问题已经在“TELEMATICA APP”解决·,可关闭该问题,"0224: The problem has been solved in the ""TELEMATICA APP"" - the problem can be closed!",Low,close,用户EXEED-APP(User),Evgeniy,24/02/2025,EXEED VX FL(M36T),LVTDD24B6PD593801,,,"image.png,image.png",Evgeniy,4.0,2025-02-20,2025-02-24 +TR462,Telegram bot,20/02/2025,Activation SIM,"Customer can't activate the remote control. Following the checking IHU ID record in the TSP a message comes up ""Request param error:param tBoxSn is inconsistent with the TSP T-BoxS"". Feedback from 2nd line: ""The production data is this FCOT1EEE527M253#, and it cannot be confirmed whether the actual car has been replaced with parts."" Dealer's feedback ""no repair had been done on the vehicle"". +客户无法激活远控。 在 TSP 中检查 IHU ID 记录后,出现一条信息 ""Request param error:param tBoxSn is inconsistent with the TSP T-BoxS""(请求参数错误:参数 tBoxSn 与 TSP T-BoxS 不一致)。 第二行反馈: ""生产数据是此 FCOT1EEE527M253#,无法确认实车是否已更换部件""。 经销商反馈 ""未对车辆进行过维修""。","1119:TSP库里的tboxSN多了一个空格,已修改,请客户再次尝试,如果问题解决了就可以关闭此问题。 +1118:TR系统已重新启用,因所需数据已提交——详见工程菜单中附带的DMC&TBOX数据截图及最新Tbox日志。 +0807: 暂时关闭。将在获取 TBOX 数据时重新开放。 +0804:目前暂无进展 +0728:等待客户进站读取实车信息 +0725:等待客户进站读取实车信息 +0320:继续等待 +0318:继续等待 +0318:继续等待 +0311:无进度反馈 +0221:等待经销商信息返回 (dealer)","24/11: remote control was restored. TR closed. +19/11: Tbox started loging in. The user was asked to try binding his car in the app. +19/11:An extra space has been added to tboxSN in the TSP library. This has been rectified; please ask the client to try again.If the issue has been resolved, you may close this case.@Vsevolod Tsoi +18/11: TR re-opened since the requested data was provided - see attached DMC&TBOX data photo from engineering menu as well as the latest Tbox log +07/08: closed temporarily. Will be-reopened when getting TBOX data. +04/08: no feedback so far. +31/07: still the same status +28/07: invitation of customer for collecting TBOX data is still pending. +22/07: TR was opened again because customer still was unable to enter into member center. TSP says that TBOX SN doesn't match to one installed in the car. A request for collecting TBOX data has been sent to dealer. +10/04:temp lose +08/04Waiting for dealer feedback +25/03: please keep it open - so, no close possible. +20/03:Still waiting. +18/03: Still waiting. +11/03: Still waiting. +21/02: Wait for the dealer's feedback. +20/02: it seems that vehicle has a different TBOX SN from the one in the TSP - see picure attached. Request has been sent to invite the customer to the dealer for collecting TBOX data. Wait for feedback.",Low,close,TBOX,Vsevolod Tsoi,24/11/2025,JAECOO J7(T1EJ),LVVDB21B9RC103393,,"TGR0000500 +elantseva.dv@rnd.borauto.ru","CHR_LVVDB21B9RC103393_20251118205043.tar,DMC_data.jpg,TBOX_data.jpg,file_714.jpg,Picture_1.JPG,file_715.jpg,file_702.jpg,file_705.jpg,file_704.jpg,file_703.jpg,file_701.jpg",Vsevolod,277.0,2025-02-20,2025-11-24 +TR464,Telegram bot,21/02/2025,Remote control ,"Customer can't start the vehicle vie remote control app. As a result an error message comes up. +Vehicle status in the TSP: last tbox login 2025-02-18 20:20:32, high frequency data 2025-02-18 20:19:26. MNO status: last apn1&2 available on 18/02.",0224:建议加入清单进行USB软件更新,"27/02: customer's feedback - apn1 is again available and as a consequence the remote control started working. +26/02: tbox log attached. +0224: Recommendation to add to the list for USB software update @Vsevolod +21/02: command name - engine start on 21/02 at 12:13. Screenshot attached.",Low,close,local O&M,Vsevolod,27/02/2025,EXEED RX(T22),LVTDD24B1RG021245,,TGR0000646,"LOG_LVTDD24B1RG021245_26_02_2025.tar,Picture_1.jpg",Vsevolod,6.0,2025-02-21,2025-02-27 +TR465,Telegram bot,21/02/2025,Application,"Customer can't bind his vehicle in the remote control app. Background: customer was using the app and everythig worked well. But one day he opened the app and noticed that he has been loged out. Then he loged in again with his account data and added the vehicle in the app (vehicle status is OK in the system). However he could not activate it in the app => status in the TSP - unbound . An error message comes up ""A07913"" - see pictures attached.","0320:等待客户反馈,追踪是否能够正常使用 +0320:后台已删除错误数据,可让用户重新绑定 +0318:已转入分析@张明亮 +0227:A07913报错 注册品牌信息失败@戴国富 +0225: 客户反馈:手机端仍然无法激活汽车。同样的错误提示“A07913”@喻起航 +0225:此问题已解决,请属地运维联系用户核实进行问题关闭@Vsevolod 7天没有反馈需要关闭 +0224:转APP分析","20/03: Customer's feedback: problem is solved. +20/03:Erroneous data has been deleted in the background, allowing users to rebind.@Vsevolod Tsoi +18/03:Transferred for analysis.@张明亮 +25/02: customer's feedback: still can't activate the car in the mobile app. The same error message comes up ""A07913""@喻起航 +Feb25: This issue has been resolved, please local O&M contact the user to verify for problem closure @Vsevolod +Feb 24: Turn APP Analysis",Low,close,车控APP(Car control),Vsevolod Tsoi,20/03/2025,CHERY TIGGO 9 (T28)),LVTDD24B9RG088529,,TGR0000590,"Picture_1.jpg,Status_admin.JPG,Vehice_status_undind.JPG,file_971.jpg",Vsevolod,27.0,2025-02-21,2025-03-20 +TR466,Telegram bot,21/02/2025,Network,Customer can't laucnh online video app -> License issue -> Other app doesn't work too -> No tbox connect since 18/02 -> No any network connection -> IHU still login well. apps works with wifi,0224:等待更多细节反馈,UPD?,"24/02 Closed, confirmed by customer feedback +21/02 PLS provide information what data we should collect for analysis +UPD solved by itself -> Waiting for feedback",Low,close,local O&M,Kostya,24/02/2025,EXEED RX(T22),LVTDD24B1RG021245,,TGR0000676,image.png,Kostya,3.0,2025-02-21,2025-02-24 +TR467,Mail,21/02/2025,Remote control ,"Remote engine start does not work via app. Status in TSP: last tbox login 2025-02-21 03:08:41. high frequency data 2025-02-21 15:14:11. MNO: apn2 - OK. No working apn1 since Feb, 18th. ","0826:暂无进展,转等待数据 +0819:请QS方更新进展 +0814: 等待TBOX硬件抵达 +0812: 预计抵达日期 W33 底 +0807: 预计抵达日期 CW33 底 +0804:已询问经销商了解预计抵达时间。 +0728: 仍在等待 TBOX 硬件的交付,预计将于 7 月底/8 月初交付。 +0722:仍在等待 TBOX 硬件的交付。 +0714: 到目前为止还没有收到货物。 +0710: 仍未收到 TBOX 硬件。 +0701:等待 8 月份交付 TBOX HW。 +0626:新的 TBOX 预计从 8 月开始交付 +0623: 等待用户更换T-BOX +0610:等待信息刷新 +0603:等待用户更换tbox +0529:等待更新 +0515:建议用户进站,先取日志之后,再更换TBOX,保留日志供分析 +0513:属地反馈问题重新出现,ANP2已在5月1日打开,但无网络连接,会后尝试抓取日志,车辆已进入深度睡眠,建议按深度睡眠流程处理 +0415:建议此问题暂时关闭 +0410:等待用户进站 +0407:等待用户进站 +0401:等待用户进站 +0325:等到用户进站 +0320:等待日志抓取 +0318:等待用户进站取日志 +0311:等待前方抓取日志 +0304:取Tbox日志并分析 +0227:查询到用户远程操控失败原因均因为提示超时,建议用户将车辆移动到信号好的地方再试试,若还不行,建议进站检查。 +0225:转TSP查询; 短信下发后 TBOX无响应,35s超时(time out) +0224:请MNO协助排查下APN。APN正常","09/10: we got really very strange feedback from dealership: we were informed by dealership that spare TBOX arrived and change might be done accordingly so that the remote control was restored. We were told by a dealership that user was contacted by CHERY representative office telling that change of TBOX was not required. +09/10: still pending. Then, status checked out in TSP and MNO: apn1&2 are normal. +25/09: TBOX arrival is pending. +22/09: arrival TBOX HW is still pending. +18/09: no feedback so far. +16/09: no change since the last comment. +11/09: dealer's feedback - TBOX HW arrival is expected soon. Once received, all the required data will be provided to modify TSP with a new TBOX HW. +02/09: repeat request on TBOX arrival date is sent to dealer. Wait for feedback. +26/08:no update ,turn to waiting for data +19/08: pls QS side update the progress +14/08: wait for TBOX HW arrival. +12/08: same status +07/08: expected arrival date end of W33 +04/08: request sent to dealer to find out an expected arrival. +31/07: same status +28/07: still wait for delivery of TBOX HW that is expected end of July/begining August. +22/07: still wait for delivery of TBOX HW. +14/07: there was no delivery so far. +10/07: TBOX HW arrival is still pending. +01.07: wait for delivery of TBOX HW in August +24/06: delivery of new TBOX is expected begining August. +16/06: request for changing TBOX HW has been sent. Waif for after-sales confirmation. +0605:need customer go to the dealer,first,catch the tbox log,secondly,replace a new tbox,that's all. +03/06: please precise when a decision has been made that tbox HW needs to be replaced. +29.05: still wait for feedback +19/05: customer is asked to start the engine with Start/Stop button to let vehicle go out of hibernation mode. Wait for feedback. +13/05:The local feedback issue has reappeared. ANP2 was opened on May 1st, but there was no network connection. After the meeting, attempts were made to capture logs, and the vehicle has entered deep sleep. It is recommended to follow the deep sleep process for handling. +13/05: status checked out in MNO: apn2 has been switched again on May, 1st but there is still no network available - see picture attached. +15/04: Suggest temporarily closing this issue +10/04: still waiting for invititaion of customer to dealer +07/04: still waiting for invititaion of customer to dealer +April 01 :Still Waiting. +March 25:Still Waiting. +March 20:waiting the customer to go to the dealer for logs. +March 18: waiting the customer to go to the dealer for logs. +March 11:need Tbox logs to analysis +March 4: need Tbox logs to analysis +Feb 27:Query to the user remote control failure reasons are because of the prompt timeout, it is recommended that the user will move the vehicle to a good signal place and try again, if it does not work, it is recommended to enter the station to check. +2/24 MNO:APN1 is configured properly without any additional restrictions(APN1配置正常,无任何额外限制) +21/02: wrong vehicle status is displayed in the app: engine shown as started as well as the doors are unlocked. However the car is locked. Commande name - lock the doors on 21/02 at 16:34. Pictures attached. Tbox log in process of upload.",Low,close,TBOX,Vsevolod,16/10/2025,CHERY TIGGO 9 (T28)),LVTDD24B7RG116179,,,"MNO_status.JPG,Picture_2.JPG,Picture-1.JPG",Vsevolod,237.0,2025-02-21,2025-10-16 +TR468,Telegram bot,24/02/2025,Remote control ,"Remote control doesn't work neither apps in the IHU. However, apps work well using the network shared from smartphone. Status TSP: last tbox login on 2024-12-22 22:01:04, high frequency data +2024-12-22 22:18:56. MNO status: no apn1 available since Dec, 22th 2024.","0319:等待OTA +0224:请抓取下tbox日志以及主机日志","24/04: upgrade successfully completed on April, 5th. No issues since upgrade => TR closed. +03/04: there is still no connection. Wait for downloading. +Mar 19: Waiting for OTA. +Feb 24:Please grab the tbox logs as well as the DMC logs.@Vsevolod",Low,close,local O&M,Vsevolod,24/04/2025,EXEED RX(T22),LVTDD24B5RG020678,,TGR0000602,"file_993.jpg,file_872.jpg,file_870.jpg,file_843.jpg,file_864.jpg,file_844.jpg,file_871.jpg",Vsevolod,59.0,2025-02-24,2025-04-24 +TR469,Mail,24/02/2025,Remote control ,Remote control doesn't work + wrong status of car.,0319:等待OTA0225:等待具体日志及信息,"Apr 17: solved after OTA +10/04: OTA upgrade success on 10/04. Wait for 2 weeks to check remote control. if no issue to be closed. +03/04: download complete. Wait for upgrade. +Feb 24: Collecting operation time /screenshots",Low,close,local O&M,Evgeniy,17/04/2025,EXEED RX(T22),LVTDD24B5RG031311,,,,Evgeniy,52.0,2025-02-24,2025-04-17 +TR470,Mail,24/02/2025,Remote control ,Remote control doesn't work + wrong status of car.,0319:等待OTA0225:等待具体日志及信息,"Apr 17: solved after OTA +03/04: download complete. Wait for upgrade. +Feb 24: Collecting operation time /screenshots",Low,close,local O&M,Evgeniy,17/04/2025,EXEED RX(T22),LVTDD24B6RG021211,,,image.png,Evgeniy,52.0,2025-02-24,2025-04-17 +TR471,Mail,24/02/2025,Remote control ,"Remote control doesn't work. Status in the TSP: last tbox login 2025-02-07 04:50:03; high frequency data 2025-01-06 16:37:14. MNO: no apn1 availabale since Jan, 10th.","0515:协商一致,今日拟关闭问题 +0424:用户与今日升级成功,一周后无反馈可关闭问题 +0403:无消息反馈,等待用户下载后OTA +0319:等待OTA0225:该车可以进行USB刷写软件,新增车辆刷写软件可以在此表格中新增,说清楚时间即可","15/05: Consensus reached through negotiation to close the issue today +24/04: upgrade completed successfully on April, 24th. The issue is going to be closed if no there are no any claimes after one week usage. +03/04: there is still no connection. Wait for downloading. +25/02: command name - unlock the doors on 25/02 at 7:52. Picture attached. +Feb 25:The car can be USB brush writing software, new vehicle brush writing software can be added in this form, say clear time can be +links:https://l5j8axkr3y6.sg.larksuite.com/share/base/view/shrlgvxQ7p0rMcpHSQpnTiQU2Dh +24/02: plese check and confirm if TBOX SW of the car might be upgraded with USB at dealer @喻起航",Low,close,local O&M,Vsevolod,15/05/2025,EXEED RX(T22),LVTDD24B1RG019253,,,Picture_1.jpg,Vsevolod,80.0,2025-02-24,2025-05-15 diff --git a/cli.py b/cli.py index d0636f4..d536367 100644 --- a/cli.py +++ b/cli.py @@ -1,27 +1,30 @@ """ 交互式 CLI —— 四层架构自适应分析 -用法: python3 cli.py [数据库路径] +用法: python cli.py [数据库路径] """ import os import sys sys.path.insert(0, os.path.dirname(__file__)) -from config import DB_PATH, LLM_CONFIG, MAX_EXPLORATION_ROUNDS +from core.config import DB_PATH, LLM_CONFIG, MAX_EXPLORATION_ROUNDS, PLAYBOOK_DIR from agent import DataAnalysisAgent def print_help(): print(""" 可用命令: - <问题> 分析一个问题 + <问题> 分析一个问题 rounds= <问题> 设置探索轮数 - schema 查看数据库 Schema - history 查看分析历史 - audit 查看 SQL 审计日志 - clear 清空分析历史 - help 显示帮助 - quit / q 退出 + report [主题] 整合所有分析,生成综合报告 + schema 查看数据库 Schema + playbooks 查看已加载的预设剧本 + regen 重新生成预设剧本 + history 查看分析历史 + audit 查看 SQL 审计日志 + clear 清空分析历史 + help 显示帮助 + quit / q 退出 """) @@ -30,17 +33,12 @@ def main(): if not os.path.exists(db_path): print(f"❌ 数据库不存在: {db_path}") - print(f" 请先运行 python3 demo.py 创建示例数据库,或指定已有数据库路径") sys.exit(1) if not LLM_CONFIG["api_key"]: - print("⚠️ 未配置 LLM_API_KEY,请先设置环境变量:") - print(" export LLM_API_KEY=sk-xxx") - print(" export LLM_BASE_URL=https://api.openai.com/v1 # 或 Ollama 等") - print(" export LLM_MODEL=gpt-4o") + print("⚠️ 未配置 LLM_API_KEY") sys.exit(1) - # 初始化 Agent agent = DataAnalysisAgent(db_path) print("=" * 60) @@ -49,6 +47,7 @@ def main(): print(f"\n🔗 LLM: {LLM_CONFIG['model']} @ {LLM_CONFIG['base_url']}") print(f"🔄 最大探索轮数: {MAX_EXPLORATION_ROUNDS}") print(f"💾 数据库: {db_path}") + print(f"📋 预设剧本: {len(agent.playbook_mgr.playbooks)} 个") print(f"\n💬 输入分析问题(help 查看命令)\n") while True: @@ -68,44 +67,69 @@ def main(): break elif cmd == "help": print_help() - continue elif cmd == "schema": print(agent.get_schema()) - continue elif cmd == "history": print(agent.get_history()) - continue elif cmd == "audit": print(agent.get_audit()) - continue elif cmd == "clear": agent.clear_history() print("✅ 历史已清空") - continue - - # 解析可选参数:rounds=3 - max_rounds = MAX_EXPLORATION_ROUNDS - question = user_input - if "rounds=" in question.lower(): - parts = question.split("rounds=") - question = parts[0].strip() + elif cmd.startswith("report"): + topic = user_input[6:].strip() try: - max_rounds = int(parts[1].strip().split()[0]) - except (ValueError, IndexError): - pass + report = agent.full_report(question=topic) + print("\n" + report) + print("\n" + "~" * 60) + except Exception as e: + print(f"\n❌ 报告整合出错: {e}") + import traceback + traceback.print_exc() + elif cmd == "playbooks": + if not agent.playbook_mgr.playbooks: + print("(无预设剧本,输入 regen 让 AI 自动生成)") + else: + for i, pb in enumerate(agent.playbook_mgr.playbooks, 1): + print(f" {i}. 📋 {pb.name} — {pb.description} ({len(pb.preset_queries)} 条预设)") + elif cmd == "regen": + if os.path.isdir(PLAYBOOK_DIR): + for f in os.listdir(PLAYBOOK_DIR): + if f.startswith("auto_") and f.endswith(".json"): + os.remove(os.path.join(PLAYBOOK_DIR, f)) + agent.playbook_mgr.playbooks.clear() + print("🤖 AI 正在重新生成预设剧本...") + generated = agent.playbook_mgr.auto_generate(agent.schema_text, save_dir=PLAYBOOK_DIR) + if generated: + print(f"✅ 生成 {len(generated)} 个剧本:") + for pb in generated: + print(f" 📋 {pb.name} — {pb.description}") + else: + print("⚠️ 生成失败") + else: + # 解析 rounds=N + max_rounds = MAX_EXPLORATION_ROUNDS + question = user_input + if "rounds=" in question.lower(): + parts = question.split("rounds=") + question = parts[0].strip() + try: + max_rounds = int(parts[1].strip().split()[0]) + except (ValueError, IndexError): + pass - try: - report = agent.analyze(question, max_rounds=max_rounds) - print("\n" + report) - print("\n" + "~" * 60) - except Exception as e: - print(f"\n❌ 分析出错: {e}") - import traceback - traceback.print_exc() + try: + report = agent.analyze(question, max_rounds=max_rounds) + print("\n" + report) + print("\n" + "~" * 60) + except Exception as e: + print(f"\n❌ 分析出错: {e}") + import traceback + traceback.print_exc() - # 退出时显示审计 print("\n📋 本次会话审计:") print(agent.get_audit()) + agent.close() if __name__ == "__main__": diff --git a/config.py b/config.py deleted file mode 100644 index f8c60b3..0000000 --- a/config.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -配置文件 -""" -import os - -# LLM 配置(兼容 OpenAI API 格式,包括 Ollama / vLLM / DeepSeek 等) -LLM_CONFIG = { - "api_key": os.getenv("LLM_API_KEY", ""), - "base_url": os.getenv("LLM_BASE_URL", "https://api.openai.com/v1"), - "model": os.getenv("LLM_MODEL", "gpt-4o"), -} - -# 沙箱安全规则 -SANDBOX_RULES = { - "max_result_rows": 1000, # 聚合结果最大行数 - "round_floats": 2, # 浮点数保留位数 - "suppress_small_n": 5, # 分组样本 < n 时模糊处理 - "banned_keywords": [ # 禁止的 SQL 关键字 - "SELECT *", - "INSERT", - "UPDATE", - "DELETE", - "DROP", - "ALTER", - "CREATE", - "ATTACH", - "PRAGMA", - ], - "require_aggregation": True, # 是否要求使用聚合函数 -} - -# 数据库路径 -DB_PATH = os.getenv("DB_PATH", os.path.join(os.path.dirname(__file__), "demo.db")) - -# 分析控制 -MAX_EXPLORATION_ROUNDS = int(os.getenv("MAX_ROUNDS", "6")) # 最大探索轮数 diff --git a/context.py b/context.py deleted file mode 100644 index faa22ef..0000000 --- a/context.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Layer 4: 上下文管理器 -管理多轮对话的分析上下文,让后续问题可以引用之前的发现。 -""" -import json -import time -from dataclasses import dataclass, field -from typing import Any, Optional - -from explorer import ExplorationStep -from insights import Insight - - -@dataclass -class AnalysisSession: - """一次分析的完整记录""" - question: str - plan: dict - steps: list[ExplorationStep] - insights: list[Insight] - report: str - timestamp: float = field(default_factory=time.time) - - def summary(self) -> str: - """生成本次分析的摘要(供后续对话引用)""" - parts = [f"**问题**: {self.question}"] - - if self.plan: - parts.append(f"**分析类型**: {self.plan.get('analysis_type', 'unknown')}") - parts.append(f"**关注维度**: {', '.join(self.plan.get('dimensions', []))}") - - # 核心发现(从成功步骤中提取) - key_findings = [] - for step in self.steps: - if step.success and step.rows: - # 提取最突出的值 - top_row = step.rows[0] if step.rows else {} - finding = f"{step.purpose}: " - finding += ", ".join( - f"{k}={v}" for k, v in top_row.items() if k.lower() not in ("id",) - ) - key_findings.append(finding) - - if key_findings: - parts.append("**核心发现**:") - for f in key_findings[:5]: - parts.append(f" - {f}") - - # 洞察 - if self.insights: - parts.append("**主动洞察**:") - for insight in self.insights[:3]: - parts.append(f" - {insight}") - - return "\n".join(parts) - - def to_reference_text(self) -> str: - """生成供 LLM 使用的上下文文本""" - return ( - f"## 之前的分析\n\n" - f"### 问题\n{self.question}\n\n" - f"### 摘要\n{self.summary()}\n\n" - f"### 详细发现\n" - + "\n".join( - f"- {step.purpose}: {step.row_count} 行结果" - for step in self.steps if step.success - ) - ) - - -class ContextManager: - """上下文管理器:管理多轮对话的分析历史""" - - def __init__(self, max_history: int = 10): - self.sessions: list[AnalysisSession] = [] - self.max_history = max_history - - def add_session( - self, - question: str, - plan: dict, - steps: list[ExplorationStep], - insights: list[Insight], - report: str, - ) -> AnalysisSession: - """记录一次分析""" - session = AnalysisSession( - question=question, - plan=plan, - steps=steps, - insights=insights, - report=report, - ) - self.sessions.append(session) - - # 保持历史大小 - if len(self.sessions) > self.max_history: - self.sessions = self.sessions[-self.max_history:] - - return session - - def get_context_for(self, new_question: str) -> Optional[str]: - """ - 根据新问题,从历史中找到相关上下文。 - 简单实现:取最近的分析会话。 - """ - if not self.sessions: - return None - - # 取最近 2 轮分析的摘要 - recent = self.sessions[-2:] - parts = [] - for session in recent: - parts.append(session.to_reference_text()) - - return "\n\n---\n\n".join(parts) - - def get_history_summary(self) -> str: - """获取所有历史的摘要""" - if not self.sessions: - return "(无历史分析)" - - lines = [f"共 {len(self.sessions)} 次分析:"] - for i, session in enumerate(self.sessions, 1): - ts = time.strftime("%H:%M", time.localtime(session.timestamp)) - lines.append(f" {i}. [{ts}] {session.question}") - if session.insights: - for insight in session.insights[:2]: - lines.append(f" {insight}") - return "\n".join(lines) - - def clear(self): - """清空历史""" - self.sessions.clear() diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..c3219b7 --- /dev/null +++ b/core/__init__.py @@ -0,0 +1,22 @@ +""" +iov_ana 核心包 + +项目结构: + core/ + config.py - 配置 + utils.py - 公共工具(JSON 提取、LLM 客户端) + schema.py - Schema 提取 + sandbox.py - SQL 沙箱执行器 + layers/ + planner.py - Layer 1: 意图规划 + playbook.py - Layer 1.5: 预设剧本 + explorer.py - Layer 2: 自适应探索 + insights.py - Layer 3: 异常洞察 + context.py - Layer 4: 上下文记忆 + output/ + reporter.py - 单次报告生成 + consolidator.py - 多次报告整合 + chart.py - 图表生成 + agent.py - Agent 编排层 + cli.py - 交互式 CLI +""" diff --git a/core/config.py b/core/config.py new file mode 100644 index 0000000..aa979d0 --- /dev/null +++ b/core/config.py @@ -0,0 +1,38 @@ +""" +配置文件 +""" +import os + +# LLM 配置(兼容 OpenAI API 格式,包括 Ollama / vLLM / DeepSeek 等) +LLM_CONFIG = { + "api_key": os.getenv("LLM_API_KEY", "sk-c44i1hy64xgzwox6x08o4zug93frq6rgn84oqugf2pje1tg4"), + "base_url": os.getenv("LLM_BASE_URL", "https://api.xiaomimimo.com/v1"), + "model": os.getenv("LLM_MODEL", "mimo-v2-flash"), +} + +# 沙箱安全规则 +SANDBOX_RULES = { + "max_result_rows": 1000, + "round_floats": 2, + "suppress_small_n": 5, + "banned_keywords": [ + "SELECT *", "INSERT", "UPDATE", "DELETE", + "DROP", "ALTER", "CREATE", "ATTACH", "PRAGMA", + ], + "require_aggregation": True, +} + +# 项目根目录 +PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__)) + +# 数据库路径 +DB_PATH = os.getenv("DB_PATH", os.path.join(PROJECT_ROOT, "demo.db")) + +# Playbook 目录 +PLAYBOOK_DIR = os.getenv("PLAYBOOK_DIR", os.path.join(PROJECT_ROOT, "playbooks")) + +# 图表输出目录 +CHARTS_DIR = os.getenv("CHARTS_DIR", os.path.join(PROJECT_ROOT, "charts")) + +# 分析控制 +MAX_EXPLORATION_ROUNDS = int(os.getenv("MAX_ROUNDS", "6")) diff --git a/sandbox_executor.py b/core/sandbox.py similarity index 52% rename from sandbox_executor.py rename to core/sandbox.py index c1094d6..392cc4f 100644 --- a/sandbox_executor.py +++ b/core/sandbox.py @@ -4,135 +4,95 @@ import sqlite3 import re from typing import Any -from config import SANDBOX_RULES, DB_PATH - - +from core.config import SANDBOX_RULES class SandboxError(Exception): """沙箱安全违规""" pass - - class SandboxExecutor: - def __init__(self, db_path: str = DB_PATH): + def __init__(self, db_path: str): self.db_path = db_path self.rules = SANDBOX_RULES self.execution_log: list[dict] = [] + # 持久连接,避免每次查询都开关 + self._conn = sqlite3.connect(db_path) + self._conn.row_factory = sqlite3.Row def execute(self, sql: str) -> dict[str, Any]: - """ - 执行 SQL,返回脱敏后的聚合结果。 - 如果违反安全规则,抛出 SandboxError。 - """ - # 验证 SQL 安全性 + """执行 SQL,返回脱敏后的聚合结果""" self._validate(sql) - conn = sqlite3.connect(self.db_path) - conn.row_factory = sqlite3.Row - cur = conn.cursor() - + cur = self._conn.cursor() try: cur.execute(sql) rows = cur.fetchall() - - # 转为字典列表 columns = [desc[0] for desc in cur.description] if cur.description else [] results = [dict(row) for row in rows] - - # 脱敏处理 sanitized = self._sanitize(results, columns) - # 记录执行日志 self.execution_log.append({ - "sql": sql, - "rows_returned": len(results), - "columns": columns, + "sql": sql, "rows_returned": len(results), "columns": columns, }) return { - "success": True, - "columns": columns, - "rows": sanitized, - "row_count": len(sanitized), - "sql": sql, + "success": True, "columns": columns, + "rows": sanitized, "row_count": len(sanitized), "sql": sql, } - except sqlite3.Error as e: - return { - "success": False, - "error": str(e), - "sql": sql, - } - finally: - conn.close() + return {"success": False, "error": str(e), "sql": sql} + + def close(self): + """关闭连接""" + if self._conn: + self._conn.close() + self._conn = None def _validate(self, sql: str): """SQL 安全验证""" sql_upper = sql.upper().strip() - # 1. 检查禁止的关键字 for banned in self.rules["banned_keywords"]: if banned.upper() in sql_upper: raise SandboxError(f"禁止的 SQL 关键字: {banned}") - # 2. 只允许 SELECT(不能有多语句) statements = [s.strip() for s in sql.split(";") if s.strip()] if len(statements) > 1: raise SandboxError("禁止多语句执行") if not sql_upper.startswith("SELECT"): raise SandboxError("只允许 SELECT 查询") - # 3. 检查是否使用了聚合函数或 GROUP BY(可选要求) if self.rules["require_aggregation"]: agg_keywords = ["COUNT", "SUM", "AVG", "MIN", "MAX", "GROUP BY", "DISTINCT", "HAVING", "ROUND", "CAST"] has_agg = any(kw in sql_upper for kw in agg_keywords) - has_limit = "LIMIT" in sql_upper + if not has_agg and "LIMIT" not in sql_upper: + raise SandboxError("要求使用聚合函数 (COUNT/SUM/AVG/MIN/MAX/GROUP BY) 或 LIMIT") - if not has_agg and not has_limit: - raise SandboxError( - "要求使用聚合函数 (COUNT/SUM/AVG/MIN/MAX/GROUP BY) 或 LIMIT" - ) - - # 4. LIMIT 检查 limit_match = re.search(r'LIMIT\s+(\d+)', sql_upper) - if limit_match: - limit_val = int(limit_match.group(1)) - if limit_val > self.rules["max_result_rows"]: - raise SandboxError( - f"LIMIT {limit_val} 超过最大允许值 {self.rules['max_result_rows']}" - ) + if limit_match and int(limit_match.group(1)) > self.rules["max_result_rows"]: + raise SandboxError(f"LIMIT 超过最大允许值 {self.rules['max_result_rows']}") def _sanitize(self, rows: list[dict], columns: list[str]) -> list[dict]: - """对结果进行脱敏处理""" - if not rows: - return rows - - # 1. 限制行数 + """脱敏处理""" rows = rows[:self.rules["max_result_rows"]] + suppress_n = self.rules["suppress_small_n"] + round_digits = self.rules["round_floats"] - # 2. 浮点数四舍五入 for row in rows: for col in columns: val = row.get(col) - if isinstance(val, float): - row[col] = round(val, self.rules["round_floats"]) - - # 3. 小样本抑制(k-anonymity) - # 如果某个分组的 count 小于阈值,标记为 " str: - """获取执行摘要""" if not self.execution_log: return "尚未执行任何查询" - lines = [f"共执行 {len(self.execution_log)} 条查询:"] for i, log in enumerate(self.execution_log, 1): lines.append(f" {i}. {log['sql'][:80]}... → {log['rows_returned']} 行结果") diff --git a/schema_extractor.py b/core/schema.py similarity index 74% rename from schema_extractor.py rename to core/schema.py index 90b6492..3ec12cb 100644 --- a/schema_extractor.py +++ b/core/schema.py @@ -6,25 +6,17 @@ from typing import Any def extract_schema(db_path: str) -> dict[str, Any]: - """ - 从数据库提取 Schema,只返回结构信息: - - 表名、列名、类型 - - 主键、外键 - - 行数 - - 枚举列的去重值(不含原始数据) - """ + """从数据库提取 Schema,只返回结构信息""" conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row cur = conn.cursor() - # 获取所有表 cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'") tables = [row["name"] for row in cur.fetchall()] schema = {"tables": []} for table in tables: - # 列信息 cur.execute(f"PRAGMA table_info('{table}')") columns = [] for col in cur.fetchall(): @@ -35,21 +27,15 @@ def extract_schema(db_path: str) -> dict[str, Any]: "is_primary_key": col["pk"] == 1, }) - # 外键 cur.execute(f"PRAGMA foreign_key_list('{table}')") - fks = [] - for fk in cur.fetchall(): - fks.append({ - "column": fk["from"], - "references_table": fk["table"], - "references_column": fk["to"], - }) + fks = [ + {"column": fk["from"], "references_table": fk["table"], "references_column": fk["to"]} + for fk in cur.fetchall() + ] - # 行数 cur.execute(f"SELECT COUNT(*) AS cnt FROM '{table}'") row_count = cur.fetchone()["cnt"] - # 对 VARCHAR / TEXT 类型列,提取去重枚举值(最多 20 个) data_profile = {} for col in columns: col_name = col["name"] @@ -59,11 +45,7 @@ def extract_schema(db_path: str) -> dict[str, Any]: cur.execute(f'SELECT DISTINCT "{col_name}" FROM "{table}" WHERE "{col_name}" IS NOT NULL LIMIT 20') vals = [row[0] for row in cur.fetchall()] if len(vals) <= 20: - data_profile[col_name] = { - "type": "enum", - "distinct_count": len(vals), - "values": vals, - } + data_profile[col_name] = {"type": "enum", "distinct_count": len(vals), "values": vals} elif any(t in col_type for t in ("INT", "REAL", "FLOAT", "DOUBLE", "DECIMAL", "NUMERIC")): cur.execute(f''' SELECT MIN("{col_name}") AS min_val, MAX("{col_name}") AS max_val, @@ -74,18 +56,13 @@ def extract_schema(db_path: str) -> dict[str, Any]: if row and row["min_val"] is not None: data_profile[col_name] = { "type": "numeric", - "min": round(row["min_val"], 2), - "max": round(row["max_val"], 2), - "avg": round(row["avg_val"], 2), - "distinct_count": row["distinct_count"], + "min": round(row["min_val"], 2), "max": round(row["max_val"], 2), + "avg": round(row["avg_val"], 2), "distinct_count": row["distinct_count"], } schema["tables"].append({ - "name": table, - "columns": columns, - "foreign_keys": fks, - "row_count": row_count, - "data_profile": data_profile, + "name": table, "columns": columns, "foreign_keys": fks, + "row_count": row_count, "data_profile": data_profile, }) conn.close() @@ -95,7 +72,6 @@ def extract_schema(db_path: str) -> dict[str, Any]: def schema_to_text(schema: dict) -> str: """将 Schema 转为可读文本,供 LLM 理解""" lines = ["=== 数据库 Schema ===\n"] - for table in schema["tables"]: lines.append(f"📋 表: {table['name']} (共 {table['row_count']} 行)") lines.append(" 列:") @@ -103,12 +79,10 @@ def schema_to_text(schema: dict) -> str: pk = " [PK]" if col["is_primary_key"] else "" null = " NULL" if col["nullable"] else " NOT NULL" lines.append(f' - {col["name"]}: {col["type"]}{pk}{null}') - if table["foreign_keys"]: lines.append(" 外键:") for fk in table["foreign_keys"]: lines.append(f' - {fk["column"]} → {fk["references_table"]}.{fk["references_column"]}') - if table["data_profile"]: lines.append(" 数据画像:") for col_name, profile in table["data_profile"].items(): @@ -120,7 +94,5 @@ def schema_to_text(schema: dict) -> str: f' - {col_name}: 范围[{profile["min"]} ~ {profile["max"]}], ' f'均值{profile["avg"]}, {profile["distinct_count"]}个不同值' ) - lines.append("") - return "\n".join(lines) diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 0000000..c4a51f7 --- /dev/null +++ b/core/utils.py @@ -0,0 +1,93 @@ +""" +公共工具 —— JSON 提取、LLM 客户端单例 +""" +import json +import re +from typing import Any + +import openai + +# ── LLM 客户端单例 ────────────────────────────────── + +_llm_client: openai.OpenAI | None = None +_llm_model: str = "" + + +def get_llm_client(config: dict) -> tuple[openai.OpenAI, str]: + """获取 LLM 客户端(单例),避免每个组件各建一个""" + global _llm_client, _llm_model + if _llm_client is None: + _llm_client = openai.OpenAI( + api_key=config["api_key"], + base_url=config["base_url"], + ) + _llm_model = config["model"] + return _llm_client, _llm_model + + +# ── JSON 提取 ──────────────────────────────────────── + +def extract_json_object(text: str) -> dict: + """从 LLM 输出提取 JSON 对象""" + text = _clean_json_text(text) + + try: + return json.loads(text) + except json.JSONDecodeError: + pass + + for pattern in [r'```json\s*\n(.*?)\n```', r'```\s*\n(.*?)\n```']: + match = re.search(pattern, text, re.DOTALL) + if match: + try: + return json.loads(_clean_json_text(match.group(1))) + except json.JSONDecodeError: + continue + + match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', text, re.DOTALL) + if match: + try: + return json.loads(_clean_json_text(match.group())) + except json.JSONDecodeError: + pass + + return {} + + +def extract_json_array(text: str) -> list[dict]: + """从 LLM 输出提取 JSON 数组(处理尾逗号、注释等)""" + text = _clean_json_text(text) + + try: + result = json.loads(text) + if isinstance(result, list): + return result + except json.JSONDecodeError: + pass + + for pattern in [r'```json\s*\n(.*?)\n```', r'```\s*\n(.*?)\n```']: + match = re.search(pattern, text, re.DOTALL) + if match: + try: + result = json.loads(_clean_json_text(match.group(1))) + if isinstance(result, list): + return result + except json.JSONDecodeError: + continue + + match = re.search(r'\[.*\]', text, re.DOTALL) + if match: + try: + return json.loads(_clean_json_text(match.group())) + except json.JSONDecodeError: + pass + + return [] + + +def _clean_json_text(s: str) -> str: + """清理 LLM 常见的非标准 JSON""" + s = re.sub(r'//.*?\n', '\n', s) + s = re.sub(r'/\*.*?\*/', '', s, flags=re.DOTALL) + s = re.sub(r',\s*([}\]])', r'\1', s) + return s.strip() diff --git a/demo.py b/demo.py index beb2c53..d9a5893 100644 --- a/demo.py +++ b/demo.py @@ -9,7 +9,7 @@ from datetime import datetime, timedelta sys.path.insert(0, os.path.dirname(__file__)) -from config import DB_PATH, LLM_CONFIG +from core.config import DB_PATH, LLM_CONFIG from agent import DataAnalysisAgent @@ -129,7 +129,8 @@ def main(): print("\n" + "=" * 60) print(" ⬆️ AI 只看到 Schema:表结构 + 数据画像") - print(" ⬇️ 四层架构分析:规划 → 探索 → 洞察 → 报告") + print(" ⬇️ 四层架构分析:规划 → 预设匹配 → 探索 → 洞察 → 报告") + print(f" 📋 已加载 {len(agent.playbook_mgr.playbooks)} 个预设剧本") print("=" * 60) questions = [ diff --git a/explorer.py b/explorer.py deleted file mode 100644 index 1538655..0000000 --- a/explorer.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -Layer 2: 自适应探索器 -基于分析计划 + 已有发现,动态决定下一步查什么。 -多轮迭代,直到 AI 判断"够了"或达到上限。 -""" -import json -import re -from typing import Any - -import openai -from config import LLM_CONFIG -from sandbox_executor import SandboxExecutor - - -EXPLORER_SYSTEM = """你是一个数据分析执行者。你的上级给了你一个分析计划,你需要通过迭代执行 SQL 来完成分析。 - -## 你的工作方式 -每一轮你看到: -1. 分析计划(上级给的目标) -2. 数据库 Schema(表结构、数据画像) -3. 之前的探索历史(查过什么、得到什么结果) - -你决定下一步: -- 输出一条 SQL 继续探索 -- 或者输出 done 表示分析足够 - -## 输出格式(严格 JSON) -```json -{ - "action": "query", - "reasoning": "为什么要做这个查询", - "sql": "SELECT ...", - "purpose": "这个查询的目的" -} -``` - -或: -```json -{ - "action": "done", - "reasoning": "为什么分析已经足够" -} -``` - -## SQL 规则 -- 只用 SELECT,必须有聚合函数或 GROUP BY -- 禁止 SELECT * -- 用 ROUND 控制精度 -- 合理使用 LIMIT(分组结果 15 行以内,时间序列 60 行以内) - -## 探索策略 -1. 第一轮:验证核心假设(计划中最关键的查询) -2. 后续轮:基于已有结果追问 - - 发现离群值 → 追问为什么 - - 发现异常比例 → 追问细分维度 - - 结果平淡 → 换个角度试试 -3. 不要重复查已经确认的事 -4. 每轮要有新发现,否则就该结束""" - - -EXPLORER_CONTINUE = """查询结果: - -{result_text} - -请基于这个结果决定下一步。如果发现异常或值得深挖的点,继续查询。如果分析足够,输出 done。""" - - -class ExplorationStep: - """单步探索结果""" - def __init__(self, round_num: int, decision: dict, result: dict): - self.round_num = round_num - self.reasoning = decision.get("reasoning", "") - self.purpose = decision.get("purpose", "") - self.sql = decision.get("sql", "") - self.action = decision.get("action", "query") - self.success = result.get("success", False) - self.error = result.get("error") - self.columns = result.get("columns", []) - self.rows = result.get("rows", []) - self.row_count = result.get("row_count", 0) - - def to_dict(self) -> dict: - d = { - "round": self.round_num, - "action": self.action, - "reasoning": self.reasoning, - "purpose": self.purpose, - "sql": self.sql, - "success": self.success, - } - if self.success: - d["result"] = { - "columns": self.columns, - "rows": self.rows, - "row_count": self.row_count, - } - else: - d["result"] = {"error": self.error} - return d - - -class Explorer: - """自适应探索器:多轮迭代执行 SQL""" - - def __init__(self, executor: SandboxExecutor): - self.executor = executor - self.client = openai.OpenAI( - api_key=LLM_CONFIG["api_key"], - base_url=LLM_CONFIG["base_url"], - ) - self.model = LLM_CONFIG["model"] - - def explore( - self, - plan: dict, - schema_text: str, - max_rounds: int = 6, - ) -> list[ExplorationStep]: - """ - 执行探索循环 - - Args: - plan: Planner 生成的分析计划 - schema_text: Schema 文本描述 - max_rounds: 最大探索轮数 - - Returns: - 探步列表 - """ - steps: list[ExplorationStep] = [] - - # 构建初始消息 - messages = [ - {"role": "system", "content": EXPLORER_SYSTEM}, - { - "role": "user", - "content": ( - f"## 分析计划\n```json\n{json.dumps(plan, ensure_ascii=False, indent=2)}\n```\n\n" - f"## 数据库 Schema\n{schema_text}\n\n" - f"请开始第一轮探索。根据计划,先执行最关键的查询。" - ), - }, - ] - - for round_num in range(1, max_rounds + 1): - print(f"\n 🔄 探索第 {round_num}/{max_rounds} 轮") - - # LLM 决策 - decision = self._llm_decide(messages) - action = decision.get("action", "query") - reasoning = decision.get("reasoning", "") - - print(f" 💭 {reasoning[:80]}{'...' if len(reasoning) > 80 else ''}") - - if action == "done": - print(f" ✅ 探索完成") - steps.append(ExplorationStep(round_num, decision, {"success": True})) - break - - # 执行 SQL - sql = decision.get("sql", "") - purpose = decision.get("purpose", "") - - if not sql: - print(f" ⚠️ 未生成 SQL,跳过") - continue - - print(f" 📝 {purpose}") - result = self.executor.execute(sql) - - if result["success"]: - print(f" ✅ {result['row_count']} 行") - else: - print(f" ❌ {result['error']}") - - step = ExplorationStep(round_num, decision, result) - steps.append(step) - - # 更新对话历史 - messages.append({ - "role": "assistant", - "content": json.dumps(decision, ensure_ascii=False), - }) - - result_text = self._format_result(result) - messages.append({ - "role": "user", - "content": EXPLORER_CONTINUE.format(result_text=result_text), - }) - - return steps - - def _llm_decide(self, messages: list[dict]) -> dict: - """LLM 决策""" - response = self.client.chat.completions.create( - model=self.model, - messages=messages, - temperature=0.2, - max_tokens=1024, - ) - content = response.choices[0].message.content.strip() - return self._extract_json(content) - - def _format_result(self, result: dict) -> str: - """格式化查询结果""" - if not result.get("success"): - return f"❌ 执行失败: {result.get('error', '未知错误')}" - - rows = result["rows"][:20] - return ( - f"✅ 查询成功,返回 {result['row_count']} 行\n" - f"列: {result['columns']}\n" - f"数据:\n{json.dumps(rows, ensure_ascii=False, indent=2)}" - ) - - def _extract_json(self, text: str) -> dict: - """从 LLM 输出提取 JSON""" - try: - return json.loads(text) - except json.JSONDecodeError: - pass - - for pattern in [r'```json\s*\n(.*?)\n```', r'```\s*\n(.*?)\n```']: - match = re.search(pattern, text, re.DOTALL) - if match: - try: - return json.loads(match.group(1)) - except json.JSONDecodeError: - continue - - match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', text, re.DOTALL) - if match: - try: - return json.loads(match.group()) - except json.JSONDecodeError: - pass - - return {"action": "done", "reasoning": f"无法解析输出: {text[:100]}"} diff --git a/import_csv.py b/import_csv.py new file mode 100644 index 0000000..950c2b9 --- /dev/null +++ b/import_csv.py @@ -0,0 +1,103 @@ +""" +将工单 CSV 数据导入 SQLite 数据库 +""" +import csv +import sqlite3 +import os +import sys + +def import_csv(csv_path: str, db_path: str): + """将工单 CSV 导入 SQLite""" + if os.path.exists(db_path): + os.remove(db_path) + print(f"🗑️ 已删除旧数据库: {db_path}") + + conn = sqlite3.connect(db_path) + cur = conn.cursor() + + # 创建工单表 + cur.execute(""" + CREATE TABLE tickets ( + 工单号 TEXT PRIMARY KEY, + 来源 TEXT, + 创建日期 TEXT, + 问题类型 TEXT, + 问题描述 TEXT, + 处理过程 TEXT, + 跟踪记录 TEXT, + 严重程度 TEXT, + 工单状态 TEXT, + 模块 TEXT, + 责任人 TEXT, + 关闭日期 TEXT, + 车型 TEXT, + VIN TEXT, + SIM TEXT, + Notes TEXT, + Attachment TEXT, + 创建人 TEXT, + 关闭时长_天 REAL, + 创建日期_解析 TEXT, + 关闭日期_解析 TEXT + ) + """) + + with open(csv_path, "r", encoding="utf-8-sig") as f: + reader = csv.DictReader(f) + rows = list(reader) + + for row in rows: + cur.execute(""" + INSERT INTO tickets VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ) + """, ( + row.get("工单号", ""), + row.get("来源", ""), + row.get("创建日期", ""), + row.get("问题类型", ""), + row.get("问题描述", ""), + row.get("处理过程", ""), + row.get("跟踪记录", ""), + row.get("严重程度", ""), + row.get("工单状态", ""), + row.get("模块", ""), + row.get("责任人", ""), + row.get("关闭日期", ""), + row.get("车型", ""), + row.get("VIN", ""), + row.get("SIM", ""), + row.get("Notes", ""), + row.get("Attachment", ""), + row.get("创建人", ""), + float(row["关闭时长(天)"]) if row.get("关闭时长(天)") else None, + row.get("创建日期_解析", ""), + row.get("关闭日期_解析", ""), + )) + + conn.commit() + print(f"✅ 导入 {len(rows)} 条工单到 {db_path}") + + # 验证 + cur.execute("SELECT COUNT(*) FROM tickets") + print(f" 数据库中共 {cur.fetchone()[0]} 条记录") + + cur.execute("SELECT DISTINCT 问题类型 FROM tickets") + types = [r[0] for r in cur.fetchall()] + print(f" 问题类型: {', '.join(types)}") + + cur.execute("SELECT DISTINCT 工单状态 FROM tickets") + statuses = [r[0] for r in cur.fetchall()] + print(f" 工单状态: {', '.join(statuses)}") + + cur.execute("SELECT DISTINCT 车型 FROM tickets") + models = [r[0] for r in cur.fetchall()] + print(f" 车型: {', '.join(models)}") + + conn.close() + + +if __name__ == "__main__": + csv_path = sys.argv[1] if len(sys.argv) > 1 else "cleaned_data.csv" + db_path = os.path.join(os.path.dirname(__file__), "demo.db") + import_csv(csv_path, db_path) diff --git a/insights.py b/insights.py deleted file mode 100644 index 39639aa..0000000 --- a/insights.py +++ /dev/null @@ -1,239 +0,0 @@ -""" -Layer 3: 洞察引擎 -对探索结果进行异常检测 + 主动洞察,输出用户没问但值得知道的事。 -""" -import json -import re -import statistics -from typing import Any - -import openai -from config import LLM_CONFIG -from explorer import ExplorationStep - - -INSIGHT_SYSTEM = """你是一个数据洞察专家。你会收到探索过程的所有结果,你需要: - -1. 从结果中发现异常和有趣现象 -2. 对比不同维度,找出差异 -3. 输出用户可能没问但值得知道的洞察 - -## 输出格式(严格 JSON 数组) -```json -[ - { - "type": "outlier" | "trend" | "distribution" | "correlation" | "recommendation", - "severity": "high" | "medium" | "low", - "title": "简短标题", - "detail": "详细描述,包含具体数字", - "evidence": "支撑这个洞察的数据来源" - } -] -``` - -## 洞察类型 -- outlier: 离群值(某个分组异常高/低) -- trend: 趋势发现(增长/下降、季节性) -- distribution: 分布异常(不均衡、集中度过高) -- correlation: 关联发现(两个维度的意外关联) -- recommendation: 行动建议(基于数据的建议) - -## 分析原则 -- 每个洞察必须有具体数字支撑 -- 用对比来说话(A 比 B 高 X%) -- 关注异常,不描述平淡的事实 -- 如果没有异常,返回空数组""" - - -class Insight: - """单条洞察""" - def __init__(self, data: dict): - self.type = data.get("type", "unknown") - self.severity = data.get("severity", "low") - self.title = data.get("title", "") - self.detail = data.get("detail", "") - self.evidence = data.get("evidence", "") - - @property - def emoji(self) -> str: - return { - "outlier": "⚠️", - "trend": "📈", - "distribution": "📊", - "correlation": "🔗", - "recommendation": "💡", - }.get(self.type, "📌") - - @property - def severity_emoji(self) -> str: - return {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(self.severity, "") - - def __str__(self): - return f"{self.emoji} {self.severity_emoji} {self.title}: {self.detail}" - - -class InsightEngine: - """洞察引擎:自动检测异常 + 主动输出""" - - def __init__(self): - self.client = openai.OpenAI( - api_key=LLM_CONFIG["api_key"], - base_url=LLM_CONFIG["base_url"], - ) - self.model = LLM_CONFIG["model"] - - def analyze(self, steps: list[ExplorationStep], question: str) -> list[Insight]: - """ - 对探索结果进行洞察分析 - - Args: - steps: 探索步骤列表 - question: 原始用户问题 - - Returns: - 洞察列表 - """ - if not steps: - return [] - - # 构建探索历史文本 - history = self._build_history(steps) - - response = self.client.chat.completions.create( - model=self.model, - messages=[ - {"role": "system", "content": INSIGHT_SYSTEM}, - { - "role": "user", - "content": ( - f"## 用户原始问题\n{question}\n\n" - f"## 探索历史\n{history}\n\n" - f"请分析以上数据,输出异常和洞察。" - ), - }, - ], - temperature=0.3, - max_tokens=2048, - ) - - content = response.choices[0].message.content.strip() - insights_data = self._extract_json_array(content) - - return [Insight(d) for d in insights_data] - - def format_insights(self, insights: list[Insight]) -> str: - """格式化洞察为可读文本""" - if not insights: - return "" - - # 按严重程度排序 - severity_order = {"high": 0, "medium": 1, "low": 2} - sorted_insights = sorted(insights, key=lambda i: severity_order.get(i.severity, 9)) - - lines = ["## 💡 主动洞察", ""] - lines.append("_以下是你没问但数据告诉我们的事:_\n") - - for insight in sorted_insights: - lines.append(f"**{insight.emoji} {insight.title}** {insight.severity_emoji}") - lines.append(f" {insight.detail}") - lines.append(f" _数据来源: {insight.evidence}_") - lines.append("") - - return "\n".join(lines) - - def _build_history(self, steps: list[ExplorationStep]) -> str: - """构建探索历史文本""" - parts = [] - for step in steps: - if step.action == "done": - parts.append(f"### 结束\n{step.reasoning}") - continue - - if step.success: - parts.append( - f"### 第 {step.round_num} 轮:{step.purpose}\n" - f"思考: {step.reasoning}\n" - f"SQL: `{step.sql}`\n" - f"结果 ({step.row_count} 行):\n" - f"列: {step.columns}\n" - f"数据: {json.dumps(step.rows, ensure_ascii=False)}" - ) - else: - parts.append( - f"### 第 {step.round_num} 轮:{step.purpose}\n" - f"SQL: `{step.sql}`\n" - f"结果: 执行失败 - {step.error}" - ) - - return "\n\n".join(parts) - - def _extract_json_array(self, text: str) -> list[dict]: - """从 LLM 输出提取 JSON 数组""" - try: - result = json.loads(text) - if isinstance(result, list): - return result - except json.JSONDecodeError: - pass - - for pattern in [r'```json\s*\n(.*?)\n```', r'```\s*\n(.*?)\n```']: - match = re.search(pattern, text, re.DOTALL) - if match: - try: - result = json.loads(match.group(1)) - if isinstance(result, list): - return result - except json.JSONDecodeError: - continue - - # 找最外层 [] - match = re.search(r'\[.*\]', text, re.DOTALL) - if match: - try: - return json.loads(match.group()) - except json.JSONDecodeError: - pass - - return [] - - -# ── 基于规则的快速异常检测(无需 LLM)──────────────── - -def quick_detect(steps: list[ExplorationStep]) -> list[str]: - """ - 基于规则的快速异常检测,不调 LLM。 - 检测离群值、不均衡分布等。 - """ - alerts = [] - - for step in steps: - if not step.success or not step.rows: - continue - - for row in step.rows: - for col in step.columns: - val = row.get(col) - if not isinstance(val, (int, float)): - continue - - # 检查 pct 列:某个分组占比异常 - if col.lower() in ("pct", "percent", "percentage", "占比"): - if isinstance(val, (int, float)) and val > 50: - alerts.append( - f"⚠️ {step.purpose} 中某个分组占比 {val}%,超过 50%,集中度过高" - ) - - # 检查 count 列:极值差异 - if col.lower() in ("count", "cnt", "n", "total", "order_count"): - count_vals = [ - r.get(col) for r in step.rows - if isinstance(r.get(col), (int, float)) - ] - if len(count_vals) >= 3 and max(count_vals) > 0: - ratio = max(count_vals) / (sum(count_vals) / len(count_vals)) - if ratio > 3: - alerts.append( - f"⚠️ {step.purpose} 中最大值是均值的 {ratio:.1f} 倍,分布极不均衡" - ) - - return alerts diff --git a/layers/__init__.py b/layers/__init__.py new file mode 100644 index 0000000..a2a6ffe --- /dev/null +++ b/layers/__init__.py @@ -0,0 +1 @@ +"""分析层:Planner → Playbook → Explorer → Insights → Context""" diff --git a/layers/context.py b/layers/context.py new file mode 100644 index 0000000..707b83f --- /dev/null +++ b/layers/context.py @@ -0,0 +1,80 @@ +""" +Layer 4: 上下文管理器 +""" +import time +from dataclasses import dataclass, field +from typing import Optional + +from layers.explorer import ExplorationStep +from layers.insights import Insight + + +@dataclass +class AnalysisSession: + """一次分析的完整记录""" + question: str + plan: dict + steps: list[ExplorationStep] + insights: list[Insight] + report: str + timestamp: float = field(default_factory=time.time) + + def summary(self) -> str: + parts = [f"**问题**: {self.question}"] + if self.plan: + parts.append(f"**分析类型**: {self.plan.get('analysis_type', 'unknown')}") + parts.append(f"**维度**: {', '.join(self.plan.get('dimensions', []))}") + key_findings = [] + for step in self.steps: + if step.success and step.rows: + top_row = step.rows[0] if step.rows else {} + finding = f"{step.purpose}: " + ", ".join(f"{k}={v}" for k, v in top_row.items() if k.lower() != "id") + key_findings.append(finding) + if key_findings: + parts.append("**核心发现**:") + for f in key_findings[:5]: + parts.append(f" - {f}") + if self.insights: + parts.append("**洞察**:") + for i in self.insights[:3]: + parts.append(f" - {i}") + return "\n".join(parts) + + def to_reference_text(self) -> str: + return ( + f"## 之前的分析\n### 问题\n{self.question}\n### 摘要\n{self.summary()}\n### 发现\n" + + "\n".join(f"- {s.purpose}: {s.row_count} 行" for s in self.steps if s.success) + ) + + +class ContextManager: + """上下文管理器""" + + def __init__(self, max_history: int = 10): + self.sessions: list[AnalysisSession] = [] + self.max_history = max_history + + def add_session(self, question: str, plan: dict, steps: list[ExplorationStep], + insights: list[Insight], report: str) -> AnalysisSession: + session = AnalysisSession(question=question, plan=plan, steps=steps, insights=insights, report=report) + self.sessions.append(session) + if len(self.sessions) > self.max_history: + self.sessions = self.sessions[-self.max_history:] + return session + + def get_context_for(self, new_question: str) -> Optional[str]: + if not self.sessions: + return None + return "\n\n---\n\n".join(s.to_reference_text() for s in self.sessions[-2:]) + + def get_history_summary(self) -> str: + if not self.sessions: + return "(无历史分析)" + lines = [f"共 {len(self.sessions)} 次分析:"] + for i, s in enumerate(self.sessions, 1): + ts = time.strftime("%H:%M", time.localtime(s.timestamp)) + lines.append(f" {i}. [{ts}] {s.question}") + return "\n".join(lines) + + def clear(self): + self.sessions.clear() diff --git a/layers/explorer.py b/layers/explorer.py new file mode 100644 index 0000000..6309f95 --- /dev/null +++ b/layers/explorer.py @@ -0,0 +1,224 @@ +""" +Layer 2: 自适应探索器 +""" +import json +from typing import Any +from dataclasses import dataclass, field + +from core.config import LLM_CONFIG +from core.utils import get_llm_client, extract_json_object +from core.sandbox import SandboxExecutor + + +EXPLORER_SYSTEM = """你是一个数据分析执行者。你的上级给了你一个分析计划,你需要通过迭代执行 SQL 来完成分析。 + +## 你的工作方式 +每一轮你看到: +1. 分析计划(上级给的目标) +2. 数据库 Schema(表结构、数据画像) +3. 之前的探索历史(查过什么、得到什么结果) + +你决定下一步: +- 输出一条 SQL 继续探索 +- 或者输出 done 表示分析足够 + +## 输出格式(严格 JSON) +```json +{ + "action": "query", + "reasoning": "为什么要做这个查询", + "sql": "SELECT ...", + "purpose": "这个查询的目的" +} +``` + +或: +```json +{ + "action": "done", + "reasoning": "为什么分析已经足够" +} +``` + +## SQL 规则(严格遵守,否则会被沙箱拒绝) +- 只用 SELECT +- 每条 SQL 必须包含聚合函数(COUNT/SUM/AVG/MIN/MAX)或 GROUP BY 或 LIMIT +- 禁止 SELECT * +- 用 ROUND 控制精度 +- 合理使用 LIMIT(分组结果 15 行以内,时间序列 60 行以内) +- 如果需要查看明细数据,必须加 LIMIT + +## 探索策略 +1. 第一轮:验证核心假设 +2. 后续轮:基于已有结果追问 +3. 不要重复查已经确认的事 +4. 每轮要有新发现,否则就该结束""" + + +@dataclass +class ExplorationStep: + """单步探索结果""" + round_num: int = 0 + reasoning: str = "" + purpose: str = "" + sql: str = "" + action: str = "query" + success: bool = False + error: str | None = None + columns: list[str] = field(default_factory=list) + rows: list[dict] = field(default_factory=list) + row_count: int = 0 + + @classmethod + def from_decision(cls, round_num: int, decision: dict, result: dict) -> "ExplorationStep": + return cls( + round_num=round_num, + reasoning=decision.get("reasoning", ""), + purpose=decision.get("purpose", ""), + sql=decision.get("sql", ""), + action=decision.get("action", "query"), + success=result.get("success", False), + error=result.get("error"), + columns=result.get("columns", []), + rows=result.get("rows", []), + row_count=result.get("row_count", 0), + ) + + def to_dict(self) -> dict: + d = { + "round": self.round_num, "action": self.action, + "reasoning": self.reasoning, "purpose": self.purpose, + "sql": self.sql, "success": self.success, + } + if self.success: + d["result"] = {"columns": self.columns, "rows": self.rows, "row_count": self.row_count} + else: + d["result"] = {"error": self.error} + return d + + +class Explorer: + """自适应探索器""" + + def __init__(self, executor: SandboxExecutor): + self.executor = executor + self.client, self.model = get_llm_client(LLM_CONFIG) + + def explore( + self, plan: dict, schema_text: str, + max_rounds: int = 6, playbook_result: dict | None = None, + ) -> list[ExplorationStep]: + steps: list[ExplorationStep] = [] + + # 阶段 A: 预设查询 + preset_context = "" + if playbook_result and playbook_result.get("preset_queries"): + preset_steps = self._run_preset_queries(playbook_result["preset_queries"]) + steps.extend(preset_steps) + preset_context = self._build_preset_context(preset_steps, playbook_result) + + # 阶段 B: 自适应探索 + preset_used = len([s for s in steps if s.success]) + remaining = max(1, max_rounds - preset_used) + + initial = ( + f"## 分析计划\n```json\n{json.dumps(plan, ensure_ascii=False, indent=2)}\n```\n\n" + f"## 数据库 Schema\n{schema_text}\n\n" + ) + + # 注入历史上下文 + prev_context = plan.pop("_prev_context", None) + if prev_context: + initial += f"## 历史分析参考\n{prev_context}\n\n" + + if preset_context: + initial += ( + f"## 预设分析结果(已执行)\n{preset_context}\n\n" + f"请基于这些已有数据,决定是否需要进一步探索。\n" + f"重点关注:预设结果中的异常、值得深挖的点。\n" + f"如果预设结果已经足够,直接输出 done。" + ) + if playbook_result.get("exploration_hints"): + initial += f"\n\n## 探索提示\n{playbook_result['exploration_hints']}" + else: + initial += "请开始第一轮探索。根据计划,先执行最关键的查询。" + + messages = [ + {"role": "system", "content": EXPLORER_SYSTEM}, + {"role": "user", "content": initial}, + ] + + offset = len(steps) + for round_num in range(offset + 1, offset + remaining + 1): + print(f"\n 🔄 探索第 {round_num}/{max_rounds} 轮") + + decision = self._llm_decide(messages) + reasoning = decision.get("reasoning", "") + print(f" 💭 {reasoning[:80]}{'...' if len(reasoning) > 80 else ''}") + + if decision.get("action") == "done": + print(f" ✅ 探索完成") + steps.append(ExplorationStep.from_decision(round_num, decision, {"success": True})) + break + + sql = decision.get("sql", "") + if not sql: + continue + + print(f" 📝 {decision.get('purpose', '')}") + try: + result = self.executor.execute(sql) + except Exception as e: + result = {"success": False, "error": str(e), "sql": sql} + print(f" {'✅' if result['success'] else '❌'} {result.get('row_count', result.get('error', ''))}") + + steps.append(ExplorationStep.from_decision(round_num, decision, result)) + + messages.append({"role": "assistant", "content": json.dumps(decision, ensure_ascii=False)}) + messages.append({"role": "user", "content": self._format_result(result)}) + + return steps + + def _run_preset_queries(self, preset_queries: list[dict]) -> list[ExplorationStep]: + steps = [] + for i, pq in enumerate(preset_queries, 1): + sql, purpose = pq["sql"], pq.get("purpose", f"预设查询 {i}") + print(f"\n 📌 预设查询 {i}/{len(preset_queries)}: {purpose}") + try: + result = self.executor.execute(sql) + except Exception as e: + result = {"success": False, "error": str(e), "sql": sql} + decision = {"action": "query", "reasoning": f"[预设] {purpose}", "sql": sql, "purpose": purpose} + steps.append(ExplorationStep.from_decision(i, decision, result)) + print(f" {'✅' if result['success'] else '❌'} {result.get('row_count', result.get('error', ''))}") + return steps + + def _build_preset_context(self, steps: list[ExplorationStep], playbook_result: dict) -> str: + parts = [f"Playbook: {playbook_result.get('playbook_name', '未知')}"] + for step in steps: + if step.success: + parts.append( + f"### {step.purpose}\nSQL: `{step.sql}`\n" + f"结果 ({step.row_count} 行): {json.dumps(step.rows[:15], ensure_ascii=False)}" + ) + else: + parts.append(f"### {step.purpose}\nSQL: `{step.sql}`\n执行失败: {step.error}") + return "\n\n".join(parts) + + def _llm_decide(self, messages: list[dict]) -> dict: + response = self.client.chat.completions.create( + model=self.model, messages=messages, temperature=0.2, max_tokens=1024, + ) + content = response.choices[0].message.content.strip() + result = extract_json_object(content) + return result if result else {"action": "done", "reasoning": f"无法解析: {content[:100]}"} + + def _format_result(self, result: dict) -> str: + if not result.get("success"): + return f"❌ 执行失败: {result.get('error', '未知错误')}" + rows = result["rows"][:20] + return ( + f"查询结果:\n\n✅ 返回 {result['row_count']} 行\n" + f"列: {result['columns']}\n数据:\n{json.dumps(rows, ensure_ascii=False, indent=2)}\n\n" + f"请基于这个结果决定下一步。如果发现异常或值得深挖的点,继续查询。如果分析足够,输出 done。" + ) diff --git a/layers/insights.py b/layers/insights.py new file mode 100644 index 0000000..5c858a0 --- /dev/null +++ b/layers/insights.py @@ -0,0 +1,148 @@ +""" +Layer 3: 洞察引擎 +""" +import json +from typing import Any + +from core.config import LLM_CONFIG +from core.utils import get_llm_client, extract_json_array +from layers.explorer import ExplorationStep + + +INSIGHT_SYSTEM = """你是一个数据洞察专家。你会收到探索过程的所有结果,你需要: + +1. 从结果中发现异常和有趣现象 +2. 对比不同维度,找出差异 +3. 输出用户可能没问但值得知道的洞察 + +## 输出格式(严格 JSON 数组) +```json +[ + { + "type": "outlier" | "trend" | "distribution" | "correlation" | "recommendation", + "severity": "high" | "medium" | "low", + "title": "简短标题", + "detail": "详细描述,包含具体数字", + "evidence": "支撑这个洞察的数据来源" + } +] +``` + +## 分析原则 +- 每个洞察必须有具体数字支撑 +- 用对比来说话(A 比 B 高 X%) +- 关注异常,不描述平淡的事实 +- 如果没有异常,返回空数组""" + + +class Insight: + """单条洞察""" + def __init__(self, data: dict): + self.type = data.get("type", "unknown") + self.severity = data.get("severity", "low") + self.title = data.get("title", "") + self.detail = data.get("detail", "") + self.evidence = data.get("evidence", "") + + @property + def emoji(self) -> str: + return {"outlier": "⚠️", "trend": "📈", "distribution": "📊", + "correlation": "🔗", "recommendation": "💡"}.get(self.type, "📌") + + @property + def severity_emoji(self) -> str: + return {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(self.severity, "") + + def __str__(self): + return f"{self.emoji} {self.severity_emoji} {self.title}: {self.detail}" + + +class InsightEngine: + """洞察引擎""" + + def __init__(self): + self.client, self.model = get_llm_client(LLM_CONFIG) + + def analyze(self, steps: list[ExplorationStep], question: str) -> list[Insight]: + if not steps: + return [] + + history = self._build_history(steps) + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": INSIGHT_SYSTEM}, + {"role": "user", "content": f"## 用户问题\n{question}\n\n## 探索历史\n{history}\n\n请分析以上数据,输出异常和洞察。"}, + ], + temperature=0.3, max_tokens=2048, + ) + content = response.choices[0].message.content.strip() + return [Insight(d) for d in extract_json_array(content)] + + def format_insights(self, insights: list[Insight]) -> str: + if not insights: + return "" + severity_order = {"high": 0, "medium": 1, "low": 2} + sorted_insights = sorted(insights, key=lambda i: severity_order.get(i.severity, 9)) + lines = ["## 💡 主动洞察", "", "_以下是你没问但数据告诉我们的事:_\n"] + for insight in sorted_insights: + lines.append(f"**{insight.emoji} {insight.title}** {insight.severity_emoji}") + lines.append(f" {insight.detail}") + lines.append(f" _数据来源: {insight.evidence}_") + lines.append("") + return "\n".join(lines) + + def _build_history(self, steps: list[ExplorationStep]) -> str: + parts = [] + for step in steps: + if step.action == "done": + parts.append(f"### 结束\n{step.reasoning}") + elif step.success: + parts.append( + f"### 第 {step.round_num} 轮:{step.purpose}\n" + f"SQL: `{step.sql}`\n结果 ({step.row_count} 行):\n" + f"数据: {json.dumps(step.rows, ensure_ascii=False)}" + ) + else: + parts.append(f"### 第 {step.round_num} 轮:{step.purpose}\nSQL: `{step.sql}`\n失败: {step.error}") + return "\n\n".join(parts) + + +def quick_detect(steps: list[ExplorationStep]) -> list[str]: + """基于规则的快速异常检测,不调 LLM""" + alerts = [] + seen = set() # 去重 + + for step in steps: + if not step.success or not step.rows: + continue + + for col in step.columns: + vals = [r.get(col) for r in step.rows if isinstance(r.get(col), (int, float))] + if not vals: + continue + + col_lower = col.lower() + + # 占比列:某个分组占比过高 + if col_lower in ("pct", "percent", "percentage", "占比"): + for v in vals: + if v > 50: + key = f"pct_{step.purpose}" + if key not in seen: + seen.add(key) + alerts.append(f"⚠️ {step.purpose} 中某个分组占比 {v}%,集中度过高") + break + + # 计数列:极值差异 + if col_lower in ("count", "cnt", "n", "total", "order_count") and len(vals) >= 3: + avg = sum(vals) / len(vals) + if avg > 0: + ratio = max(vals) / avg + if ratio > 3: + key = f"count_{step.purpose}" + if key not in seen: + seen.add(key) + alerts.append(f"⚠️ {step.purpose} 中最大值是均值的 {ratio:.1f} 倍") + + return alerts diff --git a/layers/planner.py b/layers/planner.py new file mode 100644 index 0000000..513de8e --- /dev/null +++ b/layers/planner.py @@ -0,0 +1,74 @@ +""" +Layer 1: 意图规划器 +""" +import json +from typing import Any + +from core.config import LLM_CONFIG +from core.utils import get_llm_client, extract_json_object + +PROMPT = """你是一个数据分析规划专家。 + +## 你的任务 +根据用户的分析问题和数据库 Schema,生成一个结构化的分析计划。 + +## 输出格式(严格 JSON) +```json +{ + "intent": "一句话描述用户想了解什么", + "analysis_type": "ranking" | "distribution" | "trend" | "comparison" | "anomaly" | "overview", + "primary_table": "主要分析的表名", + "dimensions": ["分组维度列名"], + "metrics": ["需要聚合的数值列名"], + "aggregations": ["SUM", "AVG", "COUNT", ...], + "filters": [{"column": "列名", "condition": "过滤条件(可选)"}], + "join_needed": false, + "join_info": {"tables": [], "on": ""}, + "expected_rounds": 3, + "rationale": "为什么这样规划,需要关注什么" +} +``` + +## 分析类型说明 +- ranking: 按某维度排名 +- distribution: 分布/占比 +- trend: 时间趋势 +- comparison: 对比分析 +- anomaly: 异常检测 +- overview: 全局概览 + +## 规划原则 +1. 只选择与问题相关的表和列 +2. 如果需要 JOIN,说明关联条件 +3. 预估需要几轮探索(1-6) +4. 标注可能的异常关注点 +5. metrics 不要包含 id 列""" + + +class Planner: + """意图规划器""" + + def __init__(self): + self.client, self.model = get_llm_client(LLM_CONFIG) + + def plan(self, question: str, schema_text: str) -> dict[str, Any]: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": PROMPT}, + {"role": "user", "content": f"## Schema\n{schema_text}\n\n## 用户问题\n{question}"}, + ], + temperature=0.1, + max_tokens=1024, + ) + content = response.choices[0].message.content.strip() + plan = extract_json_object(content) + + if not plan: + plan = {"intent": content[:100], "analysis_type": "overview"} + + plan.setdefault("analysis_type", "overview") + plan.setdefault("expected_rounds", 3) + plan.setdefault("filters", []) + plan.setdefault("join_needed", False) + return plan diff --git a/layers/playbook.py b/layers/playbook.py new file mode 100644 index 0000000..3b8864b --- /dev/null +++ b/layers/playbook.py @@ -0,0 +1,179 @@ +""" +Layer 1.5: 预设分析剧本 +""" +import json +import os +import re +from typing import Optional + +from core.config import LLM_CONFIG +from core.utils import get_llm_client, extract_json_object, extract_json_array + + +class Playbook: + """一个预设分析剧本""" + def __init__(self, data: dict): + self.name = data["name"] + self.description = data["description"] + self.tags = data.get("tags", []) + self.preset_queries: list[dict] = data.get("preset_queries", []) + self.exploration_hints = data.get("exploration_hints", "") + self.placeholders = data.get("placeholders", {}) + + def to_summary(self) -> str: + return f"[{self.name}] {self.description} (标签: {', '.join(self.tags)})" + + def render_queries(self, schema: dict) -> list[dict]: + rendered = [] + for q in self.preset_queries: + sql, purpose = q["sql"], q.get("purpose", "") + for key, val in self.placeholders.items(): + sql = sql.replace(f"{{{{{key}}}}}", val) + purpose = purpose.replace(f"{{{{{key}}}}}", val) + rendered.append({"sql": sql, "purpose": purpose}) + return rendered + + +class PlaybookManager: + """加载和匹配 Playbook""" + + def __init__(self, playbook_dir: str = ""): + self.playbooks: list[Playbook] = [] + self.client, self.model = get_llm_client(LLM_CONFIG) + if playbook_dir and os.path.isdir(playbook_dir): + self._load_from_dir(playbook_dir) + + def _load_from_dir(self, dir_path: str): + for fname in sorted(os.listdir(dir_path)): + if not fname.endswith(".json"): + continue + try: + with open(os.path.join(dir_path, fname), "r", encoding="utf-8") as f: + data = json.load(f) + items = data if isinstance(data, list) else [data] + for item in items: + self.playbooks.append(Playbook(item)) + except (json.JSONDecodeError, KeyError) as e: + print(f" ⚠️ 加载 playbook 失败 {fname}: {e}") + + def add(self, playbook: Playbook): + self.playbooks.append(playbook) + + def auto_generate(self, schema_text: str, save_dir: str = "") -> list[Playbook]: + """让 LLM 根据 Schema 自动生成 Playbook""" + prompt = f"""你是一个数据分析专家。根据以下数据库 Schema,生成 3-5 个预设分析剧本。 + +## 数据库 Schema +{schema_text} + +## 输出格式(严格 JSON 数组) +```json +[ + {{ + "name": "剧本名称", + "description": "一句话描述", + "tags": ["关键词1", "关键词2"], + "preset_queries": [ + {{"purpose": "查询目的", "sql": "SELECT ... GROUP BY ..."}} + ], + "exploration_hints": "后续探索提示" + }} +] +``` + +## SQL 规则 +- 只用 SELECT,必须有聚合函数或 GROUP BY +- 禁止 SELECT *,用 ROUND 控制精度,合理 LIMIT +- 直接使用实际表名和列名""" + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是数据分析专家。只输出 JSON,不要其他内容。"}, + {"role": "user", "content": prompt}, + ], + temperature=0.3, max_tokens=4096, + ) + content = response.choices[0].message.content.strip() + playbooks_data = extract_json_array(content) + if not playbooks_data: + return [] + + generated = [] + for i, pb_data in enumerate(playbooks_data): + pb_data.setdefault("tags", []) + pb_data.setdefault("exploration_hints", "") + pb_data.setdefault("placeholders", {}) + try: + pb = Playbook(pb_data) + self.playbooks.append(pb) + generated.append(pb) + if save_dir: + os.makedirs(save_dir, exist_ok=True) + safe = re.sub(r'[^\w\u4e00-\u9fff]', '_', pb.name)[:30] + fpath = os.path.join(save_dir, f"auto_{i+1}_{safe}.json") + with open(fpath, "w", encoding="utf-8") as f: + json.dump(pb_data, f, ensure_ascii=False, indent=2) + except (KeyError, TypeError) as e: + print(f" ⚠️ 跳过无效 Playbook: {e}") + return generated + except Exception as e: + print(f" ⚠️ 自动生成 Playbook 出错: {e}") + return [] + + def match(self, plan: dict, schema_text: str) -> Optional[dict]: + """用 LLM 判断当前分析计划是否匹配某个 Playbook""" + if not self.playbooks: + return None + + pb_summaries = [] + for i, pb in enumerate(self.playbooks): + queries_desc = "\n".join(f" - {q.get('purpose', '')}: {q['sql'][:100]}" for q in pb.preset_queries) + pb_summaries.append(f"{i+1}. {pb.to_summary()}\n 预设查询:\n{queries_desc}") + + prompt = f"""判断当前分析计划是否适合使用某个预设剧本。 + +## 分析计划 +```json +{json.dumps(plan, ensure_ascii=False, indent=2)} +``` + +## Schema +{schema_text} + +## 可用剧本 +{chr(10).join(pb_summaries)} + +## 输出(严格 JSON) +匹配: {{"matched": true, "playbook_index": 1, "reasoning": "原因", "placeholders": {{}}}} +不匹配: {{"matched": false, "reasoning": "原因"}}""" + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是分析计划匹配器。"}, + {"role": "user", "content": prompt}, + ], + temperature=0.1, max_tokens=512, + ) + result = extract_json_object(response.choices[0].message.content.strip()) + if not result.get("matched"): + return None + + idx = result.get("playbook_index", 1) - 1 + if idx < 0 or idx >= len(self.playbooks): + return None + + pb = self.playbooks[idx] + pb.placeholders = {**pb.placeholders, **result.get("placeholders", {})} + return { + "matched": True, "playbook_name": pb.name, + "reasoning": result.get("reasoning", ""), + "preset_queries": pb.render_queries({}), + "exploration_hints": pb.exploration_hints, + } + except Exception as e: + print(f" ⚠️ Playbook 匹配出错: {e}") + return None diff --git a/output/__init__.py b/output/__init__.py new file mode 100644 index 0000000..2b1e001 --- /dev/null +++ b/output/__init__.py @@ -0,0 +1 @@ +"""输出层:报告、图表、整合""" diff --git a/output/chart.py b/output/chart.py new file mode 100644 index 0000000..2d50918 --- /dev/null +++ b/output/chart.py @@ -0,0 +1,247 @@ +""" +图表生成器 —— 根据探索结果自动生成可视化图表 +""" +import json +import os +import re +from typing import Any + +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm + +from core.config import LLM_CONFIG +from core.utils import get_llm_client, extract_json_array +from layers.explorer import ExplorationStep + + +def _setup_chinese_font(): + candidates = [ + "SimHei", "Microsoft YaHei", "STHeiti", "WenQuanYi Micro Hei", + "Noto Sans CJK SC", "PingFang SC", "Source Han Sans CN", + ] + available = {f.name for f in fm.fontManager.ttflist} + for font in candidates: + if font in available: + plt.rcParams["font.sans-serif"] = [font] + plt.rcParams["axes.unicode_minus"] = False + return font + plt.rcParams["axes.unicode_minus"] = False + return None + +_setup_chinese_font() + + +CHART_PLAN_PROMPT = """你是一个数据可视化专家。根据以下分析结果,规划需要生成的图表。 + +## 探索结果 +{exploration_summary} + +## 规划规则 +1. 每个有意义的查询结果生成 1 张图,最多 5 张 +2. 图表类型:bar / horizontal_bar / pie / line / stacked_bar +3. 跳过数据量太少(<2 行)的结果 +4. 标题要简洁 + +## 输出格式(纯 JSON 数组,不要代码块) +[ + {{ + "step_index": 0, + "chart_type": "bar", + "title": "图表标题", + "x_column": "分类轴列名", + "y_column": "数值轴列名", + "y2_column": null, + "top_n": 10, + "sort_desc": true + }} +]""" + + +class ChartGenerator: + """图表生成器""" + + def __init__(self, output_dir: str = "charts"): + self.output_dir = output_dir + self.client, self.model = get_llm_client(LLM_CONFIG) + + def generate(self, steps: list[ExplorationStep], question: str) -> list[dict]: + valid_steps = [(i, s) for i, s in enumerate(steps) if s.success and s.rows and s.row_count >= 2 and s.action != "done"] + if not valid_steps: + return [] + + plans = self._plan_charts(valid_steps, question) + if not plans: + return [] + + self._clean_old_charts() + os.makedirs(self.output_dir, exist_ok=True) + + charts = [] + for i, plan in enumerate(plans): + try: + path = self._render_chart(plan, steps, i) + if path: + charts.append({"path": path, "title": plan.get("title", f"图表 {i+1}")}) + except Exception as e: + print(f" ⚠️ 图表生成失败: {e}") + return charts + + def _plan_charts(self, valid_steps: list[tuple[int, ExplorationStep]], question: str) -> list[dict]: + summary_parts = [] + for idx, step in valid_steps: + summary_parts.append( + f"### 步骤 {idx}: {step.purpose}\n列: {step.columns}\n行数: {step.row_count}\n" + f"前 5 行: {json.dumps(step.rows[:5], ensure_ascii=False)}" + ) + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是数据可视化专家。只输出纯 JSON 数组,不要 markdown 代码块。"}, + {"role": "user", "content": CHART_PLAN_PROMPT.format(exploration_summary="\n\n".join(summary_parts))}, + ], + temperature=0.1, max_tokens=1024, + ) + plans = extract_json_array(response.choices[0].message.content.strip()) + return plans if plans else self._fallback_plan(valid_steps) + except Exception as e: + print(f" ⚠️ 图表规划失败: {e},使用 fallback") + return self._fallback_plan(valid_steps) + + def _fallback_plan(self, valid_steps: list[tuple[int, ExplorationStep]]) -> list[dict]: + plans = [] + for idx, step in valid_steps[:4]: + if len(step.columns) < 2 or step.row_count < 2: + continue + x_col = step.columns[0] + y_col = None + for col in step.columns[1:]: + if isinstance(step.rows[0].get(col), (int, float)): + y_col = col + break + if not y_col: + continue + + chart_type = "bar" + if any(kw in x_col for kw in ("月", "日期", "时间", "month", "date")): + chart_type = "line" + elif step.row_count <= 6: + chart_type = "pie" + elif len(str(step.rows[0].get(x_col, ""))) > 10: + chart_type = "horizontal_bar" + + plans.append({ + "step_index": idx, "chart_type": chart_type, + "title": f"各{x_col}的{y_col}", + "x_column": x_col, "y_column": y_col, + "y2_column": None, "top_n": 10, + "sort_desc": chart_type != "line", + }) + return plans + + def _render_chart(self, plan: dict, steps: list[ExplorationStep], chart_idx: int) -> str | None: + step_idx = plan.get("step_index", 0) + if step_idx >= len(steps): + return None + step = steps[step_idx] + if not step.success or not step.rows: + return None + + chart_type = plan.get("chart_type", "bar") + title = plan.get("title", f"图表 {chart_idx + 1}") + x_col, y_col = plan.get("x_column", ""), plan.get("y_column", "") + y2_col = plan.get("y2_column") + top_n = plan.get("top_n", 15) + sort_desc = plan.get("sort_desc", True) + + rows = step.rows[:top_n] if top_n else step.rows + x_vals = [str(r.get(x_col, "")) for r in rows] + y_vals = [self._to_number(r.get(y_col, 0)) for r in rows] + + if sort_desc and chart_type not in ("line",): + paired = sorted(zip(x_vals, y_vals), key=lambda p: p[1], reverse=True) + x_vals, y_vals = zip(*paired) if paired else ([], []) + + if not x_vals or not y_vals: + return None + + fig, ax = plt.subplots(figsize=(10, 6)) + + if chart_type == "bar": + bars = ax.bar(range(len(x_vals)), y_vals, color="#4C78A8") + ax.set_xticks(range(len(x_vals))) + ax.set_xticklabels(x_vals, rotation=45, ha="right", fontsize=9) + self._add_bar_labels(ax, bars) + elif chart_type == "horizontal_bar": + bars = ax.barh(range(len(x_vals)), y_vals, color="#4C78A8") + ax.set_yticks(range(len(x_vals))) + ax.set_yticklabels(x_vals, fontsize=9) + ax.invert_yaxis() + elif chart_type == "pie": + filtered = [(x, y) for x, y in zip(x_vals, y_vals) if y > 0] + if not filtered: + plt.close(fig) + return None + x_vals, y_vals = zip(*filtered) + ax.pie(y_vals, labels=x_vals, autopct="%1.1f%%", startangle=90, textprops={"fontsize": 9}) + elif chart_type == "line": + ax.plot(range(len(x_vals)), y_vals, marker="o", color="#4C78A8", linewidth=2) + ax.set_xticks(range(len(x_vals))) + ax.set_xticklabels(x_vals, rotation=45, ha="right", fontsize=9) + ax.fill_between(range(len(x_vals)), y_vals, alpha=0.1, color="#4C78A8") + if y2_col: + y2_vals = [self._to_number(r.get(y2_col, 0)) for r in rows] + ax2 = ax.twinx() + ax2.plot(range(len(x_vals)), y2_vals, marker="s", color="#E45756", linewidth=2, linestyle="--") + ax2.set_ylabel(y2_col, fontsize=10, color="#E45756") + elif chart_type == "stacked_bar": + ax.bar(range(len(x_vals)), y_vals, label=y_col, color="#4C78A8") + if y2_col: + y2_vals = [self._to_number(r.get(y2_col, 0)) for r in rows] + ax.bar(range(len(x_vals)), y2_vals, bottom=y_vals, label=y2_col, color="#E45756") + ax.set_xticks(range(len(x_vals))) + ax.set_xticklabels(x_vals, rotation=45, ha="right", fontsize=9) + ax.legend() + + ax.set_title(title, fontsize=13, fontweight="bold", pad=12) + if chart_type not in ("pie",): + ax.set_xlabel(x_col, fontsize=10) + if chart_type != "horizontal_bar": + ax.set_ylabel(y_col, fontsize=10) + ax.grid(axis="y", alpha=0.3) + + plt.tight_layout() + fname = f"chart_{chart_idx + 1}.png" + fpath = os.path.join(self.output_dir, fname) + fig.savefig(fpath, dpi=150, bbox_inches="tight") + plt.close(fig) + return fpath + + def _clean_old_charts(self): + if os.path.isdir(self.output_dir): + for f in os.listdir(self.output_dir): + if f.endswith(".png"): + try: + os.remove(os.path.join(self.output_dir, f)) + except OSError: + pass + + def _add_bar_labels(self, ax, bars): + for bar in bars: + h = bar.get_height() + if h > 0: + label = f"{h:.1f}" if isinstance(h, float) else str(int(h)) + ax.text(bar.get_x() + bar.get_width() / 2, h, label, ha="center", va="bottom", fontsize=8) + + def _to_number(self, val) -> float: + if isinstance(val, (int, float)): + return float(val) + if isinstance(val, str): + try: + return float(val.replace("<", "").replace(",", "").strip()) + except ValueError: + return 0.0 + return 0.0 diff --git a/output/consolidator.py b/output/consolidator.py new file mode 100644 index 0000000..b9ad5ee --- /dev/null +++ b/output/consolidator.py @@ -0,0 +1,84 @@ +""" +报告整合器 —— 将多次分析结果合并为一份完整报告 +""" +import json + +from core.config import LLM_CONFIG +from core.utils import get_llm_client +from layers.context import AnalysisSession + + +CONSOLIDATE_PROMPT = """你是一个高级数据分析总监。下面是你的团队针对同一份数据做的多次分析,请整合为一份完整的综合报告。 + +## 核心问题 +{question} + +## 各次分析结果 +{sections} + +## 可用图表 +{charts_text} + +## 整合要求 +1. **执行摘要**:3-5 句话概括全局结论 +2. **核心发现**:从所有分析中提炼最重要的发现,去重,按重要性排列 +3. **交叉洞察**:不同维度之间的关联 +4. **图表引用**:用 `![标题](路径)` 嵌入相关段落 +5. **风险与建议**:按优先级排列 +6. **数据附录**:关键统计数字 + +中文,专业简报风格。先结论后细节。""" + + +class ReportConsolidator: + """报告整合器""" + + def __init__(self): + self.client, self.model = get_llm_client(LLM_CONFIG) + + def consolidate(self, sessions: list[AnalysisSession], question: str = "", + charts: list[dict] | None = None) -> str: + if not sessions: + return "(无分析数据可整合)" + if not question: + question = sessions[0].question + + sections = self._build_sections(sessions) + charts_text = "\n".join(f"{i}. {c['title']}: {c['path']}" for i, c in enumerate(charts or [], 1)) or "无图表。" + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是高级数据分析总监,整合多维度分析结果。"}, + {"role": "user", "content": CONSOLIDATE_PROMPT.format(question=question, sections=sections, charts_text=charts_text)}, + ], + temperature=0.3, max_tokens=4096, + ) + return response.choices[0].message.content + except Exception as e: + print(f" ⚠️ LLM 整合失败: {e},使用拼接模式") + return self._fallback_concat(sessions, charts) + + def _build_sections(self, sessions: list[AnalysisSession]) -> str: + parts = [] + for i, session in enumerate(sessions, 1): + section = f"### 分析 {i}: {session.question}\n" + section += f"类型: {session.plan.get('analysis_type', '未知')}\n\n" + for step in session.steps: + if not step.success or not step.rows or step.action == "done": + continue + section += f"- {step.purpose} ({step.row_count} 行)\n" + section += f" 数据: {json.dumps(step.rows[:8], ensure_ascii=False)}\n\n" + if session.insights: + section += "#### 洞察\n" + "\n".join(f"- {i}" for i in session.insights) + "\n" + parts.append(section) + return "\n---\n".join(parts) + + def _fallback_concat(self, sessions: list[AnalysisSession], charts: list[dict] | None) -> str: + parts = ["# 综合分析报告\n"] + for i, s in enumerate(sessions, 1): + parts.append(f"## 第 {i} 部分: {s.question}\n{s.report}\n") + if charts: + parts.append("\n## 可视化\n" + "\n".join(f"![{c['title']}]({c['path']})" for c in charts)) + return "\n".join(parts) diff --git a/output/reporter.py b/output/reporter.py new file mode 100644 index 0000000..13f8ab1 --- /dev/null +++ b/output/reporter.py @@ -0,0 +1,84 @@ +""" +报告生成器 —— 单次分析报告 +""" +import json +from typing import Any + +from core.config import LLM_CONFIG +from core.utils import get_llm_client +from layers.explorer import ExplorationStep +from layers.insights import Insight + + +REPORT_PROMPT = """你是一个数据分析报告撰写专家。基于以下信息撰写报告。 + +## 用户问题 +{question} + +## 分析计划 +{plan} + +## 探索过程 +{exploration} + +## 主动洞察 +{insights_text} + +## 可用图表 +{charts_text} + +## 撰写要求 +1. **开头**:一句话总结核心结论 +2. **核心发现**:按重要性排列,带具体数字 +3. **图表引用**:用 `![标题](路径)` 嵌入到相关段落 +4. **深入洞察**:异常、趋势、关联 +5. **建议**:基于数据的行动建议 +6. **审计**:末尾附上所有 SQL + +中文,专业简报风格。图表自然嵌入对应段落。""" + + +class ReportGenerator: + """报告生成器""" + + def __init__(self): + self.client, self.model = get_llm_client(LLM_CONFIG) + + def generate(self, question: str, plan: dict, steps: list[ExplorationStep], + insights: list[Insight], charts: list[dict] | None = None) -> str: + exploration = self._build_exploration(steps) + insights_text = "\n".join(str(i) for i in insights) if insights else "未检测到异常。" + charts_text = "\n".join(f"{i}. 标题: {c['title']}, 路径: {c['path']}" for i, c in enumerate(charts or [], 1)) or "无图表。" + + prompt = REPORT_PROMPT.format( + question=question, + plan=json.dumps(plan, ensure_ascii=False, indent=2), + exploration=exploration, + insights_text=insights_text, + charts_text=charts_text, + ) + + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是专业的数据分析师,撰写清晰、有洞察力的分析报告。"}, + {"role": "user", "content": prompt}, + ], + temperature=0.3, max_tokens=4096, + ) + return response.choices[0].message.content + + def _build_exploration(self, steps: list[ExplorationStep]) -> str: + parts = [] + for step in steps: + if step.action == "done": + parts.append(f"### 结束\n{step.reasoning}") + elif step.success: + parts.append( + f"### 第 {step.round_num} 轮:{step.purpose}\n" + f"SQL: `{step.sql}`\n结果 ({step.row_count} 行):\n" + f"数据: {json.dumps(step.rows, ensure_ascii=False)}" + ) + else: + parts.append(f"### 第 {step.round_num} 轮:{step.purpose}\nSQL: `{step.sql}`\n失败: {step.error}") + return "\n\n".join(parts) if parts else "无探索步骤" diff --git a/planner.py b/planner.py deleted file mode 100644 index 6a671e8..0000000 --- a/planner.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Layer 1: 意图规划器 -将用户问题解析为结构化的分析计划,替代硬编码模板。 -""" -import json -import re -from typing import Any - -PROMPT = """你是一个数据分析规划专家。 - -## 你的任务 -根据用户的分析问题和数据库 Schema,生成一个结构化的分析计划。 - -## 输出格式(严格 JSON) -```json -{ - "intent": "一句话描述用户想了解什么", - "analysis_type": "ranking" | "distribution" | "trend" | "comparison" | "anomaly" | "overview", - "primary_table": "主要分析的表名", - "dimensions": ["分组维度列名"], - "metrics": ["需要聚合的数值列名"], - "aggregations": ["SUM", "AVG", "COUNT", ...], - "filters": [{"column": "列名", "condition": "过滤条件(可选)"}], - "join_needed": false, - "join_info": {"tables": [], "on": ""}, - "expected_rounds": 3, - "rationale": "为什么这样规划,需要关注什么" -} -``` - -## 分析类型说明 -- ranking: 按某维度排名(哪个地区最高) -- distribution: 分布/占比(各地区占比多少) -- trend: 时间趋势(月度变化) -- comparison: 对比分析(A vs B) -- anomaly: 异常检测(有没有异常值) -- overview: 全局概览(整体情况如何) - -## 规划原则 -1. 只选择与问题相关的表和列 -2. 如果需要 JOIN,说明关联条件 -3. 预估需要几轮探索(1-6) -4. 标注可能的异常关注点 -5. metrics 不要包含 id 列""" - -import openai -from config import LLM_CONFIG - - -class Planner: - """意图规划器:将自然语言问题转为结构化分析计划""" - - def __init__(self): - self.client = openai.OpenAI( - api_key=LLM_CONFIG["api_key"], - base_url=LLM_CONFIG["base_url"], - ) - self.model = LLM_CONFIG["model"] - - def plan(self, question: str, schema_text: str) -> dict[str, Any]: - """ - 生成分析计划 - - Returns: - { - "intent": str, - "analysis_type": str, - "primary_table": str, - "dimensions": [str], - "metrics": [str], - "aggregations": [str], - "filters": [dict], - "join_needed": bool, - "join_info": dict, - "expected_rounds": int, - "rationale": str, - } - """ - response = self.client.chat.completions.create( - model=self.model, - messages=[ - {"role": "system", "content": PROMPT}, - { - "role": "user", - "content": ( - f"## Schema\n{schema_text}\n\n" - f"## 用户问题\n{question}" - ), - }, - ], - temperature=0.1, - max_tokens=1024, - ) - - content = response.choices[0].message.content.strip() - plan = self._extract_json(content) - - # 补充默认值 - plan.setdefault("analysis_type", "overview") - plan.setdefault("expected_rounds", 3) - plan.setdefault("filters", []) - plan.setdefault("join_needed", False) - - return plan - - def _extract_json(self, text: str) -> dict: - """从 LLM 输出提取 JSON""" - try: - return json.loads(text) - except json.JSONDecodeError: - pass - - for pattern in [r'```json\s*\n(.*?)\n```', r'```\s*\n(.*?)\n```']: - match = re.search(pattern, text, re.DOTALL) - if match: - try: - return json.loads(match.group(1)) - except json.JSONDecodeError: - continue - - # 找最外层 {} - match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', text, re.DOTALL) - if match: - try: - return json.loads(match.group()) - except json.JSONDecodeError: - pass - - return {"intent": text[:100], "analysis_type": "overview"} diff --git a/playbooks/auto_1_工单处理效率分析.json b/playbooks/auto_1_工单处理效率分析.json new file mode 100644 index 0000000..0ed1573 --- /dev/null +++ b/playbooks/auto_1_工单处理效率分析.json @@ -0,0 +1,22 @@ +{ + "name": "工单处理效率分析", + "description": "分析不同问题类型和模块的工单关闭时长,识别处理瓶颈。", + "tags": [ + "关闭时长", + "问题类型", + "模块", + "效率" + ], + "preset_queries": [ + { + "purpose": "计算每个问题类型的平均关闭时长,识别处理最慢的问题类型。", + "sql": "SELECT 问题类型, ROUND(AVG(关闭时长_天), 2) AS 平均关闭时长 FROM tickets GROUP BY 问题类型 ORDER BY 平均关闭时长 DESC LIMIT 10" + }, + { + "purpose": "计算每个模块的平均关闭时长,识别处理最慢的模块。", + "sql": "SELECT 模块, ROUND(AVG(关闭时长_天), 2) AS 平均关闭时长 FROM tickets GROUP BY 模块 ORDER BY 平均关闭时长 DESC LIMIT 10" + } + ], + "exploration_hints": "查看平均关闭时长高的问题类型和模块,结合跟踪记录和处理过程,分析是否存在流程问题或资源分配不均。关注关闭时长分布的异常值。", + "placeholders": {} +} \ No newline at end of file diff --git a/playbooks/auto_2_工单来源与状态分布.json b/playbooks/auto_2_工单来源与状态分布.json new file mode 100644 index 0000000..fe86ebf --- /dev/null +++ b/playbooks/auto_2_工单来源与状态分布.json @@ -0,0 +1,22 @@ +{ + "name": "工单来源与状态分布", + "description": "分析不同来源的工单状态分布,评估渠道效果。", + "tags": [ + "来源", + "工单状态", + "分布", + "渠道" + ], + "preset_queries": [ + { + "purpose": "统计每个来源的工单数量及状态分布。", + "sql": "SELECT 来源, 工单状态, COUNT(*) AS 工单数量 FROM tickets GROUP BY 来源, 工单状态 ORDER BY 来源, 工单状态" + }, + { + "purpose": "计算每个来源的工单关闭比例。", + "sql": "SELECT 来源, ROUND(SUM(CASE WHEN 工单状态 = 'close' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS 关闭比例 FROM tickets GROUP BY 来源 ORDER BY 关闭比例 DESC" + } + ], + "exploration_hints": "比较不同来源的关闭比例,识别效果最佳的渠道。分析状态为'temporary close'的工单特征,如问题类型或责任人,以优化处理流程。", + "placeholders": {} +} \ No newline at end of file diff --git a/playbooks/auto_3_责任人绩效分析.json b/playbooks/auto_3_责任人绩效分析.json new file mode 100644 index 0000000..57b89f5 --- /dev/null +++ b/playbooks/auto_3_责任人绩效分析.json @@ -0,0 +1,22 @@ +{ + "name": "责任人绩效分析", + "description": "分析不同责任人的工单处理数量和平均关闭时长。", + "tags": [ + "责任人", + "绩效", + "处理数量", + "关闭时长" + ], + "preset_queries": [ + { + "purpose": "统计每个责任人的工单处理数量。", + "sql": "SELECT 责任人, COUNT(*) AS 处理工单数量 FROM tickets GROUP BY 责任人 ORDER BY 处理工单数量 DESC LIMIT 10" + }, + { + "purpose": "计算每个责任人的平均关闭时长。", + "sql": "SELECT 责任人, ROUND(AVG(关闭时长_天), 2) AS 平均关闭时长 FROM tickets GROUP BY 责任人 ORDER BY 平均关闭时长 DESC LIMIT 10" + } + ], + "exploration_hints": "结合处理数量和平均关闭时长,评估责任人的效率。关注处理数量高但关闭时长也高的责任人,可能存在工作负荷过重或技能不足的问题。", + "placeholders": {} +} \ No newline at end of file diff --git a/playbooks/auto_4_车型与问题关联分析.json b/playbooks/auto_4_车型与问题关联分析.json new file mode 100644 index 0000000..5fac5de --- /dev/null +++ b/playbooks/auto_4_车型与问题关联分析.json @@ -0,0 +1,22 @@ +{ + "name": "车型与问题关联分析", + "description": "分析不同车型的工单问题类型分布,识别车型特有问题。", + "tags": [ + "车型", + "问题类型", + "关联", + "分布" + ], + "preset_queries": [ + { + "purpose": "统计每个车型的工单数量及主要问题类型。", + "sql": "SELECT 车型, 问题类型, COUNT(*) AS 工单数量 FROM tickets GROUP BY 车型, 问题类型 ORDER BY 车型, 工单数量 DESC" + }, + { + "purpose": "计算每个车型的平均关闭时长,识别处理难度高的车型。", + "sql": "SELECT 车型, ROUND(AVG(关闭时长_天), 2) AS 平均关闭时长 FROM tickets GROUP BY 车型 ORDER BY 平均关闭时长 DESC LIMIT 10" + } + ], + "exploration_hints": "分析特定车型的高频问题类型,结合问题描述和跟踪记录,识别车型设计或软件问题。关注平均关闭时长高的车型,分析是否存在共性问题。", + "placeholders": {} +} \ No newline at end of file diff --git a/playbooks/auto_5_时间趋势分析.json b/playbooks/auto_5_时间趋势分析.json new file mode 100644 index 0000000..11c6473 --- /dev/null +++ b/playbooks/auto_5_时间趋势分析.json @@ -0,0 +1,22 @@ +{ + "name": "时间趋势分析", + "description": "分析工单创建和关闭的时间趋势,识别高峰期和处理延迟。", + "tags": [ + "时间趋势", + "创建日期", + "关闭日期", + "高峰期" + ], + "preset_queries": [ + { + "purpose": "统计每月创建的工单数量,识别高峰期。", + "sql": "SELECT SUBSTR(创建日期_解析, 1, 7) AS 月份, COUNT(*) AS 创建工单数量 FROM tickets GROUP BY 月份 ORDER BY 月份" + }, + { + "purpose": "计算每月关闭的工单平均关闭时长,识别处理延迟趋势。", + "sql": "SELECT SUBSTR(关闭日期_解析, 1, 7) AS 月份, ROUND(AVG(关闭时长_天), 2) AS 平均关闭时长 FROM tickets WHERE 关闭日期_解析 IS NOT NULL GROUP BY 月份 ORDER BY 月份" + } + ], + "exploration_hints": "比较创建高峰期和关闭时长趋势,分析是否存在资源调配问题。关注特定月份的异常值,如关闭时长突然增加,结合跟踪记录查找原因。", + "placeholders": {} +} \ No newline at end of file diff --git a/reporter.py b/reporter.py deleted file mode 100644 index 5a29c53..0000000 --- a/reporter.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -报告生成器 -将分析计划 + 探索结果 + 洞察,综合为一份可读报告。 -""" -import json -from typing import Any - -import openai -from config import LLM_CONFIG -from explorer import ExplorationStep -from insights import Insight - - -REPORT_PROMPT = """你是一个数据分析报告撰写专家。基于以下信息撰写报告。 - -## 用户问题 -{question} - -## 分析计划 -{plan} - -## 探索过程 -{exploration} - -## 主动洞察 -{insights_text} - -## 撰写要求 -1. **开头**:一句话总结核心结论(先给答案) -2. **核心发现**:按重要性排列,每个发现带具体数字 -3. **深入洞察**:异常、趋势、关联(从洞察中提取) -4. **建议**:基于数据的行动建议 -5. **审计**:末尾附上所有执行的 SQL - -使用 Markdown,中文撰写。 -语气:专业但不枯燥,像一个聪明的分析师在做简报。""" - - -class ReportGenerator: - """报告生成器""" - - def __init__(self): - self.client = openai.OpenAI( - api_key=LLM_CONFIG["api_key"], - base_url=LLM_CONFIG["base_url"], - ) - self.model = LLM_CONFIG["model"] - - def generate( - self, - question: str, - plan: dict, - steps: list[ExplorationStep], - insights: list[Insight], - ) -> str: - """生成分析报告""" - - # 构建探索过程文本 - exploration = self._build_exploration(steps) - - # 构建洞察文本 - insights_text = self._build_insights(insights) - - prompt = REPORT_PROMPT.format( - question=question, - plan=json.dumps(plan, ensure_ascii=False, indent=2), - exploration=exploration, - insights_text=insights_text, - ) - - response = self.client.chat.completions.create( - model=self.model, - messages=[ - {"role": "system", "content": "你是专业的数据分析师,撰写清晰、有洞察力的分析报告。"}, - {"role": "user", "content": prompt}, - ], - temperature=0.3, - max_tokens=4096, - ) - - return response.choices[0].message.content - - def _build_exploration(self, steps: list[ExplorationStep]) -> str: - parts = [] - for step in steps: - if step.action == "done": - parts.append(f"### 探索结束\n{step.reasoning}") - continue - - if step.success: - parts.append( - f"### 第 {step.round_num} 轮:{step.purpose}\n" - f"思考: {step.reasoning}\n" - f"SQL: `{step.sql}`\n" - f"结果 ({step.row_count} 行):\n" - f"列: {step.columns}\n" - f"数据: {json.dumps(step.rows, ensure_ascii=False)}" - ) - else: - parts.append( - f"### 第 {step.round_num} 轮:{step.purpose}\n" - f"SQL: `{step.sql}`\n" - f"执行失败: {step.error}" - ) - return "\n\n".join(parts) if parts else "无探索步骤" - - def _build_insights(self, insights: list[Insight]) -> str: - if not insights: - return "未检测到异常。" - return "\n".join(str(i) for i in insights) diff --git a/requirements.txt b/requirements.txt index aa2b704..5e4bc7f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ openai>=1.0.0 +matplotlib>=3.5.0 diff --git a/test_run.py b/test_run.py new file mode 100644 index 0000000..2a4f48b --- /dev/null +++ b/test_run.py @@ -0,0 +1,14 @@ +"""快速测试""" +import sys +import os +sys.path.insert(0, os.path.dirname(__file__)) + +from agent import DataAnalysisAgent + +agent = DataAnalysisAgent("demo.db") + +print(f"\n📋 Playbook: {len(agent.playbook_mgr.playbooks)} 个") +print("🚀 开始测试...\n") + +report = agent.analyze("帮我分析一下工单的整体情况", max_rounds=4) +print("\n" + report)