尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
å°å°å¸æ·¡ï¼ä»æ´ç´ RAG å° Graph RAGââä¸ä¸ªç¹é¥ªé®çç³»ç»çä¸å±æ£ç´¢å级ä¹è·¯
boooooooom 2026-08-22 0 é 读14åéä¸ä¸ªç¬ç«å®æçç产级 Graph RAG åºç¨ãå端约 9,000 è¡ Pythonãå端约 4,400 è¡ Vue3ï¼ç¥è¯å¾è°± 5,760 个èç¹ / 6,055 æ¡å ³ç³» / 323 éèè°±ãæ¬æè®°å½å®ä»"ååâåéåâtop-k"çæ´ç´ RAGï¼éæ¥æ¼è¿å°"ç¥è¯å¾è°± + ä¸è·¯æ··åæ£ç´¢ + å¾å¤è·³æ¨ç + LLM æºè½è·¯ç±"ç宿´è®¾è®¡ä¸å·¥ç¨æè¡¡ã
ä¸ã为ä»ä¹ä¸æ»¡è¶³äºæ´ç´ RAG
æ´ç´ RAG çèå¼å¾ç®åï¼ææ¡£åå â embedding â åéç¸ä¼¼åº¦ top-k â å¡è¿ prompt åç» LLMãå®å¨"æ¾ä¸æ®µé¿å¾åçææ¬"è¿ç±»ä»»å¡ä¸è¶³å¤å¥½ç¨ï¼ä½è½å°èè°±é¢åå°±æ´é²äºä¸ä¸ªç»ææ§é®é¢ï¼
- åçä¸äºå ³ç³»åé®é¢ã"鸡èé ä»ä¹è¬è""åæ¶ç¨å°é¸¡èåçªèçèæåªäº"ââè¿äºæ¬è´¨æ¯å¾éååéå交éï¼åéæ£ç´¢åªè½é è¯ä¹æ¨¡ç³å¹é ï¼å¬åçå¾å¾æ¯"å¬èµ·æ¥ç¸å ³"çææ¬åï¼è䏿¯ç²¾ç¡®çå ³ç³»ã
- **ååæç¢äºç»æåç¥è¯ã**ä¸éèå¤©ç¶æ¯"èâé£æâæ¥éª¤âåç±»"çå ³ç³»ç½ç»ï¼æ 500 åæ»çªåååï¼ä¸æ¬¡å½ä¸å¯è½åªæå°"æ ç¾"æ"æä¸æ¥éª¤"ï¼LLM æ¿çç¢çåªè½ç¼ã
- **åä¸å¬åééæç²åºã**å ³é®è¯å¹é æ é¿ç²¾ç¡®è¯ï¼"宫ä¿é¸¡ä¸"ï¼ï¼åéæ é¿åä¹/è¿ä¹ï¼"æä¹å"â"åæ³"ï¼ï¼ä½æ²¡æä»»ä½åè·¯è½åæ¶è¦ç精确å¹é ãè¯ä¹æ³ååå ³ç³»æ¨çã
äºæ¯è¿ä¸ªé¡¹ç®æ²¡æåå¨ baseline ä¸ï¼èæ¯åäºä¸å±å级ï¼
| 屿¬¡ | æ´ç´ RAG | æ¬é¡¹ç® |
|---|---|---|
| æ°æ®æº | çº¯ææ¬å | Neo4j ç¥è¯å¾è°±ï¼ç»æåå ³ç³»ï¼+ ææ¬ |
| æ£ç´¢ | åè·¯åé | ä¸è·¯å¼æå¬å + RRF èå + Cross-encoder ç²¾æ |
| æ¨ç | å ³é®è¯å¹é | å¾å¤è·³éå + LLM æ¥è¯¢è§å + æºè½è·¯ç± |
æç»ç³»ç»è½åçå¤è·³/交éç±»å
³ç³»æ¥è¯¢ï¼åæ¶ä¿çä¼ ç»æ£ç´¢å¨ç®åäºå®æ¥è¯¢ä¸"åå¿«åå"çä¼å¿ââèè¦åªä¸ç§ï¼ç±ä¸ä¸ª LLM è·¯ç±å¨èªå¨å³å®ã
äºãæ´ä½æ¶æ
ç³»ç»åå端å离ï¼å端æ¯ç¼ææ ¸å¿ï¼å端è´è´£æµå¼å±ç¤ºä¸æ£ç´¢è½¨è¿¹å¯è§åã
ç¨æ· query
â æºè½è·¯ç±ï¼LLM åç»´åæï¼éçç¥
ââ ä¼ ç»æ··åæ£ç´¢ï¼
â LLM æååå±å
³é®è¯ï¼å®ä½çº§/主é¢çº§ï¼
â â å¾ KV åå± + Milvus åé + BM25 ä¸è·¯å¹¶è¡å¬å
â â RRF(k=60) èååº 20 åé
â â bge-reranker cross-encoder ç²¾æ â top_k(5)
â â ç¶ææ¡£åå¡«å 5 æ¡ï¼å®æ´èè°±æ¿æ¢ç¢ç chunkï¼
ââ å¾ RAGï¼
â LLM åºå¾æ¥è¯¢è®¡åï¼6 ç§ç±»åï¼
â â Cypher å¤è·³éå / åå¾ / äº¤éæ¥è¯¢ + è·¯å¾è¯å
â â ç©ºç»æèªå¨ fallback æ··åæ£ç´¢
ââ ç»åï¼ä¼ ç»æ»¡é¢ + å¾é¢ç® 2ï¼Round-robin 交æ¿å»é
â ç»è£
ä¸ä¸æ â LLM æµå¼çæï¼éè¯ + éçº§éæµå¼ï¼
â SSE é token è¿å + æ£ç´¢è½¨è¿¹/æ¥æº/å¾è°±å¯è§å
ææ¯æ ï¼
- å端ï¼Python / FastAPI / Neo4j / Milvusï¼Milvus Liteï¼/ DeepSeekï¼OpenAI å ¼å®¹æ¥å£ï¼/ BGEï¼embedding + rerankerï¼/ jieba + rank_bm25 / RRF / RAGAS / LangChain
- å端ï¼Vue3 + TypeScript + Vite + Element Plus + Pinia + SSEï¼æå fetch + ReadableStreamï¼+ vis-network å¾è°±æ¸²æ
- è§æ¨¡ï¼å¾è°± 5,760 èç¹ / 6,055 å ³ç³»ï¼å ¶ä¸ REQUIRES 2,905ãCONTAINS_STEP 2,514ï¼ï¼323 个 Markdown èè°±ï¼10 个åç±»ï¼Embedding ç¨ BGE-small-zh-v1.5ï¼512 ç»´ã
å端模åçèè´£åå徿¸ æ°ï¼
backend/
âââ app.py FastAPI å
¥å£ï¼lifespan åå°å¼æ¥åå§åï¼
âââ main.py AdvancedGraphRAGSystem ç¼æå±
âââ api/ è·¯ç± / Schema / ä¼è¯ / è¯ä¼°åå¨
âââ config.py å
¨é¨è¶
åï¼dataclassï¼
âââ rag_modules/
âââ graph_data_preparation.py 徿°æ®å è½½ãææ¡£æ¼è£
ãæºè½åå
âââ milvus_index_construction.py åéç´¢å¼ï¼HNSWï¼
âââ markdown_parser.py ä¸ä¼ è谱解æ
âââ graph_indexing.py å¾ KV åå±ç´¢å¼ï¼LightRAG 飿 ¼ï¼
âââ hybrid_retrieval.py ä¸è·¯å¬å + RRF + ç¶ææ¡£åå¡«
âââ reranker.py cross-encoder ç²¾æ
âââ graph_rag_retrieval.py å¾å¤è·³æ¨ç
âââ intelligent_query_router.py LLM æºè½è·¯ç± + ç»åçç¥
âââ generation_integration.py æµå¼çæ¡çæ
âââ evaluation.py RAGAS è¯ä¼°
ä¸ã离线建åºï¼æå¾è°±åæå¯æ£ç´¢çä¸ç§ç´¢å¼
3.1 ä»å¾èç¹å°èªç¶è¯è¨ Document
Neo4j éåçæ¯ç»æåç Recipe / Ingredient / CookingStep èç¹å REQUIRES / CONTAINS_STEP / BELONGS_TO_CATEGORY å
³ç³»ãå»ºåºæ¶å
ææ¯ä¸ªèè°± JOIN å®çé£æãæ¥éª¤ãåç±»ï¼æ¼æä¸ç¯å¸¦ ## æ é¢çèªç¶è¯è¨ææ¡£ï¼è§ graph_data_preparation.build_recipe_documentsï¼ï¼
# 西红æ¿ç鸡è
## èåæè¿° ...
## æé飿
1. 西红æ¿(2个) 2. 鸡è(3个) ...
## å¶ä½æ¥éª¤
### 第1æ¥
æ¥éª¤: 西红æ¿ååï¼é¸¡èææ£...
## æ ç¾
ä¸é¥èãå¿«æè
è¿ä¸æ¥å¾å ³é®ï¼åéå BM25 æ£ç´¢ç对象æ¯"å¯è¯»çèè°±æç« "ï¼è䏿¯å¹²çªçä¸å ç»ï¼ä¿è¯äºå¬åå 容åç» LLM æ¶æ¯é顺çã
3.2 æºè½ååï¼ä¼å ä¿ç»æï¼é¿åº¦å åº
ååçç¥ä¸æ¯æ èæ»çªï¼èæ¯ä¼å
æ ## äºçº§æ é¢åå以ä¿çè¯ä¹å®æ´æ§ï¼åªæå½ææ¡£æ²¡æäºçº§æ 颿¶ï¼æéå为 500 åãoverlap 50 çæ»å¨çªå£ï¼chunk_documentsï¼ï¼
sections = content.split('\## ')
if len(sections) <= 1:
# æ æ é¢ï¼åºå®é¿åº¦æ»çª
total_chunks = (len(content) - 1) // (chunk_size - chunk_overlap) + 1
...
else:
# æäºçº§æ é¢ååï¼æ¯æ®µä»¥ ## å¼å¤´ä¿çç« èç»æ
chunk_content = f"## {section}"
æ¯ä¸ª chunk è®°å½ chunk_idãparent_idï¼èè°± nodeIdï¼ãchunk_indexï¼ä¸ºåé¢ç"ç¶ææ¡£åå¡«"åä¸ä¼ç¬ã
3.3 ä¸å¥å¼æç´¢å¼
åä¸ä»½ chunks åæ¶è¿å ¥ä¸å¥ç´¢å¼ï¼äºä¸ºè¡¥å ï¼
- Milvus åéç´¢å¼ï¼BGE-small-zh ç¼ç æ 512 ç»´å½ä¸ååéï¼å»º HNSW ç´¢å¼ï¼
M=16ãefConstruction=200ãæ¥è¯¢ef=64ãCOSINEï¼ãå½ä¸ååä½å¼¦çä»·äºå ç§¯ï¼æ¯è¾é«æã - BM25 ç´¢å¼ï¼jieba 精确åè¯ + æå·¥ä¸æåç¨è¯è¡¨ï¼å©è¯/è¿è¯/çé®è¯/代è¯çï¼ï¼ç¨
rank_bm25.BM25Okapi建索å¼ï¼æ é¿ç²¾ç¡®å ³é®è¯ã - å¾ KV åå±ç´¢å¼ï¼åé´ LightRAG ç
(K,V)ææ³ï¼æå¾å®ä½åå ³ç³»æå¹³æå ååå¸è¡¨ï¼å¹¶å»ºååç´¢å¼æ¯æ O(1) æå ³é®è¯åæ¥ï¼ä¸ä¸è详述ï¼ã
åãä¸è·¯æ··åæ£ç´¢ä¸ RRF èå
4.1 å¾ KV å屿£ç´¢ï¼å®ä½çº§ + 主é¢çº§
æ··åæ£ç´¢ç第ä¸è·¯æ¯å¾é®å¼ç´¢å¼ãGraphIndexingModule ç»´æ¤ä¸¤ä¸ªåå¨ï¼
entity_kv_storeï¼nodeId â EntityKeyValueï¼æèå/飿/æ¥éª¤ç屿§æ¼ævalue_contentï¼index_keysè³å°å å«å®ä½åï¼relation_kv_storeï¼relation_id â RelationKeyValueï¼å¹¶æå ³ç³»ç±»åé¢çæä¸»é¢å ³é®è¯åååç´¢å¼ã
主é¢é®ççææ¯è¿ä¸è·¯çç¹çä¹ç¬ï¼_generate_relation_index_keysï¼ï¼
if relation_type == "REQUIRES":
keys.extend(["飿æé
", "ç¹é¥ªåæ", f"{source_entity.entity_name}_飿", target_entity.entity_name])
elif relation_type == "HAS_STEP":
keys.extend(["å¶ä½æ¥éª¤", "ç¹é¥ªè¿ç¨", f"{source_entity.entity_name}_æ¥éª¤", "å¶ä½æ¹æ³"])
elif relation_type == "BELONGS_TO_CATEGORY":
keys.extend(["èååç±»", "ç¾é£ç±»å«", target_entity.entity_name])
è¿æ ·"鸡èé ä»ä¹"éç"é "è½éè¿"飿æé "å½ä¸ REQUIRES å ³ç³»ï¼"å·èæä»ä¹"è½éè¿"èååç±»"å½ä¸ BELONGS_TO_CATEGORYââ主é¢çº§æ£ç´¢æ ééåæ´å¼ å¾ï¼æ¥åå¸è¡¨å³å¯ã
æ£ç´¢åå
ç¨ LLM æ query ææä¸¤å±å
³é®è¯ï¼extract_query_keywordsï¼temperature=0.1 ä¿è¯ç¨³å®ï¼ï¼
- å®ä½çº§ï¼å ·ä½é£æ/èå/å·¥å ·ï¼é¸¡è¸èãå¹³åºé ï¼ï¼
- 主é¢çº§ï¼æ½è±¡æ¦å¿µ/飿 ¼ï¼åè¥ãå·èãä¸é¥èï¼ã
两å±å嫿£ç´¢å¹¶èµäºä¸åè§ååï¼å®ä½ç²¾ç¡®å¹é
0.9ãå
³ç³»çº§ä¸»é¢å¹é
0.95ãåç±»å¹é
0.85ãNeo4j å
åº 0.75ãæ¯è·¯å½ä¸è¿ä¼æ¥ä¸è·³é»å±
æ¼å°å
容éåä¸ä¸æå¢å¼ºã妿å
å KV 没å½ä¸å¤ï¼ä¼èªå¨éçº§å° Neo4j fulltext ç´¢å¼ãåéçº§å° Cypher CONTAINS 模ç³å¹é
ã
4.2 ä¸è·¯å¹¶è¡å¬å
hybrid_search 䏿¬¡åèµ·ä¸è·¯å¬åï¼æ¯è·¯é½ç» candidate_k = max(rrf_k*2, 10) 个åéï¼ç¡®ä¿èåæ¶æè¶³å¤äº¤éï¼ï¼
dual_docs = self.dual_level_retrieval(query, candidate_k) # å¾ KV åå±
vector_docs = self.vector_search_enhanced(query, candidate_k) # Milvus + ä¸è·³é»å±
bm25_docs = self.bm25_search(query, candidate_k) # BM25
åéæ£ç´¢ä¹åäºå¾å¢å¼ºï¼æ¯ä¸ªå½ä¸ç Recipe èç¹ä¼å» Neo4j æ¥ä¸è·³é»å± ï¼é£æ/æ¥éª¤ï¼ï¼æ¼å°åææ«å°¾ã
4.3 为ä»ä¹å¿ é¡»ç¨ RRF è䏿¯å æåæ°
ä¸è·¯åæ°å®å ¨ä¸å¨ä¸ä¸ªé纲ï¼BM25 æ¯æ çç TF-IDF åãå鿝 0â1 ä½å¼¦ãå¾ KV æ¯æè®¾ç 0.75â0.95 è§ååãç¡¬å ææ²¡æç©çæä¹ï¼æéè°åæ¯æ åºæ´ã
项ç®éç¨ Reciprocal Rank Fusionï¼Cormack et al. 2009ï¼ï¼åªç¨æåãä¸çåå§åï¼
score(d)=âi1k+best_ranki(d),k=60 ext{score}(d) = \sum_i \frac{1}{k + ext{best\_rank}_i(d)},\quad k=60score(d)=âiâk+best_rankiâ(d)1â,k=60
å®ç°å¨ _rrf_mergeï¼å·¥ç¨ç»èåå¾ç¸å½æå®ï¼
- å source å»éç®åï¼ä¸éèçå¤ä¸ª chunk å
񄧮
node_idï¼åä¸éééåªåæä½³ï¼æå°ï¼rank ç®ä¸æ¬¡åï¼é¿å éå¤å åï¼å½ä¸ chunk æ°å¦å为rrf_chunk_hitsã - å»é key å
åºï¼ä¼å
node_idï¼ç¼ºå¤±æ¶åépage_content[:200]ç MD5ã - canonical doc éæ©ï¼æç»å±ç¤ºç» LLM çå 容åå ¨å±æå° rank 对åºç chunkï¼rank ç¸åæééä¼å 级ã
- ä¿çæ¯è·¯åå§åï¼
rrf_raw_scoresè®°ä¸åééåå§åä¾åç«¯åæ°å¯¹æ¯ï¼ä½ä¸åä¸ç®åã - æµ æ·è´è¿åï¼æ°å»º Documentï¼ä¸ mutate 䏿¸¸å¯¹è±¡ã
rrf_scores = {
doc_id: sum(1.0 / (k + r) for r in source_ranks.values())
for doc_id, source_ranks in best_rank_per_source.items()
}
RRF å
èåº rerank_candidate_k=20 个åéï¼å¼å¯éææ¶ï¼ï¼è䏿¯ç´æ¥å top_kââè¿æ¯ä¸ºä¸ä¸é¶æ®µç²¾æåå¤è¶³å¤å¤§çåéæ± ã
äºã䏤鶿®µæ£ç´¢ï¼Cross-encoder ç²¾æ
å¬åé¶æ®µç¨çæ¯ bi-encoderï¼query å doc åèªç¼ç ç®ä½å¼¦ï¼ï¼é度快ä½ç»ç²åº¦å¼±ï¼ç²¾æé¶æ®µç¨ cross-encoder æ (query, doc) æ¼å¨ä¸èµ·è¿æ¨¡åï¼è½ææ token 级交äºï¼ç¸å ³æ§å¤æåå¾å¤ã
æ åç two-stage retrieval å°±æ¯"ç²çå¤åé â cross-encoder ç²¾æ â åªç top_k"ã项ç®ç¨ BAAI/bge-reranker-v2-m3ï¼
pairs = [(query, d.page_content) for d in documents]
scores = self._model.predict(pairs, batch_size=16, show_progress_bar=False)
ranked = sorted(zip(documents, scores), key=lambda x: float(x[1]), reverse=True)
rerank_score = self._sigmoid(raw_score) # logit â (0,1)ï¼ä¿æåè°æ§
设计ä¸è´¯å½»äº"æå è½½ + å¯é级"ï¼
- 模å馿¬¡
rerank()æå è½½ï¼å¯å¨é¶ææ¬ï¼_ensure_modelç¨å鿣æ¥é鲿¢å¹¶å馿¥éå¤å è½½ï¼ - æ¨¡åæªç¼åï¼ç¦»çº¿ç¯å¢ï¼/å 载失败/æåå¼å¸¸æ¶ï¼ä¼é é级为 RRF ååºå top_kï¼ä¸å½±ååçï¼
- ç²¾æåæ
final_score设为rerank_scoreï¼è®©"å¾å"䏿åºä¸è´ï¼åæ¶ä¿çrrf_scoreä¾å¯¹æ¯é¢æ¿ã
å ãç¶ææ¡£åå¡«ï¼å«è®© LLM æ¿çç¢çç¼çæ¡
chunk åç¢åï¼æåé¢çç»æå¯è½åªæ¯"## æ ç¾"æ"### 第3æ¥"è¿ç±»å±é¨å 容ãLLM æ¿å°ä¸å®æ´çç¢çï¼å¾å®¹æé æ³è±¡è¡¥å ¨ââè¿æ£æ¯å¹»è§çæ¥æºä¹ä¸ã
项ç®å¨ rerank ä¹ååç¶ææ¡£åå¡«ï¼_attach_parent_documentsï¼ï¼å¯¹å parent_doc_top_n=5 æ¡å½ä¸ï¼ç¨åå§åæ¶æå»ºç {node_id: 宿´èè°± Document} æ å°ï¼æ chunk ç page_content æ¿æ¢æå®æ´èè°±ï¼é£æ+æ¥éª¤é½å
¨ï¼è¶
4000 åæªæï¼ï¼æååæ°éä¸åï¼
for i, doc in enumerate(docs):
if i >= top_n: # åªåå¡«å N æ¡ï¼å
¶ä½åæ ·ä¼ é
out.append(doc); continue
parent = pmap.get(str(nid))
if parent is None: # æ°èè°±æ¾ä¸å°ç¶ææ¡£ï¼ä¿æåæ ·
out.append(doc); continue
out.append(Document(page_content=pc, metadata=dict(doc.metadata)))
è¿æ¯ä¸ªå¯é
ç½®å¼å
³ï¼enable_parent_doc_retrievalï¼ï¼ææä¸è®©åç» LLM ç头é¨ä¸ä¸æä»"ä¸å¥è¯ç¢ç"åæ"宿´èè°±"ã
ä¸ãå¾ RAGï¼çæ£çå¤è·³å ³ç³»æ¨ç
æ··åæ£ç´¢åå¼ºï¼æ¬è´¨è¿æ¯"å¹é
"ãè¦åçå
³ç³»åé®é¢ï¼å¾è®©å¾èªå·±"èµ°"èµ·æ¥ãGraphRAGRetrieval æ¯è¿å¥ç³»ç»åºå«äºæ®é RAG çæ ¸å¿ã
7.1 LLM æèªç¶è¯è¨ç¿»è¯æå¾æ¥è¯¢è®¡å
å
¥å£ understand_graph_query 让 LLM æé®é¢æ å°æä¸ä¸ªç»æåç GraphQueryï¼å
å« 6 ç§æ¥è¯¢ç±»åï¼
| ç±»å | å ¸åé®é¢ | å¾æä½ |
|---|---|---|
ENTITY_RELATION | 鸡èåè¡èåè½ä¸èµ·åå | ä¸è·³å ³ç³» |
MULTI_HOP | 鸡èé ä»ä¹è¬è | åé¿è·¯å¾éå |
SUBGRAPH | å·èæä»ä¹ç¹è² | å´ç»æ ¸å¿å®ä½çå±é¨åå¾ |
PATH_FINDING | ä»é£æå°æåèçè·¯å¾ | æçè·¯å¾ï¼å ä½ï¼ |
CLUSTERING | å宫ä¿é¸¡ä¸ç±»ä¼¼çè | åå¾/èç±» |
INTERSECTION | åæ¶ç¨å°é¸¡èåçªèçè | å¤å®ä½äº¤é |
prompt éæå¾ schemaï¼èç¹ç±»åãå
³ç³»ç±»åã屿§ï¼ç´æ¥åè¯ LLMï¼å¹¶å¼ºè°"åªæ¾å¾é大æ¦çæå¯¹åºèç¹çå
·ä½å®ä½ï¼æç³å°¿ç
/30åéè¿ç±»çº¦ææ¾è¿ constraints"ï¼è¿ç»äº 3 个带 JSON ç few-shotãLLM 失败æ¶é级为é»è®¤ subgraph æ¥è¯¢ã
7.2 å¤è·³éåä¸è·¯å¾è¯å
MULTI_HOP ç¨ Cypher åé¿è·¯å¾è¯æ³ (source)-[*1..N]-(target)ï¼å¹¶å¨æ¥è¯¢éç´æ¥ç®ç¸å
³æ§ï¼
MATCH path = (source)-[*1..{max_depth}]-(target)
WHERE NOT source = target
WITH path, ..., length(path) as path_len, relationships(path) as rels, nodes(path) as path_nodes
WITH ...,
(1.0 / path_len) + -- çè·¯å¾å¾åé«
(REDUCE(s=0.0, n IN path_nodes | s + COUNT{ (n)--() }) / 10.0 / size(path_nodes)) + -- é«åº¦æ°èç¹å å
(CASE WHEN ANY(r IN rels WHERE type(r) IN $relation_types) THEN 0.3 ELSE 0.0 END) as relevance
ORDER BY relevance DESC LIMIT 20
è¯å综åäºä¸ä¸ªä¿¡å·ï¼è·¯å¾è¶çè¶æå¨ãéç»é«åº¦æ°ï¼æ¢çº½ï¼èç¹è¶å¥½ãå
³ç³»ç±»åå½ä¸ LLM æå®çå好类åå åãæ¯æ¡è·¯å¾éåè¢«æ¸²ææ å®«ä¿é¸¡ä¸ --REQUIRES--> 鸡è¸è --REQUIRES--> ... çèªç¶è¯è¨æè¿°åç» LLMï¼åæ¶ä¿çç»æåçèç¹/å
³ç³»é¾ä¾åç«¯ç»æ¨çè·¯å¾ã
7.3 å徿åä¸å¾è°±å¯åº¦
SUBGRAPH ç¨åç Cypherï¼ä¸ä¾èµ APOC æä»¶ï¼ææ ¸å¿å®ä½ N è·³å
çé»å±
ï¼å¹¶è®¡ç®å¾è°±ææ ï¼
density: CASE WHEN node_count > 1
THEN toFloat(rel_count) / (node_count * (node_count - 1) / 2) -- è¾¹æ°/æå¤§å¯è½è¾¹æ°
ELSE 0.0 END
è¿éæä¸ªå¾å®ç¨çå·¥ç¨é²å¾¡ï¼ç©ºå£³åå¾ç»ä¸çæ Documentã代ç 鿦äºä¸ç§"空"ââæ ä¸å¿ä¹æ è¿éèç¹ãä¸å¿å ¨æ¯æ åèç¹ãæä¸å¿ä½å¤ç«æ è¾¹ï¼é¿å æ"å ³äº çç¥è¯ç½ç»ï¼å å« 0 个æ¦å¿µå 0 ä¸ªå ³ç³»"è¿ç§å徿æ¬åç» LLMãå ç¨ top_k åé¢ãè¿ä¸ªè¿æ»¤å¨åç»ç»åçç¥éè³å ³éè¦ã
7.4 äº¤éæ¥è¯¢ï¼åä¹è¯æ©å± + å å·¥åè¿æ»¤
"åæ¶ç¨å°é¸¡èåçªèçè"æ¯æè½ä½ç°å¾æ°æ®åºä¼å¿çæ¥è¯¢ï¼ä½å®ç°éæä¸¤ä¸ªä¸æé¢åçæåã
第ä¸ï¼åä¹è¯ã ç¨æ·è¯´"çªè"ï¼å¾éå¯è½åçæ¯"西红æ¿"ã_expand_synonyms ç»´æ¤äºä¸å¼ 常è§é£æåä¹è¯è¡¨ï¼çªèâ西红æ¿âå£å¥³æãåè±â马éè¯âæ´èã鸡èâèé»âèç½â¦â¦ï¼ï¼æ¥è¯¢æ¶ææ¯ä¸ªå®ä½æ©æåä¹è¯ç»ã
第äºï¼å å·¥å误å¹é
ã 妿åªå i.name CONTAINS 'çªè'ï¼"çªèé
±""çªèæ±"é½ä¼è¢«å½æ"çªè"å¹é
è¿æ¥ï¼çéæé®ãCypher éç¨åç¼é»ååæé¤å å·¥åï¼
WHERE (i.name = syn
OR (i.name CONTAINS syn
AND NOT ANY(suffix IN ['é
±','æ±','ç²','æ²¹','è','é²'] WHERE i.name ENDS WITH suffix)))
SQL 屿¿å°åéåï¼Python å±ååäºæ¬¡æåï¼å¿ é¡»æ¯ä¸ªæ¥è¯¢å®ä½é½æç²¾ç¡®å¹é ï¼åè¯ææ ¸å¿åä¹è¯ï¼ï¼åæåå¹é åªç» 0.3 æéä¸ä¼è¢«æ´éèè¿æ»¤æï¼èåé嫿¥è¯¢è¯åé¢å¤å 0.2ï¼å¦"西红æ¿ç鸡è"ï¼ãè¿å¥é»è¾ä¿è¯äº"æ¾åæ¶ç¨å°é¸¡èåçªèçè"ä¸ä¼è¢«"鸡è+çªèé ±"污æã
äº¤éæ¬èº«ç¨"æ Recipe åç»ï¼ç»è®¡å¹é
å°å 个ä¸åå®ä½ç» collect(DISTINCT group.entity_idx)ï¼è¦æ±ç»æ° ⥠å®ä½æ°"å®ç°ï¼æ¯ä¸ªå¾ç»å
¸ç Cypher éå交éåæ³ã
7.5 å¾ç´¢å¼é¢ç
徿¥è¯¢é¢ç¹æåº¦æ°åæ¢çº½èç¹ï¼æä»¥åå§åæ¶ææåº¦æ°æåºç Top 1000 å®ä½åå
¨é¨å
³ç³»ç±»å颿¬¡é¢å è½½å°å
åç¼åï¼_build_graph_indexï¼ï¼åå°å¨çº¿ DB 访é®ï¼
MATCH (n) WITH n, COUNT { (n)--() } as degree
RETURN labels(n), n.nodeId, n.name, n.category, degree
ORDER BY degree DESC LIMIT 1000
å «ãLLM æºè½è·¯ç±ä¸ç»åçç¥
䏿¯æ¯ä¸ªé®é¢é½éè¦å¾æ¨çââ"红ç§èæä¹å"ç¨ä¼ ç»æ£ç´¢åå¿«å好ï¼å¼ºè¡èµ°å¾åèå¯è½æ½ä¸å°å®ä½ãäºæ¯å äºä¸å±è·¯ç±å¨ã
8.1 åç»´åæéçç¥
IntelligentQueryRouter.analyze_query 让 LLM ä»åä¸ªç»´åº¦ç» query æåï¼temperature=0ï¼å»æä¿è¯åé®åçç¥ï¼é¿å
è·¯ç±æå¨è®©è¯ä¼°åå¨çº¿ç»æä¸ä¸è´ï¼ï¼
query_complexityï¼ç®åä¿¡æ¯æ¥æ¾ 0â0.3 â ä¸ç â é«å¤æåº¦æ¨ç 0.8+ï¼relationship_intensityï¼åä¸å®ä½ â å®ä½é´å ³ç³» â å¤æå ³ç³»ç½ç»ï¼reasoning_requiredï¼æ¯å¦éè¦å¤è·³/å æ/对æ¯ï¼entity_count
è¾åºä¸éä¸ï¼hybrid_traditional / graph_rag / combinedãprompt éç¹å«å¼ºè°ï¼"åªè¦æ¶åå¤ä¸ªå®ä½å
³èï¼åæ¶ç¨å°/ä¸èµ·/é½/æ¢â¦åâ¦ï¼ï¼åªæçèµ·æ¥æ¯æ¾èï¼ä¹åºæ¨è graph_rag"ã
LLM æäºæè§åå
åºï¼_rule_based_analysisï¼ï¼ç¨å¤æåº¦/å
³ç³»å
³é®è¯å½ä¸ãæ£åè¯å«äº¤é模å¼ï¼åæ¶.*ç¨ãæ¢.*å.*ï¼ï¼ç½®ä¿¡åº¦ 0.6ã
8.2 ç»åçç¥ï¼ä¸æ¬¡ææç¡®æè¡¡çéæ
combined çç¥ç设计ç»åè¿ä¸æ¬¡å»æéæï¼å¼å¾ä¸é¨è®²ã
ææ©ççæ¬æ¯æ top_k åé¢å¹³åç»ä¸¤è·¯ï¼ä¼ ç» //2ãå¾ //2ï¼ãä¸çº¿ååç°é®é¢ï¼å¾ RAG è¿åçæ¯"å¤è·³è·¯å¾/åå¾"è¿ç±»æ¨ç线索ï¼ä¸æ¯çæ¡ä¸»ä½è¯æ®ï¼å¹³åä¼è®©è´¨éåå·®çå徿¤æé«è´¨éç宿´èè°±ã
éæåç v2 çç¥ï¼_combined_searchï¼ï¼
- ä¼ ç»è·¯ç»å®æ´ top_k åé¢ï¼è®© reranker ä» 20 åéæ± èªç±ææä¼ top_k ä¸ªå®æ´èè°±ï¼
- å¾ RAG åªç»ä¸ä¸ªå°é¢ç®
combined_graph_budget=2ï¼æç¡®å®åªæä¾è¾ å©çº¿ç´¢ï¼ - ç¨ Round-robin 交æ¿åå¹¶ï¼ä¸å¾ä¼å ï¼ææ¨ç线索åç½®ï¼å¸® LLM å 建ç«å ³ç³»è®¤ç¥ï¼ï¼
- ç¨
page_content[:100]ç MD5 å 容åå¸å»éï¼æç»æªæå° top_kã
for i in range(max_len):
if i < len(graph_docs): # å¾ä¼å
add_if_new(graph_docs[i], "graph_rag")
if i < len(traditional_docs):
add_if_new(traditional_docs[i], "traditional")
return combined_docs[:top_k]
è¿æ¯ä¸ªå ¸åçå·¥ç¨æè¡¡ï¼æ¿è®¤å¾ç»æ"æ¯çº¿ç´¢ä¸æ¯çæ¡"ï¼ç¨é¢ç®å顺åºè䏿¯å¹³åæ¥è¡¨è¾¾è¿ä¸ªå¤æã
8.3 å¾ç©ºç»æçå åº
è·¯ç±å¨éè¿æä¸éå
³é®ä¿é©ï¼å¾ RAG æ½ä¸å°å®ä½æåå¾ä¸ºç©ºæ¶ä¼è¿å []ï¼æ¤æ¶èªå¨ fallback å°ä¼ ç»æ··åæ£ç´¢ï¼é¿å
"çç¥éäºå¾ä½ç»æç©º"çæç«¯æ
åµââè¿æ¡è·¯å¾å¨è¯ä¼°æ¶å°¤å
¶å®¹æè§¦åã
ä¹ãçæ¡çæï¼æµå¼ãéè¯ä¸é级
çææ¨¡åï¼GenerationIntegrationModuleï¼ææ£ç´¢ææ¡£åé®é¢ç»è£
æç»ä¸æç¤ºè¯ï¼æµå¼/éæµå¼å
±ç¨ä¸ä»½æ¨¡æ¿ï¼é¿å
æ¼ç§»ï¼ï¼è° DeepSeek æµå¼æ¥å£ã
å¥å£®æ§æ¯è¿ä¸ªæ¨¡åçéç¹ï¼
- æµå¼å¸¦ 3 次éè¯ï¼éå¢çå¾ 2s/4s/6sï¼åºå¯¹ 429/è¶ æ¶/ç½ç»æå¨ï¼
- æµå¼å½»åºå¤±è´¥ â éçº§éæµå¼ï¼éæµå¼ä¹æ 60s timeout + éè¯ï¼
- SSE å¨çº¿ç¨æ± éé token
next()ï¼ä¸é¨ç¨å¨å µ_STREAM_ENDå¤çStopIterationââå 为 generator çStopIterationè¿ asyncio Future ä¼"interacts badly with generators"ï¼å¿ é¡»å¨çº¿ç¨å æè·è½¬æå¨å µã
åãå ¨é¾è·¯ä¼é é级
"ä»»ä½ä¸ä¸ªç»ä»¶æäºé½ä¸è½è®©è天ç½å±"æ¯è´¯ç©¿å ¨æ ç设计ååãéçº§é¾æ¡å ä¹åºç°å¨æ¯ä¸å±ï¼
| ç¯è | é级çç¥ |
|---|---|
| LLM è·¯ç±åæ | è§åå ³é®è¯å åºï¼ç½®ä¿¡åº¦ 0.6ï¼ |
| å¾ KV æªå½ä¸ | Neo4j fulltext â Cypher CONTAINS 两级é级 |
| å¾ RAG ç©ºç»æ | èªå¨ fallback ä¼ ç»æ··åæ£ç´¢ |
| åå¾/è·¯å¾ä¸ºç©º | ä¸çæç©ºå£³ Documentï¼ç´æ¥è·³è¿ |
| Reranker 模å缺失 | è·³è¿éæï¼è¿å RRF ååº |
| æµå¼çæ | éè¯ 3 次 â éçº§éæµå¼ |
| RAGAS æªå®è£ | è¯ä¼°æ¥å£è¿ 503 + å®è£ æç¤ºï¼ä¸»åºç¨ç §å¸¸è¿è¡ |
| BM25 åè¯ç¼åæå | 丢å¼ç¼åéæ°åè¯ |
| å端æªå°±ç»ª | /api/health è¿ ready=falseï¼ä¸å¡æ¥å£è¿ 503ï¼å端轮询 |
è¿å¥è®¾è®¡è®©ç³»ç»å¨æ¨¡å没ä¸å ¨ãç½ç»æå¨ã徿°æ®ä¸å ¨æ¶é½è½"带伤è¿è½¬"ï¼è䏿¯æ´ä½ä¸å¯ç¨ã
åä¸ãRAGAS è¯ä¼°éç¯ï¼ç¨æ°æ®é©±å¨è¿ä»£
妿ä¸è½éåï¼"ææå好äº"å°±åªæ¯æè§ã项ç®éæäº RAGAS åé¡¹ææ ï¼æä¼ååæå¯æµéçéç¯ã
11.1 å项ææ
- å¿ å®åº¦ Faithfulnessï¼çæ¡çæ¯ä¸ªè®ºæè½å¦ç±æ£ç´¢ä¸ä¸ææ¯æï¼æ å¹»è§ï¼ï¼ä¸éè¦åèçæ¡ï¼
- çæ¡ç¸å ³æ§ Answer Relevancyï¼è®© LLM ä»çæ¡åæ¨é®é¢ï¼ä¸åé®é¢æ¯ç¸ä¼¼åº¦ï¼ä¸éåèçæ¡ï¼ä½é embeddingï¼ï¼
- ä¸ä¸æå¬å Context Recallï¼åèçæ¡çä¿¡æ¯æ¯å¦é½è¢«æ£ç´¢ä¸ä¸æè¦çï¼é ground truthï¼ï¼
- ä¸ä¸æç²¾ç¡® Context Precisionï¼ç¸å ³é¡¹æ¯å¦æå¨åé¢ï¼MRR 飿 ¼ï¼é ground truthï¼ã
å
ç½® 8 æ¡ç¹é¥ªæµè¯éï¼data/eval_dataset.jsonï¼å¸¦åèçæ¡ï¼ï¼è¦ç飿ãå¾ RAG å
³ç³»ãåç±»å举ãåæ³çç±»å«ãè¯ä¼°å¤ç¨ç°æç DeepSeek ä½ judgeãBGE ä½ embeddingã
11.2 å·¥ç¨ç»è
- æå è½½ï¼ragas / judge LLM / embeddings 馿¬¡è¯ä¼°æåå§åï¼å¯å¨é¶ææ¬ï¼
- é²å¾¡å¼å¯¼å
¥ï¼ragas 0.2 ææ åä¼å
ã0.1 å«ååéï¼éé
çæ¬å·®å¼ï¼ææ åå读
metric.nameèªéåºï¼0.4.x éResponseRelevancyå®é è¾åºåæ¯answer_relevancyï¼ï¼ - ç¨³å®æ§è°ä¼ï¼judge
temperature=0ãmax_retries=6ï¼max_workersé»è®¤ 2ï¼åæ ·æ¬å¸¸è§¦å 10+ 次 judge è°ç¨ï¼å¹¶å 3+ å¨é¿çæ¡ä¸æ 429 å¯¼è´æ´æ¹ NaNï¼ï¼raise_exceptions=Falseè®©åæ ·æ¬å¼å¸¸è¿ NaN ä¸ä¸ææ´æ¹ï¼ - ä¸¤ä¸ªå ¥å£ï¼æµè¯éæ¹éè¯ä¼°ï¼è·å®æ´ RAG 管线ï¼+ å¨çº¿åæ¡è¯ä¼°ï¼å¯ä»ä¼è¯åå²é䏿¡ï¼éæ°æ£ç´¢å®æ´ä¸ä¸æè¯ä¼°ï¼ï¼åå²ç»æè½çå¯å¯¹æ¯ã
11.3 æ°æ®é©±å¨çææè·è¿
evaluation_results.json éçåäº 12 次è¯ä¼°è®°å½ï¼æ¸
æ°å°è®°å½äºè¿ä»£è½¨è¿¹ï¼
| é¶æ®µ | å¿ å®åº¦ | ä¸ä¸æå¬å | ä¸ä¸æç²¾ç¡® |
|---|---|---|---|
| èµ·ç¹ eval-1ï¼æ´ç´ ä¸è·¯åçï¼ | 0.214 | 0.208 | â |
| æä½³ eval-11ï¼+reranker/ç¶ææ¡£/åä¹è¯çï¼ | 0.721 | 0.696 | 0.763 |
å¿ å®åº¦ä» 0.21 æå° 0.72ãå¬åä» 0.21 æå° 0.70ï¼ä¸»è¦ç±è¿å 项è¿ä»£é©±å¨ï¼å¼å ¥ cross-encoder ç²¾ææå头é¨ç¸å ³æ§ãç¶ææ¡£åå¡«ç» LLM 宿´ä¸ä¸æåå°ç¼é ãåä¹è¯æ©å±åå å·¥åè¿æ»¤ä¿®å¤äº¤éæ¥è¯¢ãå¾ç©ºç»æ fallback é¿å 空ä¸ä¸æã
乿è¯å®çä¸è¶³ï¼answer_relevancy å judge 模åéæµä¸ç´æ¯ nullï¼è¯ä¼°åæ°å¨ä¸å run é´ææ³¢å¨ï¼eval-5 æ¾è·å° 0.144ï¼ï¼è¯´æå¯¹ judge LLM ç鲿£æ§è¿ä¸å¤ââè¿äºé½å¨ææ¡£éæç¡®è®°å½ï¼è䏿¯åè£
å®ç¾ã
åäºãå·¥ç¨å
12.1 FastAPI + SSEï¼åå°åå§åä¸é»å¡
app.py ç¨ lifespan å¨åå° asyncio.to_thread åå§å RAG ç³»ç»ï¼è¿ Neo4j/Milvusãå è½½ embeddingã建索å¼é½æ¯é»å¡æä½ï¼ï¼HTTP æå¡ç«å³å¯å¨ï¼/api/health åæ å°±ç»ªç¶æï¼æªå°±ç»ªä¸å¡æ¥å£è¿ 503ãææé»å¡çæ£ç´¢/çæ/ä¸ä¼ å¨çº¿ç¨æ± æ§è¡ï¼ä¸å¡äºä»¶å¾ªç¯ã
SSE ç¨ sse_starlette.EventSourceResponseï¼äºä»¶åä¸ç±»ï¼å
æ¨ä¸ä¸ª analysis äºä»¶ï¼è·¯ç±åæ + æ¥æº + æ£ç´¢è½¨è¿¹ï¼ï¼åé chunk æ¨ tokenï¼æå done/errorãå端å ä¸ºè¦ POSTï¼EventSource 䏿¯æ POSTï¼ï¼æåäº fetch + ReadableStream 客æ·ç«¯ã
12.2 ç¼å䏿§è½
- BM25 åè¯æçº¹ç¼åï¼jieba åè¯æ¯ CPU å¯éæä½ï¼å¯å¨æ
¢ä¸»è¦æ
¢å¨è¿ãæ
tokenized_corpuspickle å°.bm25_cache/ï¼ç¨"chunk æ° + æ¯ä¸ª chunk_id åå 容ç MD5"ç®è¯ææçº¹ï¼å 容/æ°é/顺åºä»»ä¸ååç¼åèªå¨å¤±æï¼å½ä¸åè·³è¿åè¯ç´æ¥ç® IDFã - ææ¡£è§£æç¼åï¼èè°±ææ¡£åæ ·æ MD5 æçº¹ç¼åã
- UNWIND æ¶é¤ N+1ï¼å¾æ¥è¯¢ç¨
UNWIND $keywords/UNWIND $entity_groupsæ¹éä¼ åï¼é¿å å¨ Python é循ç¯åæ¥è¯¢ã - HNSW åæ°ï¼M=16ãefConstruction=200ãæ¥è¯¢ ef=64ï¼å¨å¬ååå»¶è¿é´å平衡ã
12.3 ä¼è¯ä¸å¹¶åå®å ¨
è天ä¼è¯è½ç JSONï¼ç¨åååï¼åä¸´æ¶æä»¶å renameï¼+ 线ç¨é鲿¢å¹¶ååå ¥æåã
12.4 åç«¯ï¼ææ£ç´¢è¿ç¨ç»åºæ¥
å端ä¸åªæ¯ä¸ªè天æ¡ï¼è¿æ"为ä»ä¹æ¨èè¿ä¸ªç»æ"å¯è§åï¼
- æ£ç´¢è½¨è¿¹é¢æ¿ï¼å±ç¤ºä¸è·¯åèªçåéæ°ãæç»å ¥éæ°ãæ¯å¦èµ°äºéæãåéæ± 大å°ï¼
- åææ ç¾ï¼æ¾ç¤ºè·¯ç±çç¥ãå¤æåº¦ãå ³ç³»å¯é度ã置信度ï¼
- å¾ RAG æ¨çè·¯å¾ï¼æ
path_nodes/path_relationships渲ææèç¹é¾ï¼æ åºèµ°äºå è·³ãåªäºå ³ç³»ï¼åå¾å±ç¤ºå¯åº¦ãèç¹æ°ï¼ - ç¥è¯å¾è°±å¯è§åï¼åèè°± 1-hop åå¾ç¨ vis-network ç»æå¯äº¤äºç½ç»å¾ã
åä¸ãæ°æ®éç¯ï¼9 æ¥å¢éä¸ä¼ èè°±
ç³»ç»æ¯æç¨æ·ä¸ä¼ Markdown èè°±ï¼upload_markdown_recipe ä¸²èµ·ä¸æ¡ 9 æ¥å¢éå
¥åºæµæ°´çº¿ï¼markdown_parser è´è´£è§£æï¼æ¯æ"å¿
å¤åæ/计ç®/æä½/éå å
容"ç« èãç¨éè¡çå¤ç§æ ¼å¼ãâ
é¾åº¦è®¡æ°ï¼ï¼
- è§£æ Markdown â ç»æå
ParsedRecipeï¼ - äºå¡åå ¥ Neo4jï¼Recipe/Ingredient/CookingStep + å ³ç³»ï¼ï¼æ´æ°å åèç¹å表ï¼
- æ¸ ç Milvus ä¸è¯¥èè°±æ§åéï¼è¦çä¸ä¼ åºæ¯ï¼ï¼
- æ¼è£ æä¸ç¦»çº¿ä¸è´ç LangChain Documentï¼
- å¤ç¨åä¸å¥ååé»è¾ååï¼æ
##æ é¢ï¼å åºæ»çªï¼ï¼ - å¢éæå ¥ Milvusï¼
- é建 BM25 ç´¢å¼ï¼
BM25Okapi䏿¯æå¢éå ææ¡£ï¼åæçº¹ç¼åè·³è¿åè¯ï¼ï¼ - å¢éæ´æ°å¾ KV ç´¢å¼ï¼å å®ä½/å ³ç³»/ååç´¢å¼ï¼å·²åå¨ç飿å¤ç¨ï¼ï¼
- æ´æ°ç¶ææ¡£æ å° + æ¸ ææ¡£ç¼åã
å ¥åºæ¶åæ ·åäºåä¹è¯åå å·¥åå¤çï¼ä¿è¯ä¸ä¼ è·¯å¾ååå²è·¯å¾çæ£ç´¢ä¸è´æ§ã
ååãä¸è¶³ä¸ä¸ä¸æ¥
ææ¡£é没æåé¿é®é¢ï¼åèæ"å·²ç¥æ¬ è´¦"åå¾å¾æ¸ æ¥ï¼
- å
³ç³»ç±»åä¸ä¸è´ bugï¼å¾ KV ç´¢å¼ç¨
HAS_STEPï¼æ°æ®å±åå¾ RAG ç¨CONTAINS_STEPï¼å¯¼è´æ¥éª¤å ³ç³»ç主é¢é®å¯è½å¹é ä¸ä¸å®é è¾¹ââæ°ä¸ä¼ è·¯å¾å·²ç»ä¸ï¼åå²è·¯å¾å¾ ä¿®ã - é
置工ç¨åæ¬ è´¦ï¼
config.pyæ¯ dataclass 硬ç¼ç é»è®¤å¼ï¼ç¯å¢åé读åä¸å ¨ï¼.env.exampleç key åä¸ä»£ç ææ¼ç§»ï¼æ¯ä»"è½è·"æ¨å"å¯é¨ç½²"çä¸ä¸æ¥ã - å ä½å®ç°ï¼å¾æ¨çé¾
_build_reasoning_chainãæçè·¯å¾ãå®ä½ä¸è·³å ³ç³»æ¥è¯¢ç®åæ¯å ä½ã - è¯ä¼°é²æ£æ§ï¼answer_relevancy å éæµé¿æ nullï¼åæ°é judge æ³¢å¨ï¼ä¸ä¸æ¥å¯æ¢æ´å¼º/æ´ç¨³ç judgeãç¼åæ¥è¯¢åæç»æãé judge å¹¶åã
- æ©å±æ§ï¼å¾ KV ç´¢å¼ç®åå¨å åï¼æ°æ®é涨 100 åéæ¹å¤é¨ç´¢å¼/åçï¼BM25 坿¢ Elasticsearchï¼Neo4j å¤è·³æ¥è¯¢è¦é深度å ç´¢å¼ï¼é«é¢æ¥è¯¢å ç¼åã
- å»¶è¿ï¼ä¸æ¬¡æ¥è¯¢æå¤ 4 次 LLM è°ç¨ï¼å ³é®è¯æåãæ¥è¯¢åæã徿å¾çè§£ãçæ¡çæï¼ï¼ä¸é´æ¥éª¤å¯ç¨å°æ¨¡åæ¿ä»£æç¼åç»æã
åäºãç»è¯
è¿ä¸ªé¡¹ç®ææä»·å¼çé¨åï¼ä¸æ¯å äºå¤å°æ°ææ¯ï¼èæ¯ä¸ç³»åæä¾æ®çæè¡¡ï¼
- ç¨ RRF è䏿¯ç¡¬å æï¼å 为ä¸è·¯åæ°é纲ä¸å¯æ¯ï¼
- ç¨ cross-encoder ç²¾æï¼å 为 bi-encoder å¬åå¿«ä½ç»ç²åº¦å¼±ï¼
- åç¶ææ¡£åå¡«ï¼å 为ç¢çä¸ä¸ææ¯å¹»è§ç温åºï¼
- ç»åçç¥éå¾åªç» 2 个é¢ç®ï¼å ä¸ºå®æ¯çº¿ç´¢ä¸æ¯çæ¡ï¼
- è·¯ç± temperature 设 0ï¼å 为è¯ä¼°ä¸è½å®¹å¿æå¨ï¼
- å ¨é¾è·¯é级ï¼å 为å¯ç¨æ§æ¯"ç论æä¼"æ´éè¦ã
å®åé´äº LightRAGï¼KV ç´¢å¼ + å屿£ç´¢ + Round-robinï¼å微软 GraphRAGï¼å¾ç»ææ¨çï¼çææ³ï¼ä½æç»è½å¨ä¸ä¸ªå ·ä½ç䏿åç´é¢åéï¼å¯¹ä»»ä½æ³ææ´ç´ RAG æ¨åç产å¯ç¨ç人æ¥è¯´ï¼è¿å¥"ç»æåæ°æ®æº + å¤è·¯å¬åèå + å¯è§£éè·¯ç± + éåè¯ä¼°éç¯"çç»åæ³ï¼é½æ¯ä¸æ¡å¯å¤ç¨çè·¯å¾ã
Aitishiku.com