sug_v6_1_2_8.py 是一个基于 LLM Agent 的智能搜索查询优化工具,主要用于小红书平台的搜索优化。通过多轮迭代的方式,从原始查询出发,逐步扩展和优化搜索词,最终获取高质量的搜索结果。
版本: v6.1.2.8 核心模型: google/gemini-2.5-flash 主要特性:
原始问题(o)
↓
[初始化阶段]
├─ 分词 → seg_list
├─ 评估分词相关度
├─ 构建 word_list_1
├─ 构建 q_list_1
└─ 构建 seed_list
↓
[第1轮迭代]
├─ 请求 sug (建议词)
├─ 评估 sug 相关度
├─ 构建 search_list (高分sug搜索)
├─ 为 seed 加词 → q_list_next
├─ 更新 seed_list
└─ 保存搜索结果
↓
[第2轮迭代] ...
↓
[第N轮迭代] ...
↓
[输出结果 + 可视化]
class Seg(BaseModel):
text: str # 分词文本
score_with_o: float = 0.0 # 与原始问题的评分
reason: str = "" # 评分理由
from_o: str = "" # 原始问题
用途: 存储原始问题分词后的每个词单元
class Word(BaseModel):
text: str # 词文本
score_with_o: float = 0.0 # 与原始问题的评分
from_o: str = "" # 原始问题
用途: 词库,用于后续组合新的查询词
class Q(BaseModel):
text: str # 查询文本
score_with_o: float = 0.0 # 与原始问题的评分
reason: str = "" # 评分理由
from_source: str = "" # 来源: seg/sug/add
用途: 待处理的查询队列,每轮从 q_list 中取 query 进行处理
class Sug(BaseModel):
text: str # 建议词文本
score_with_o: float = 0.0 # 与原始问题的评分
reason: str = "" # 评分理由
from_q: QFromQ | None # 来自哪个 q
用途: 存储从小红书 API 获取的建议词
class Seed(BaseModel):
text: str # 种子文本
added_words: list[str] # 已添加的词
from_type: str = "" # 来源: seg/sug
score_with_o: float = 0.0 # 与原始问题的评分
用途: 用于加词扩展的基础词,记录已经添加过的词以避免重复
class Post(BaseModel):
title: str # 标题
body_text: str # 正文
type: str = "normal" # 类型: video/normal
images: list[str] # 图片URL列表
video: str = "" # 视频URL
interact_info: dict # 互动信息(点赞/收藏/评论/分享)
note_id: str # 笔记ID
note_url: str # 笔记URL
用途: 存储小红书搜索结果的帖子详情
class Search(Sug):
post_list: list[Post] # 搜索到的帖子列表
用途: 继承 Sug,附加实际搜索到的帖子数据
class RunContext(BaseModel):
version: str # 版本号
input_files: dict # 输入文件路径
c: str # 原始需求
o: str # 原始问题
log_url: str # 日志URL
log_dir: str # 日志目录
rounds: list[dict] # 每轮的详细数据
final_output: str | None # 最终结果
用途: 记录整个运行过程的上下文信息和中间结果
功能: 将原始问题拆分成有意义的最小单元
输入: 原始查询文本 输出:
class WordSegmentation:
words: list[str] # 分词结果列表
reasoning: str # 分词理由
分词原则:
示例:
功能: 评估文本与原始问题的匹配程度
输入: 原始问题 + 待评估文本 输出:
class RelevanceEvaluation:
relevance_score: float # 0-1的相关性分数
reason: str # 评估理由
评估标准:
示例:
功能: 从候选词中选择最合适的词与 seed 组合
输入: 原始问题 + 当前 seed + 候选词列表 输出:
class WordSelection:
selected_word: str # 选择的词
combined_query: str # 组合后的新query
reasoning: str # 选择理由
选择原则:
示例:
目标: 从原始问题创建初始数据结构
流程:
步骤1: 分词
o → [word_segmenter] → WordSegmentation → seg_list
步骤2: 评估分词
for each seg in seg_list:
seg + o → [relevance_evaluator] → score + reason
更新 seg.score_with_o, seg.reason
步骤3: 构建 word_list_1
seg_list → word_list_1 (直接转换)
步骤4: 构建 q_list_1
seg_list → q_list_1 (from_source="seg")
步骤5: 构建 seed_list
seg_list → seed_list (from_type="seg")
输入:
o: 原始问题(例如: "如何获取川西秋季风光摄影素材?")输出:
seg_list: 分词结果列表word_list_1: 初始词库q_list_1: 第一轮待处理查询列表seed_list: 初始种子列表示例数据流:
o = "川西秋季摄影素材"
↓
seg_list = [
Seg(text="川西", score_with_o=0.85),
Seg(text="秋季", score_with_o=0.90),
Seg(text="摄影", score_with_o=0.88),
Seg(text="素材", score_with_o=0.75)
]
↓
word_list_1 = [Word("川西"), Word("秋季"), ...]
q_list_1 = [Q("川西"), Q("秋季"), ...]
seed_list = [Seed("川西"), Seed("秋季"), ...]
目标: 基于当前 q_list 扩展搜索,生成下一轮的数据
输入:
round_num: 轮次编号q_list: 当前轮的查询列表word_list: 当前词库seed_list: 当前种子列表sug_threshold: 建议词阈值(默认 0.7)输出:
word_list_next: 下一轮词库q_list_next: 下一轮查询列表seed_list_next: 下一轮种子列表search_list: 本轮搜索结果for each q in q_list:
sug_texts = xiaohongshu_api.get_recommendations(q.text)
for sug_text in sug_texts:
sug_list.append(Sug(
text=sug_text,
from_q=QFromQ(text=q.text, score=q.score_with_o)
))
并发处理: 所有 q 的请求可以并发执行
数据流:
q_list = [Q("川西"), Q("秋季")]
↓ [小红书API]
sug_list_list = [
[Sug("川西旅游"), Sug("川西攻略"), ...], # 来自 "川西"
[Sug("秋季景色"), Sug("秋季摄影"), ...] # 来自 "秋季"
]
async def evaluate_sug(sug: Sug) -> Sug:
sug.score_with_o, sug.reason = await evaluate_with_o(sug.text, o)
return sug
# 并发评估所有 sug
await asyncio.gather(*[evaluate_sug(sug) for sug in all_sugs])
评估标准: 使用 relevance_evaluator Agent
数据流:
Sug("川西旅游") + o → score=0.75, reason="..."
Sug("秋季摄影") + o → score=0.92, reason="..."
high_score_sugs = [sug for sug in all_sugs if sug.score_with_o > sug_threshold]
async def search_for_sug(sug: Sug) -> Search:
result = xiaohongshu_search.search(sug.text)
posts = process_notes(result)
return Search(text=sug.text, post_list=posts, ...)
search_list = await asyncio.gather(*[search_for_sug(sug) for sug in high_score_sugs])
阈值过滤: 只搜索评分 > sug_threshold 的建议词
并发搜索: 所有高分 sug 并发搜索
数据流:
high_score_sugs = [Sug("秋季摄影", score=0.92), ...]
↓ [小红书搜索API]
search_list = [
Search(text="秋季摄影", post_list=[Post(...), ...])
]
word_list_next = word_list.copy() # 暂时直接复制
说明: 当前版本词库保持不变,未来可扩展从 sug 中提取新词
5.1 为每个 seed 加词
for each seed in seed_list:
# 过滤候选词
candidate_words = [w for w in word_list_next
if w.text not in seed.text
and w.text not in seed.added_words]
# Agent 选词
selection_input = f"""
原始问题: {o}
当前Seed: {seed.text}
候选词: {candidate_words}
"""
result = await Runner.run(word_selector, selection_input)
# 创建新 query
new_q = Q(
text=result.combined_query,
score_with_o=...,
from_source="add"
)
q_list_next.append(new_q)
# 更新 seed
seed.added_words.append(result.selected_word)
关键逻辑:
示例:
seed = Seed("川西", added_words=[])
candidate_words = ["秋季", "摄影"]
↓ [word_selector]
selected_word = "秋季"
combined_query = "川西秋季"
↓ [relevance_evaluator]
new_q = Q("川西秋季", score=0.88, from_source="add")
5.2 高分 sug 加入 q_list_next
for sug in all_sugs:
if sug.score_with_o > sug.from_q.score_with_o:
new_q = Q(
text=sug.text,
score_with_o=sug.score_with_o,
from_source="sug"
)
q_list_next.append(new_q)
条件: sug 分数 > 来源 query 分数
示例:
sug = Sug("秋季摄影技巧", score=0.92, from_q=Q("秋季", score=0.85))
↓ (0.92 > 0.85)
q_list_next.append(Q("秋季摄影技巧", score=0.92, from_source="sug"))
seed_list_next = seed_list.copy() # 保留原有 seed
for sug in all_sugs:
if (sug.score_with_o > sug.from_q.score_with_o
and sug.text not in existing_seed_texts):
new_seed = Seed(
text=sug.text,
from_type="sug",
score_with_o=sug.score_with_o
)
seed_list_next.append(new_seed)
条件:
示例:
sug = Sug("川西秋季攻略", score=0.90, from_q=Q("川西", score=0.85))
↓ (0.90 > 0.85 且未重复)
seed_list_next.append(Seed("川西秋季攻略", from_type="sug"))
流程控制:
# 初始化
seg_list, word_list, q_list, seed_list = await initialize(o, context)
# 迭代
round_num = 1
while q_list and round_num <= max_rounds:
word_list, q_list, seed_list, search_list = await run_round(
round_num, q_list, word_list, seed_list, ...
)
all_search_list.extend(search_list)
round_num += 1
return all_search_list
终止条件:
q_list 为空(没有更多查询需要处理)max_rounds 限制数据累积: 所有轮次的 search_list 合并到 all_search_list
输入:
├─ input_dir/context.md (原始需求 c)
└─ input_dir/q.md (原始问题 o)
↓
[初始化]
o → seg_list → word_list_1, q_list_1, seed_list
↓
[第1轮]
q_list_1 → sug_list_1 → search_list_1
→ q_list_2, seed_list_2 (通过加词+高分sug)
↓
[第2轮]
q_list_2 → sug_list_2 → search_list_2
→ q_list_3, seed_list_3
↓
[第N轮] ...
↓
输出:
├─ all_search_list (所有搜索结果)
├─ log_dir/run_context.json (运行上下文)
├─ log_dir/search_results.json (详细搜索结果)
└─ log_dir/visualization.html (可视化HTML)
轮次输入 轮次输出
┌─────────────────┐ ┌─────────────────┐
│ q_list │──┐ │ q_list_next │
│ word_list │ │ │ word_list_next │
│ seed_list │ │ │ seed_list_next │
└─────────────────┘ │ │ search_list │
│ └─────────────────┘
↓
┌──────────────────┐
│ run_round() │
│ │
│ 1. 请求sug │
│ 2. 评估sug │
│ 3. 搜索高分sug │
│ 4. 为seed加词 │
│ 5. 构建q_next │
│ 6. 更新seed_list │
└──────────────────┘
评分函数: evaluate_with_o(text, o)
输入:
text: 待评估文本o: 原始问题输出: (score, reason)
实现:
async def evaluate_with_o(text: str, o: str) -> tuple[float, str]:
eval_input = f"""
<原始问题>{o}</原始问题>
<当前文本>{text}</当前文本>
请评估当前文本与原始问题的相关度。
"""
result = await Runner.run(relevance_evaluator, eval_input)
return result.final_output.relevance_score, result.final_output.reason
应用场景:
目标: 从词库中为 seed 选择最佳词进行组合
候选词过滤:
candidate_words = [
w for w in word_list
if w.text not in seed.text # 词不在seed中
and w.text not in seed.added_words # 词未被添加过
]
智能选择:
selection_input = f"""
<原始问题>{o}</原始问题>
<当前Seed>{seed.text}</当前Seed>
<候选词列表>{', '.join([w.text for w in candidate_words])}</候选词列表>
请从候选词中选择一个最合适的词,与当前seed组合成新的query。
"""
result = await Runner.run(word_selector, selection_input)
验证和评估:
# 验证选择的词在候选列表中
if selection.selected_word not in [w.text for w in candidate_words]:
continue
# 评估组合后的query
new_q_score, new_q_reason = await evaluate_with_o(
selection.combined_query, o
)
晋升到 q_list 的条件:
if sug.score_with_o > sug.from_q.score_with_o:
q_list_next.append(Q(
text=sug.text,
score_with_o=sug.score_with_o,
from_source="sug"
))
晋升到 seed_list 的条件:
if (sug.score_with_o > sug.from_q.score_with_o
and sug.text not in existing_seed_texts):
seed_list_next.append(Seed(
text=sug.text,
from_type="sug",
score_with_o=sug.score_with_o
))
逻辑: 只有当 sug 的评分超过其来源 query 时,才认为 sug 是更优的查询词
目标: 只搜索高质量的建议词
实现:
high_score_sugs = [
sug for sug in all_sugs
if sug.score_with_o > sug_threshold
]
# 并发搜索
search_list = await asyncio.gather(*[
search_for_sug(sug) for sug in high_score_sugs
])
默认阈值: 0.7(可通过 --sug-threshold 参数调整)
类: XiaohongshuSearchRecommendations
方法: get_recommendations(keyword: str) -> list[str]
功能: 获取指定关键词的搜索建议词
使用场景: 在每轮中为 q_list 中的每个 query 请求建议词
类: XiaohongshuSearch
方法: search(keyword: str) -> dict
功能: 搜索指定关键词,返回帖子列表
返回数据处理:
def process_note_data(note: dict) -> Post:
note_card = note.get("note_card", {})
return Post(
note_id=note.get("id"),
title=note_card.get("display_title"),
body_text=note_card.get("desc"),
type=note_card.get("type", "normal"),
images=[img.get("image_url") for img in note_card.get("image_list", [])],
interact_info={
"liked_count": ...,
"collected_count": ...,
"comment_count": ...,
"shared_count": ...
},
note_url=f"https://www.xiaohongshu.com/explore/{note.get('id')}"
)
保存内容:
{
"version": "sug_v6_1_2_8.py",
"input_files": {...},
"c": "原始需求",
"o": "原始问题",
"log_dir": "...",
"log_url": "...",
"rounds": [
{
"round_num": 0,
"type": "initialization",
"seg_list": [...],
"word_list_1": [...],
"q_list_1": [...],
"seed_list": [...]
},
{
"round_num": 1,
"input_q_list": [...],
"sug_count": 20,
"high_score_sug_count": 5,
"search_count": 5,
"total_posts": 50,
"sug_details": {...},
"add_word_details": {...},
"search_results": [...]
},
...
],
"final_output": "..."
}
保存内容:
[
{
"text": "秋季摄影",
"score_with_o": 0.92,
"reason": "...",
"from_q": {
"text": "秋季",
"score_with_o": 0.85
},
"post_list": [
{
"note_id": "...",
"note_url": "...",
"title": "...",
"body_text": "...",
"images": [...],
"interact_info": {...}
},
...
]
},
...
]
生成方式:
subprocess.run([
"node",
"visualization/sug_v6_1_2_8/index.js",
abs_context_file,
abs_output_html
])
依赖: Node.js + React + esbuild
生成的文件: log_dir/visualization.html
python3 sug_v6_1_2_8.py \
--input-dir "input/旅游/如何获取川西秋季风光摄影素材?" \
--max-rounds 4 \
--sug-threshold 0.7 \
--visualize
参数说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--input-dir |
str | input/旅游-逸趣玩旅行/... |
输入目录路径 |
--max-rounds |
int | 4 | 最大迭代轮数 |
--sug-threshold |
float | 0.7 | 建议词评分阈值 |
--visualize |
flag | True | 是否生成可视化 |
input_dir/
├── context.md # 原始需求描述
└── q.md # 原始问题
input_dir/output/sug_v6_1_2_8/{timestamp}/
├── run_context.json # 运行上下文
├── search_results.json # 详细搜索结果
└── visualization.html # 可视化页面
分词评估: 所有 seg 并发评估
await asyncio.gather(*[evaluate_seg(seg) for seg in seg_list])
python
await asyncio.gather(*[evaluate_sug(sug) for sug in all_sugs])
搜索: 所有高分 sug 并发搜索
await asyncio.gather(*[search_for_sug(sug) for sug in high_score_sugs])
问题: word_list 在初始化后不再更新,可能错过新的有价值的词
改进方向:
问题: 每个 seed 每轮必须加一个词,即使候选词质量不高
改进方向:
问题: 不同 query 可能返回相同的 sug,导致重复搜索
改进方向:
问题: 搜索到的帖子内容没有被进一步分析和利用
改进方向:
问题: sug_threshold 固定,可能导致某些轮次没有搜索结果
改进方向:
假设:
QSKR每轮时间复杂度:
O(Q)(并发)O(Q * S)(并发)O(高分sug数量)(并发)O(K * word_list大小)(串行,但每个加词操作并发评估)总时间复杂度: O(R * (Q + Q*S + K*W))
seg_list: O(分词数)word_list: O(分词数)(当前版本)q_list: O(Q) 每轮seed_list: O(K) 每轮sug_list: O(Q * S) 每轮search_list: O(高分sug数) * O(每个搜索的帖子数)总空间复杂度: O(R * (Q*S + 高分sug数*帖子数))
sug_v6_1_2_8.py 是一个设计精良的搜索查询优化系统,具有以下特点:
```
原始问题 → 分词 → 评估 → 迭代(请求sug → 评估 → 搜索 → 加词 → 更新) → 输出结果 ```
文档生成时间: 2025-11-03 代码版本: sug_v6_1_2_8.py 作者: Knowledge Agent Team