feat(agent): move mobile ask to unified agent
This commit is contained in:
@@ -121,7 +121,26 @@
|
||||
v-if="msg.result.rewrittenQuery && msg.result.rewrittenQuery !== msg.result.queryText"
|
||||
class="context-used"
|
||||
>已结合本次对话理解</text>
|
||||
<view v-if="msg.result.sourceSummary?.length" class="agent-source-row">
|
||||
<text
|
||||
v-for="source in msg.result.sourceSummary"
|
||||
:key="`${source.type}-${source.label}`"
|
||||
class="agent-source-chip"
|
||||
>{{ source.label }}</text>
|
||||
</view>
|
||||
<text class="bubble-text">{{ msg.result.answer || '未生成直接答案,请查看引用依据。' }}</text>
|
||||
<view
|
||||
v-if="msg.result.agentStatus === 'NEEDS_INPUT' && msg.result.clarification"
|
||||
class="agent-clarification"
|
||||
>
|
||||
<text class="agent-clarification-text">{{ msg.result.clarification.prompt }}</text>
|
||||
<button
|
||||
v-if="msg.result.agentIntent === 'WEB_RESEARCH'"
|
||||
class="bubble-action primary"
|
||||
:disabled="loading"
|
||||
@click="consentWebResearch(msg)"
|
||||
>同意查询全网</button>
|
||||
</view>
|
||||
<view v-if="msg.result.keyPoints?.length" class="bubble-keypoints">
|
||||
<text v-for="(point, index) in msg.result.keyPoints" :key="point" class="bubble-keypoint">
|
||||
{{ index + 1 }}. {{ cleanOrderedText(point) }}
|
||||
@@ -133,7 +152,7 @@
|
||||
<text>{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="msg.result.noEvidence" class="bubble-noevidence">当前授权空间内没有足够依据。</text>
|
||||
<text v-if="msg.result.noEvidence" class="bubble-noevidence">{{ noEvidenceText(msg.result) }}</text>
|
||||
|
||||
<view class="bubble-actions">
|
||||
<button v-if="msg.voiceStatus === 'unavailable'" class="bubble-action" @click="toggleAnswerSpeech(msg)">
|
||||
@@ -142,12 +161,12 @@
|
||||
<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.toolCode === '' && !broadcastContext" class="bubble-action" :disabled="summaryLoading" @click="buildSummaryCard(msg)">
|
||||
<button v-if="msg.result.agentIntent === 'KNOWLEDGE_QA' && !broadcastContext" class="bubble-action" :disabled="summaryLoading" @click="buildSummaryCard(msg)">
|
||||
{{ summaryLoading ? '生成中' : '总结卡' }}
|
||||
</button>
|
||||
<button v-if="msg.result.noEvidence && !broadcastContext" class="bubble-action primary" @click="openWebAi">去查全网</button>
|
||||
<button v-if="msg.result.noEvidence && msg.result.agentIntent === 'KNOWLEDGE_QA' && !broadcastContext" class="bubble-action primary" @click="openWebAi">去查全网</button>
|
||||
</view>
|
||||
<view v-if="msg.toolCode === ''" class="bubble-actions feedback-row">
|
||||
<view v-if="msg.result.snippets?.length" class="bubble-actions feedback-row">
|
||||
<button
|
||||
class="bubble-action"
|
||||
:class="{ active: msg.feedbackVerdict === 'up' }"
|
||||
@@ -422,12 +441,10 @@ import MemoryCandidateCard from '@/components/chat/MemoryCandidateCard.vue';
|
||||
import {
|
||||
chooseKnowledgeMedia,
|
||||
generateSummaryCard,
|
||||
queryKnowledgeTool,
|
||||
requestKnowledgeResourceDownloadLink,
|
||||
searchKnowledge,
|
||||
searchKnowledgeWithMedia,
|
||||
submitAnswerFeedback
|
||||
} from '@/services/knowledge';
|
||||
import { askAgent, askAgentWithMedia } from '@/services/agent';
|
||||
import {
|
||||
getSelectedPosition,
|
||||
getSelectedPositionProfile,
|
||||
@@ -909,7 +926,10 @@ const requestErrorText = (error: unknown, fallback: string) => {
|
||||
return error instanceof Error ? error.message : fallback;
|
||||
};
|
||||
|
||||
const sendQuestion = async (rawText: string, options: { voice?: boolean } = {}) => {
|
||||
const sendQuestion = async (
|
||||
rawText: string,
|
||||
options: { voice?: boolean; externalConsent?: boolean } = {}
|
||||
) => {
|
||||
if (loading.value) {
|
||||
uni.showToast({ title: '上一条还在检索中,请稍候', icon: 'none' });
|
||||
return;
|
||||
@@ -921,7 +941,7 @@ const sendQuestion = async (rawText: string, options: { voice?: boolean } = {})
|
||||
uni.showToast({ title: '消息咨询暂不支持图片或视频,请改用文字或语音', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const text = rawText.trim() || (media ? '请根据附件现场情况,告诉我应该按什么 SOP 处理' : '');
|
||||
const text = rawText.trim() || (media ? '请分析附件内容并回答我的问题' : '');
|
||||
if (!text) {
|
||||
uni.showToast({ title: '请先输入问题', icon: 'none' });
|
||||
return;
|
||||
@@ -955,18 +975,17 @@ const sendQuestion = async (rawText: string, options: { voice?: boolean } = {})
|
||||
loading.value = true;
|
||||
const generation = ++knowledgeSearchGeneration;
|
||||
try {
|
||||
const agentOptions = {
|
||||
conversation: conversationState(),
|
||||
projectCode: broadcastMessageId ? undefined : currentProject.value?.projectCode,
|
||||
broadcastMessageId,
|
||||
externalConsent: Boolean(options.externalConsent)
|
||||
};
|
||||
const nextResult = media
|
||||
? await searchKnowledgeWithMedia(media, text, getSelectedPosition(), (task) => {
|
||||
? await askAgentWithMedia(media, text, agentOptions, (task) => {
|
||||
if (generation === knowledgeSearchGeneration) activeMediaUploadTask = task;
|
||||
}, conversationState(), currentProject.value?.projectCode)
|
||||
: await searchKnowledge(
|
||||
text,
|
||||
getSelectedPosition(),
|
||||
conversationState(),
|
||||
broadcastMessageId ? undefined : currentProject.value?.projectCode,
|
||||
options.voice ? 'mobile_uni_voice' : 'mobile_uni_sop',
|
||||
broadcastMessageId
|
||||
);
|
||||
})
|
||||
: await askAgent(text, agentOptions);
|
||||
if (generation !== knowledgeSearchGeneration) return;
|
||||
applyConversationState(nextResult);
|
||||
if (broadcastMessageId && nextResult.broadcastContext?.messageId === broadcastMessageId) {
|
||||
@@ -1038,38 +1057,7 @@ const runDataTool = async (toolCode: 'MY_PRACTICE_SUMMARY' | 'TEAM_PRACTICE_SUMM
|
||||
: toolCode;
|
||||
const question = effectiveToolCode === 'TEAM_PRACTICE_SUMMARY' ? '查询我的团队训练概况' : '查询我的训练概况';
|
||||
selectedMedia.value = null;
|
||||
pushMsg<UserMsg>({ role: 'user', text: question });
|
||||
const pending = pushMsg<PendingMsg>({ role: 'pending' });
|
||||
loading.value = true;
|
||||
const generation = ++knowledgeSearchGeneration;
|
||||
try {
|
||||
const nextResult = await queryKnowledgeTool(
|
||||
effectiveToolCode, question, conversationState(), currentProject.value?.projectCode);
|
||||
if (generation !== knowledgeSearchGeneration) return;
|
||||
applyConversationState(nextResult);
|
||||
replaceMsg(pending.id, {
|
||||
id: pending.id,
|
||||
role: 'master',
|
||||
result: nextResult,
|
||||
toolCode: effectiveToolCode,
|
||||
feedbackVerdict: '',
|
||||
feedbackSubmitting: false,
|
||||
feedbackPanelOpen: false,
|
||||
feedbackReasonCodes: [],
|
||||
feedbackComment: '',
|
||||
citationsOpen: false,
|
||||
voiceStatus: 'unavailable',
|
||||
voiceDuration: null,
|
||||
unread: false,
|
||||
transcriptOpen: false
|
||||
});
|
||||
} catch (error) {
|
||||
if (generation !== knowledgeSearchGeneration) return;
|
||||
replaceMsg(pending.id, null);
|
||||
pushSystem(requestErrorText(error, '训练概况查询失败'));
|
||||
} finally {
|
||||
if (generation === knowledgeSearchGeneration) loading.value = false;
|
||||
}
|
||||
void sendQuestion(question);
|
||||
};
|
||||
|
||||
const pickMedia = async () => {
|
||||
@@ -1271,6 +1259,12 @@ const serviceMemoryStatusLabel = (status?: string | null) => ({
|
||||
|
||||
const displayCitationDate = (value?: string | null) => value ? value.replace('T', ' ').slice(0, 16) : '未记录';
|
||||
|
||||
const noEvidenceText = (result: KnowledgeSearchResponse) => {
|
||||
if (result.agentIntent === 'WEB_RESEARCH') return '公开网络中没有找到可核验来源。';
|
||||
if (result.agentIntent === 'MEDIA_UNDERSTANDING') return '已分析附件,但当前资料中没有补充依据。';
|
||||
return '当前授权空间内没有足够依据。';
|
||||
};
|
||||
|
||||
const feedbackReasonOptions = [
|
||||
{ code: 'NO_ANSWER', label: '缺少制度或SOP依据' },
|
||||
{ code: 'NOT_SPECIFIC', label: '处理步骤不够具体' },
|
||||
@@ -1419,6 +1413,11 @@ const openWebAi = () => {
|
||||
uni.navigateTo({ url: '/pages/user/web-ai/index' });
|
||||
};
|
||||
|
||||
const consentWebResearch = (msg: MasterMsg) => {
|
||||
if (loading.value || msg.result.agentIntent !== 'WEB_RESEARCH') return;
|
||||
void sendQuestion(msg.result.queryText, { externalConsent: true });
|
||||
};
|
||||
|
||||
const openTodayResult = () => {
|
||||
uni.navigateTo({ url: '/pages/user/work-results/index' });
|
||||
};
|
||||
@@ -1437,7 +1436,10 @@ const refreshMemoryCandidate = async (msg: MasterMsg, candidateId: number) => {
|
||||
try {
|
||||
const candidates = await getMemoryCandidates('DRAFT');
|
||||
if (generation !== candidateRefreshGeneration || accountKey !== currentAccountKey()) return;
|
||||
msg.result.memoryCandidate = candidates.find((item) => item.id === candidateId) || null;
|
||||
const refreshed = candidates.find((item) => item.id === candidateId);
|
||||
msg.result.memoryCandidate = refreshed
|
||||
? { ...refreshed, agentDraftId: msg.result.memoryCandidate?.agentDraftId }
|
||||
: null;
|
||||
} catch (error) {
|
||||
if (generation !== candidateRefreshGeneration || accountKey !== currentAccountKey()) return;
|
||||
uni.showToast({ title: requestErrorText(error, '确认卡刷新失败'), icon: 'none' });
|
||||
@@ -2977,6 +2979,36 @@ onUnload(() => {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.agent-source-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.agent-source-chip {
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
background: #eef3f8;
|
||||
color: #5b6b7f;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.agent-clarification {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed #eceff2;
|
||||
}
|
||||
|
||||
.agent-clarification-text {
|
||||
color: #38424e;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.bubble-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user