rent-assist 租房全能助手
测试纪律:联调/测试一律小量验证:批次编排 run_collect.py 用 --target 5,直跑单脚本用 --limit 3-5;正式查询才用正式量级。
正式量级(用户 2026-08-15 定):总量目标由用户在采集前从菜单选择(见工作流 A 第 3 步:轻量 60 / 标准 150 / 深度 300,可自定义),由批次编排器自动累计:每批每平台 limit 20、自动多轮换词、--min-per-platform 20 保底;评论 --top-comments 20 且按点赞数(最热)排序取。单批 20 只是批次上限,绝不能当作正式查询的总量。
用户以自然语言提问,skill 先做意图路由,再走五条工作流之一: A 小区综合了解 / B 推荐评价 / C 选址建议 / D 找转租房源 / E 租房咨询。 五条工作流最终都产出可视化报告:A/B/C/D 走采集→分析→报告;E 先在回复里直答,再出 FAQ 可视化报告(不采集,秒出)。
数据流:run_collect.py 批次编排采集脚本(opencli 小红书 / 豆瓣 HTTP / Exa+Jina / 抖音 MediaCrawler;12315 不进编排器单独跑)→ <data>/raw/*.jsonl
→ clean.py → <data>/cleaned/*.json → Claude 只读 cleaned 做语义分析 → <data>/analysis/<标的>.json(按标的命名,防多标的互相覆盖)
→ render.py → <data>/reports/*.html。
诚实边界:只聚合公开口碑帖与转租帖;不爬贝壳/链家挂牌数据(反爬+判例风险),不做全网房源比价。报告仅为公开舆情聚合,不构成决策依据,无数据不等于安全。
第 0 步:依赖探测(采集类工作流第一步)
采集类工作流(意图 A/B/C/D)先运行;意图 E(纯咨询,不采集)直接作答,不跑(避免无谓的真实网络探测等待):
python <skill>/scripts/check_deps.py
<skill> 指本 skill 目录。路径占位符(全文通用):<data>=数据目录、<tools>=第三方工具目录(MediaCrawler/asr-venv/模型/playwright 内核/douban-profile 所在)、<keys>=密钥文件;统一解析规则:环境变量(RENT_ASSIST_DATA / RENT_ASSIST_TOOLS / RENT_ASSIST_KEYS)优先 → 开发机 E:\租房\ 下对应路径(存在时)→ ~/.rent-assist/ 下对应路径;check_deps.py 首屏会打印三者实际解析结果(环境变量需在脚本进程启动前设置)。数据目录下 raw/cleaned/analysis/geo/reports/media、缓存 cache.db、批次进度账本 progress/(断点续采与 summary 都在这里)均在 <data> 下,不落 skill 目录。按输出处理:
- 未装 agent-reach:向用户转述安装指引并停止本次采集类工作流(意图 E 可继续):
- 从 GitHub 仓库
github.com/Panniantong/Agent-Reach安装:agent-reach install --system。 - 警告:PyPI 上的同名包 "agent-reach" 是冒名包,禁止
pip install agent-reach。
- 从 GitHub 仓库
- 小红书源不可用:小红书需要 OpenCLI + Chrome 及 OpenCLI 扩展,并执行
opencli xiaohongshu login在浏览器里登录一次。未就绪则跳过小红书源,在报告coverage.note中说明,不阻塞其他源。 - Exa/Jina 等可选源缺失:跳过对应源并说明。
输入预处理(意图路由之前)
用户提问常啰嗦、信息密集,路由前先从原话提取五元组:
| 字段 | 取值 |
|---|---|
| 标的 | 小区/公寓/中介/房东/片区名;没有则留空(意图 C/E 常见) |
| 城市 | 缺失则按工作流规则一次性问齐 |
| 标的类型 | 小区 / 中介 / 片区(超大片区照常采集,分析层按子区归类,见「超大片区」节) |
| 关注点 | 从原话提取:推荐 / 价格 / 二房东 / 转租 / 噪音 / 押金 / 靠谱 等 |
| 约束 | 预算 / 通勤 / 合租整租 / 时间窗——仅取用户原话明说的,缺失不补不猜 |
提问啰嗦或信息密集时,提取后用一句话向用户复述确认(例:"你是想了解北京天通苑的租房口碑,预算 3000、要合租,对吗?"),确认后再路由;问题本身简短清晰则直接路由,缺项按各工作流的问询规则补齐。后续查询词 = 标的 + 关注点场景词现场组合(正本见 references/queries.md);约束不进搜索词,留给过滤层。
零假设铁律(分析/报告/推荐全程适用):约束类信息(合租/单间/次卧/整租/户型/预算/通勤/电梯房等)只使用用户明说的。未提及的一律按"未指定"处理——分析和报告里不得出现"默认按合租单间考虑""适合合租人群"这类凭空推断(用户没说预算 2500 就该合租)。缺关键约束影响结论时问一句,绝不猜。
第 1 步:意图路由
对用户问题分类,判断规则按优先级从上到下:
| 意图 | 判断规则 | 示例问法 |
|---|---|---|
| A 小区综合了解 | 有明确标的(小区/公寓/片区/板块/镇等任意居住地名/房东/中介名),泛了解"它怎么样"或直接"XX 租房":口碑、价格、房源、出租方,用户问什么就查什么 | "天通苑怎么样""天通苑租金多少钱""海淀永丰租房""永丰租房贵不贵""这家中介靠谱吗" |
| B 推荐评价 | 有明确标的 + 明确求推荐/值不值/对比的决策判断 | "天通苑租房推荐吗""XX公寓值得租吗""天通苑和回龙观选哪个" |
| C 选址建议 | 无明确意向居住地名,只有工作地/预算/通勤要求,问住哪 | "在国贸上班预算3000住哪合适""望京上班哪个性价比高";反例:"海淀永丰租房"=用户点名了想住的地,走 A |
| D 找转租房源 | 求具体房源/转租/合租信息,常带位置或预算 | "XX附近有没有个人转租""2000以内合租有吗" |
| E 租房咨询 | 合同/押金/流程等知识性问题,无标的 | "押金一般押几付几""签合同注意什么""中介费谁出" |
- 判断不了就用一句话向用户澄清再路由。
- A/C 分界铁律:用户点名了想住的地名(哪怕是大片区/板块,如"海淀永丰租房")→ 一律走 A,直接搜该地名,不得生成候选片区;只有"无意向地名 + 在哪上班/问住哪"才是 C。字面判不准(如"永丰上班预算2500"既可理解为查永丰本地、也可理解为找通勤范围)→ 一句话问用户,禁止猜。
- 零假设/零添加总纲:用户说什么就拆什么、搜什么——约束(合租/单间/整租/预算/通勤/户型)只取用户原话明说的;搜索词只加 2-3 个通用评价词,不加用户没说的限定词(地铁站/线路/户型等)。查询效果不佳时先换场景词补采,仍不佳才在最后向用户建议加词——建议是兜底,不是起手。
- 复合问题("住哪好+有没有转租")拆成多次路由依次执行。
- 意图 B 为多标的对比("A 和 B 哪个好")时:逐个标的按工作流 A 完整采集分析,再合并对比给结论(见工作流 B)。
- 意图 A/B 输入为超大片区时照常走 A/B:搜索词=「片区名+场景词」(用户原话的片区名),绝不枚举子小区拼搜索词;子区差异由分析层从帖内信息归类呈现(见下方「超大片区」节)。
- 触发词参考:租房避坑/踩坑/口碑/推荐租吗/住哪合适/选址/转租/直租/合租/押金/合同/房东/中介。
超大片区(如天通苑/回龙观)
铁律:搜索词只含用户原话里的名字——用户打了"天通苑"就搜"天通苑 避坑""天通苑 怎么样",绝不自己枚举"本一区/本三区/东区…"去逐个拼词采集(历史教训:LLM 拍的子区清单+逐区采集=自作聪明,已废除)。超大片区与普通小区走完全相同的管线,区别只在分析层:
- 问整体("天通苑怎么样")→ 按维度汇总,帖内出现的子区差异作为 findings 呈现。
- 问子区粒度("哪个区合适""本区和东区比")→ 分析层把帖内提及的各子区当对比项(B 模式逐条 pros/cons+证据;或 locate 模式 candidates),数据里提到几个子区就写几个,不预设清单。
- 子区粒度信息不足 → 明示"公开讨论未覆盖到子区层面,建议实地核验",禁止编造。
工作流 A:小区综合了解(意图 B/C 复用)
输入解析:确定
标的名 + 城市 + 标的类型,并记下用户的问题本身(问的是推荐、价格、直租还是二房东,第 2 步和第 8 步都要用它)。- 标的类型默认
小区;名称含"地产/置业/房产/管家/公寓运营"或用户明说"这个中介/房东"则记为中介。 - 用户给的是人名房东:先反问可标识信息(地址/电话/挂房平台),并明示人名舆情检索的局限(同名多,证据仅作参考)。
- 只有中介/房东类型才运行 collect_12315.py,小区类型跳过 12315。
- 城市缺失:能由唯一知名片区/地标推知就直接推知并在复述中确认;推不出或可能歧义(同城同名小区)才问用户,同一轮把缺失信息一次问齐。
- 同一轮顺带问一句"上班地点/通勤目的地是哪?"(可选,用户可拒绝;仅当标的为小区/片区时问,中介/房东人名标的不问):有答案留给地理层画通勤线(见地理层第 2 条),拒绝或没有就跳过 route,不追问、不阻塞采集。
- 标的类型默认
搜索词策略(动态生成,不预设;词库正本与平台传法见 references/queries.md §1):搜索词 =
小区名 + 场景词,从用户的问题现场组合,生成的 3-5 组词拼进 run_collect 的--queries(逗号分隔),分批与换词轮转由编排器自动执行。铁律:- 场景词按意图选视角:口碑类(意图 A/B/C)只用评价视角词,找房类(意图 D)只用供给侧词(两栏词库与组合示例见 queries.md §1)。
- "租房"泛词禁止单独作场景词(求租帖混入的头号源头),要带评价后缀如"租房体验";用户没提的角度只补 2-3 个通用评价词,禁止铺开维度词矩阵、不从避坑出发。
- 中介/房东标的词(靠谱吗/套路/退费)、平台传参(豆瓣 --intent word|listing)、标的词形规范均见 queries.md;搜索层挡不住的求租帖由 clean.py 兜底剔除(双层防御)。
- 豆瓣平台传裸标的词(平台词形差异见 queries.md §1.5)。
- 零添加铁律:搜索词 = 用户原话拆出的标的 + 用户提到的场景词(+少量通用评价词)。禁止擅自加入用户没说的限定词——地铁站名/地铁线路(如"16号线")/小区开发商/户型/合租单间/预算数字一律不加。效果不佳(不足下限或不相关)先换通用场景词补采;仍不佳,最后才向用户建议(例:"要不要加地铁站名缩小范围?")——建议是兜底不是起手。
量级确认(采集前必做):给用户三档菜单让其选择(可自定义数字):
- 轻量 60 条 ≈ 20 分钟(--target 60)
- 标准 150 条 ≈ 1 小时(--target 150)
- 深度 300 条 ≈ 2 小时+(--target 300)
耗时依据实测约 2.5 条/分钟/平台(串行);加
--parallel四平台并行约减半。用户不选就默认轻量档(60 条约 20 分钟——采集成本承诺最小,数据不够按下限表规则补采,不起步就重采)。前端衔接(主动拉起 + 队列直达闭环):量级确认前先探测门面页——curl -s http://127.0.0.1:8770/ping返回"ok":true即服务已在跑(alive只反映心跳,false 时仅补挂 Monitor,不要重复启动);没起则后台以直达模式启动RENT_ASSIST_INBOX=1 python <skill>/scripts/serve_home.py(run_in_background,电脑浏览器自动弹出),并挂 Monitor 接收队列(touch<data>/inbox/heartbeat起 5 秒心跳循环 +tail -n 0 -f <data>/inbox/queue.jsonl,队列每行即一条页面提交的命令,按本 skill 工作流处理)。把启动横幅里「手机访问(需同一WiFi): http://<局域网IP>:8770/」转述给用户。闭环保证:页面提交 → 本会话接收处理 → 报告落<data>/reports/→ serve_home 盯到新报告自动在原页面弹出(电脑/手机同享)。页面提交自带【量级:X】标注(X 含档名与条数),Claude 不再重复追问量级;勿重复启动(8770 双实例会抢占端口)。
批次采集(一条 run_collect 命令,编排器自动多轮累计;脚本内部请求间隔随机、批间休眠 10-30s;评论一律按点赞最热排序取 top;时间窗与排序按下方硬规则传参):
python <skill>/scripts/run_collect.py --target 300 --parallel --queries "天通苑住过,天通苑避坑,天通苑二房东" --platforms "xhs,douban,web,douyin" --days 180 --sort "discussion,hot" --min-per-platform 20 --douban-intent word- 语义:外层遍历查询词、内层遍历平台,每个(词 × 平台)组合一批(每批每平台 limit 20),自动累计到
--target总量(--per-platform N可直接定每平台上限);剩余配额不足一批时自动以剩余量作本批 --limit(配额即钳位,不整页超采);断点续采:中断后重跑自动跳过已完成组合(进度账本 /progress/.json,done 超 7 天自动失效)。 - 7 天复用闸门(代码强制):某(平台×查询词)组合近 7 天已采 ≥10 条 → 自动跳过并在
[复用]行打印明细;--refresh强制全部重采(--parallel 下会透传给各平台 worker)。--parallel下各 worker 的 auth_state 写隔离目录、不回写主 state(只影响展示层,不影响采集)。 - 停止规则(按平台):达到 target 配额即停;连续 2 批新增率 <10% 或连续 3 批失败则停该平台;查询词耗尽仍未达
--min-per-platform时依次启用--extra-queries "补位词1,补位词2"续采。 - 退出码:0=正常结束(允许部分批次失败);3=子脚本需登录,整体中止,先跑
python <skill>/scripts/ensure_auth.py再重跑;130=用户中断。子脚本退出 4=该源数据质量差,记 degraded 继续并按降级处理;2 及其他=失败继续,重跑会重试。 - 12315 不进编排器:中介/房东标的另跑
python <skill>/scripts/collect_12315.py --query "XX地产" --limit 20 --days 180。 - 视频笔记与图文同样采集(collect_xhs 已在 extra.note_type 标记 video/image/unknown):视频帖的标题+简介就是文字信号,直接可用;抖音口播转写见第 7 步。
- 轻量场景(意图 C 等)可直跑单脚本(collect_xhs/douban/web/douyin),传参同矩阵。
- 同标的 7 天内已采集 → run_collect 启动时自动识别复用(
[复用]明细,--refresh强制重采);cleaned/analysis/report 产物同样直接沿用,除非用户要求重跑。
采集后健康检查(必做):读 run_collect stdout 末尾
[汇总]行(总量、当日去重新增、summary 与账本路径),必要时读 progress/.summary.json 里各平台条数与失败/降级组合;并核对 cleaned 数据中note_fetch_failed/comments_fetch_failed标记占比:任一占比 ≥50% → 视为该源本轮失败,按降级规则处理(跳过该源、在coverage.note说明),不基于残缺数据下结论。- 语义:外层遍历查询词、内层遍历平台,每个(词 × 平台)组合一批(每批每平台 limit 20),自动累计到
清洗:
python <skill>/scripts/clean.py --query <标的>→ 产出<data>/cleaned/<标的>.json(去重、广告过滤、求租帖剔除、8 类风险粗分类、房源帖识别、评论取 top)。媒体处理(用户 2026-08-15 定:小红书不读图):xhs 以正文+评论为主,不默认下载图片识别;fetch_media.py 保留为可选工具,仅在用户点名"看看帖子里的图"时使用(
--note-ids "id1,id2"或完整 URL)。抖音视频口播转写(默认执行,抖音源的核心价值在口播内容):主批次(含
--parallel)跑完后,默认追加一条独立小量命令单独跑(--get-video与--parallel互斥,必须后置单跑):python <skill>/scripts/collect_douyin.py --query "<主查询词>" --limit 5 --get-video 3 --top-comments 10 --days 180 --sort discussion,hot(N≤3,对讨论 top 视频帖:MediaCrawler 下载→ffmpeg 抽音频→sherpa-onnx+SenseVoice 本地转写→文本以"【口播转写】"并入该帖 content,extra.asr=true)。带宽铁律:limit 必须小——MediaCrawler 开视频后会下载搜索页全部结果,
--get-video务必配--limit 3级别控带宽。用户明说"不要转写/不用视频"才跳过。频控口径不变(≤3 帖)。运行时装于 /asr-venv + models(缺依赖时脚本 exit 3 给指引);手动转写单文件用python <skill>/scripts/asr.py --video <文件>。语义分析(Claude 亲自做,只读 /cleaned/*.json):首要任务是回答用户提出的那个问题:用户问推荐就给推荐结论,问价格就汇总价格锚点,问二房东就查二房东证据。references/risk-signals.md 的风险八维只作为组织发现的分类词汇表(复核 clean.py 粗分类、剔除否定语境误命中如"从来没漏过水"),不是分析的目的。按下方 schema 写出
<data>/analysis/<标的>.json(按标的命名,防多标的互相覆盖;schema 不变)。渲染:
python <skill>/scripts/render.py --analysis <data>/analysis/<标的>.json --cleaned <data>/cleaned/<标的>.json(启用地理层时再加--geo <data>/geo/<标的>.json)→<data>/reports/<标的>_<YYYYMMDD>.html。最后告知用户报告绝对路径,并给一句话结论。
数据稀疏降级(报告照出):cleaned 有效帖总量 <3 条 → 仍写 analysis.json 走 render 出轻量报告(coverage 如实记低量;findings/candidates 只放有证据的,没有就不写;结论按"数据不足"表述),报告数据说明卡明示"无数据≠安全,公开舆情未覆盖不代表无风险,建议实地核验"并附 checklist 对应阶段要点。交付层不变:报告必出——冒烟实测旧版降级不出报告时,前端页面无任何回执,提交者分不清"跑完但没数据"还是"卡死"(问题记录_v3 #4)。
工作流 B:推荐评价(意图 B)
- 完整执行工作流 A 第 1-9 步,但
mode = "recommend"。多标的对比输入("A 和 B 哪个好"):逐个标的走完 A 的采集与清洗(各自 query),再写一份对比 analysis(findings/candidates 分标的),verdict 给对比结论。 - 客观维度按用户问的来:用户提了交通/通勤才评估交通配套(依据帖子中的地铁/商圈信息,标注"未实地验证"),没提就不自加维度(零假设铁律同样适用于评估维度)。
verdict字段必须是一句话结论,只在用户明说了约束时附适用人群(例:用户说了"通勤优先"才写"通勤优先者可考虑"),没说约束就只给结论不给人群画像,不给绝对化承诺。- positive 数组放真实好评证据引用,suggestions 放"租前需当面核验的事项"。
工作流 C:选址建议(意图 C)
设计原则:候选片区由数据归纳,不是 LLM 生成。 平台上"XX 上班住哪合适"本身就是大量真实讨论帖——直接搜用户的问法,数据里被推荐的片区就是候选,每条带原帖证据;用户通勤范围大,数据自然覆盖更多片区,不预设数量也不硬凑。
- 信息确认:工作地(地标/地铁站)、预算、可接受通勤时长、其他要求(合租/整租、电梯房等),缺则一次问齐;用户拒绝答或说"随便"就按未指定继续,不阻塞。
- 搜索词 = 用户问法 + 分平台词形(不生成候选、不预设范围)。原则(问题记录_v3 #1 定调):格式转换 ≠ 自作主张——把用户的人话拆成关键词是翻译,红线是改意思(加没说的限定词、换标的、生成片区)。2-3 组词里问法词与关键词组混搭,各平台各取所需:
- 问法词组(web 语义搜索整句直接懂;xhs/douyin 搜索半语义、问法帖是真实体裁):
{工作地}上班住哪、{工作地} 通勤 租房 - 关键词组(关键词平台的正路, douban 裸词见 references/queries.md §1.5):
{城市前缀}{工作地} 租房推荐、{工作地} 怎么样 - 一条编排直接采:
python <skill>/scripts/run_collect.py --target 60 --queries "{工作地}上班住哪,{城市}{工作地}租房推荐" --platforms "xhs,web,douyin,douban" --days 180 --sort "discussion,hot"(量级按第 3 步菜单定,示例为轻量档;不跑 12315)。豆瓣拿到问法词 0 命中、xhs/douyin 问法词 0 命中时,脚本自愈会自动剥问法/场景词降级成工作地裸词重搜一次(lexicon.SCENARIO_WORDS,格式转换层)。命中仍不足(关键词平台 0-1 条)→ 换另一词形补采一轮(问法词↔关键词组互换)→ 仍不足按数据稀疏出说明报告,建议兜底再向用户提议加词。用户输入已含意向地名的照 A/C 分界走 A。
- 问法词组(web 语义搜索整句直接懂;xhs/douyin 搜索半语义、问法帖是真实体裁):
- 候选片区从清洗后数据归纳:Claude 统计帖子里实际被推荐/讨论的居住片区,candidates 每条必带 evidence(引文+原帖链接),被推荐频次高、口碑一致者优先;数据里有几个写几个(如"中关村 2 小时通勤"数据可能覆盖全城,就照数据写)。
- 通勤分析:对数据归纳出的候选做通勤测算(起点=候选片区、终点=用户工作地,用户给了工作地才跑);M3 之前文本估算(标注"文本估算"),之后
geocode.pyroute 实测。通勤信息只进报告的通勤区块,绝不进平台搜索词。 - 汇总写
mode = "locate"的 analysis.json(candidates:name/pros/cons/commute/evidence + 口碑风险摘要)→ render → 输出数据归纳的片区排名 + 提醒实地看房。
工作流 D:找转租房源(意图 D)
信息确认:位置(小区/地铁/片区)、预算、整租或合租、入住时间,缺则问。
采集:搜索词按动态规则组合 = 位置词 +
转租/直租/合租/个人转租/无中介(用户问直租就优先"房东直租"),2-3 组拼进一条编排命令(豆瓣 cat=1013 租房小组为主源 + 小红书,web 源可选加进 --platforms):python <skill>/scripts/run_collect.py --target 60 --queries "天通苑转租,天通苑个人转租,天通苑房东直租" --platforms "douban,xhs" --days 7 --sort "time" --min-per-platform 10评论深度沿用 run_collect 透传的
--top-comments默认值(20)即可;时间窗与排序只有意图 D 用 time + 近 7 天。过滤:
clean.py的is_listing结果只保留房源帖;再按预算/位置/发布时间筛选,按发布时间最新在前排序,超过 7 天的转租帖基本失效,只作参考且必须标注发布时间。写
mode = "listings"的 analysis.json(listings 数组:标题/链接/价格提示/户型/平台/发布时间)→ render → 输出列表 + 防骗提示(引用 references/checklist.md 看房前阶段与虚假房源风险类)。提醒:只做聚合展示,不核验房源真实性,联系前先按 checklist 核身。
工作流 E:租房咨询(意图 E)
不采集任何数据、不跑清洗,两步走(先直答、再出报告——报告是固定交付物,必出):
- 直接答:读
references/checklist.md,定位与问题对应的阶段小节,结合问题具体作答(可引用清单条目),答案直接写在回复里,不让用户等报告。 - FAQ 可视化报告:把答案整理成
mode = "faq"的 analysis.json,跑 render.py 出报告落<data>/reports/:question用用户问题原话;sections2-4 个分节、每节 3-6 要点;risk_notes风险提示;action_checklist可勾选行动清单。- 不传
--cleaned/--geo,秒出。 - 诚实边界:faq 报告基于通用知识与会话材料(checklist 等),无平台数据支撑,报告中如实标注。
- 若用户顺带提到具体标的,提示"可对该标的跑意图 A 尽调"。
地理层(M3)
意图 A/B/C(尽调/推荐/选址)且标的可定位(小区/片区名能 geocode 出坐标)时,在写完 analysis.json 之后、render 之前追加地理层;意图 D/E 不跑。标的为中介/房东(无固定坐标)或 geocode 无结果时跳过,报告自动显示"地图数据未采集"。
采集(geocode.py 全量走 cache.db 缓存,同参数复跑 0 新请求;测试纪律同样适用):
python <skill>/scripts/geocode.py geocode "天通苑" --city 北京 # 定位 python <skill>/scripts/geocode.py around "116.42,40.07" --city 北京 # 周边 8 组配套各取最近 3 python <skill>/scripts/geocode.py around "116.42,40.07" --city 北京 --custom "健身房" # 按需自定义关键词,取最近 5 python <skill>/scripts/geocode.py route "天通苑地铁站" "国贸地铁站" --city 北京 # 通勤测算(--mode transit|walking|riding),仅当用户提供了通勤目的地around 的 target 直接用 geocode 返回的 location;route 的起点=标的、终点=用户提供的通勤目的地(用户没给就不跑 route)。起终点地名后缀『地铁站』更稳:高德对裸小片区名常解析失败(报 30001,如"马连洼"会被误解析成"西苑"),加"地铁站"后缀即通;geocode 定位同理。该后缀只用于 geocode/route 命令参数,禁止进入任何平台搜索词(搜索词只用用户原话的地名)。noise 命令保留但默认不跑:仅用户主动问噪音时执行
python <skill>/scripts/geocode.py noise <坐标> --city <城市>。主动问通勤(交互):意图 A/B 在输入解析时顺带问一句"上班地点/通勤目的地是哪?"(用户可拒绝;仅当标的为小区/片区时问,中介/房东人名标的不问)。用户给了 → 跑 route(起点=标的、终点=该地点),geo.json 写 route 段,模板自动画通勤虚线+摘要;拒绝或没有 → 跳过 route,不追问。报告出来后用户补充工作地也一样补跑。
按需周边查询(交互):用户问"附近有没有大超市/医院/健身房"这类配套 → 直接跑
python <skill>/scripts/geocode.py around <坐标> --city <城市> --custom "超市"(--custom 单独给定时只查该关键词不查预置 8 组,省配额;逗号可分隔多个)→ 先用文字直接回答(名称+距离,取最近 5),同时把返回的 group(group 名=关键词)追加进 geo.json 的 around.groups(见第 5 条),重跑 render 后地图上以灰点可视化。组装
<data>/geo/<标的>.json:四段输出拼成{"geocode":{location,formatted_address,city},"around":{"radius_m":1500,"groups":[{group,pois:[{name,distance_m,address,location}]}]},"noise":{"total":0,"hits":[{type,name,distance_m,address,location}]},"route":{mode,summary,distance_m,duration_min,transfers,origin_location,dest_location}},可参照<data>/geo/下已有 geo 文件(如有)。groups 运行时追加:同一标的的多次 around 结果(预置组/自定义组)合并写回同一个 geo.json——新 group 直接 append 进 around.groups,同名 group 用新结果覆盖;追加后只重跑 render,不重采 geocode/noise/route 段。
渲染:
python <skill>/scripts/render.py --analysis <data>/analysis/<标的>.json --cleaned <data>/cleaned/<标的>.json --geo <data>/geo/<标的>.json。模板渲染地图块(小区强调色 marker、配套灰点;仅当 geo.json 带 noise 段才画噪音红点,仅当 coverage.note 标了「通勤目的地:」才画通勤虚线)+ 断网可读的文字层(配套分组网格;通勤摘要仅当用户提供了目的地)。自定义组与预置组同构(group=关键词、pois 同字段),模板按组名直接显示,无需改动。密钥:
<keys>的AMAP_JSAPI_KEY(进模板加载地图,客户端可见属正常,靠高德控制台域名白名单保护)与AMAP_JSAPI_SECRET(securityJsCode)。AMAP_JSAPI_SECRET待填时地图自动降级为文字版,填好后无需改模板。AMAP_WEB_KEY只供 geocode.py 本地调用,绝不进报告。
analysis.json schema(Claude 按此产出,render.py 消费)
{"mode":"diligence|recommend|locate|listings|faq",
"faq 模式专有":{"question":"用户问题原话","sections":[{"title":"","points":[""]}],"risk_notes":[""],"action_checklist":[""]},
"target":{"name":"","city":"","type":"小区|中介|片区"},
"coverage":{"xhs":0,"douban":0,"complaint12315":0,"web":0,"note":""},
"overall_score":0,"risk_level":"低|中|高|数据不足",
"verdict":"recommend/locate 必填:一句话结论(约束仅取用户原话,未明说不附人群画像)",
"dimensions":[{"name":"房屋质量","score":0,"reason":"","evidence_idx":[]}],
"findings":[{"risk":"","severity":"高|中|低","confidence":"高|中|低","summary":"","evidence":[{"title":"","url":"","platform":"","quote":""}]}],
"listings":[{"title":"","url":"","price_hint":"","room_hint":"","platform":"","published_at":""}],
"candidates":[{"name":"","pros":"", "cons":"","commute":"","evidence":""}],
"positive":[""],"suggestions":[""],
"disclaimer":"无数据≠安全,本报告仅为公开舆情聚合"}
字段约定:
dimensions固定 8 项(与 references/risk-signals.md 分类法一一对应,口径统一便于跨报告对比): 房屋质量 / 房东中介 / 押金费用 / 虚假房源 / 噪音 / 物业 / 治安 / 通勤配套。 数组顺序即展示顺序:用户问题点名的关注维排最前,其余按默认序,无数据维度(score 0 且 evidence_idx 空)沉底——报告模板按数组顺序渲染,问"二房东多吗"时房东中介维置顶,不让空维度占卡抢视线。 score 为 0-100;每维必带reason(一句话中文归因,如"通勤配套 22:距地铁 1.5km+ 公交线少");无数据的维度 score 置 0 且 evidence_idx 留空。overall_score为 8 维加权(押金费用/房东中介/虚假房源权重更高);risk_level依据 findings 最高 severity 与数据量综合。findings.evidence.quote摘原文不超过 100 字;evidence_idx指向 cleaned JSON 中帖子的下标。coverage记各平台清洗后有效帖数;降级/跳源的写进note。verdict仅 recommend/locate 模式必填,且不得包含用户未明说的约束假设(用户没提合租就不写"适合合租单间人群",按用户实际提及或"未指定"表述);listings/candidates分别为意图 D/C 必填;candidates每条带evidence(一句话来源线索)。positive为好评精选 8-10 条(不足时如实少给,禁止编造);suggestions3-8 条;均需有 cleaned 数据或 checklist 依据。- faq 模式(意图 E)不采集不清洗:只填 question/sections/risk_notes/action_checklist(见工作流 E),coverage 等字段留空;无标的的 FAQ 可整体省略
target(或 type 填"咨询"),渲染层已容错。 - 禁止捏造通勤:仅当用户明确给出上班地/通勤目的地才做通勤分析与地图画线,绝不假设、绝不替用户编工作地。用户提供了就在 coverage.note 写明「通勤目的地:X」,报告模板据此才渲染通勤块并画线;未提供时 geo.json 里残留的 route 数据不展示、不参与结论。
- 默认不做周边噪音查询:地理层默认只跑 geocode + around 配套查询;noise 仅用户主动问噪音时单独跑(geocode.py 的 noise 能力保留),geo.json 默认不含 noise 段,报告默认无噪音区块。
- 报告内容矩阵(报告是全屏卡片左右翻页;分析层按此组织字段,第一卡永远是用户问题的直接答案):
- A 尽调:卡1 结论(overall_score/risk_level/verdict 要点摘要)+八维(用户关注维前置、无数据维沉底) → 卡2 关键发现(按 severity 排、每条带 evidence 可点溯源) → 卡3 房源/价格 → 卡4 好评精选 → 末卡 数据说明(coverage+方法+免责,紧凑)
- B 推荐:卡1 大字 verdict+评分(适合人群仅当用户明说约束时出现)→ 卡2 正反理由+证据 → 卡3 价格与房源 → 卡4 配套(仅当用户给了通勤地或主动问配套才有) → 末卡 数据说明
- C 选址:卡1 数据归纳的候选片区排名(数量由数据定,每条带证据)+一句话理由 → 卡2 片区对比(价格/通勤/口碑 对齐) → 卡3 各区代表小区+口碑 → 卡4 地图 → 末卡
- D 找房源:卡1「近7天 N 条、预算内 M 条」→ 卡2 房源卡流(整卡可点开原帖,按新鲜度) → 卡3 价格分布 → 末卡
- E FAQ:问题原话 → 分节要点(2-4 节、每节 3-6 条) → 风险提示 → 可勾选行动清单(无数据说明卡,明示基于通用知识与会话材料)
- A/B 输入为超大片区时报告照 A/B 卡序,子区对比内容并入卡 2(数据里提到几个子区写几个)
- 溯源样式=「平台 · 标题(链接) · 发布日期」,不许出现 tag/chip 标签堆与裸长 URL;无数据的卡不生成,不放空壳区块。
排序组合矩阵(时间窗 × 排序,铁律摘要;完整正本见 references/queries.md §2)
- 排序键三种(collect_xhs/douban/web/douyin 与 run_collect 均支持,可逗号组合成多队列、两条队列都爬):
discussion(评论最多)、hot(点赞最热)、time(发布时间最新在前)。 - 只有意图 D 用 time + 近 7 天(超 7 天转租帖基本失效,只作参考且必须标注发布时间);口碑/讨论类(意图 A/B/C 一律)
--days 180 --sort discussion,hot双队列;争议热点(近期风波、舆情发酵中)改近 7 天、排序不变。 - 用户明确指定时间范围或排序口径时按用户的组合传参(例:"最近一个月讨论最多的" →
--days 30 --sort discussion)。 - 讨论类评论必抓(
--top-comments按最热排序取),选证据优先看高评论数帖,低互动帖不作为主证据(评论优先原则见 queries.md §3)。
爬取下限表(正式查询最低采集量)
按 clean 后各源有效帖数计(与 coverage 同口径):
| 意图 | 各源下限 |
|---|---|
| A/B | xhs ≥20、douban ≥10、web ≥10、douyin ≥15 |
| C | 按问法采集后总有效帖 ≥15(轻量档 60 通常足够);不足先换问法补采,仍不足在 coverage.note 说明并降 confidence |
| D | douban ≥10、xhs ≥10 |
不达标处置(依次):
- 换场景词补采:重跑 run_collect 并加
--extra-queries "新词1,新词2"(仍守频控红线:每批每平台 limit 20、批间随机间隔 10-30s,由编排器执行)。 - 补采后仍不足:在报告
coverage.note明示"数据量不足",对应 findings 的 confidence 降一档,risk_level倾向给"数据不足"。 - 禁止硬凑:不拿广告帖、无关帖、重复帖充数,不虚报 coverage。
时效规则(老帖降权,铁律)
2017 年的证据不能和 2026 年的同权重,分析时必须按发布时间降权:
clean.py会给发布时间 >24 个月的帖子打old = true标记,并在输出中给出时间分布统计(近 1 年 / 1-2 年 / 更早 三档计数)。- LLM 分析铁律:
old = true的证据,其 findings 的confidence自动降一档(高→中、中→低)。- 发布时间 >48 个月的证据只作背景参考,不进 findings 主证据链。
- findings 里确需引用老证据时,必须在 summary 中标注"较早证据(YYYY年)"。
coverage.note里必须写一句时间分布(例:"时间分布:近1年 12 条 / 1-2年 5 条 / 更早 3 条")。- 同一问题新旧证据冲突时,近期证据优先,并在 summary 中说明变化(例:"早年投诉较多,近一年未见同类反馈")。
脚本与数据约定
| 脚本 | CLI | 输出 |
|---|---|---|
| check_deps.py | 无参数 | 退出码 0=就绪;缺依赖打印安装指引后退出 |
| run_collect.py | --per-platform N(每平台上限,推荐,消除总量歧义)或 --target(总量停止线);--queries "Q1,Q2" --platforms "xhs,douban,web,douyin" --days --sort "discussion,hot" --min-per-platform [--extra-queries --batch-limit --top-comments 20 --parallel --douban-intent word|listing --refresh --reuse-min N]。--parallel 分平台并行子进程(各平台独立账本/日志,默认串行);7 天复用闸门自动跳过已采组合,--refresh 强制重采 |
/raw/*.jsonl + progress/.summary.json |
| collect_xhs.py | --query --limit --top-comments --days --sort discussion|hot|time(可逗号组合);note/comments 失败自动重试 1 次 |
/raw/xhs_<标的slug>_<日期>.jsonl |
| collect_douyin.py | --query --limit --top-comments --days --sort discussion|hot|time(可逗号组合) --get-video N(N≤3 下载 top 视频并口播转写,须配小 limit) |
/raw/douyin_*.jsonl |
| collect_douban.py | --query --limit --top-comments --days --sort --intent word|listing(口碑=word 裸搜评价词;找房=listing 拼租赁词)--rental-words --group |
/raw/douban_*.jsonl |
| collect_web.py | --query(可传多次) --limit --days --sort discussion|hot|time(可逗号组合);Exa 失败自动重试、Jina 失败降级直连再降级摘要 |
/raw/web_*.jsonl |
| collect_12315.py | --query --limit --days |
/raw/complaint12315_*.jsonl |
| ensure_auth.py | --platform xhs|douban|douyin|all |
子脚本退出码 3 后先跑它恢复登录态(douban 弹浏览器轮询等扫码,绝不秒退) |
| fetch_media.py | --note-ids "id1,id2"(自动回 raw 查签名 URL)或直接传完整 URL;--max-images-per-note 5 |
/media//(图片已压缩,供 Claude 视觉读图) |
| asr.py | --video <文件> 或 --video-dir <目录>(sherpa-onnx+SenseVoice int8,已装 /asr-venv+models;缺依赖 exit 3 带指引) |
同名 .txt 转写文本(自动 ffmpeg 抽 16k wav) |
| clean.py | --query <标的>(必填);可选 --raw-dir --out --aliases --city --max-age-months --no-entity-filter --keep-seek(--city <城市> 城市消歧:黑名单 72 城+北京信号白名单,剔外地同名小区帖,meta 计 city_mismatch/city_kept_signal;默认剔除求租帖并计 meta.seek_posts) |
/cleaned/<标的>.json |
| render.py | --analysis <data>/analysis/<标的>.json [--cleaned <data>/cleaned/<标的>.json] [--geo <data>/geo/<标的>.json] [--out] |
/reports/<标的>_<日期>.html |
| serve_home.py | 门面页本地服务(8770,默认绑 0.0.0.0 手机同 WiFi 可访问,RENT_ASSIST_BIND=127.0.0.1 可仅本机)。无参数直接跑;测试开关 RENT_ASSIST_PORT / RENT_ASSIST_NO_BROWSER / RENT_ASSIST_LAUNCH_CMD | 浏览器自动开门面页;输入需求拉终端跑 claude;盯 reports 新报告自动打开 |
| test_render_check.py | 无参数 | 报告模板数据完整性自检(改模板前后必跑) |
| test_*_offline.py(asr/auth/clean/intent/media/reuse/web 共 7 个) | 无参数 | 对应脚本的离线自检(改动对应脚本后运行该测试) |
门面页与本地入口(2026-08-15 新增)
- 用户入口 = 双击启动 bat(开发机为
E:\租房\启动租房助手.bat;他机自建 bat 跑python <skill>/scripts/serve_home.py,可用 RENT_ASSIST_WORK_DIR 指定 claude 工作目录):起本地服务(8770;默认绑 0.0.0.0,手机同 WiFi 可访问,设 RENT_ASSIST_BIND=127.0.0.1 可仅本机)并自动打开浏览器门面页(templates/landing.html,蓝白风+SVG楼群动画,手机/桌面双端适配)。 - 门面页输入任意租房需求 → 点"开始查询" → 服务拉起新终端窗口跑
claude "<需求>"(在项目目录内跑,权限生效;量级菜单等交互在该终端里完成)→ 服务每 5s 盯 data/reports,新报告生成后页面自动弹出。 - 历史报告与报告列表:门面页有"历史报告"入口,或直接访问
http://<主机>:8770/reports/(索引页,mtime 倒序);inbox 收到非租房输入时用scripts/out_of_scope.py生成范围外说明回传手机。 - 没起服务时直接双击打开 landing.html 会自动降级为"复制提问"模式(零后端可用)。会话衔接:Claude 量级确认前主动拉起门面页并挂队列接收(探测→后台启动→Monitor 接收→报告原页自动弹出,完整规则见工作流 A 第 3 步「前端衔接」);页面提交的文本自带"【量级:X】"标注,Claude 据此直接定档,不再重复追问。
- 报告模板改版纪律:改 templates/report.html.j2 前先跑
python <skill>/scripts/test_render_check.py记基线,改完再跑(不变量:新模板不得丢失旧模板已展示字段);视觉验证用 Playwright 375/1280 截图。
raw jsonl 每行 schema:{"platform","query","collected_at","url","title","content"(≤2000字),"author","published_at","likes","comments_count","comments":[{"text","likes","author"}],"extra":{}}(xhs 源的 extra 含 note_type: video|image|unknown)。
抖音源(MediaCrawler)
- 位置:
<tools>/MediaCrawler(本地部署,要求 Python ≥3.11 与 Node.js ≥16,用其.venv运行)。 - 首次使用需扫码登录:collect_douyin.py 会拉起 MediaCrawler 浏览器窗口,用抖音 App 扫码一次,登录态缓存在 MediaCrawler/browser_data/,之后免扫。缺 venv/登录失效时脚本会在 stderr 给重建指引并 exit 2,跳过该源不阻塞其他源。
- 用法:
python <skill>/scripts/collect_douyin.py --query "天通苑住过" --limit 20 --top-comments 10 --days 180 --sort discussion,hot(经 run_collect 编排时 --platforms 加 douyin 即可)。query 原样透传(组合规则见 references/queries.md,评价词/供给侧词按意图选),抖音标题带小区名比例低、信号在正文+评论,结果侧不过滤交给 clean.py。要口播转写加--get-video N(N≤3,务必配小 limit——MediaCrawler 开视频会下载搜索页全部结果;与 --parallel 互斥)。 - 测试纪律同样适用:联调一律
--limit 3-5;频控与 7 天缓存规则与其他源一致。 - Playwright 内核集中安装:
PLAYWRIGHT_BROWSERS_PATH=<tools>/playwright-browsers(不设则用 playwright 默认位置)。collect_douyin.py 启动 MediaCrawler 时已自动注入,手动跑 MediaCrawler 时需自己 export。
Token 纪律(铁律)
- 永不读取 /raw/*.jsonl,原始数据只供脚本消费。
- 采集/编排脚本 stdout 只看末尾统计行(run_collect 读
[汇总]行,单脚本读摘要行),不读全文、不把输出重定向进上下文。 - 评论只看 clean.py 截取后的 top 评论,不看全量评论(小红书已不默认读图,用户点名时 fetch_media 每帖 ≤5 张)。
- 报告一律由 render.py 渲染,Claude 不写 HTML/CSS。
- evidence 引文每条 ≤100 字,同帖多句合并。
频控与缓存(红线)
- 采集以批次为单位:每批每平台 ≤20 帖,run_collect 自动多批累计到 target(正式建议 300、测试 --target 5),批间随机间隔 10-30s(脚本内实现,不并发轰炸单平台)。
- 评论按点赞(最热)排序取 top,正式 --top-comments 20、测试 5。
- 同标的 7 天内复用 /raw 已有数据,不重复采集。
- 定位为个人低频自用,禁止批量爬取、禁止并发多开、禁止绕过平台限制。
第三方工具排障原则
- 用别人的工具/skill(opencli/agent-reach、MediaCrawler、mcporter/Exa、r.jina.ai、sherpa-onnx)报错时:先查源头再动手——原仓库 issue、官方文档、联网搜报错原文,定位根因后再修;不许只在自己代码里绕过。绕过式修复必须在输出里说明根因与绕过理由。
- 第三方配置的"拼写错误"照抄别修正(例:MediaCrawler 的下载开关就叫
ENABLE_GET_MEIDAS)。 - 根因结论记进项目问题记录留痕(开发机为 E:\租房\终测记录.md;例:r.jina.ai 免费无 key 走共享池约 20 RPM 不稳,加免费 key 后独立配额约 200 RPM)。
密钥安全
- 高德密钥只放
<keys>(数据盘,skill 目录外;旧 ~/.rent-assist/keys.env 仅回退兼容);绝不放进 skill 目录、报告、缓存或任何仓库。 AMAP_WEB_KEY只供 geocode.py 本地调用 REST,禁止出现在任何 HTML/日志/输出;AMAP_JSAPI_KEY仅允许出现在 reports/*.html(前端加载地图必需)。- 分发/拷贝 skill 时默认不含任何 key,接收方需自建
<keys>(申请方法见 references/amap-api.md)。
参考文件
references/queries.md:检索策略正本:动态搜索词组合规则(小区名+场景词,五元组现场组词后拼 run_collect 的 --queries 串)、排序组合矩阵、评论优先原则、完整示例。references/risk-signals.md:8 类风险分类法 + 关键词词典(clean.py 粗分类正本)+ 严重度分级 + 广告贴识别。references/checklist.md:看房前/看房时/签合同前/入住后四阶段清单(意图 E 知识库 + 降级输出)。references/amap-api.md:高德 Web REST + JS API 速查(M3 通勤与地图开发用)。references/auth.md:三平台登录态排障(xhs/豆瓣/抖音探测与恢复路径、ensure_auth 用法,含何时需再扫码)。