96SEO 2026-08-09 14:25 34
说起来,
这是一篇系列文。请按顺序阅读,
前面已经完成生成阶段:
运行阶段 通过 Jieba 从使用者问题中提取关键词:
async def extract_keywords(
说到state。State,runtime: Runtime,) -> State:
query = state.get
extracted_keywords = jieba.analyse.extract_tags
keywords =
return {"keywords": keywords}
但关键词本身不能直接生成 SQL。说起来,例如这方面,
北京今年销售额是多少?不过,
程序至少需要继续找到:
字段:fact_order.order_amount
说到指标,GMV
维度值这方面。dim_region.province = 北京市
这篇文章实现流程图中的三个并行召回节点:
抽取关键词 │ ├─ 召回字段 → Qdrant column_info_collection
├─ 召回指标 → Qdrant metric_info_collection
└─ 召回值 → Elasticsearch dim_value
│ ▼ 合并召回信息
▼
merge_retrieved_info
这篇文章暂不实现后面的 LLM 过滤表、过滤指标和 SQL 生成。
项目实现按以下顺序讲解:
说到痛点一。使用者往往觉得程序返回的结果“多得不堪言”,导致后续过滤变得繁琐。我们的设计正是把宽松候选拆分为三条平行方法。让每条路专注自己的职责,避免一次性把所有噪声聚集到同一个集合里。
state.query 获取原始问题。state.keywords 做基础 Query。| 通用步骤 | 当前实现 |
|---|---|
| 原始问题 → state.query → 基础关键词Jieba → state.keywords → Query LLM + prompts/extend_keywords_for_* | - TEI + Qdrant关键词检索 - Elasticsearch + IK多路合并 - 按业务 ID 去重排序保留候选的最高分 - 截断column_top_k / metric_top_k / value_top_k - 汇合merge_retrieved_info |
痛点二的观点是。不同检索器返回的分数程序不一致,导致难以统一排序。我们方法是将各来源的结果保持独立。只在最终合并时保留来源信息,避免跨源比较误差过大。
说到痛点三。使用者说“销售额”,程序直接匹配不到精确列名,却能通过语义匹配找到相似字段。使用向量检索可以弥补词典限制,让同义词也能被识别。
使用者问题中的“北京”通常需要命中真实值“北京市”。此处关注的是这方面,
Meta concept ➜ Vector retrieval ➜ Fulltext 搜索更稳健,多源结合带来更高 Recall 与更低误差率。如果只用向量,可能会错过短文本匹配;如果只用全文,则无法捕捉同义表达。这两者结合,可以兼顾 Recall 与 Precision。—— 《大模型与搜索引擎常用方法》 — 文献引用,仅作示例参考。其实,— 作者 链接 https://example.com/research/embedding-hybrid-search 摘要 向量+全文混合提高搜索质量。作者简介 张某某是 AI 搜索领域专家。曾在 X 公司任技术总监,目前在 Y 大学担任教授。作者背景 毕业于清华大学计算机科学与技术专业。在机器学习与信息检索方面有十余年经验,并在多家公司开展相关研究工作。——— ———— Source: Zhang & Co.,“Hybrid Search for Big Data”,Journal of Information Retrieval,vol.34。no.1. `
— 示例引用结束 —
请注意,本示例引用,请自行确认版权及可用性。
如有侵权。请及时联系我进行删改或屏蔽处理,谢谢!
python class PromptLoader: def init: self.promptdir = promptdir
def load:
path = self.prompt_dir / f"{name}.prompt"
return path.read_text
bash
uv add "langchain"
python from langchain.chatmodels import initchat_model
def createchatmodelresources: model = initchatmodel( model=settings.modelname,modelprovider= "openai",baseurl=settings.baseurl。apikey=settings.api_key,timeout=settings.timeout,)
python
async def expand:
prompt = PromptTemplate.from_template(
self.prompt_loader.load
)
chain = prompt | self.chat_model | JsonOutputParser
expanded_queries = await chain.ainvoke
...
python
async def _expand_or_fallback:
再看try,return await recall.keyword_expansion_service.expand
except Exception as error:
runtime.stream_writer({
至于"type","warning","message": f"
关键词失败,改用基础关键词:{error}",})
return KeywordExpansionService.unique_queries
痛点四 : **LLM 服务不稳定或超时会导致整个查询链路失效;老实说,降级机制确保至少能继续做基础关键词匹配,而不会完全卡住整个流程。
python async def search(self,collectionname:str,vector:list,limit:int。scorethreshold=None):
response = await self._client.query_points(
collection_name=collection_name,query=vector,limit=limit,with_payload=True)
# 返回 payload 中业务 ID 用于去重,而不是 UUID。
| 步骤 | 操作 |
|---|---|
| 批量生成向量 | embedding.batch_size 控制一次请求数量 |
| 并发查询 Qdrant | asyncio.gar |
| 结果去重 & 合并 | 按业务 ID 去重;记录所有命中查询,保留最高分;截取 TopK |
痛点五 : 单条向量请求延迟高且单体错误会让整条链路失效,为此我们批量请求同时做去重以减少成本。
python
{
"should":
}
python
async def recall_column:
recall=_get_recall_dependencies
base_queries= or ]
queries=await _expand_or_fallback
results=await recall.vector_recall_service.recall(
recall.column_collection。queries,recall.column_top_k)
return {"column_queries":queries,"retrieved_column_infos":results}
差不多,实现 recall_metric 与 recall_value。说起来,
为了避免冲突,每个子方法写自己的键:
text
recall_column -> column_queries / retrieved_column_infos
recall_metric -> metric_queries / retrieved_metric_infos
recall_value -> value_queries / retrieved_value_infos
python
async def merge_retrieved_info:
return {
"retrieved_info":{
"columns": state.get。"metrics": state.get,"values" : state.get
}
}
痛点七 : 若多个子任务写同一个键,会产生竞争导致结果缺失或混乱。我们设计了独立键名策略保证线程安全且易于调试。
yaml
从recall来看。column_top_k:10
metric_top_k:10
value_top_k :10
max_queries :30
vector_score_threshold:null
json
{
"type"这方面,"recall","target":"column"。"queries":,"count":123,"data":
}
可展示的观点是,
bash
uv run python scripts/rebuild_metadata.py
bash
uv run python -m unittest discover -s tests -v
bash
uv run fastapi dev app/main.py
bash
curl -N -X POST http://localhost/api/query \
-H 'Content-Type: application/json' \
-d '{"query":"北京今年销售额是多少?"}'
记忆一下“Recall”就是从海量数据里快速挑出一批可能相关的候选。话说回来,
举个例子,如果你问 “销售额”。程序不可能把百万个列都给 LLM 决策,而先把范围缩小到 TopK,接下来再交给 LLM 做最终筛选。
| Recall | Precision | ||
|---|---|---|---|
| 真正这些内容中,有多少被找回来?话说回来, | 找回来的一部分有多少是真正相关? | ||
| Recall = 找到的相关 / 所有相关 | Precision = 找到的相关 / 所全体找到结果 |
TopK 表示仅保留得分最高 K 条结果。
- K 太小容易漏掉真正答案;怎么说呢,
- K 太大则带来更多噪声。并拖慢后续 LLM 的推理速度。
阈值可以直接丢弃低于该分数的结果:
score_threshold=
不同模型之间 Cosine 分数尺度不一样,需要先观察数据再设定。
• 使用专用 Prompt 字段/指标和值 Query
• 使用 TEI 批量生成查询向量
• 使用 Qdrant 检索字段 & 指标
• 使用 Elasticsearch 精准匹配 + 全文检索维度值
• 多 Query 按业务 ID 去重保留最高分及命中来源
• 三个 LangGraph 节点写入独立 State 字段
• 使用列表边等待三个并行节点完成统一汇合
• FastAPI lifespan 管理共享客户端
• LLM 扩词失败退为基础关键词
• TopK 与最大 Query 数控制延迟与噪声
如果你想进一步完善程序。可接下来实施「过滤表」与「过滤指標」逻辑,从宽松候选中过滤掉无关元素,只留下真正需要用于 SQL 的最小集合。老实说,
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback