统一 voc_llm 密钥解析与默认模型;向量化改为本地 mlx 模型;更新 README、gitignore 与流水线文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
14 KiB
main_voc分析.py 说明文档
VOC(Voice of Customer)分析全流程编排入口:合并原始 CSV → 清洗 → LLM 结构化 → 向量化 →(聚类 + 词频)→ 生成 HTML 报告。
本文档与
main_voc分析.py同目录放置,风格对齐prompts/README.md(表格 + 可执行示例 + 路径约定)。
1. 流程总览
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 常用命令
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 程序式调用
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。
- 本地 MLX:
- 输出:
voc_embeddings.sqlite(embedding_items)。 - 运行:推荐
./310py/bin/python(Python 3.10+)。
步骤 5:聚类(聚类.py)
-
输入:
voc_embeddings.sqlite中向量与元数据。 -
核心数学管线(对每个聚类 stage 的子集):
-
UMAP 降维(
umap-learn)n_components = min(30, n-2)metric = cosinemin_dist = 0.1random_state = 42n_neighbors由自动调参循环递增(初值 10,上限 45)
-
HDBSCAN(
hdbscan)min_samples = 1,cluster_selection_method = eom- 初始
min_cluster_size = max(2, n // 20),若簇数 > 20 则增大min_cluster_size直至 ≤ 20 或无法再增 - 标签
-1表示离群点(各 stage 是否参与后续见模块内注释)
-
轮廓系数(
sklearn.metrics.silhouette_score)- 在 UMAP 空间、非离群点上计算;用于早停:连续 8 轮中后 7 轮均低于窗口首值则回退轮廓最高的一轮。
-
LLM 聚类质量评估(非传统指标,辅助调参)
- 对各簇抽样短语,统计「跨簇语义相似」比例;> 10% 则继续增大
n_neighbors;≤ 10% 且轮廓 > 0.6 则停止。
- 对各簇抽样短语,统计「跨簇语义相似」比例;> 10% 则继续增大
-
-
特例:子集样本数
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。
密钥优先级:
DEEPSEEK_API_KEYDEEPSEEK_API_KEY_FILE- 项目根
.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. 依赖与环境
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 七步流程一致。