GEO 智能内容优化平台
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
|||||||
|
# SQLite 数据库文件
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# Streamlit
|
||||||
|
.streamlit/secrets.toml
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# 数据目录(如果使用JSON方式)
|
||||||
|
data/
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[theme]
|
||||||
|
base="light"
|
||||||
|
primaryColor="#2563EB"
|
||||||
|
backgroundColor="#FFFFFF"
|
||||||
|
secondaryBackgroundColor="#F7FAFC"
|
||||||
|
textColor="#1A202C"
|
||||||
|
borderColor="#E2E8F0"
|
||||||
|
baseRadius="10"
|
||||||
|
buttonRadius="10"
|
||||||
|
showSidebarBorder=true
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
# 🚀 GEO 工具高级功能建议
|
||||||
|
|
||||||
|
## 📋 功能分类
|
||||||
|
|
||||||
|
### 🧠 一、智能化增强(AI 驱动)
|
||||||
|
|
||||||
|
#### 1. **智能内容质量评分系统** ⭐⭐⭐⭐⭐
|
||||||
|
**价值**:自动评估内容是否符合 GEO 原则,提供改进建议
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 自动分析生成内容的结构化程度(标题、清单、FAQ 等)
|
||||||
|
- 品牌提及位置和频率评分
|
||||||
|
- 内容权威性评估(数据支撑、案例引用)
|
||||||
|
- 给出具体的优化建议(如"建议在开头增加结论摘要")
|
||||||
|
- 内容 GEO 分数(0-100分)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用 LLM 分析内容,输出结构化评分
|
||||||
|
- 建立评分标准(结构化、品牌提及、权威性、可引用性)
|
||||||
|
- 在内容生成后自动评分,并提供改进建议
|
||||||
|
|
||||||
|
**预期效果**:用户无需手动判断内容质量,系统自动优化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. **智能关键词挖掘与趋势分析** ⭐⭐⭐⭐⭐
|
||||||
|
**价值**:发现新的高价值关键词,预测关键词趋势
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 基于行业热点自动挖掘新关键词
|
||||||
|
- 分析关键词竞争度(在 AI 中的提及频率)
|
||||||
|
- 预测关键词趋势(上升/下降)
|
||||||
|
- 推荐高价值低竞争关键词
|
||||||
|
- 关键词组合建议(长尾词挖掘)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用 LLM 分析行业趋势和用户搜索意图
|
||||||
|
- 结合历史验证数据,分析关键词效果
|
||||||
|
- 提供关键词价值矩阵(高价值/低价值 × 高竞争/低竞争)
|
||||||
|
|
||||||
|
**预期效果**:帮助用户发现蓝海关键词,提升 ROI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. **A/B 测试与内容对比** ⭐⭐⭐⭐
|
||||||
|
**价值**:对比不同版本内容的效果,数据驱动优化
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 为同一关键词生成多个版本内容(不同风格、结构)
|
||||||
|
- 同时验证多个版本,对比提及率
|
||||||
|
- 自动推荐最优版本
|
||||||
|
- 记录 A/B 测试历史,建立最佳实践库
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 在内容生成时支持"生成多个版本"
|
||||||
|
- 批量验证不同版本
|
||||||
|
- 对比分析提及率、位置等指标
|
||||||
|
- 建立内容模板库(基于效果最好的版本)
|
||||||
|
|
||||||
|
**预期效果**:通过数据找到最佳内容策略
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📊 二、数据洞察增强
|
||||||
|
|
||||||
|
#### 4. **ROI 分析与成本优化** ⭐⭐⭐⭐⭐
|
||||||
|
**价值**:量化 GEO 投入产出比,优化成本结构
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 计算每次验证的 API 成本
|
||||||
|
- 统计内容生成成本(按平台、按关键词)
|
||||||
|
- 分析提及率提升带来的价值(估算)
|
||||||
|
- 成本效益分析(哪些关键词/平台 ROI 最高)
|
||||||
|
- 预算管理和成本预警
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 记录每次 API 调用的成本(基于各平台定价)
|
||||||
|
- 计算总投入成本
|
||||||
|
- 分析提及率提升幅度,估算品牌曝光价值
|
||||||
|
- 提供 ROI 报表和优化建议
|
||||||
|
|
||||||
|
**预期效果**:让用户清楚知道投入产出,优化预算分配
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 5. **竞品监控与预警** ⭐⭐⭐⭐
|
||||||
|
**价值**:自动监控竞品在 AI 中的表现,及时调整策略
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 定期自动验证竞品提及率
|
||||||
|
- 竞品提及率变化趋势
|
||||||
|
- 竞品内容策略分析(哪些关键词/平台效果好)
|
||||||
|
- 竞品超越预警(当竞品提及率超过自己时)
|
||||||
|
- 竞品对比报告(自动生成)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 定时任务自动验证竞品
|
||||||
|
- 对比分析竞品和自身的数据
|
||||||
|
- 识别竞品的优势策略
|
||||||
|
- 提供应对建议
|
||||||
|
|
||||||
|
**预期效果**:保持竞争优势,及时应对市场变化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 6. **内容效果预测模型** ⭐⭐⭐⭐
|
||||||
|
**价值**:预测内容发布后的效果,优化内容策略
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 基于历史数据训练预测模型
|
||||||
|
- 预测新内容的提及率
|
||||||
|
- 预测不同平台的效果差异
|
||||||
|
- 推荐最优发布策略(平台组合、发布时间等)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 收集历史数据(内容特征、平台、提及率)
|
||||||
|
- 使用机器学习模型预测效果
|
||||||
|
- 提供预测置信度
|
||||||
|
- 持续优化模型准确性
|
||||||
|
|
||||||
|
**预期效果**:在发布前就知道效果,避免无效投入
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔄 三、自动化增强
|
||||||
|
|
||||||
|
#### 7. **智能工作流自动化** ⭐⭐⭐⭐⭐
|
||||||
|
**价值**:一键完成从关键词到验证的完整流程
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 自定义工作流(关键词生成 → 内容创作 → 自动验证)
|
||||||
|
- 定时任务(每天/每周自动验证)
|
||||||
|
- 条件触发(当提及率低于阈值时自动优化)
|
||||||
|
- 批量处理(一次性处理多个关键词)
|
||||||
|
- 工作流模板(保存常用工作流)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 创建工作流配置界面
|
||||||
|
- 支持条件判断和循环
|
||||||
|
- 集成定时任务(使用 APScheduler)
|
||||||
|
- 提供工作流执行日志
|
||||||
|
|
||||||
|
**预期效果**:大幅减少重复工作,提升效率
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 8. **内容模板库与最佳实践** ⭐⭐⭐⭐
|
||||||
|
**价值**:积累成功经验,复用最佳内容模板
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 自动保存高效果内容为模板
|
||||||
|
- 模板分类(按平台、按行业、按效果)
|
||||||
|
- 模板搜索和推荐
|
||||||
|
- 基于模板快速生成内容
|
||||||
|
- 模板效果统计(使用次数、平均提及率)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 识别高效果内容(提及率 > 阈值)
|
||||||
|
- 提取内容结构作为模板
|
||||||
|
- 模板参数化(品牌、优势等可替换)
|
||||||
|
- 提供模板管理界面
|
||||||
|
|
||||||
|
**预期效果**:复用成功经验,提升内容质量
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 9. **智能内容去重与相似度检测** ⭐⭐⭐
|
||||||
|
**价值**:避免重复内容,确保内容多样性
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 检测新生成内容与历史内容的相似度
|
||||||
|
- 自动去重(相似度 > 阈值时提示)
|
||||||
|
- 内容多样性分析(确保覆盖不同角度)
|
||||||
|
- 推荐内容角度(基于已有内容分析)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用文本相似度算法(如余弦相似度)
|
||||||
|
- 对比新内容与历史内容
|
||||||
|
- 提供相似度评分和建议
|
||||||
|
|
||||||
|
**预期效果**:确保内容多样性,避免重复投入
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🌐 四、平台与集成增强
|
||||||
|
|
||||||
|
#### 10. **多语言支持** ⭐⭐⭐⭐
|
||||||
|
**价值**:扩展国际市场,提升品牌全球影响力
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 支持英文、日文等多语言内容生成
|
||||||
|
- 多语言关键词挖掘
|
||||||
|
- 多语言平台支持(Medium、Dev.to、Reddit 等)
|
||||||
|
- 多语言验证(使用海外 AI 平台)
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 扩展 Prompt 模板支持多语言
|
||||||
|
- 添加多语言平台列表
|
||||||
|
- 集成海外 AI 平台(Claude、Gemini 等)
|
||||||
|
|
||||||
|
**预期效果**:拓展国际市场,提升全球品牌影响力
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 11. **API 接口与集成** ⭐⭐⭐⭐
|
||||||
|
**价值**:与其他系统集成,支持自动化流程
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- RESTful API 接口
|
||||||
|
- Webhook 支持(内容生成完成时通知)
|
||||||
|
- 与 CMS 系统集成
|
||||||
|
- 与营销自动化工具集成
|
||||||
|
- API 文档和示例代码
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用 FastAPI 创建 API 服务
|
||||||
|
- 提供认证和限流
|
||||||
|
- 支持异步任务
|
||||||
|
- 提供 SDK(Python/JavaScript)
|
||||||
|
|
||||||
|
**预期效果**:支持企业级集成,提升工具价值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 12. **团队协作与权限管理** ⭐⭐⭐
|
||||||
|
**价值**:支持团队使用,提升协作效率
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 多用户支持(注册/登录)
|
||||||
|
- 角色权限管理(管理员、编辑、查看者)
|
||||||
|
- 内容审核流程
|
||||||
|
- 团队数据共享
|
||||||
|
- 操作日志和审计
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 集成用户认证系统(如 Streamlit-Authenticator)
|
||||||
|
- 数据库添加用户和权限表
|
||||||
|
- 实现基于角色的访问控制
|
||||||
|
|
||||||
|
**预期效果**:支持团队协作,适合企业使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🎯 五、内容质量提升
|
||||||
|
|
||||||
|
#### 13. **内容个性化与定制** ⭐⭐⭐⭐
|
||||||
|
**价值**:根据目标受众定制内容风格和角度
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 目标受众画像(技术专家、业务人员、决策者等)
|
||||||
|
- 内容风格选择(专业、通俗、故事化等)
|
||||||
|
- 内容角度选择(功能对比、使用教程、案例分析等)
|
||||||
|
- 个性化内容生成
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 在 Prompt 中加入受众和风格参数
|
||||||
|
- 提供预设的受众模板
|
||||||
|
- 根据受众调整内容深度和语言风格
|
||||||
|
|
||||||
|
**预期效果**:提升内容针对性和效果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 14. **内容结构化增强** ⭐⭐⭐
|
||||||
|
**价值**:确保内容符合 GEO 最佳实践
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 自动检查内容结构完整性(标题、摘要、清单、FAQ 等)
|
||||||
|
- 结构化建议(缺失部分自动补充)
|
||||||
|
- 内容层次优化(确保逻辑清晰)
|
||||||
|
- Markdown/HTML 格式优化
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用 LLM 分析内容结构
|
||||||
|
- 识别缺失的结构元素
|
||||||
|
- 自动生成补充内容
|
||||||
|
|
||||||
|
**预期效果**:确保所有内容都符合 GEO 原则
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📈 六、高级分析功能
|
||||||
|
|
||||||
|
#### 15. **预测性分析与趋势预测** ⭐⭐⭐⭐
|
||||||
|
**价值**:预测未来趋势,提前布局
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 提及率趋势预测(未来 30 天)
|
||||||
|
- 关键词热度预测
|
||||||
|
- 竞品趋势预测
|
||||||
|
- 最佳行动时机推荐
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用时间序列分析(ARIMA、LSTM 等)
|
||||||
|
- 分析历史趋势
|
||||||
|
- 预测未来变化
|
||||||
|
- 提供置信区间
|
||||||
|
|
||||||
|
**预期效果**:提前布局,抢占先机
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 16. **内容关联分析** ⭐⭐⭐
|
||||||
|
**价值**:发现内容之间的关联,优化内容策略
|
||||||
|
|
||||||
|
**功能点**:
|
||||||
|
- 关键词关联分析(哪些关键词经常一起被提及)
|
||||||
|
- 平台关联分析(哪些平台组合效果好)
|
||||||
|
- 内容主题聚类
|
||||||
|
- 内容网络图可视化
|
||||||
|
|
||||||
|
**实现思路**:
|
||||||
|
- 使用关联规则挖掘(Apriori 算法)
|
||||||
|
- 构建内容关联图
|
||||||
|
- 可视化展示
|
||||||
|
|
||||||
|
**预期效果**:发现隐藏的内容策略规律
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 推荐优先级(综合价值与实现难度)
|
||||||
|
|
||||||
|
### 🔥 第一优先级(高价值 + 中等难度)
|
||||||
|
1. **智能内容质量评分系统** - 直接提升内容质量
|
||||||
|
2. **ROI 分析与成本优化** - 量化价值,优化投入
|
||||||
|
3. **智能工作流自动化** - 大幅提升效率
|
||||||
|
4. **智能关键词挖掘** - 发现新机会
|
||||||
|
|
||||||
|
### 🟡 第二优先级(高价值 + 较高难度)
|
||||||
|
5. **A/B 测试与内容对比** - 数据驱动优化
|
||||||
|
6. **竞品监控与预警** - 保持竞争优势
|
||||||
|
7. **内容效果预测模型** - 提前优化策略
|
||||||
|
|
||||||
|
### 🟢 第三优先级(中等价值)
|
||||||
|
8. **内容模板库** - 复用最佳实践
|
||||||
|
9. **多语言支持** - 扩展市场
|
||||||
|
10. **API 接口** - 企业级集成
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 实施建议
|
||||||
|
|
||||||
|
1. **分阶段实施**:先实现第一优先级功能,验证价值后再扩展
|
||||||
|
2. **数据积累**:先运行一段时间,积累足够数据后再做预测和分析
|
||||||
|
3. **用户反馈**:根据实际使用情况调整功能优先级
|
||||||
|
4. **技术选型**:考虑使用现有开源库(如 scikit-learn 用于预测模型)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始建议
|
||||||
|
|
||||||
|
**第一步**:实现"智能内容质量评分系统"
|
||||||
|
- 价值高,实现相对简单
|
||||||
|
- 可以立即提升用户体验
|
||||||
|
- 为后续功能打下基础
|
||||||
|
|
||||||
|
**第二步**:实现"ROI 分析与成本优化"
|
||||||
|
- 帮助用户量化价值
|
||||||
|
- 提升工具的商业价值
|
||||||
|
- 为定价策略提供依据
|
||||||
|
|
||||||
|
**第三步**:实现"智能工作流自动化"
|
||||||
|
- 大幅提升效率
|
||||||
|
- 增强用户粘性
|
||||||
|
- 差异化竞争优势
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# SQLite 持久化集成说明
|
||||||
|
|
||||||
|
## ✅ 已完成的功能
|
||||||
|
|
||||||
|
### 1. 数据自动保存
|
||||||
|
- ✅ **关键词生成**:自动保存到数据库
|
||||||
|
- ✅ **内容生成**:每生成一篇文章自动保存
|
||||||
|
- ✅ **文章优化**:优化记录自动保存
|
||||||
|
- ✅ **验证结果**:验证结果自动保存
|
||||||
|
|
||||||
|
### 2. 历史记录查看(新增 Tab5)
|
||||||
|
- ✅ 统计数据总览(关键词、文章、优化、验证数量)
|
||||||
|
- ✅ 历史文章列表和详情查看
|
||||||
|
- ✅ 历史优化记录和详情查看
|
||||||
|
- ✅ 历史验证结果和可视化
|
||||||
|
|
||||||
|
## 📁 数据库文件
|
||||||
|
|
||||||
|
- **位置**:`geo_data.db`(项目根目录)
|
||||||
|
- **格式**:SQLite 单文件数据库
|
||||||
|
- **已添加到**:`.gitignore`(不会提交到版本控制)
|
||||||
|
|
||||||
|
## 🚀 使用方法
|
||||||
|
|
||||||
|
### 正常使用
|
||||||
|
所有数据会自动保存,无需额外操作。只需正常使用各个功能模块即可。
|
||||||
|
|
||||||
|
### 查看历史记录
|
||||||
|
1. 点击 **"5 历史记录"** Tab
|
||||||
|
2. 查看统计数据
|
||||||
|
3. 浏览历史文章、优化记录、验证结果
|
||||||
|
4. 点击选择框查看详情
|
||||||
|
|
||||||
|
## 🔧 技术细节
|
||||||
|
|
||||||
|
### 错误处理
|
||||||
|
- 所有数据库操作都包含 try-except 错误处理
|
||||||
|
- 即使数据库保存失败,也不会影响主要功能
|
||||||
|
- 会显示警告提示,但不会中断流程
|
||||||
|
|
||||||
|
### 数据存储结构
|
||||||
|
- **keywords 表**:关键词列表
|
||||||
|
- **articles 表**:生成的文章内容
|
||||||
|
- **optimizations 表**:优化记录
|
||||||
|
- **verify_results 表**:验证结果
|
||||||
|
|
||||||
|
## 📊 性能
|
||||||
|
|
||||||
|
- SQLite 是轻量级数据库,性能优秀
|
||||||
|
- 单文件存储,易于备份和迁移
|
||||||
|
- 支持复杂查询和统计分析
|
||||||
|
|
||||||
|
## 🔄 数据迁移
|
||||||
|
|
||||||
|
如果需要迁移数据:
|
||||||
|
1. 复制 `geo_data.db` 文件即可
|
||||||
|
2. 或使用 SQLite 工具导出/导入
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **首次运行**:会自动创建数据库文件
|
||||||
|
2. **数据持久化**:关闭应用后数据不会丢失
|
||||||
|
3. **多品牌支持**:数据按品牌分类存储
|
||||||
|
4. **清空功能**:侧边栏的"重置全部结果"只清空 Session State,**不会删除数据库记录**
|
||||||
|
|
||||||
|
## 🐛 故障排除
|
||||||
|
|
||||||
|
如果遇到数据库相关错误:
|
||||||
|
1. 检查是否有写入权限
|
||||||
|
2. 检查 `geo_data.db` 文件是否被其他程序占用
|
||||||
|
3. 可以删除 `geo_data.db` 文件重新创建(会丢失所有历史数据)
|
||||||
|
|
||||||
|
## 📝 后续扩展建议
|
||||||
|
|
||||||
|
1. **数据导出**:可以添加导出功能,将数据导出为 CSV/Excel
|
||||||
|
2. **数据搜索**:可以添加搜索功能,按关键词搜索历史记录
|
||||||
|
3. **数据统计**:可以添加更详细的统计分析
|
||||||
|
4. **数据备份**:可以添加定期备份功能
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# 平台扩展安装说明
|
||||||
|
|
||||||
|
## 已支持的平台
|
||||||
|
|
||||||
|
### 基础平台(已包含依赖)
|
||||||
|
- DeepSeek
|
||||||
|
- OpenAI (GPT)
|
||||||
|
- Tongyi (通义千问)
|
||||||
|
- Groq
|
||||||
|
- Moonshot (Kimi)
|
||||||
|
|
||||||
|
### 新增平台(需要额外安装)
|
||||||
|
|
||||||
|
#### 1. 豆包(字节跳动)
|
||||||
|
|
||||||
|
**安装命令:**
|
||||||
|
```bash
|
||||||
|
pip install 'volcengine-python-sdk[ark]'
|
||||||
|
```
|
||||||
|
|
||||||
|
**API Key 格式:**
|
||||||
|
```
|
||||||
|
access_key:secret_key:endpoint_id
|
||||||
|
```
|
||||||
|
用冒号分隔三个值:
|
||||||
|
- `access_key`: 火山引擎 Access Key
|
||||||
|
- `secret_key`: 火山引擎 Secret Key
|
||||||
|
- `endpoint_id`: 接入点名称(Endpoint ID)
|
||||||
|
|
||||||
|
**获取方式:**
|
||||||
|
1. 访问 [火山引擎官网](https://www.volcengine.com/)
|
||||||
|
2. 注册账号并完成实名认证
|
||||||
|
3. 在控制台获取 Access Key 和 Secret Key
|
||||||
|
4. 创建模型接入点,获取 Endpoint ID
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
在侧边栏"生成&优化 LLM"或"验证用LLM"中选择"豆包(字节跳动)",输入格式化的 API Key。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. 文心一言(百度)
|
||||||
|
|
||||||
|
**安装命令:**
|
||||||
|
```bash
|
||||||
|
pip install qianfan
|
||||||
|
```
|
||||||
|
|
||||||
|
**API Key 格式:**
|
||||||
|
```
|
||||||
|
app_key:app_secret
|
||||||
|
```
|
||||||
|
用冒号分隔两个值:
|
||||||
|
- `app_key`: 百度智能云 App Key
|
||||||
|
- `app_secret`: 百度智能云 App Secret
|
||||||
|
|
||||||
|
**获取方式:**
|
||||||
|
1. 访问 [百度智能云千帆平台](https://cloud.baidu.com/product/qianfan.html)
|
||||||
|
2. 注册账号并完成认证
|
||||||
|
3. 创建应用,获取 App Key 和 App Secret
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
在侧边栏"生成&优化 LLM"或"验证用LLM"中选择"文心一言(百度)",输入格式化的 API Key。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速安装所有平台
|
||||||
|
|
||||||
|
如果需要使用所有平台,可以运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install 'volcengine-python-sdk[ark]' qianfan
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **API Key 格式**:豆包和文心一言的 API Key 需要使用冒号分隔多个值
|
||||||
|
2. **依赖冲突**:某些包可能有版本冲突,如遇到问题请单独安装
|
||||||
|
3. **可选安装**:这些平台是可选的,如果不使用可以不安装,不影响其他功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 豆包安装失败
|
||||||
|
- 确保 Python 版本 >= 3.7
|
||||||
|
- Windows 系统可能需要启用长路径支持
|
||||||
|
- 尝试:`pip install 'volcengine-python-sdk[ark]' -U`
|
||||||
|
|
||||||
|
### 文心一言初始化失败
|
||||||
|
- 确保已安装 `qianfan` 包
|
||||||
|
- 检查 API Key 格式是否正确(app_key:app_secret)
|
||||||
|
- 确认环境变量或参数中的 AK/SK 是否正确
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
# 运行命令
|
||||||
|
|
||||||
|
`streamlit run geo_tool.py`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 功能迭代计划
|
||||||
|
|
||||||
|
## ✅ 已完成功能
|
||||||
|
|
||||||
|
- [x] **数据持久化(SQLite)** - 已完成
|
||||||
|
- 关键词、文章、优化记录、验证结果自动保存
|
||||||
|
- 历史记录查看功能(Tab5)
|
||||||
|
- 详见 `INTEGRATION_NOTES.md`
|
||||||
|
|
||||||
|
- [x] **AI 蒸馏词 - 托词工具** - 已完成
|
||||||
|
- 支持三种生成模式:AI生成、托词工具、混合模式
|
||||||
|
- 词库管理(编辑、导入、导出)
|
||||||
|
- 组合算法(支持10种组合模式)
|
||||||
|
- LLM 润色功能(混合模式)
|
||||||
|
- 自动去重和相似度过滤
|
||||||
|
|
||||||
|
- [x] **收录平台扩展** - 已完成
|
||||||
|
- 新增豆包(字节跳动)支持
|
||||||
|
- 新增文心一言(百度)支持
|
||||||
|
- API Key 格式提示和验证
|
||||||
|
- 详见 `PLATFORM_SETUP.md`
|
||||||
|
|
||||||
|
- [x] **自媒体账号平台扩展** - 已完成
|
||||||
|
- 新增微信公众号(长文)支持
|
||||||
|
- 新增抖音图文(短内容)支持
|
||||||
|
- 新增百家号、网易号、企鹅号、简书支持
|
||||||
|
- 每个平台都有专门的 Prompt 模板
|
||||||
|
- 支持 Markdown 格式输出
|
||||||
|
|
||||||
|
- [x] **AI 数据报表** - 已完成
|
||||||
|
- 自动验证任务(使用历史关键词)
|
||||||
|
- 提及率趋势图(按日期展示)
|
||||||
|
- 平台贡献度分析(文章平台分布)
|
||||||
|
- 关键词效果排名(Top 20)
|
||||||
|
- 竞品对比分析(多维度对比)
|
||||||
|
- 数据导出功能(CSV 格式)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 待实现功能(按优先级排序)
|
||||||
|
|
||||||
|
> **优先级说明**:优先级基于对 GEO 效果的直接影响、实现成本和用户价值综合评估。
|
||||||
|
> **调整原则**:优先实现能直接提升 GEO 效果的功能(平台扩展、内容渠道),延后实现辅助性功能(图库)。
|
||||||
|
|
||||||
|
### 🔥 高优先级(核心功能增强)
|
||||||
|
|
||||||
|
#### 1. 收录平台扩展
|
||||||
|
|
||||||
|
**当前支持:** DeepSeek, OpenAI, Tongyi, Groq, Moonshot
|
||||||
|
|
||||||
|
**待添加平台:**
|
||||||
|
- 豆包(字节跳动)- ⭐ 高优先级(用户量大)
|
||||||
|
- 文心一言(百度)- ⭐ 高优先级(用户量大)
|
||||||
|
- 腾讯元宝 - 需确认 API 可用性
|
||||||
|
- 纳米 - 需确认具体 API
|
||||||
|
|
||||||
|
**重要性分析:**
|
||||||
|
- ✅ **直接影响 GEO 效果**:更多平台 = 更全面的验证覆盖
|
||||||
|
- ✅ **提升验证准确性**:国内主流平台(豆包、文心一言)用户量大,验证结果更有参考价值
|
||||||
|
- ✅ **实现成本低**:主要是 API 接入,技术难度不高
|
||||||
|
|
||||||
|
**评估与优化建议:**
|
||||||
|
- ⚠️ **需要优化**:
|
||||||
|
1. **API 接入优先级**:优先接入豆包、文心一言(用户量大)
|
||||||
|
2. **平台分类管理**:按平台类型分类(国内/国外、通用/专业)
|
||||||
|
3. **验证成本控制**:支持批量验证时的并发控制,避免 API 费用过高
|
||||||
|
|
||||||
|
**实现建议:**
|
||||||
|
- 在 `build_llm` 函数中扩展新平台支持
|
||||||
|
- 在侧边栏配置中增加新平台选项
|
||||||
|
- 添加平台可用性检测
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. 自媒体账号平台扩展
|
||||||
|
|
||||||
|
**当前支持:** 知乎、小红书、CSDN、B站、头条号、GitHub
|
||||||
|
|
||||||
|
**待添加平台:**
|
||||||
|
- 微信公众号 - ⭐ 高优先级(用户量大、影响力强)
|
||||||
|
- 抖音图文 - ⭐ 高优先级(流量大)
|
||||||
|
- 百家号 - 中优先级(百度生态)
|
||||||
|
- 网易号 - 中优先级
|
||||||
|
- 企鹅号 - 中优先级
|
||||||
|
- 简书 - 低优先级
|
||||||
|
|
||||||
|
**重要性分析:**
|
||||||
|
- ✅ **扩大内容投放渠道**:更多平台 = 更多曝光机会
|
||||||
|
- ✅ **提升品牌影响力**:微信公众号、抖音等平台用户量大
|
||||||
|
- ✅ **实现成本中等**:主要是 Prompt 模板和格式转换
|
||||||
|
|
||||||
|
**评估与优化建议:**
|
||||||
|
- ⚠️ **需要优化**:
|
||||||
|
1. **平台特性差异**:
|
||||||
|
- 微信公众号:需要特殊格式(富文本、排版)
|
||||||
|
- 抖音图文:图片为主,文字为辅
|
||||||
|
- 百家号/网易号/企鹅号:可能有字数限制、格式要求
|
||||||
|
2. **内容适配策略**:
|
||||||
|
- 为每个平台创建专门的 Prompt 模板
|
||||||
|
- 支持平台特定的格式要求(如微信公众号的 Markdown 转 HTML)
|
||||||
|
3. **发布功能(可选)**:
|
||||||
|
- 初期只生成内容,后续可考虑接入各平台 API 实现自动发布
|
||||||
|
|
||||||
|
**实现建议:**
|
||||||
|
- 扩展 `platforms` 列表
|
||||||
|
- 为每个平台创建专门的 Prompt 模板
|
||||||
|
- 添加平台格式转换功能(如 Markdown → HTML)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. 稿件记录(数据持久化)
|
||||||
|
|
||||||
|
**功能描述:**
|
||||||
|
- 保留所有的稿件记录
|
||||||
|
|
||||||
|
**状态:** ✅ **已完成**
|
||||||
|
- 已实现 SQLite 数据持久化
|
||||||
|
- 支持关键词、文章、优化记录、验证结果的保存和查看
|
||||||
|
- 详见 `INTEGRATION_NOTES.md`
|
||||||
|
|
||||||
|
**后续扩展建议:**
|
||||||
|
- 数据导出功能(CSV/Excel)
|
||||||
|
- 数据搜索功能(按关键词搜索历史记录)
|
||||||
|
- 更详细的统计分析
|
||||||
|
- 数据备份功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🟡 中优先级(功能扩展)
|
||||||
|
|
||||||
|
#### 4. AI 数据报表
|
||||||
|
|
||||||
|
**功能描述:**
|
||||||
|
- 系统自动模拟用户提问
|
||||||
|
- 收录结果实时同步至 AI 数据报表
|
||||||
|
- 清晰展示哪些词已被引用、哪些平台贡献了曝光
|
||||||
|
|
||||||
|
**重要性分析:**
|
||||||
|
- ✅ **监控 GEO 效果**:自动化监控,数据可视化
|
||||||
|
- ✅ **指导优化方向**:通过数据反馈优化内容策略
|
||||||
|
- ⚠️ **实现成本较高**:需要定时任务、数据可视化等
|
||||||
|
|
||||||
|
**评估与优化建议:**
|
||||||
|
- ⚠️ **需要优化**:
|
||||||
|
1. **模拟提问的策略**:
|
||||||
|
- 定期自动验证(如每天/每周)
|
||||||
|
- 支持自定义验证频率
|
||||||
|
- 记录历史趋势(提及率变化)
|
||||||
|
2. **数据存储**:
|
||||||
|
- 使用数据库(SQLite)存储历史数据
|
||||||
|
- 支持数据导出和分析
|
||||||
|
3. **报表功能**:
|
||||||
|
- 提及率趋势图
|
||||||
|
- 平台贡献度分析
|
||||||
|
- 关键词效果排名
|
||||||
|
- 竞品对比分析
|
||||||
|
4. **实时同步**:
|
||||||
|
- 后台任务 + 实时更新 UI
|
||||||
|
|
||||||
|
**实现建议:**
|
||||||
|
- 新增模块:AI 数据报表(可放在 Tab5 或独立 Tab)
|
||||||
|
- 自动验证任务(定时/手动触发)
|
||||||
|
- 数据可视化(趋势图、对比图、热力图)
|
||||||
|
- 数据导出功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🟢 低优先级(高级功能 / 可选功能)
|
||||||
|
|
||||||
|
#### 5. 企业知识库 - 企业图库
|
||||||
|
|
||||||
|
**功能描述:**
|
||||||
|
- 分类上传产品图、场景图、资质证书等
|
||||||
|
- 这些素材会在后续内容生成中自动嵌入,确保品牌一致性
|
||||||
|
|
||||||
|
**重要性分析:**
|
||||||
|
- ⚠️ **对 GEO 直接贡献有限**:GEO 核心是文本内容,大模型主要从文本中提取信息
|
||||||
|
- ⚠️ **适用场景有限**:主要适用于小红书、抖音等图文平台,对知乎、CSDN 等文字平台作用不大
|
||||||
|
- ⚠️ **实现成本较高**:需要图片存储、管理、智能匹配等功能
|
||||||
|
- ✅ **替代方案**:可手动配图,或让 LLM 生成图片描述/建议
|
||||||
|
|
||||||
|
**评估与优化建议:**
|
||||||
|
- ✅ **优点**:提升品牌一致性,素材复用
|
||||||
|
- ⚠️ **需要优化**:
|
||||||
|
1. **图片存储与管理**:
|
||||||
|
- 使用本地文件系统或云存储(OSS/S3)
|
||||||
|
- 支持图片分类、标签、搜索
|
||||||
|
2. **图片在内容中的嵌入方式**:
|
||||||
|
- 文本内容:生成图片描述,提示"可配图:xxx"
|
||||||
|
- Markdown:自动插入图片链接
|
||||||
|
- 小红书/抖音:生成图片使用建议
|
||||||
|
3. **图片与内容的智能匹配**:
|
||||||
|
- 使用 LLM 分析内容主题,自动推荐匹配图片
|
||||||
|
4. **版权与合规**:
|
||||||
|
- 增加图片版权信息记录
|
||||||
|
|
||||||
|
**实现建议:**
|
||||||
|
- 新增模块:企业图库管理(可放在侧边栏或独立 Tab)
|
||||||
|
- 图片上传(支持批量)
|
||||||
|
- 图片分类(产品图、场景图、资质证书等)
|
||||||
|
- 图片标签系统
|
||||||
|
- 内容生成时自动匹配图片
|
||||||
|
|
||||||
|
**建议:** 可延后实现,或先实现简化版(仅图片上传和描述生成)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 6. 数据报表高级分析
|
||||||
|
|
||||||
|
- 更详细的统计分析
|
||||||
|
- 预测性分析
|
||||||
|
- 竞品深度对比
|
||||||
|
|
||||||
|
#### 7. 自动发布功能
|
||||||
|
|
||||||
|
- 接入各平台 API
|
||||||
|
- 自动发布生成的内容
|
||||||
|
- 发布状态跟踪
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 整体架构建议
|
||||||
|
|
||||||
|
### 优先级排序
|
||||||
|
|
||||||
|
1. **高优先级**(核心功能增强)
|
||||||
|
- 收录平台扩展(豆包、文心一言等)⭐ ✅ 已完成
|
||||||
|
- 自媒体平台扩展(微信公众号、抖音等)⭐ ✅ 已完成
|
||||||
|
- 稿件记录 ✅ 已完成
|
||||||
|
|
||||||
|
2. **中优先级**(功能扩展)
|
||||||
|
- AI 数据报表(基础版)✅ 已完成
|
||||||
|
|
||||||
|
3. **低优先级**(高级功能 / 可选功能)
|
||||||
|
- 企业图库(对 GEO 直接贡献有限,可延后)
|
||||||
|
- 数据报表高级分析
|
||||||
|
- 自动发布功能
|
||||||
|
|
||||||
|
### 技术架构建议
|
||||||
|
|
||||||
|
1. **数据持久化** ✅ 已完成
|
||||||
|
- 使用 SQLite(轻量)
|
||||||
|
- 存储:关键词、内容、优化记录、验证结果、图片元数据
|
||||||
|
|
||||||
|
2. **模块化重构**
|
||||||
|
- 将功能拆分为独立模块
|
||||||
|
- 便于维护和扩展
|
||||||
|
|
||||||
|
3. **配置管理**
|
||||||
|
- 使用配置文件管理平台模板、词库等
|
||||||
|
- 支持用户自定义
|
||||||
|
|
||||||
|
4. **性能优化**
|
||||||
|
- 批量操作使用并发/异步
|
||||||
|
- 缓存常用数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 实现记录
|
||||||
|
|
||||||
|
### 已完成
|
||||||
|
- [x] SQLite 数据持久化(2024)
|
||||||
|
- [x] 历史记录查看功能
|
||||||
|
- [x] 托词工具(AI 蒸馏词)- 2024
|
||||||
|
- [x] 收录平台扩展(豆包、文心一言)- 2024
|
||||||
|
- [x] 自媒体平台扩展(微信公众号、抖音等)- 2024
|
||||||
|
- [x] AI 数据报表 - 2024
|
||||||
|
|
||||||
|
### 待开始(按优先级)
|
||||||
|
- [ ] 企业图库 - 低优先级(可延后)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 相关文档
|
||||||
|
|
||||||
|
- `INTEGRATION_NOTES.md` - SQLite 持久化集成说明
|
||||||
|
- `STORAGE_GUIDE.md` - 数据持久化方案对比
|
||||||
|
- `PLATFORM_SETUP.md` - 平台扩展安装说明(豆包、文心一言)
|
||||||
|
- `data_storage.py` - 数据存储模块实现
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
# 数据持久化方案对比
|
||||||
|
|
||||||
|
## 为什么不能用 IndexedDB?
|
||||||
|
|
||||||
|
**IndexedDB 是浏览器 API**,只能在 JavaScript 前端使用。
|
||||||
|
**Streamlit 是 Python 后端应用**,运行在服务器端,无法使用 IndexedDB。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案对比
|
||||||
|
|
||||||
|
### 方案1:SQLite(⭐ 推荐)
|
||||||
|
|
||||||
|
**优点:**
|
||||||
|
- ✅ Python 内置支持(`sqlite3`),无需安装额外依赖
|
||||||
|
- ✅ 单文件数据库,易于备份和迁移
|
||||||
|
- ✅ 查询性能好,支持复杂查询
|
||||||
|
- ✅ 支持事务,数据安全
|
||||||
|
- ✅ 支持 SQL 查询,灵活强大
|
||||||
|
- ✅ 适合 MVP 到生产环境的平滑升级
|
||||||
|
|
||||||
|
**缺点:**
|
||||||
|
- ⚠️ 需要学习基本的 SQL(但很简单)
|
||||||
|
- ⚠️ 多进程写入需要处理锁(Streamlit 单进程,无此问题)
|
||||||
|
|
||||||
|
**代码复杂度:** ⭐⭐(非常简单)
|
||||||
|
|
||||||
|
**适用场景:** MVP 和生产环境都适用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 方案2:JSON 文件
|
||||||
|
|
||||||
|
**优点:**
|
||||||
|
- ✅ 最简单,无需学习 SQL
|
||||||
|
- ✅ 人类可读,易于调试
|
||||||
|
- ✅ 无需数据库知识
|
||||||
|
|
||||||
|
**缺点:**
|
||||||
|
- ❌ 查询性能差(需要加载整个文件)
|
||||||
|
- ❌ 数据量大时很慢
|
||||||
|
- ❌ 并发写入可能丢失数据
|
||||||
|
- ❌ 不支持复杂查询
|
||||||
|
|
||||||
|
**代码复杂度:** ⭐(极简单)
|
||||||
|
|
||||||
|
**适用场景:** 仅适合数据量很小(<1000条)的 MVP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 推荐方案:SQLite
|
||||||
|
|
||||||
|
### 为什么推荐 SQLite?
|
||||||
|
|
||||||
|
1. **其实很简单**:只需要几行代码
|
||||||
|
```python
|
||||||
|
import sqlite3
|
||||||
|
conn = sqlite3.connect('data.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("INSERT INTO table VALUES (?)", (value,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **性能好**:即使数据量增长到几万条,依然很快
|
||||||
|
|
||||||
|
3. **功能强大**:支持统计、查询、分析,为后续功能扩展打好基础
|
||||||
|
|
||||||
|
4. **零依赖**:Python 内置,无需安装任何包
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 使用已封装好的 DataStorage 类
|
||||||
|
|
||||||
|
我已经为你创建了 `data_storage.py`,提供了统一的接口:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from data_storage import DataStorage
|
||||||
|
|
||||||
|
# 初始化(SQLite方式)
|
||||||
|
storage = DataStorage(storage_type="sqlite", db_path="geo_data.db")
|
||||||
|
|
||||||
|
# 保存关键词
|
||||||
|
storage.save_keywords(["关键词1", "关键词2"], "品牌名")
|
||||||
|
|
||||||
|
# 获取关键词
|
||||||
|
keywords = storage.get_keywords("品牌名")
|
||||||
|
|
||||||
|
# 保存文章
|
||||||
|
storage.save_article("关键词", "平台", "内容", "文件名", "品牌名")
|
||||||
|
|
||||||
|
# 获取统计数据
|
||||||
|
stats = storage.get_stats("品牌名")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 最小改动集成
|
||||||
|
|
||||||
|
在 `geo_tool.py` 中,只需要在关键位置添加几行保存代码:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 文件顶部
|
||||||
|
from data_storage import DataStorage
|
||||||
|
storage = DataStorage(storage_type="sqlite", db_path="geo_data.db")
|
||||||
|
|
||||||
|
# 关键词生成后(约第533行)
|
||||||
|
if cleaned:
|
||||||
|
st.session_state.keywords = cleaned
|
||||||
|
storage.save_keywords(cleaned, brand) # 新增这一行
|
||||||
|
st.success(f"生成完成({len(cleaned)} 条)")
|
||||||
|
|
||||||
|
# 内容生成后(约第714行)
|
||||||
|
st.session_state.generated_contents = contents
|
||||||
|
storage.save_article(keyword, plat, content, filename, brand) # 在循环中添加
|
||||||
|
|
||||||
|
# 优化后(约第838行)
|
||||||
|
st.session_state.optimized_article = optimized_article
|
||||||
|
storage.save_optimization(
|
||||||
|
original_article, optimized_article, changes, target_platform, brand
|
||||||
|
) # 新增
|
||||||
|
|
||||||
|
# 验证后(约第932行)
|
||||||
|
st.session_state.verify_combined = combined
|
||||||
|
storage.save_verify_results(all_results) # 新增
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 添加历史记录查看功能(可选)
|
||||||
|
|
||||||
|
可以新增一个 Tab 来查看历史数据:
|
||||||
|
|
||||||
|
```python
|
||||||
|
tab5 = st.tabs([..., "5 历史记录"])
|
||||||
|
|
||||||
|
with tab5:
|
||||||
|
st.header("历史记录")
|
||||||
|
|
||||||
|
# 统计数据
|
||||||
|
stats = storage.get_stats(brand)
|
||||||
|
col1, col2, col3, col4 = st.columns(4)
|
||||||
|
col1.metric("关键词", stats["keywords_count"])
|
||||||
|
col2.metric("文章", stats["articles_count"])
|
||||||
|
col3.metric("优化", stats["optimizations_count"])
|
||||||
|
col4.metric("验证", stats["verify_results_count"])
|
||||||
|
|
||||||
|
# 历史文章列表
|
||||||
|
articles = storage.get_articles(brand=brand)
|
||||||
|
if articles:
|
||||||
|
df = pd.DataFrame(articles)
|
||||||
|
st.dataframe(df[["keyword", "platform", "created_at"]])
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据库文件位置
|
||||||
|
|
||||||
|
- **SQLite 文件**:`geo_data.db`(项目根目录)
|
||||||
|
- **JSON 文件**:`data/` 目录(如果使用 JSON 方式)
|
||||||
|
|
||||||
|
**建议:** 将 `geo_data.db` 添加到 `.gitignore`,避免提交到版本控制。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 性能对比(参考)
|
||||||
|
|
||||||
|
| 数据量 | SQLite | JSON文件 |
|
||||||
|
|--------|--------|----------|
|
||||||
|
| 100条 | <10ms | <10ms |
|
||||||
|
| 1000条 | <50ms | ~100ms |
|
||||||
|
| 10000条 | ~200ms | ~5秒 |
|
||||||
|
| 100000条 | ~1秒 | 很慢 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
**对于 MVP 版本,强烈推荐使用 SQLite:**
|
||||||
|
|
||||||
|
1. ✅ 简单:使用封装好的 `DataStorage` 类,只需几行代码
|
||||||
|
2. ✅ 高效:性能好,支持未来扩展
|
||||||
|
3. ✅ 可靠:数据安全,支持事务
|
||||||
|
4. ✅ 零依赖:Python 内置,无需安装
|
||||||
|
|
||||||
|
**如果数据量真的非常小(<100条),可以考虑 JSON 文件。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
1. 查看 `data_storage.py` 了解实现细节
|
||||||
|
2. 查看 `storage_example.py` 了解使用方法
|
||||||
|
3. 在 `geo_tool.py` 中集成(参考上面的最小改动示例)
|
||||||
|
|
||||||
|
需要我帮你直接集成到 `geo_tool.py` 吗?
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
"""
|
||||||
|
内容质量评分系统
|
||||||
|
自动评估内容是否符合 GEO 原则,提供改进建议
|
||||||
|
"""
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from langchain_core.prompts import PromptTemplate
|
||||||
|
from langchain_core.output_parsers import StrOutputParser
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class ContentScorer:
|
||||||
|
"""内容质量评分器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.scoring_prompt_template = """
|
||||||
|
你是一名 GEO(生成式引擎优化)内容质量评估专家。请对以下内容进行全面评估,并给出详细的评分和改进建议。
|
||||||
|
|
||||||
|
【内容】
|
||||||
|
{content}
|
||||||
|
|
||||||
|
【品牌】{brand}
|
||||||
|
【优势】{advantages}
|
||||||
|
【平台】{platform}
|
||||||
|
|
||||||
|
【评估维度】
|
||||||
|
请从以下维度进行评估(每个维度 0-25 分,总分 100 分):
|
||||||
|
|
||||||
|
1. **结构化程度**(25分)
|
||||||
|
- 是否有清晰的标题层级?
|
||||||
|
- 是否包含清单、列表、FAQ 等结构化元素?
|
||||||
|
- 内容层次是否清晰?
|
||||||
|
- 是否有结论摘要?
|
||||||
|
|
||||||
|
2. **品牌提及质量**(25分)
|
||||||
|
- 品牌提及次数是否合适(2-4次)?
|
||||||
|
- 品牌提及位置是否靠前(前1/3优先)?
|
||||||
|
- 品牌提及是否自然(先通用标准,再品牌适用)?
|
||||||
|
- 品牌与内容的关联度如何?
|
||||||
|
|
||||||
|
3. **内容权威性**(25分)
|
||||||
|
- 是否有数据支撑或案例引用?
|
||||||
|
- 是否有评估维度或选择标准?
|
||||||
|
- 是否避免编造数据(使用占位建议)?
|
||||||
|
- 内容是否专业可信?
|
||||||
|
|
||||||
|
4. **可引用性**(25分)
|
||||||
|
- 信息密度是否高?
|
||||||
|
- 结论是否先行?
|
||||||
|
- 是否容易被 AI 提取和引用?
|
||||||
|
- 是否符合目标平台的格式要求?
|
||||||
|
|
||||||
|
【输出格式】
|
||||||
|
请严格按照以下 JSON 格式输出,不要添加任何其他内容:
|
||||||
|
|
||||||
|
{{
|
||||||
|
"scores": {{
|
||||||
|
"structure": <结构化得分 0-25>,
|
||||||
|
"brand_mention": <品牌提及得分 0-25>,
|
||||||
|
"authority": <权威性得分 0-25>,
|
||||||
|
"citations": <可引用性得分 0-25>,
|
||||||
|
"total": <总分 0-100>
|
||||||
|
}},
|
||||||
|
"details": {{
|
||||||
|
"structure": "<结构化评估详情>",
|
||||||
|
"brand_mention": "<品牌提及评估详情>",
|
||||||
|
"authority": "<权威性评估详情>",
|
||||||
|
"citations": "<可引用性评估详情>"
|
||||||
|
}},
|
||||||
|
"improvements": [
|
||||||
|
"<改进建议1>",
|
||||||
|
"<改进建议2>",
|
||||||
|
"<改进建议3>"
|
||||||
|
],
|
||||||
|
"strengths": [
|
||||||
|
"<优点1>",
|
||||||
|
"<优点2>"
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
|
||||||
|
【开始评估】
|
||||||
|
"""
|
||||||
|
|
||||||
|
def score_content(self, content: str, brand: str, advantages: str,
|
||||||
|
platform: str, llm_chain) -> Dict:
|
||||||
|
"""
|
||||||
|
对内容进行质量评分
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: 要评分的内容
|
||||||
|
brand: 品牌名称
|
||||||
|
advantages: 品牌优势
|
||||||
|
platform: 发布平台
|
||||||
|
llm_chain: LangChain 链对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
包含评分、详情和改进建议的字典
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
prompt = PromptTemplate.from_template(self.scoring_prompt_template)
|
||||||
|
chain = prompt | llm_chain | StrOutputParser()
|
||||||
|
|
||||||
|
result = chain.invoke({
|
||||||
|
"content": content,
|
||||||
|
"brand": brand,
|
||||||
|
"advantages": advantages,
|
||||||
|
"platform": platform
|
||||||
|
})
|
||||||
|
|
||||||
|
# 尝试解析 JSON
|
||||||
|
score_data = self._parse_score_result(result)
|
||||||
|
|
||||||
|
return score_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# 如果评分失败,返回默认评分
|
||||||
|
return {
|
||||||
|
"scores": {
|
||||||
|
"structure": 0,
|
||||||
|
"brand_mention": 0,
|
||||||
|
"authority": 0,
|
||||||
|
"citations": 0,
|
||||||
|
"total": 0
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"structure": f"评分失败:{str(e)}",
|
||||||
|
"brand_mention": "",
|
||||||
|
"authority": "",
|
||||||
|
"citations": ""
|
||||||
|
},
|
||||||
|
"improvements": ["评分系统暂时无法评估此内容,请手动检查"],
|
||||||
|
"strengths": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def _parse_score_result(self, result: str) -> Dict:
|
||||||
|
"""解析评分结果"""
|
||||||
|
# 尝试提取 JSON
|
||||||
|
json_match = re.search(r'\{.*\}', result, re.DOTALL)
|
||||||
|
if json_match:
|
||||||
|
try:
|
||||||
|
score_data = json.loads(json_match.group())
|
||||||
|
# 验证数据结构
|
||||||
|
if "scores" in score_data and "total" in score_data["scores"]:
|
||||||
|
return score_data
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 如果无法解析 JSON,尝试从文本中提取信息
|
||||||
|
return self._extract_scores_from_text(result)
|
||||||
|
|
||||||
|
def _extract_scores_from_text(self, text: str) -> Dict:
|
||||||
|
"""从文本中提取评分信息(备用方案)"""
|
||||||
|
# 尝试提取总分
|
||||||
|
total_match = re.search(r'总分[::]\s*(\d+)', text)
|
||||||
|
total_score = int(total_match.group(1)) if total_match else 0
|
||||||
|
|
||||||
|
# 简单分配分数(如果无法精确提取)
|
||||||
|
avg_score = total_score // 4 if total_score > 0 else 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
"scores": {
|
||||||
|
"structure": avg_score,
|
||||||
|
"brand_mention": avg_score,
|
||||||
|
"authority": avg_score,
|
||||||
|
"citations": avg_score,
|
||||||
|
"total": total_score
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"structure": "无法解析详细评分",
|
||||||
|
"brand_mention": "无法解析详细评分",
|
||||||
|
"authority": "无法解析详细评分",
|
||||||
|
"citations": "无法解析详细评分"
|
||||||
|
},
|
||||||
|
"improvements": ["请检查内容是否符合 GEO 原则"],
|
||||||
|
"strengths": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_score_level(self, total_score: int) -> tuple:
|
||||||
|
"""
|
||||||
|
根据总分返回等级和颜色
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(等级名称, 颜色代码)
|
||||||
|
"""
|
||||||
|
if total_score >= 90:
|
||||||
|
return ("优秀", "#10B981") # 绿色
|
||||||
|
elif total_score >= 75:
|
||||||
|
return ("良好", "#3B82F6") # 蓝色
|
||||||
|
elif total_score >= 60:
|
||||||
|
return ("中等", "#F59E0B") # 橙色
|
||||||
|
else:
|
||||||
|
return ("需改进", "#EF4444") # 红色
|
||||||
|
|
||||||
|
def get_quick_assessment(self, content: str, brand: str) -> Dict:
|
||||||
|
"""
|
||||||
|
快速评估(不调用 LLM,基于规则)
|
||||||
|
用于在 LLM 评分前提供初步评估
|
||||||
|
"""
|
||||||
|
assessment = {
|
||||||
|
"has_title": bool(re.search(r'^#+\s+|^标题|^##', content, re.MULTILINE)),
|
||||||
|
"has_list": bool(re.search(r'[-*•]\s+|^\d+[\.\)]\s+', content, re.MULTILINE)),
|
||||||
|
"has_faq": bool(re.search(r'FAQ|常见问题|Q[::]|问[::]', content, re.IGNORECASE)),
|
||||||
|
"brand_count": len(re.findall(re.escape(brand), content, re.IGNORECASE)),
|
||||||
|
"word_count": len(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 计算初步分数
|
||||||
|
quick_score = 0
|
||||||
|
if assessment["has_title"]:
|
||||||
|
quick_score += 5
|
||||||
|
if assessment["has_list"]:
|
||||||
|
quick_score += 5
|
||||||
|
if assessment["has_faq"]:
|
||||||
|
quick_score += 5
|
||||||
|
if 2 <= assessment["brand_count"] <= 4:
|
||||||
|
quick_score += 10
|
||||||
|
elif assessment["brand_count"] > 4:
|
||||||
|
quick_score += 5
|
||||||
|
|
||||||
|
assessment["quick_score"] = min(quick_score, 30) # 最高30分(快速评估)
|
||||||
|
|
||||||
|
return assessment
|
||||||
+453
@@ -0,0 +1,453 @@
|
|||||||
|
"""
|
||||||
|
轻量级数据持久化模块 - MVP版本
|
||||||
|
支持 SQLite 和 JSON 两种存储方式
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Dict, Optional, Any
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
class DataStorage:
|
||||||
|
"""统一的数据存储接口,支持SQLite和JSON两种后端"""
|
||||||
|
|
||||||
|
def __init__(self, storage_type: str = "sqlite", db_path: str = "geo_data.db"):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
storage_type: "sqlite" 或 "json"
|
||||||
|
db_path: SQLite数据库路径,或JSON文件目录
|
||||||
|
"""
|
||||||
|
self.storage_type = storage_type
|
||||||
|
self.db_path = db_path
|
||||||
|
|
||||||
|
if storage_type == "sqlite":
|
||||||
|
self._init_sqlite()
|
||||||
|
else:
|
||||||
|
self._init_json()
|
||||||
|
|
||||||
|
def _init_sqlite(self):
|
||||||
|
"""初始化SQLite数据库"""
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 关键词表
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS keywords (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
keyword TEXT NOT NULL,
|
||||||
|
brand TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 内容表(生成的文章)
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS articles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
keyword TEXT,
|
||||||
|
platform TEXT,
|
||||||
|
content TEXT,
|
||||||
|
filename TEXT,
|
||||||
|
brand TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 优化记录表
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS optimizations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
original_content TEXT,
|
||||||
|
optimized_content TEXT,
|
||||||
|
changes TEXT,
|
||||||
|
platform TEXT,
|
||||||
|
brand TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 验证结果表
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS verify_results (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
query TEXT,
|
||||||
|
brand TEXT,
|
||||||
|
verify_model TEXT,
|
||||||
|
mention_count INTEGER,
|
||||||
|
mention_position TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def _init_json(self):
|
||||||
|
"""初始化JSON存储目录"""
|
||||||
|
Path(self.db_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# ==================== 关键词相关 ====================
|
||||||
|
|
||||||
|
def save_keywords(self, keywords: List[str], brand: str):
|
||||||
|
"""保存关键词列表"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
for keyword in keywords:
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO keywords (keyword, brand) VALUES (?, ?)",
|
||||||
|
(keyword, brand)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
# JSON方式:追加到文件
|
||||||
|
json_file = Path(self.db_path) / "keywords.json"
|
||||||
|
data = []
|
||||||
|
if json_file.exists():
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
for keyword in keywords:
|
||||||
|
data.append({
|
||||||
|
"keyword": keyword,
|
||||||
|
"brand": brand,
|
||||||
|
"created_at": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(json_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def get_keywords(self, brand: Optional[str] = None) -> List[str]:
|
||||||
|
"""获取关键词列表"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
if brand:
|
||||||
|
cursor.execute("SELECT keyword FROM keywords WHERE brand = ?", (brand,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT keyword FROM keywords")
|
||||||
|
keywords = [row[0] for row in cursor.fetchall()]
|
||||||
|
conn.close()
|
||||||
|
return keywords
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "keywords.json"
|
||||||
|
if not json_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
if brand:
|
||||||
|
return [item["keyword"] for item in data if item.get("brand") == brand]
|
||||||
|
return [item["keyword"] for item in data]
|
||||||
|
|
||||||
|
# ==================== 文章内容相关 ====================
|
||||||
|
|
||||||
|
def save_article(self, keyword: str, platform: str, content: str,
|
||||||
|
filename: str, brand: str):
|
||||||
|
"""保存生成的文章"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO articles (keyword, platform, content, filename, brand)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", (keyword, platform, content, filename, brand))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "articles.json"
|
||||||
|
data = []
|
||||||
|
if json_file.exists():
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
"keyword": keyword,
|
||||||
|
"platform": platform,
|
||||||
|
"content": content,
|
||||||
|
"filename": filename,
|
||||||
|
"brand": brand,
|
||||||
|
"created_at": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(json_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def get_articles(self, brand: Optional[str] = None,
|
||||||
|
platform: Optional[str] = None) -> List[Dict]:
|
||||||
|
"""获取文章列表"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
if brand and platform:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"SELECT * FROM articles WHERE brand = ? AND platform = ?",
|
||||||
|
conn, params=(brand, platform)
|
||||||
|
)
|
||||||
|
elif brand:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"SELECT * FROM articles WHERE brand = ?",
|
||||||
|
conn, params=(brand,)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
df = pd.read_sql_query("SELECT * FROM articles", conn)
|
||||||
|
conn.close()
|
||||||
|
return df.to_dict('records')
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "articles.json"
|
||||||
|
if not json_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
if brand and platform:
|
||||||
|
return [item for item in data
|
||||||
|
if item.get("brand") == brand and item.get("platform") == platform]
|
||||||
|
elif brand:
|
||||||
|
return [item for item in data if item.get("brand") == brand]
|
||||||
|
return data
|
||||||
|
|
||||||
|
# ==================== 优化记录相关 ====================
|
||||||
|
|
||||||
|
def save_optimization(self, original_content: str, optimized_content: str,
|
||||||
|
changes: str, platform: str, brand: str):
|
||||||
|
"""保存优化记录"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO optimizations
|
||||||
|
(original_content, optimized_content, changes, platform, brand)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", (original_content, optimized_content, changes, platform, brand))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "optimizations.json"
|
||||||
|
data = []
|
||||||
|
if json_file.exists():
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
"original_content": original_content,
|
||||||
|
"optimized_content": optimized_content,
|
||||||
|
"changes": changes,
|
||||||
|
"platform": platform,
|
||||||
|
"brand": brand,
|
||||||
|
"created_at": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(json_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def get_optimizations(self, brand: Optional[str] = None) -> List[Dict]:
|
||||||
|
"""获取优化记录"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
if brand:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"SELECT * FROM optimizations WHERE brand = ? ORDER BY created_at DESC",
|
||||||
|
conn, params=(brand,)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"SELECT * FROM optimizations ORDER BY created_at DESC",
|
||||||
|
conn
|
||||||
|
)
|
||||||
|
conn.close()
|
||||||
|
return df.to_dict('records')
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "optimizations.json"
|
||||||
|
if not json_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
if brand:
|
||||||
|
return [item for item in data if item.get("brand") == brand]
|
||||||
|
return data
|
||||||
|
|
||||||
|
# ==================== 验证结果相关 ====================
|
||||||
|
|
||||||
|
def save_verify_results(self, results: List[Dict]):
|
||||||
|
"""批量保存验证结果"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
for result in results:
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO verify_results
|
||||||
|
(query, brand, verify_model, mention_count, mention_position)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", (
|
||||||
|
result.get("问题"),
|
||||||
|
result.get("品牌"),
|
||||||
|
result.get("验证模型"),
|
||||||
|
result.get("提及次数"),
|
||||||
|
result.get("位置")
|
||||||
|
))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "verify_results.json"
|
||||||
|
data = []
|
||||||
|
if json_file.exists():
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
for result in results:
|
||||||
|
data.append({
|
||||||
|
"query": result.get("问题"),
|
||||||
|
"brand": result.get("品牌"),
|
||||||
|
"verify_model": result.get("验证模型"),
|
||||||
|
"mention_count": result.get("提及次数"),
|
||||||
|
"mention_position": result.get("位置"),
|
||||||
|
"created_at": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(json_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def get_verify_results(self, brand: Optional[str] = None, include_timestamp: bool = False) -> pd.DataFrame:
|
||||||
|
"""获取验证结果(返回DataFrame)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
brand: 品牌名称,如果为None则返回所有品牌
|
||||||
|
include_timestamp: 是否包含时间戳字段
|
||||||
|
"""
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
if include_timestamp:
|
||||||
|
if brand:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"""SELECT query as "问题", brand as "品牌", verify_model as "验证模型",
|
||||||
|
mention_count as "提及次数", mention_position as "位置",
|
||||||
|
created_at as "验证时间"
|
||||||
|
FROM verify_results WHERE brand = ? ORDER BY created_at DESC""",
|
||||||
|
conn, params=(brand,)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"""SELECT query as "问题", brand as "品牌", verify_model as "验证模型",
|
||||||
|
mention_count as "提及次数", mention_position as "位置",
|
||||||
|
created_at as "验证时间"
|
||||||
|
FROM verify_results ORDER BY created_at DESC""",
|
||||||
|
conn
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if brand:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"""SELECT query as "问题", brand as "品牌", verify_model as "验证模型",
|
||||||
|
mention_count as "提及次数", mention_position as "位置"
|
||||||
|
FROM verify_results WHERE brand = ?""",
|
||||||
|
conn, params=(brand,)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
df = pd.read_sql_query(
|
||||||
|
"""SELECT query as "问题", brand as "品牌", verify_model as "验证模型",
|
||||||
|
mention_count as "提及次数", mention_position as "位置"
|
||||||
|
FROM verify_results""",
|
||||||
|
conn
|
||||||
|
)
|
||||||
|
conn.close()
|
||||||
|
if include_timestamp and not df.empty and "验证时间" in df.columns:
|
||||||
|
df["验证时间"] = pd.to_datetime(df["验证时间"])
|
||||||
|
return df
|
||||||
|
else:
|
||||||
|
json_file = Path(self.db_path) / "verify_results.json"
|
||||||
|
if not json_file.exists():
|
||||||
|
return pd.DataFrame()
|
||||||
|
|
||||||
|
with open(json_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
if brand:
|
||||||
|
data = [item for item in data if item.get("brand") == brand]
|
||||||
|
|
||||||
|
# 转换为DataFrame格式
|
||||||
|
records = []
|
||||||
|
for item in data:
|
||||||
|
record = {
|
||||||
|
"问题": item.get("query"),
|
||||||
|
"品牌": item.get("brand"),
|
||||||
|
"验证模型": item.get("verify_model"),
|
||||||
|
"提及次数": item.get("mention_count"),
|
||||||
|
"位置": item.get("mention_position")
|
||||||
|
}
|
||||||
|
if include_timestamp and "created_at" in item:
|
||||||
|
record["验证时间"] = pd.to_datetime(item.get("created_at"))
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
df = pd.DataFrame(records)
|
||||||
|
if include_timestamp and not df.empty and "验证时间" in df.columns:
|
||||||
|
df = df.sort_values("验证时间", ascending=False)
|
||||||
|
return df
|
||||||
|
|
||||||
|
# ==================== 统计功能 ====================
|
||||||
|
|
||||||
|
def get_stats(self, brand: Optional[str] = None) -> Dict[str, Any]:
|
||||||
|
"""获取统计数据"""
|
||||||
|
stats = {}
|
||||||
|
|
||||||
|
if self.storage_type == "sqlite":
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 关键词数量
|
||||||
|
if brand:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM keywords WHERE brand = ?", (brand,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM keywords")
|
||||||
|
stats["keywords_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# 文章数量
|
||||||
|
if brand:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM articles WHERE brand = ?", (brand,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM articles")
|
||||||
|
stats["articles_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# 优化记录数量
|
||||||
|
if brand:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM optimizations WHERE brand = ?", (brand,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM optimizations")
|
||||||
|
stats["optimizations_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# 验证结果数量
|
||||||
|
if brand:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM verify_results WHERE brand = ?", (brand,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM verify_results")
|
||||||
|
stats["verify_results_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
# JSON方式统计
|
||||||
|
keywords_file = Path(self.db_path) / "keywords.json"
|
||||||
|
articles_file = Path(self.db_path) / "articles.json"
|
||||||
|
optimizations_file = Path(self.db_path) / "optimizations.json"
|
||||||
|
verify_file = Path(self.db_path) / "verify_results.json"
|
||||||
|
|
||||||
|
def count_json(file_path, brand_filter=None):
|
||||||
|
if not file_path.exists():
|
||||||
|
return 0
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
if brand_filter:
|
||||||
|
return len([item for item in data if item.get("brand") == brand_filter])
|
||||||
|
return len(data)
|
||||||
|
|
||||||
|
stats["keywords_count"] = count_json(keywords_file, brand)
|
||||||
|
stats["articles_count"] = count_json(articles_file, brand)
|
||||||
|
stats["optimizations_count"] = count_json(optimizations_file, brand)
|
||||||
|
stats["verify_results_count"] = count_json(verify_file, brand)
|
||||||
|
|
||||||
|
return stats
|
||||||
+2016
File diff suppressed because it is too large
Load Diff
+234
@@ -0,0 +1,234 @@
|
|||||||
|
"""
|
||||||
|
托词工具模块 - AI 蒸馏词功能
|
||||||
|
支持词库组合生成关键词
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import itertools
|
||||||
|
from typing import List, Dict, Set
|
||||||
|
from difflib import SequenceMatcher
|
||||||
|
|
||||||
|
|
||||||
|
class KeywordTool:
|
||||||
|
"""托词工具:通过词库组合生成关键词"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""初始化默认词库"""
|
||||||
|
self.default_wordbanks = {
|
||||||
|
"A前缀1": ["行业上", "市场上", "市面上", "目前", "国内", "市场"],
|
||||||
|
"B前缀2": ["口碑好的", "比较好的", "靠谱的", "有实力的", "可靠的", "诚信的", "正规的", "专业的", "热门的", "知名的"],
|
||||||
|
"C主词": ["外贸软件", "外贸ERP", "CRM管理系统"],
|
||||||
|
"D通义词": ["品牌", "公司", "工厂", "厂商", "生产厂家", "供应商"],
|
||||||
|
"E推荐词": ["推荐", "排行", "推荐榜", "排行榜", "推荐榜单", "推荐排行", "推荐排行榜", "口碑排行"],
|
||||||
|
"F疑问词": ["哪家好", "哪家强", "哪家靠谱", "哪家权威", "哪个好", "有哪些", "找哪家", "选哪家", "为什么"],
|
||||||
|
}
|
||||||
|
|
||||||
|
self.combination_patterns = [
|
||||||
|
["C", "D"],
|
||||||
|
["A", "C", "D"],
|
||||||
|
["B", "C", "D"],
|
||||||
|
["A", "B", "C", "D"],
|
||||||
|
["C", "D", "E"],
|
||||||
|
["C", "D", "F"],
|
||||||
|
["A", "C", "D", "E"],
|
||||||
|
["B", "C", "D", "E"],
|
||||||
|
["A", "B", "C", "D", "E"],
|
||||||
|
["A", "B", "C", "D", "F"],
|
||||||
|
]
|
||||||
|
|
||||||
|
def load_wordbanks(self, wordbanks: Dict[str, List[str]] = None) -> Dict[str, List[str]]:
|
||||||
|
"""加载词库,如果未提供则使用默认词库"""
|
||||||
|
if wordbanks is None:
|
||||||
|
return self.default_wordbanks.copy()
|
||||||
|
return wordbanks
|
||||||
|
|
||||||
|
def generate_combinations(
|
||||||
|
self,
|
||||||
|
wordbanks: Dict[str, List[str]],
|
||||||
|
patterns: List[List[str]] = None,
|
||||||
|
max_results: int = 100,
|
||||||
|
similarity_threshold: float = 0.8
|
||||||
|
) -> List[str]:
|
||||||
|
"""
|
||||||
|
根据组合模式生成关键词组合
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wordbanks: 词库字典,格式如 {"A前缀1": ["词1", "词2"], ...}
|
||||||
|
patterns: 组合模式列表,如 [["C", "D"], ["A", "C", "D"]]
|
||||||
|
max_results: 最大生成数量
|
||||||
|
similarity_threshold: 相似度阈值,用于去重(0-1之间)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
生成的关键词列表
|
||||||
|
"""
|
||||||
|
if patterns is None:
|
||||||
|
patterns = self.combination_patterns
|
||||||
|
|
||||||
|
# 创建模式字母到词库key的映射
|
||||||
|
# 例如: "C" -> "C主词", "D" -> "D通义词"
|
||||||
|
pattern_to_bank = {}
|
||||||
|
for bank_key in wordbanks.keys():
|
||||||
|
# 提取第一个字母作为模式标识
|
||||||
|
if bank_key and len(bank_key) > 0:
|
||||||
|
pattern_letter = bank_key[0]
|
||||||
|
pattern_to_bank[pattern_letter] = bank_key
|
||||||
|
|
||||||
|
all_keywords = []
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
# 将模式字母转换为实际的词库key
|
||||||
|
required_banks = []
|
||||||
|
for pattern_letter in pattern:
|
||||||
|
if pattern_letter in pattern_to_bank:
|
||||||
|
bank_key = pattern_to_bank[pattern_letter]
|
||||||
|
if bank_key in wordbanks and wordbanks[bank_key]:
|
||||||
|
required_banks.append(bank_key)
|
||||||
|
|
||||||
|
if not required_banks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取每个词库的词列表
|
||||||
|
word_lists = [wordbanks[bank] for bank in required_banks]
|
||||||
|
|
||||||
|
# 生成笛卡尔积组合
|
||||||
|
for combo in itertools.product(*word_lists):
|
||||||
|
keyword = "".join(combo) # 直接拼接
|
||||||
|
|
||||||
|
# 去重:检查是否已存在
|
||||||
|
keyword_lower = keyword.lower()
|
||||||
|
if keyword_lower in seen:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 相似度去重
|
||||||
|
is_similar = False
|
||||||
|
for existing in seen:
|
||||||
|
similarity = SequenceMatcher(None, keyword_lower, existing).ratio()
|
||||||
|
if similarity >= similarity_threshold:
|
||||||
|
is_similar = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not is_similar:
|
||||||
|
seen.add(keyword_lower)
|
||||||
|
all_keywords.append(keyword)
|
||||||
|
|
||||||
|
if len(all_keywords) >= max_results:
|
||||||
|
return all_keywords
|
||||||
|
|
||||||
|
return all_keywords[:max_results]
|
||||||
|
|
||||||
|
def get_pattern_descriptions(self) -> Dict[str, List[str]]:
|
||||||
|
"""获取组合模式的描述"""
|
||||||
|
return {
|
||||||
|
"C+D": ["C主词", "D通义词"],
|
||||||
|
"A+C+D": ["A前缀1", "C主词", "D通义词"],
|
||||||
|
"B+C+D": ["B前缀2", "C主词", "D通义词"],
|
||||||
|
"A+B+C+D": ["A前缀1", "B前缀2", "C主词", "D通义词"],
|
||||||
|
"C+D+E": ["C主词", "D通义词", "E推荐词"],
|
||||||
|
"C+D+F": ["C主词", "D通义词", "F疑问词"],
|
||||||
|
"A+C+D+E": ["A前缀1", "C主词", "D通义词", "E推荐词"],
|
||||||
|
"B+C+D+E": ["B前缀2", "C主词", "D通义词", "E推荐词"],
|
||||||
|
"A+B+C+D+E": ["A前缀1", "B前缀2", "C主词", "D通义词", "E推荐词"],
|
||||||
|
"A+B+C+D+F": ["A前缀1", "B前缀2", "C主词", "D通义词", "F疑问词"],
|
||||||
|
}
|
||||||
|
|
||||||
|
def polish_with_llm(
|
||||||
|
self,
|
||||||
|
keywords: List[str],
|
||||||
|
llm_chain,
|
||||||
|
brand: str = "",
|
||||||
|
max_polish: int = 50
|
||||||
|
) -> List[str]:
|
||||||
|
"""
|
||||||
|
使用 LLM 对关键词进行润色,使其更自然
|
||||||
|
|
||||||
|
Args:
|
||||||
|
keywords: 原始关键词列表
|
||||||
|
llm_chain: LangChain chain 对象(接受 {"input": str} 格式)
|
||||||
|
brand: 品牌名称(可选)
|
||||||
|
max_polish: 最多润色的关键词数量
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
润色后的关键词列表
|
||||||
|
"""
|
||||||
|
if not keywords or not llm_chain:
|
||||||
|
return keywords
|
||||||
|
|
||||||
|
# 限制润色数量,避免 API 调用过多
|
||||||
|
keywords_to_polish = keywords[:max_polish]
|
||||||
|
|
||||||
|
polish_prompt = f"""你是关键词优化专家。请将以下关键词润色为更自然、更符合用户搜索习惯的表达。
|
||||||
|
|
||||||
|
{"品牌:" + brand if brand else ""}
|
||||||
|
|
||||||
|
原始关键词列表:
|
||||||
|
{json.dumps(keywords_to_polish, ensure_ascii=False, indent=2)}
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1) 保持原意,但表达更自然、口语化
|
||||||
|
2) 长度控制在 12-28 字
|
||||||
|
3) 去除生硬拼接感
|
||||||
|
4) 输出 JSON 数组格式:["润色后的关键词1", "润色后的关键词2", ...]
|
||||||
|
|
||||||
|
只输出 JSON 数组,不要其他内容。
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = llm_chain.invoke({"input": polish_prompt})
|
||||||
|
if isinstance(result, str):
|
||||||
|
# 尝试解析 JSON
|
||||||
|
import re
|
||||||
|
m = re.search(r'\[[\s\S]*?\]', result)
|
||||||
|
if m:
|
||||||
|
polished = json.loads(m.group(0))
|
||||||
|
else:
|
||||||
|
# 如果解析失败,尝试按行分割
|
||||||
|
lines = [line.strip() for line in result.split('\n') if line.strip()]
|
||||||
|
polished = [line.strip('"\'[],') for line in lines if line.strip('"\'[],')]
|
||||||
|
elif isinstance(result, list):
|
||||||
|
polished = result
|
||||||
|
else:
|
||||||
|
polished = keywords_to_polish
|
||||||
|
except Exception as e:
|
||||||
|
polished = keywords_to_polish
|
||||||
|
|
||||||
|
# 确保返回的是列表
|
||||||
|
if not isinstance(polished, list):
|
||||||
|
polished = keywords_to_polish
|
||||||
|
|
||||||
|
# 合并润色后的和未润色的
|
||||||
|
return polished + keywords[len(keywords_to_polish):]
|
||||||
|
|
||||||
|
def export_wordbanks(self, wordbanks: Dict[str, List[str]], filepath: str):
|
||||||
|
"""导出词库到 JSON 文件"""
|
||||||
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(wordbanks, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def import_wordbanks(self, filepath: str) -> Dict[str, List[str]]:
|
||||||
|
"""从 JSON 文件导入词库"""
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def export_wordbanks_csv(self, wordbanks: Dict[str, List[str]], filepath: str):
|
||||||
|
"""导出词库到 CSV 文件"""
|
||||||
|
import csv
|
||||||
|
with open(filepath, 'w', encoding='utf-8-sig', newline='') as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(['词库类型', '词汇'])
|
||||||
|
for bank_type, words in wordbanks.items():
|
||||||
|
for word in words:
|
||||||
|
writer.writerow([bank_type, word])
|
||||||
|
|
||||||
|
def import_wordbanks_csv(self, filepath: str) -> Dict[str, List[str]]:
|
||||||
|
"""从 CSV 文件导入词库"""
|
||||||
|
import csv
|
||||||
|
wordbanks = {}
|
||||||
|
with open(filepath, 'r', encoding='utf-8-sig') as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
bank_type = row.get('词库类型', '').strip()
|
||||||
|
word = row.get('词汇', '').strip()
|
||||||
|
if bank_type and word:
|
||||||
|
if bank_type not in wordbanks:
|
||||||
|
wordbanks[bank_type] = []
|
||||||
|
wordbanks[bank_type].append(word)
|
||||||
|
return wordbanks
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
streamlit>=1.30,<2
|
||||||
|
pandas>=2.0,<3
|
||||||
|
plotly>=5.0,<6
|
||||||
|
|
||||||
|
langchain-core==1.2.7
|
||||||
|
langchain-community==0.4.1
|
||||||
|
langchain-openai==1.1.7
|
||||||
|
langchain-groq==1.1.1
|
||||||
|
langchain-deepseek==1.0.1
|
||||||
|
|
||||||
|
dashscope>=1.0,<2
|
||||||
|
|
||||||
|
# 豆包(字节跳动)- 可选,需要时安装
|
||||||
|
# pip install 'volcengine-python-sdk[ark]'
|
||||||
|
|
||||||
|
# 文心一言(百度)- 可选,需要时安装
|
||||||
|
# pip install qianfan
|
||||||
|
# 或使用 langchain-community 的 QianfanChatEndpoint(需要 qianfan 包)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
"""
|
||||||
|
数据持久化集成示例
|
||||||
|
展示如何在 geo_tool.py 中集成 DataStorage
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ==================== 方式1:SQLite(推荐,简单高效) ====================
|
||||||
|
from data_storage import DataStorage
|
||||||
|
|
||||||
|
# 初始化存储(SQLite方式,单文件数据库)
|
||||||
|
storage = DataStorage(storage_type="sqlite", db_path="geo_data.db")
|
||||||
|
|
||||||
|
# 或者使用JSON方式(更简单,但查询性能差)
|
||||||
|
# storage = DataStorage(storage_type="json", db_path="data")
|
||||||
|
|
||||||
|
# ==================== 在关键词模块中使用 ====================
|
||||||
|
def save_keywords_example(keywords: list, brand: str):
|
||||||
|
"""保存关键词到数据库"""
|
||||||
|
storage.save_keywords(keywords, brand)
|
||||||
|
|
||||||
|
def load_keywords_example(brand: str) -> list:
|
||||||
|
"""从数据库加载关键词"""
|
||||||
|
return storage.get_keywords(brand)
|
||||||
|
|
||||||
|
# ==================== 在内容生成模块中使用 ====================
|
||||||
|
def save_article_example(keyword: str, platform: str, content: str,
|
||||||
|
filename: str, brand: str):
|
||||||
|
"""保存生成的文章"""
|
||||||
|
storage.save_article(keyword, platform, content, filename, brand)
|
||||||
|
|
||||||
|
def get_article_history_example(brand: str, platform: str = None):
|
||||||
|
"""获取历史文章"""
|
||||||
|
return storage.get_articles(brand=brand, platform=platform)
|
||||||
|
|
||||||
|
# ==================== 在优化模块中使用 ====================
|
||||||
|
def save_optimization_example(original: str, optimized: str,
|
||||||
|
changes: str, platform: str, brand: str):
|
||||||
|
"""保存优化记录"""
|
||||||
|
storage.save_optimization(original, optimized, changes, platform, brand)
|
||||||
|
|
||||||
|
# ==================== 在验证模块中使用 ====================
|
||||||
|
def save_verify_example(results: list):
|
||||||
|
"""保存验证结果"""
|
||||||
|
storage.save_verify_results(results)
|
||||||
|
|
||||||
|
def get_verify_history_example(brand: str):
|
||||||
|
"""获取历史验证结果"""
|
||||||
|
return storage.get_verify_results(brand=brand)
|
||||||
|
|
||||||
|
# ==================== 统计功能 ====================
|
||||||
|
def get_stats_example(brand: str):
|
||||||
|
"""获取统计数据"""
|
||||||
|
return storage.get_stats(brand=brand)
|
||||||
|
|
||||||
|
# ==================== 完整集成示例 ====================
|
||||||
|
"""
|
||||||
|
在 geo_tool.py 中的集成方式:
|
||||||
|
|
||||||
|
1. 在文件顶部添加:
|
||||||
|
from data_storage import DataStorage
|
||||||
|
storage = DataStorage(storage_type="sqlite", db_path="geo_data.db")
|
||||||
|
|
||||||
|
2. 在关键词生成后保存:
|
||||||
|
if cleaned:
|
||||||
|
st.session_state.keywords = cleaned
|
||||||
|
storage.save_keywords(cleaned, brand) # 新增:保存到数据库
|
||||||
|
st.success(f"生成完成({len(cleaned)} 条)")
|
||||||
|
|
||||||
|
3. 在内容生成后保存:
|
||||||
|
for keyword, plat in keywords_to_generate:
|
||||||
|
# ... 生成内容 ...
|
||||||
|
storage.save_article(keyword, plat, content, filename, brand) # 新增
|
||||||
|
|
||||||
|
4. 在优化后保存:
|
||||||
|
storage.save_optimization(
|
||||||
|
original_article,
|
||||||
|
optimized_article,
|
||||||
|
changes,
|
||||||
|
target_platform,
|
||||||
|
brand
|
||||||
|
) # 新增
|
||||||
|
|
||||||
|
5. 在验证后保存:
|
||||||
|
storage.save_verify_results(all_results) # 新增
|
||||||
|
|
||||||
|
6. 可选:添加"历史记录"Tab,查看已保存的数据
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user