包含七步编排入口、结构化/向量化/聚类/词频/报告模块与 prompts 配置;忽略原始 CSV 与本地密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
297 lines
14 KiB
Markdown
297 lines
14 KiB
Markdown
# main_voc分析.py 说明文档
|
||
|
||
VOC(Voice of Customer)分析**全流程编排入口**:合并原始 CSV → 清洗 → LLM 结构化 → 向量化 →(聚类 + 词频)→ 生成 HTML 报告。
|
||
|
||
> 本文档与 `main_voc分析.py` 同目录放置,风格对齐 `prompts/README.md`(表格 + 可执行示例 + 路径约定)。
|
||
|
||
---
|
||
|
||
## 1. 流程总览
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
S1[步骤1 合并CSV] --> S2[步骤2 清洗]
|
||
S2 --> S3[步骤3 结构化 LLM]
|
||
S3 --> S4[步骤4 向量化 API]
|
||
S4 --> S5[步骤5 聚类 UMAP+HDBSCAN]
|
||
S4 --> S6[步骤6 词频 spaCy+LLM]
|
||
S5 --> S7[步骤7 报告 HTML]
|
||
S6 --> S7
|
||
```
|
||
|
||
| 步骤 | 模块 | 是否调用 DashScope API |
|
||
|------|------|------------------------|
|
||
| 1 | `合并评论数据.py` | 否 |
|
||
| 2 | `content清洗.py` | 否 |
|
||
| 3 | `结构化_server.py` | **是**(Chat 结构化) |
|
||
| 4 | `向量化.py` | **是**(Embedding) |
|
||
| 5–6 | `聚类.py` ∥ `词频.py` | **是**(聚类调参评估 + 词频术语提取) |
|
||
| 7 | `voc_report.py` | **是**(报告撰写、词频分类、翻译等) |
|
||
|
||
步骤 5 与 6 在 `from_step ≤ 5` 且两步均需执行时,由 `ThreadPoolExecutor(max_workers=2)` **并行**运行。
|
||
|
||
---
|
||
|
||
## 2. 命令行输入 / 输出
|
||
|
||
### 2.1 输入(CLI 参数)
|
||
|
||
| 参数 | 必填条件 | 默认值 | 说明 |
|
||
|------|----------|--------|------|
|
||
| `--input-dir` | 是 | — | 原始 VOC评论数据文件 CSV 目录(目录内的所有`*.csv` 表头须一致) |
|
||
| `--industry` | 是 | `Pet Supplies` | 行业名,写入结构化任务 |
|
||
| `--product` | 是 | — | 产品名 |
|
||
| `--merged-csv` | 否 | `merged_reviews.csv` | 步骤 1 输出路径 |
|
||
| `--cleaned-csv` | 否 | `merged_reviews_cleaned.csv` | 步骤 2 输出路径 |
|
||
| `--from-step N` | 否 | `1` | 从第 N 步执行到结束(N=1…7) |
|
||
| `--only-step N` | 否 | — | 仅执行第 N 步(需已有前置产物) |
|
||
| `--keep-db` | 否 | 默认**会**清理库 | 保留已有三个 `.sqlite`,不覆盖删除 |
|
||
| `--clean-intermediates` | 否 | 关闭 | 步骤 7 成功后删除中间 csv/sqlite,仅保留报告目录 |
|
||
| `--skip-wordfreq-llm` | 否 | 关闭 | 词频复用已有 `output/voc_terms.json`,跳过 LLM 术语提取 |
|
||
| `--filter-small-clusters` | 否 | 关闭 | 报告仅纳入簇内去重评论占比 ≥ 10% 的簇 |
|
||
| `--save-llm-raw` | 否 | 关闭 | 将报告 LLM 完整原文写入 `{product}_voc_report.html` 同目录 `report_llm_raw.txt` |
|
||
|
||
### 2.2 输出
|
||
|
||
**标准输出(stdout)**:流程结束后打印 JSON 字典(`ensure_ascii=False`),键随实际执行步骤变化,常见字段:
|
||
|
||
| 键 | 含义 |
|
||
|----|------|
|
||
| `merged_csv` | 合并后 CSV 路径 |
|
||
| `cleaned_csv` | 清洗后 CSV 路径 |
|
||
| `structured_job_id` | 结构化任务 ID |
|
||
| `structured_db` | `voc_structured.sqlite` |
|
||
| `embed` | 向量化模块返回信息(含 `job_id` 等) |
|
||
| `clustering` | 聚类模块返回信息 |
|
||
| `wordfreq` | 词频模块返回信息 |
|
||
| `report_html` | 报告 HTML 绝对/相对路径 |
|
||
| `report` | `generate_report` 返回值 |
|
||
|
||
**磁盘产物(项目根为 `PROJECT_ROOT`,即本文件所在目录)**:
|
||
|
||
| 路径 | 产生步骤 | 说明 |
|
||
|------|----------|------|
|
||
| `merged_reviews.csv` | 1 | 多文件纵向合并 |
|
||
| `merged_reviews_cleaned.csv` | 2 | 清洗、去重后的评论 |
|
||
| `voc_structured.sqlite` | 3 | 结构化任务与逐条提取结果 |
|
||
| `voc_embeddings.sqlite` | 4 | 各实体短语 256 维向量 |
|
||
| `voc_clustering.sqlite` | 5 | 多阶段 UMAP+HDBSCAN 簇标签 |
|
||
| `output/voc_terms.json` | 6 | LLM 归纳的产品专有名词 / 停用词 |
|
||
| `output/word_freq.csv` | 6 | 全量词频表(`word`, `count`) |
|
||
| `output/{product}/{product}_voc_report.html` | 7 | 最终 VOC 分析报告(产品名经路径安全化) |
|
||
|
||
`{product}` 目录名由 `_safe_product_dir_name` 生成:去除 `<>:"/\|?*` 等非法字符。
|
||
|
||
### 2.3 常用命令
|
||
|
||
```bash
|
||
cd "/Users/onesvmwhoops/Cursor_Project/VOC_LLM结构化"
|
||
|
||
# 全流程(默认写入 sqlite 前会清理旧库;加 --keep-db 则保留)
|
||
python3 main_voc分析.py --input-dir "某目录" --product "产品名"
|
||
|
||
# 从步骤 4 续跑(product 可省略)
|
||
python3 main_voc分析.py --from-step 4 --keep-db
|
||
|
||
# 仅重跑词频 LLM 之前的 spaCy 统计
|
||
python3 main_voc分析.py --from-step 6 --skip-wordfreq-llm
|
||
|
||
# 仅生成报告
|
||
python3 main_voc分析.py --only-step 7
|
||
```
|
||
|
||
### 2.4 程序式调用
|
||
|
||
```python
|
||
from main_voc分析 import run_voc_analysis # 别名 run_pipeline
|
||
|
||
result = run_voc_analysis(
|
||
input_dir=Path("reviews_export"),
|
||
industry="Pet Supplies",
|
||
product_name="cat deterrent indoor",
|
||
merged_csv=Path("merged_reviews.csv"),
|
||
cleaned_csv=Path("merged_reviews_cleaned.csv"),
|
||
from_step=1,
|
||
clean_databases=True, # 对应 CLI 未加 --keep-db
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 七步数据流与数学 / 算法
|
||
|
||
### 步骤 1:合并 CSV(`合并评论数据.py`)
|
||
|
||
- **输入**:目录内表头一致的 `*.csv`。
|
||
- **方法**:`pandas.concat` 纵向合并;表头不一致则报错。
|
||
- **输出**:单表 `merged_reviews.csv`(`utf-8-sig`)。
|
||
|
||
### 步骤 2:清洗(`content清洗.py`)
|
||
|
||
- **输入**:合并 CSV,正文列 `content`(或兼容列名由下游结构化处理)。
|
||
- **方法**(规则型,无机器学习):
|
||
- 去空行、亚马逊媒体占位文案、HTML 实体、常见噪声前缀;
|
||
- 保留含字母/数字的行的词数 ≥ 4;
|
||
- 按 `content` 去重(`keep="first"`)。
|
||
- **输出**:`merged_reviews_cleaned.csv`。
|
||
|
||
### 步骤 3:结构化(`结构化_server.py` + `结构化_Prompt.py` + `prompts/`)
|
||
|
||
- **输入**:清洗 CSV、`industry`、`product_name`。
|
||
- **方法**:DashScope **Chat Completions**(默认 `qwen3.6-flash`),按 token 估算批量调用,从评论中提取 JSON 字段(audience、pain_points、aspect、opinion、category、sentiment 等,以 `prompts/schema.yaml` 为准)。
|
||
- **输出**:`voc_structured.sqlite`(`analysis_jobs`、`comment_extractions`)。
|
||
|
||
### 步骤 4:向量化(`向量化.py`)
|
||
|
||
- **输入**:最新或指定 `job_id` 的结构化实体;`embed_text` 由 audience / pain_point / aspect / opinion / aspect_opinion 等展开。
|
||
- **方法**:
|
||
- API:`text-embedding-v4`,**256 维**,余弦相似度空间中的稠密向量;
|
||
- 存储:`float32` 打包为 BLOB(`struct.pack`);
|
||
- 批大小 ≤ 10/请求,默认 6 线程并行多批。
|
||
- **输出**:`voc_embeddings.sqlite`(`embedding_items`)。
|
||
|
||
### 步骤 5:聚类(`聚类.py`)
|
||
|
||
- **输入**:`voc_embeddings.sqlite` 中向量与元数据。
|
||
- **核心数学管线**(对每个聚类 stage 的子集):
|
||
|
||
1. **UMAP 降维**(`umap-learn`)
|
||
- `n_components = min(30, n-2)`
|
||
- `metric = cosine`
|
||
- `min_dist = 0.1`
|
||
- `random_state = 42`
|
||
- `n_neighbors` 由自动调参循环递增(初值 10,上限 45)
|
||
|
||
2. **HDBSCAN**(`hdbscan`)
|
||
- `min_samples = 1`,`cluster_selection_method = eom`
|
||
- 初始 `min_cluster_size = max(2, n // 20)`,若簇数 > 20 则增大 `min_cluster_size` 直至 ≤ 20 或无法再增
|
||
- 标签 `-1` 表示离群点(各 stage 是否参与后续见模块内注释)
|
||
|
||
3. **轮廓系数**(`sklearn.metrics.silhouette_score`)
|
||
- 在 UMAP 空间、非离群点上计算;用于早停:连续 8 轮中后 7 轮均低于窗口首值则回退轮廓最高的一轮。
|
||
|
||
4. **LLM 聚类质量评估**(非传统指标,辅助调参)
|
||
- 对各簇抽样短语,统计「跨簇语义相似」比例;> 10% 则继续增大 `n_neighbors`;≤ 10% 且轮廓 > 0.6 则停止。
|
||
|
||
- **特例**:子集样本数 `n < 10` 时不跑 HDBSCAN,每条独立簇 `0..n-1`。
|
||
- **输出**:`voc_clustering.sqlite`(多 stage:如 audience、簇内 pain_point、按情感分桶的 aspect_opinion 等)。
|
||
|
||
### 步骤 6:词频(`词频.py`)
|
||
|
||
- **第 1 步(LLM)**:固定种子 `SAMPLE_SEED=42` 随机 **25** 条 `content`,归纳产品专有名词与专属停用词 → `output/voc_terms.json`。
|
||
- **第 2 步(spaCy)**:
|
||
- 英文分词与停用词过滤(含 EN stop words + 自定义停用词);
|
||
- 专有名词按完整短语计数(protected spans);
|
||
- 词形归并:`_merge_word_forms` 将复数/时态等 lemmatize 后合并计数;
|
||
- 纯数字词剔除。
|
||
- **输出**:`output/word_freq.csv`。
|
||
|
||
### 步骤 7:报告(`voc_report.py` + `prompts/report/`)
|
||
|
||
- **输入**:`voc_clustering.sqlite`、`voc_embeddings.sqlite`、`merged_reviews_cleaned.csv`、`word_freq.csv`、可选 `voc_structured.sqlite`。
|
||
- **方法**:
|
||
- 从库中聚合各 stage 簇与代表短语;
|
||
- 多次 LLM 调用生成分析正文、词频六类归类、短语翻译等;
|
||
- 词云字号:`count^0.5` 映射到 `[14, 96]` px;
|
||
- 可选 `min_cluster_review_ratio`(默认 0.10)过滤小簇。
|
||
- **输出**:`output/{product}/{product}_voc_report.html`(内嵌词云、词频表、AI 报告、分簇表述)。
|
||
|
||
---
|
||
|
||
## 4. API Key 使用说明
|
||
|
||
`main_voc分析.py` **本身不读取、不持有 API Key**;密钥解析在各子模块内统一实现,优先级一致:
|
||
|
||
1. 环境变量 `DASHSCOPE_API_KEY`
|
||
2. 环境变量 `DASHSCOPE_API_KEY_FILE` 指向的单行密钥文件
|
||
3. 项目根文件 `.dashscope_key`(单行,无引号)
|
||
|
||
可选环境变量:`DASHSCOPE_MODEL`(默认各模块为 `qwen3.6-flash`)。
|
||
|
||
| 模块 | 使用 API Key 的位置 | API 类型 / 用途 |
|
||
|------|---------------------|-----------------|
|
||
| `结构化_server.py` | `_resolve_dashscope_api_key()` → `OpenAI(...)` | Chat:批量/单条评论结构化 |
|
||
| `向量化.py` | `_resolve_api_key()` → `_embed_one_api_batch` | Embeddings:`text-embedding-v4` |
|
||
| `聚类.py` | `_resolve_api_key()` → `run_clustering` 内 `OpenAI` | Chat:簇间相似度评估、调 `n_neighbors` |
|
||
| `词频.py` | `_resolve_api_key()` → `_step1_extract_terms` / `_call_llm` | Chat:专有名词与停用词提取 |
|
||
| `voc_report.py` | `_resolve_api_key()` → `generate_report` 及子函数 | Chat:报告生成、词频分类、翻译等 |
|
||
| `prompts/smoke.py` | `--live` 时 | 冒烟测试(非 main 流程) |
|
||
|
||
**仓库安全规范**(见 `.gitignore`):
|
||
|
||
- **禁止提交** `.dashscope_key`、`voc_structured.sqlite` 及含真实评论/密钥的敏感导出;
|
||
- 密钥仅通过环境变量或本机未跟踪文件提供;
|
||
- 文档与代码中勿写入真实 `sk-` 密钥。
|
||
|
||
---
|
||
|
||
## 5. 各模块职责
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `main_voc分析.py` | 七步编排、断点续跑、库清理策略、步骤 5/6 并行、CLI |
|
||
| `合并评论数据.py` | 目录多 CSV 合并为一张表 |
|
||
| `content清洗.py` | 评论正文清洗、过滤、去重 |
|
||
| `结构化_server.py` | LLM 结构化入库、job 管理、库清理钩子 |
|
||
| `结构化_Prompt.py` | 组装结构化 prompt(被 server 动态加载) |
|
||
| `向量化.py` | 结构化实体 → 256 维向量库 |
|
||
| `聚类.py` | UMAP + HDBSCAN 多阶段聚类 + LLM 调参 |
|
||
| `词频.py` | LLM 术语 + spaCy 全量词频 |
|
||
| `voc_report.py` | 聚类/词频/原文 → 单页 HTML 报告 |
|
||
| `prompts/` | 可编辑 prompt 与 `schema.yaml`(见 `prompts/README.md`) |
|
||
| `main_voc分析_jieba.py` | 可选变体入口:词频走 `词频_jieba.py`(jieba 分词),其余步骤与主流程类似 |
|
||
|
||
### main 内主要函数
|
||
|
||
| 函数 | 作用 |
|
||
|------|------|
|
||
| `run_voc_analysis` / `run_pipeline` | 核心流水线 |
|
||
| `_should_run` | 根据 `--from-step` / `--only-step` 判断是否执行某步 |
|
||
| `_latest_job_meta` | 从结构化库读最新 `job_id`、`industry`、`product_name` |
|
||
| `_resolve_industry_product` | CLI 下 product 自动补全 |
|
||
| `_clean_intermediates` | `--clean-intermediates` 时删除中间文件 |
|
||
| `_report_html_path` | 计算报告 HTML 路径 |
|
||
|
||
---
|
||
|
||
## 6. SQLite 与路径约定
|
||
|
||
| 库文件 | 写入步骤 | 主要内容 |
|
||
|--------|----------|----------|
|
||
| `voc_structured.sqlite` | 3 | `analysis_jobs`、`comment_extractions` |
|
||
| `voc_embeddings.sqlite` | 4 | `embedding_items`(向量 BLOB + 实体元数据) |
|
||
| `voc_clustering.sqlite` | 5 | 各 `stage` 簇标签、调参日志、过滤元数据 |
|
||
|
||
**默认清理策略**(未加 `--keep-db`):
|
||
|
||
- 步骤 3 运行 `run_analysis(clean_databases=True)` 时清理三个 sqlite(结构化写入逻辑见 `结构化_server.py`);
|
||
- 步骤 4 若未重跑步骤 3,main 会单独删除 `voc_embeddings.sqlite` 与 `voc_clustering.sqlite` 再向量化;
|
||
- 步骤 5/6 的 `reset_db` 与 `clean_databases` 联动。
|
||
|
||
**行号溯源**:结构化与向量化的 `source_row` 为 CSV **首条数据行为 1**;对应 `merged_reviews_cleaned.csv` 物理行号 = `source_row + 1`(第 1 行为表头)。
|
||
|
||
---
|
||
|
||
## 7. 依赖与环境
|
||
|
||
```bash
|
||
cd "/Users/onesvmwhoops/Cursor_Project/VOC_LLM结构化"
|
||
python3 -m venv 310py && source 310py/bin/activate # 可选,与 .gitignore 一致
|
||
pip install -r requirements.txt
|
||
python -m spacy download en_core_web_sm # 词频步骤需要
|
||
export DASHSCOPE_API_KEY="sk-xxx" # 或配置 .dashscope_key(勿提交)
|
||
```
|
||
|
||
`requirements.txt` 中与数学/ NLP 相关的主要包:`numpy`、`umap-learn`、`hdbscan`、`scikit-learn`、`spacy`、`openai`、`pyyaml`。
|
||
|
||
---
|
||
|
||
## 8. 维护说明
|
||
|
||
- 修改 LLM 话术:编辑 `prompts/` 下对应 `.md`,**勿改** `schema.yaml` 中 `report.markers` 四段标记名(见 `prompts/README.md`)。
|
||
- 修改主流程步骤顺序或默认路径:改 `main_voc分析.py` 后请同步更新**本文档**。
|
||
- 验收 prompt 加载:`python3 prompts/smoke.py`(无需 Key);联调模型:`python3 prompts/smoke.py --live`。
|
||
|
||
---
|
||
|
||
*文档版本:与仓库 `main_voc分析.py` 七步流程一致。*
|