feat: expose knowledge type priority traces
This commit is contained in:
@@ -1556,7 +1556,7 @@ function formatRecordDateTime(value: string, boundary: "start" | "end") {
|
||||
</dl>
|
||||
<h3>本轮 KnowledgeList</h3><pre class="trace-json">{{ JSON.stringify(selectedRetrievalLog.catalogSnapshot, null, 2) }}</pre>
|
||||
<h3>Agent 工具轨迹</h3><pre class="trace-json">{{ JSON.stringify(selectedRetrievalLog.toolTrace, null, 2) }}</pre>
|
||||
<h3>候选切片与取舍</h3><el-table :data="selectedRetrievalLog.candidates"><el-table-column prop="knowledgeId" label="知识库" width="90" /><el-table-column prop="versionId" label="版本" width="80" /><el-table-column prop="sectionId" label="章节" width="80" /><el-table-column prop="lexicalScore" label="词法分" width="100" /><el-table-column prop="rerankScore" label="重排分" width="100" /><el-table-column label="采用" width="80"><template #default="{ row }">{{ row.selected ? '是' : '否' }}</template></el-table-column><el-table-column prop="discardReason" label="舍弃原因" min-width="180" /></el-table>
|
||||
<h3>候选切片与取舍</h3><el-table :data="selectedRetrievalLog.candidates"><el-table-column prop="knowledgeId" label="知识库" width="90" /><el-table-column prop="knowledgeName" label="名称" min-width="150" show-overflow-tooltip /><el-table-column prop="knowledgeTypeLabel" label="类型优先级" width="170" /><el-table-column prop="versionId" label="版本" width="80" /><el-table-column prop="sectionId" label="章节" width="80" /><el-table-column prop="lexicalScore" label="词法分" width="100" /><el-table-column prop="rerankScore" label="重排分" width="100" /><el-table-column label="采用" width="80"><template #default="{ row }">{{ row.selected ? '是' : '否' }}</template></el-table-column><el-table-column prop="discardReason" label="舍弃原因" min-width="180" /></el-table>
|
||||
<h3>最终回答</h3><pre class="trace-json">{{ selectedRetrievalLog.finalAnswer || '尚未生成回答' }}</pre>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
@@ -379,6 +379,15 @@ function typeLabel(value: string) {
|
||||
return knowledgeTypeOptions.find((item) => item.value === value)?.label || value;
|
||||
}
|
||||
|
||||
function typeDescription(value: string) {
|
||||
return ({
|
||||
fixed: "固定信息类用于上课时间、带练安排、回放音频、课程作业、会议链接、服务权益、助理联系方式等当前安排;与其他库冲突时优先采用固定信息类。",
|
||||
course: "课程知识库用于课程概念、功课步骤、老师观点和课程原文解释;优先级高于答疑和通用知识。",
|
||||
qa: "答疑知识库用于参考过往问答和常见问题,但不能覆盖课程原文或固定信息类的当前安排。",
|
||||
general: "通用知识库只做背景补充,不能覆盖课程知识、答疑知识或固定信息类。",
|
||||
} as Record<string, string>)[value] || "用于补充背景信息。";
|
||||
}
|
||||
|
||||
function contentItemTypeLabel(value: string) {
|
||||
return ({ section: "章节", card: "知识卡", chunk: "切片" } as Record<string, string>)[value] || value;
|
||||
}
|
||||
@@ -552,6 +561,7 @@ async function confirmAction(
|
||||
<el-tab-pane label="基本信息" name="overview">
|
||||
<div class="knowledge-section-head"><h4>基本信息</h4><el-button type="primary" plain @click="openEdit">编辑基本信息</el-button></div>
|
||||
<dl class="knowledge-meta"><dt>后台名称</dt><dd>{{ detail.knowledge.name }}</dd><dt>知识库类型</dt><dd>{{ typeLabel(detail.knowledge.knowledgeType) }}</dd><dt>版本更新</dt><dd>同步成功后自动替换</dd><dt>SpaceID</dt><dd>{{ detail.knowledge.feishuSpaceId }}</dd><dt>NodeID</dt><dd>{{ detail.knowledge.feishuNodeId }}</dd><dt>当前版本</dt><dd>{{ detail.currentVersion ? `V${detail.currentVersion.versionNo}` : '暂无' }}</dd></dl>
|
||||
<el-alert :title="typeDescription(detail.knowledge.knowledgeType)" type="info" :closable="false" show-icon />
|
||||
<section v-if="detail.manifest" class="manifest-panel"><h4>Knowledge Manifest</h4><p><strong>用途:</strong>{{ detail.manifest.purpose }}</p><p><strong>适用:</strong>{{ detail.manifest.applicableQuestions }}</p><p><strong>不适用:</strong>{{ detail.manifest.inapplicableQuestions }}</p><p><strong>核心主题:</strong>{{ detail.manifest.coreTopics }}</p><p><strong>边界:</strong>{{ detail.manifest.boundaries }}</p></section>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="内容预览" name="content" lazy>
|
||||
|
||||
@@ -14,6 +14,7 @@ from app.models.admin import Admin
|
||||
from app.models.knowledge import (
|
||||
HumanAttentionHistory,
|
||||
HumanAttentionRecord,
|
||||
Knowledge,
|
||||
KnowledgeRetrievalCandidate,
|
||||
KnowledgeRetrievalLog,
|
||||
)
|
||||
@@ -113,11 +114,19 @@ def retrieval_detail(
|
||||
.where(KnowledgeRetrievalCandidate.retrieval_log_id == log.id)
|
||||
.order_by(KnowledgeRetrievalCandidate.selected.desc(), KnowledgeRetrievalCandidate.id)
|
||||
).all()
|
||||
knowledge_ids = {item.knowledge_id for item in candidates}
|
||||
knowledge_map = {
|
||||
item.id: item
|
||||
for item in db.scalars(select(Knowledge).where(Knowledge.id.in_(knowledge_ids))).all()
|
||||
} if knowledge_ids else {}
|
||||
data = _retrieval_dict(log, detail=True)
|
||||
data["candidates"] = [
|
||||
{
|
||||
"id": item.id,
|
||||
"knowledgeId": item.knowledge_id,
|
||||
"knowledgeName": knowledge_map[item.knowledge_id].name if item.knowledge_id in knowledge_map else "",
|
||||
"knowledgeType": knowledge_map[item.knowledge_id].knowledge_type if item.knowledge_id in knowledge_map else "general",
|
||||
"knowledgeTypeLabel": _knowledge_type_label(knowledge_map[item.knowledge_id].knowledge_type if item.knowledge_id in knowledge_map else "general"),
|
||||
"versionId": item.version_id,
|
||||
"chunkId": item.chunk_id,
|
||||
"sectionId": item.section_id,
|
||||
@@ -131,6 +140,15 @@ def retrieval_detail(
|
||||
return api_success(data)
|
||||
|
||||
|
||||
def _knowledge_type_label(value: str) -> str:
|
||||
return {
|
||||
"fixed": "固定信息类·最高优先级",
|
||||
"course": "课程知识库",
|
||||
"qa": "答疑知识库",
|
||||
"general": "通用知识库",
|
||||
}.get(value, value or "通用知识库")
|
||||
|
||||
|
||||
@router.get("/attention/list")
|
||||
def attention_list(
|
||||
priority: str = Query(default=""),
|
||||
|
||||
@@ -154,7 +154,7 @@ class KnowledgeAgentService:
|
||||
candidate_limit=candidate_limit,
|
||||
prefer_fixed_information=cls._is_fixed_information_query(retrieval_question),
|
||||
)
|
||||
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))
|
||||
trace.append(cls._trace("search_knowledge", len(trace) + 1, {"queryTerms": terms, "knowledgeIds": selected_ids, "candidateLimit": candidate_limit, "preferFixedInformation": cls._is_fixed_information_query(retrieval_question)}, {"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,
|
||||
@@ -391,7 +391,7 @@ class KnowledgeAgentService:
|
||||
|
||||
@staticmethod
|
||||
def _candidate_trace(item: Candidate) -> dict:
|
||||
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}
|
||||
return {"knowledgeId": item.knowledge.id, "knowledgeName": item.knowledge.name, "knowledgeType": item.knowledge.knowledge_type, "knowledgeTypePriority": _knowledge_type_priority_label(item.knowledge.knowledge_type), "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(
|
||||
@@ -732,6 +732,15 @@ def _json_trace_value(value):
|
||||
return str(value)
|
||||
|
||||
|
||||
def _knowledge_type_priority_label(value: str) -> str:
|
||||
return {
|
||||
"fixed": "最高优先级:固定信息类冲突时优先",
|
||||
"course": "课程内容优先于答疑和通用知识",
|
||||
"qa": "过往答疑参考,不覆盖课程原文",
|
||||
"general": "背景补充,不覆盖业务知识",
|
||||
}.get(value, "背景补充")
|
||||
|
||||
|
||||
def _heading_level(content: str) -> int | None:
|
||||
match = re.match(r"^(#{1,6})\s+", content.lstrip())
|
||||
return len(match.group(1)) if match else None
|
||||
|
||||
@@ -539,6 +539,10 @@ Agent 已经可以:
|
||||
- 检索日志能看到知识库类型和优先级;
|
||||
- 知识冲突时能说明采用固定信息类。
|
||||
|
||||
#### 开发进度
|
||||
|
||||
- 2026-07-31:已有固定信息类优先召回、Prompt 类型规则注入和冲突处理规则;本次补充检索日志候选切片的知识库类型/优先级展示,并在知识库详情页展示类型使用规则说明。
|
||||
|
||||
---
|
||||
|
||||
### 08. 柔性额度提醒
|
||||
|
||||
Reference in New Issue
Block a user