fix(rag): recall complete course homework lists

This commit is contained in:
2026-07-16 15:48:40 +08:00
parent cbbdb41b80
commit 245b6b3eca
2 changed files with 72 additions and 8 deletions

View File

@@ -32,7 +32,8 @@ from app.services.rag_service import PromptService, RagResult, RetrievedChunk
SAFETY_RULE_VERSION = "minimum-safety-v1"
MAX_LEXICAL_CANDIDATES = 12
MAX_SELECTED_SECTIONS = 4
MAX_OVERVIEW_SELECTED_SECTIONS = 8
MAX_OVERVIEW_LEXICAL_CANDIDATES = 48
MAX_OVERVIEW_SELECTED_SECTIONS = 24
BUSINESS_MARKERS = {"课程", "大本营", "训练营", "老师", "卢慧", "功课", "学员", "课堂", "练习", "觉察", "内在"}
@@ -105,10 +106,17 @@ class KnowledgeAgentService:
try:
if need_knowledge and selected_ids:
terms = cls._query_terms(retrieval_question)
candidates = cls.search_knowledge(db, terms, selected_ids, catalog)
trace.append(cls._trace("search_knowledge", len(trace) + 1, {"queryTerms": terms, "knowledgeIds": selected_ids}, {"candidateCount": len(candidates), "candidates": [cls._candidate_trace(x) for x in candidates]}, started))
await cls._rerank(db, retrieval_question, candidates, trace, started)
practice_overview = cls._is_practice_overview(retrieval_question)
candidate_limit = cls._candidate_limit(retrieval_question)
candidates = cls.search_knowledge(
db,
terms,
selected_ids,
catalog,
candidate_limit=candidate_limit,
)
trace.append(cls._trace("search_knowledge", len(trace) + 1, {"queryTerms": terms, "knowledgeIds": selected_ids, "candidateLimit": candidate_limit}, {"candidateCount": len(candidates), "candidates": [cls._candidate_trace(x) for x in candidates]}, started))
await cls._rerank(db, retrieval_question, candidates, trace, started)
selected = cls._select_sections(
candidates,
limit=cls._selection_limit(retrieval_question),
@@ -323,7 +331,15 @@ class KnowledgeAgentService:
return {"knowledgeId": item.knowledge.id, "knowledgeName": item.knowledge.name, "versionId": item.version.id, "chunkId": item.chunk.id, "sectionId": item.section.id, "title": item.chunk.title, "lexicalScore": item.lexical_score, "rerankScore": item.rerank_score, "selected": item.selected, "discardReason": item.discard_reason}
@classmethod
def search_knowledge(cls, db: Session, terms: list[str], selected_ids: list[int], catalog: list[dict]) -> list[Candidate]:
def search_knowledge(
cls,
db: Session,
terms: list[str],
selected_ids: list[int],
catalog: list[dict],
*,
candidate_limit: int = MAX_LEXICAL_CANDIDATES,
) -> list[Candidate]:
versions_by_kb = {item["knowledgeId"]: item["versionId"] for item in catalog}
version_ids = [versions_by_kb[item] for item in selected_ids if item in versions_by_kb]
if not version_ids:
@@ -349,7 +365,7 @@ class KnowledgeAgentService:
if score > 0:
candidates.append(Candidate(chunk, section, knowledge, version, score))
candidates.sort(key=lambda item: (item.lexical_score, item.version.published_at or item.version.created_at), reverse=True)
return candidates[:MAX_LEXICAL_CANDIDATES]
return candidates[:candidate_limit]
@classmethod
async def _rerank(cls, db: Session, question: str, candidates: list[Candidate], trace: list[dict], started: float) -> None:
@@ -385,6 +401,15 @@ class KnowledgeAgentService:
prefer_numbered_practice: bool = False,
) -> list[Candidate]:
if prefer_numbered_practice:
numbered_candidates = [
item for item in candidates
if KnowledgeAgentService._is_numbered_practice_title(item.chunk.title)
]
if numbered_candidates:
for item in candidates:
if not KnowledgeAgentService._is_numbered_practice_title(item.chunk.title):
item.discard_reason = "作业清单优先采用编号练习章节"
candidates = numbered_candidates
candidates = sorted(
candidates,
key=lambda item: (
@@ -415,6 +440,12 @@ class KnowledgeAgentService:
return MAX_OVERVIEW_SELECTED_SECTIONS
return MAX_SELECTED_SECTIONS
@staticmethod
def _candidate_limit(question: str) -> int:
if KnowledgeAgentService._is_practice_overview(question):
return MAX_OVERVIEW_LEXICAL_CANDIDATES
return MAX_LEXICAL_CANDIDATES
@staticmethod
def _is_practice_overview(question: str) -> bool:
overview_markers = ("有哪些", "是什么", "包括什么", "都有什么", "列出", "汇总", "总结")