vecyang1/place-intel
GitHub: vecyang1/place-intel
利用 AI 自动抓取并分析 Google Maps 地点的完整评价历史,生成价格情报和风险预警报告,帮助用户在进店前充分了解商家。
Stars: 0 | Forks: 0
# placeintel — 有备而来 🎯
进店之前,先读完它的几百条评价。从此不再毫无准备地被报出“游客价”。
只需说出 *"会安 吉他租赁"* —— 使用**任意语言** —— AI 便会自动规划搜索任务:
将其翻译为双语 Google Maps 查询,提取位置,选择对应的
报告配置,发现地点,**附带明确理由过滤掉不在范围内的垃圾信息**
(在搜索吉他时不会再出现摩托车租赁结果),抓取**完整的评价**
历史(数百条评价 —— 官方 API 限制最多仅 5 条),将所有内容在本地缓存,对评价进行 embedding 以实现语义搜索,最后让 Gemini 推理出一份情报
报告:**价格情报 · 硬信息核实 · 红旗预警 · 30 秒进店简报**。
## 安装
```
git clone https://github.com/vecyang1/place-intel.git
cd place-intel
python -m venv .venv && source .venv/bin/activate
pip install -e ".[web]" # add the web app; use `pip install -e .` for CLI-only
# 审查 scraper(单独 vendored,MIT 许可证)以保持此 repo 精简:
git clone https://github.com/georgekhananaev/google-reviews-scraper-pro.git \
vendor/google-reviews-scraper-pro
cp .env.example .env # then add at least one Gemini key
```
## 快速开始
```
.venv/bin/placeintel scout "会安 吉他租赁" # AI plans everything
.venv/bin/placeintel shop "D'Class Guitar" --near "Hoi An" # ONE shop (name or Maps URL)
.venv/bin/placeintel ask "哪家有耐心的老师?" # RAG over everything cached
.venv/bin/placeintel plan "在岘港学冲浪" # debug: see the AI's plan
.venv/bin/placeintel-web # web app → http://127.0.0.1:9618
```
Web 应用会开启一个指挥中心式的 Scout 输入界面:粘贴 Maps URL,输入店铺
名称,基于缓存证据提问,或者描述一个宽泛的需求,它会在提交前推荐你使用
Scout、Shop 或 Ask 模式。四个视图依然保留:**侦察 Scout**(自由
文本输入 + 实时进度时间线,以及展示 AI 计划、过滤
结论、深度调研的店铺、重试/缓存组和 Compare 选择的最终结果),**单店 Shop**
(单个名称/URL → 聚焦档案),**资料库 Library**
(缓存店铺 + 历史搜索 → 包含报告、范围限定提问、评价
浏览器的店铺档案),**提问 Ask**(跨店 RAG + 所有历史提问,包括
按店铺标记的店铺范围限定提问)。
## 本地验证
```
.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v
npm install
npm run test:web # requires the local web app on http://127.0.0.1:9618
.venv/bin/placeintel doctor --json
```
Agent 和运维契约:
- HTTP API:[`docs/API.md`](docs/API.md)
- Agent CLI:[`docs/agent-cli.md`](docs/agent-cli.md)
- 运维手册:[`docs/operations.md`](docs/operations.md)
## 私有部署
私有部署路径为 GitHub Actions → SSH → 原生 systemd 服务。
除非位于明确的受保护代理之后,否则 FastAPI 应用应保持在 loopback (`127.0.0.1:9618`)。
所需的私有仓库 secrets 在此公开 README 中使用了占位符名称:
```
PLACEINTEL_DEPLOY_HOST
PLACEINTEL_DEPLOY_USER
PLACEINTEL_DEPLOY_PORT
PLACEINTEL_DEPLOY_SSH_KEY
PLACEINTEL_DEPLOY_DIR
GOOGLE_API_KEY
VECTORENGINE_API_KEY
SERPAPI_API_KEY
PLACEINTEL_REASON_MODEL
```
部署后,通过 SSH 隧道或经过身份验证的内部 URL 进行验证:
```
ssh -fN -L 9619:127.0.0.1:9618
EXPECTED_VERSION=$(.venv/bin/python -c "import placeintel; print(placeintel.__version__)")
.venv/bin/placeintel deploy-smoke \
--base-url "http://127.0.0.1:9619" \
--expected-version "$EXPECTED_VERSION" \
--format json
```
对于受保护的公共域名,请将真实的 URL 和 Basic Auth 值保留在本地的
gitignored 文件或部署 secrets 中。公共安全的身份验证检查如下:
```
EXPECTED_VERSION=$(.venv/bin/python -c "import placeintel; print(placeintel.__version__)")
.venv/bin/placeintel deploy-smoke \
--base-url "http://127.0.0.1:9619" \
--public-url "https://PLACEHOLDER_PROTECTED_DOMAIN" \
--expected-version "$EXPECTED_VERSION" \
--format json
```
完整的部署冒烟测试、备份、还原和回滚手册请参见 [`docs/operations.md`](docs/operations.md)。
## 工作原理
```
free text ─► planner.py ──── AI plan: intent, bilingual queries, location, profile,
│ discover-vs-single mode (fail-open: raw passthrough)
▼
discover.py ──────── gosom/google-maps-scraper (Docker, free)
│ └ fallback: SerpAPI google_maps
▼
planner.filter ───── AI relevance verdicts per candidate (fail-open: keep all)
▼
reviews.py ────────── vendor/google-reviews-scraper-pro (Selenium, incremental)
│ └ fallback: SerpAPI google_maps_reviews
▼
cache.py ──────────── data/placeintel.db (SQLite: places/reviews/reports/vectors)
│
embed.py ──────────── Gemini Embedding 2, Google official (768-dim, true batch)
│
analyze.py ────────── Gemini Flash (VectorEngine) long-context over ALL reviews
│
cli.py / server.py (events → live timeline) / web/ SPA / Claude skill
```
重要的设计选择:
- **AI 在各个环节均采用失败放行机制**:失效的 LLM 会降级为原始查询透传并
保留所有候选者 —— 它绝不会阻塞抓取 pipeline。
- **针对单个地点的报告采用推理而非检索**:某个地点的完整评价集可以放入
Flash 的上下文中,因此报告会读取*所有内容* —— 而 embedding 则用于在不断扩充的缓存上执行跨店
`ask` 查询。
- **缓存优先**:7 天内进行相同搜索 = 不重新发现;评价通过增量方式抓取;当没有
新评价产生时,报告将被**复用**。
- **提供商划分**(由用户决定):embedding → Google 官方(真正的 Content-list
批处理,64 个文档/2 秒);推理 → VectorEngine(相同的模型,价格更低)。
- **Profiles** (`profiles/*.yaml`):`_core.yaml`(价格/硬事实/红旗预警)会合并
到每一个 profile 中;添加一个 YAML 即可增加一个领域(课程、租赁等)。
- **透明度是一项功能**:每个阶段都会发出 `{t, stage, msg}` 事件,并
在 CLI 和 Web 端渲染为实时时间线 —— 包括*为什么*每家店铺会被排除。
## 环境要求与密钥
- Docker(用于免费发现)—— 在 macOS 上自动启动;否则使用 `--force-serpapi`
- Chrome(用于评价抓取器)
- 通过 `.env` 或环境变量提供密钥(参见 `.env.example`):`GOOGLE_API_KEY`
(AIza…,用于 embedding)、`VECTORENGINE_API_KEY` (sk-…,用于推理) 以及可选的
`SERPAPI_API_KEY`(后备方案)。至少需要一个 Gemini 密钥。
## 踩坑经验(来之不易)
- 两者提供商都会将普通的字符串列表 embed 输入**聚合为一个向量** —— 真正的
批处理需要显式的 `types.Content` 对象 (embed.py)。
- 推理 prompt 必须包含**今天的日期**,否则模型会将最近的评价标记为
“伪造的未来日期” (analyze.py)。
- 越南语音符会破坏简单的名称匹配 ("Hội An" ≠ "Hoi An") —— 请使用
`cache.norm_name` (NFD strip + đ→d + token-AND)。
- 必须在展开线程之前构造好 `genai.Client`。
- gosom 输出可能是 NDJSON 或 JSON 数组;reviews-scraper-pro 通过
`places.original_url` 进行映射;SeleniumBase 需要 9222 端口冲突引导 (reviews.py)。
## 致谢
站在开源的肩膀上 —— 请给他们点个 ⭐:
- [gosom/google-maps-scraper](https://github.com/gosom/google-maps-scraper) — 免费地点发现 (Docker)
- [georgekhananaev/google-reviews-scraper-pro](https://github.com/georgekhananaev/google-reviews-scraper-pro) — 完整评价历史抓取 (MIT)
- [Google Gemini](https://ai.google.dev/) — embedding + 推理 · [SerpAPI](https://serpapi.com) — 可选后备方案
## 许可证
MIT — 详见 [LICENSE](LICENSE)。
标签:Gemini, RAG, URL抓取, 人工智能, 情报分析, 数据抓取, 用户模式Hook绕过, 网络诊断, 请求拦截, 逆向工具