# 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 结构化;默认 8 路批间并行) | | 4 | `向量化.py` | **是**(Embedding;默认 8 路批间并行) | | 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` | | `--struct-workers` | 否 | `8`(`VOC_STRUCT_WORKERS`) | 步骤 3 结构化**批间并行** Chat 请求数 | | `--embed-workers` | 否 | `8`(`VOC_EMBED_WORKERS`) | 步骤 4 向量化**批间并行** Embedding 请求数 | ### 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 则保留) ./310py/bin/python main_voc分析.py --input-dir "某目录" --product "产品名" # 从步骤 4 续跑(product 可省略) ./310py/bin/python main_voc分析.py --from-step 4 --keep-db # 仅重跑词频 LLM 之前的 spaCy 统计 ./310py/bin/python main_voc分析.py --from-step 6 --skip-wordfreq-llm # 仅生成报告 ./310py/bin/python 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`。 - **方法**:DeepSeek **Chat Completions**(默认 `deepseek-v4-pro`,思考关闭),按**输入 token** 与**条数**动态分批(默认单批估算输入 ≤ 200K、最多 **200** 条/批,`VOC_STRUCT_BATCH_MAX_REVIEWS` 可覆盖);从评论中提取 JSON 字段(以 `prompts/schema.yaml` 为准)。 - **输出**:`voc_structured.sqlite`(`analysis_jobs`、`comment_extractions`)。 ### 步骤 4:向量化(`向量化.py` + `local_embedding.py`) - **输入**:最新或指定 `job_id` 的结构化实体;`embed_text` 由 audience / pain_point / aspect / opinion / aspect_opinion 等展开。 - **方法**: - 本地 MLX:`Qwen3-Embedding-4B-mxfp8`(默认 **2560 维**,L2 归一化); - 存储:`float32` 打包为 BLOB(`struct.pack`); - 默认批大小 **16**、串行推理(`VOC_EMBED_BATCH_SIZE`);M4 16GB 实测峰值约 1.5GB。 - **输出**:`voc_embeddings.sqlite`(`embedding_items`)。 - **运行**:推荐 `./310py/bin/python`(Python 3.10+)。 ### 步骤 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 与模型 ### Chat(步骤 3 / 5 / 6 / 7) 统一经 `voc_llm.py`,默认 **`deepseek-v4-pro`**(`chat_extra_body` 关闭思考),`https://api.deepseek.com`。步骤 7 主报告在 `voc_report.py` 单独使用 Pro + `reasoning_effort=max`。 密钥优先级: 1. `DEEPSEEK_API_KEY` 2. `DEEPSEEK_API_KEY_FILE` 3. 项目根 `.deepseek_key` 可选:`DEEPSEEK_MODEL`、`DEEPSEEK_BASE_URL`。 | 模块 | 用途 | |------|------| | `结构化_server.py` | 评论结构化 | | `聚类.py` | 簇质量评估与调参 | | `词频.py` | 专有名词 / 停用词 | | `voc_report.py` | 报告、词频分类、翻译 | ### Embedding(步骤 4) **无需 API Key**。本地目录 `Qwen3-Embedding-4B-mxfp8`(或 `VOC_EMBED_MODEL_PATH`)。 **仓库安全规范**(见 `.gitignore`): - **禁止提交** `.deepseek_key`、`.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结构化" uv venv 310py --python 3.12 # 与 .gitignore 中 310py/ 一致 uv pip install --python 310py/bin/python -r requirements.txt # uv 虚拟环境无 pip,勿用「python -m spacy download」;直接装模型 wheel: uv pip install --python 310py/bin/python "en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl" export DEEPSEEK_API_KEY="sk-xxx" # 或配置 .deepseek_key(勿提交) # 向量化推荐:./310py/bin/python main_voc分析.py ... ``` `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 加载:`./310py/bin/python prompts/smoke.py`(无需 Key);联调模型:`./310py/bin/python prompts/smoke.py --live`。 --- *文档版本:与仓库 `main_voc分析.py` 七步流程一致。*