fix(knowledge): bind summary cards to cited evidence
This commit is contained in:
@@ -161,7 +161,7 @@
|
||||
<button v-if="msg.result.snippets?.length" class="bubble-action" @click="msg.citationsOpen = !msg.citationsOpen">
|
||||
引用 {{ msg.result.snippets.length }} 条 {{ msg.citationsOpen ? '▲' : '▼' }}
|
||||
</button>
|
||||
<button v-if="msg.result.agentIntent === 'KNOWLEDGE_QA' && !broadcastContext" class="bubble-action" :disabled="summaryLoading" @click="buildSummaryCard(msg)">
|
||||
<button v-if="msg.result.agentIntent === 'KNOWLEDGE_QA' && summaryFragmentIds(msg).length && !broadcastContext" class="bubble-action" :disabled="summaryLoading" @click="buildSummaryCard(msg)">
|
||||
{{ summaryLoading ? '生成中' : '总结卡' }}
|
||||
</button>
|
||||
<button v-if="msg.result.noEvidence && msg.result.agentIntent === 'KNOWLEDGE_QA' && !broadcastContext" class="bubble-action primary" @click="openWebAi">去查全网</button>
|
||||
@@ -1332,11 +1332,15 @@ const buildSummaryCard = async (msg: MasterMsg) => {
|
||||
}
|
||||
if (summaryLoading.value) return;
|
||||
const value = (msg.result.queryText || '').trim();
|
||||
if (!value) return;
|
||||
const fragmentIds = summaryFragmentIds(msg);
|
||||
if (!value || !fragmentIds.length) {
|
||||
pushSystem('当前回答没有可用于总结卡的引用片段,请重新提问');
|
||||
return;
|
||||
}
|
||||
summaryLoading.value = true;
|
||||
summaryCard.value = null;
|
||||
try {
|
||||
summaryCard.value = await generateSummaryCard(value);
|
||||
summaryCard.value = await generateSummaryCard({ queryText: value, fragmentIds });
|
||||
} catch (error) {
|
||||
pushSystem(requestErrorText(error, '总结卡生成失败'));
|
||||
} finally {
|
||||
@@ -1344,6 +1348,13 @@ const buildSummaryCard = async (msg: MasterMsg) => {
|
||||
}
|
||||
};
|
||||
|
||||
const summaryFragmentIds = (msg: MasterMsg) => Array.from(new Set(
|
||||
(msg.result.snippets || [])
|
||||
.filter((item) => !item.sourceType || item.sourceType === 'DOCUMENT')
|
||||
.map((item) => item.fragmentId)
|
||||
.filter((id): id is number => typeof id === 'number' && Number.isSafeInteger(id) && id > 0)
|
||||
)).slice(0, 5);
|
||||
|
||||
const closeSummary = () => {
|
||||
if (speechState.value.key === 'summary') speechPlayback.stop();
|
||||
summaryCard.value = null;
|
||||
|
||||
@@ -250,13 +250,14 @@ export const submitAnswerFeedback = (params: {
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const generateSummaryCard = (queryText: string) =>
|
||||
export const generateSummaryCard = (params: { queryText: string; fragmentIds: number[] }) =>
|
||||
apiRequest<SummaryCardResponse>({
|
||||
url: '/api/knowledge/summary-card',
|
||||
method: 'POST',
|
||||
data: {
|
||||
queryText,
|
||||
category: 'sop'
|
||||
queryText: params.queryText,
|
||||
category: 'sop',
|
||||
fragmentIds: params.fragmentIds
|
||||
},
|
||||
timeout: 45000
|
||||
});
|
||||
|
||||
@@ -286,13 +286,20 @@ test('超大字模式下今日安排将文字和操作按钮纵向排布', async
|
||||
});
|
||||
|
||||
test('问师傅页保留搜索框、答案区和总结卡入口', async () => {
|
||||
const source = await pageSource('../src/pages/user/sop/index.vue');
|
||||
const [source, service] = await Promise.all([
|
||||
pageSource('../src/pages/user/sop/index.vue'),
|
||||
pageSource('../src/services/knowledge.ts')
|
||||
]);
|
||||
|
||||
assert.match(source, /query-panel/);
|
||||
assert.match(source, /answer-panel/);
|
||||
assert.match(source, /summary-panel/);
|
||||
assert.match(source, /MY_PRACTICE_SUMMARY/);
|
||||
assert.match(source, /TEAM_PRACTICE_SUMMARY/);
|
||||
assert.match(source, /const summaryFragmentIds = \(msg: MasterMsg\)/);
|
||||
assert.match(source, /generateSummaryCard\(\{ queryText: value, fragmentIds \}\)/);
|
||||
assert.match(service, /fragmentIds: number\[\]/);
|
||||
assert.match(service, /fragmentIds: params\.fragmentIds/);
|
||||
});
|
||||
|
||||
test('SOP 没解决先收集结构化原因再提交', async () => {
|
||||
|
||||
Reference in New Issue
Block a user