mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-05 21:05:39 +08:00
feat: 完善问问微信查询理解与检索链路
This commit is contained in:
@@ -236,6 +236,9 @@ export function AISearchWorkspace({
|
||||
range,
|
||||
timeRangeOverride,
|
||||
activeContactMd5: activeContact?.md5,
|
||||
knowledgeGeneration: knowledgeStatus
|
||||
? `${knowledgeStatus.state}:${knowledgeStatus.indexedMessageCount}:${knowledgeStatus.indexedChunkCount}:${knowledgeStatus.processedMessages}`
|
||||
: undefined,
|
||||
retry
|
||||
})
|
||||
if (!normalizedQuery) {
|
||||
@@ -672,8 +675,12 @@ export function AISearchWorkspace({
|
||||
<span className="ai-search-kicker">✓ 已完成</span>
|
||||
<h2>{resultQuery || query}</h2>
|
||||
<p>
|
||||
知识库已收录 {messageCount.toLocaleString()} 条消息 → 找到{' '}
|
||||
{searchTrace?.retrievedEvidence || 0} 条相关消息 → {evidence.length} 条 Evidence →
|
||||
知识库已收录 {messageCount.toLocaleString()} 条消息 →{' '}
|
||||
{cachedAt
|
||||
? `缓存中保留 ${evidenceCollection.length} 条 Evidence`
|
||||
: searchTrace?.retrievedEvidence !== undefined
|
||||
? `读取 ${searchTrace.retrievedEvidence} 条范围消息`
|
||||
: `读取 ${evidence.length} 条消息`}{' '}→ {evidence.length} 条 Evidence →
|
||||
已生成回答{cachedAt ? ' · 已使用缓存' : ''}
|
||||
</p>
|
||||
{searchTrace &&
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState, type Dispatch, type SetStateAction } from 'react'
|
||||
import { aiSearchRangeStart } from '../../../../../shared/ai-search'
|
||||
import { aiSearchRangeStart, inferAiSearchTimeRange } from '../../../../../shared/ai-search'
|
||||
import type { AiSearchTimeRange } from '../../../../../shared/ai-search'
|
||||
import {
|
||||
RANGE_LABELS,
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
buildSearchCacheKey,
|
||||
parseSearchCacheKey,
|
||||
readSearchCache,
|
||||
readSearchCacheByQuery,
|
||||
writeSearchCache
|
||||
} from '../searchUtils'
|
||||
import { createSearchCacheRecord, mapCacheRecordToResult } from '../searchMappers'
|
||||
@@ -178,13 +177,15 @@ export function useSearchHistory({
|
||||
setQuery(historyQuery)
|
||||
setSelectedEvidence(0)
|
||||
setHistoryOpen(false)
|
||||
const resolvedTimeRange = inferAiSearchTimeRange(historyQuery, range, new Date())
|
||||
const cacheKey = buildSearchCacheKey(
|
||||
scope,
|
||||
scope === 'conversation' ? conversationContactMd5 : '',
|
||||
range,
|
||||
historyQuery
|
||||
historyQuery,
|
||||
resolvedTimeRange
|
||||
)
|
||||
const cached = readSearchCache(cacheKey) || readSearchCacheByQuery(historyQuery)?.record || null
|
||||
const cached = readSearchCache(cacheKey)
|
||||
if (!cached) {
|
||||
setAnswer('')
|
||||
setEvidence([])
|
||||
|
||||
@@ -58,7 +58,10 @@ export const mapSearchResultToTrace = (
|
||||
finalEvidenceCount: number
|
||||
): SearchTrace => ({
|
||||
knowledgeMessages: result.knowledge.indexedMessageCount,
|
||||
retrievedEvidence: result.candidateEvidenceCount,
|
||||
retrievedEvidence:
|
||||
result.retrieval.intent === 'conversation_recall'
|
||||
? result.retrieval.sourceMessageCount ?? result.candidateEvidenceCount
|
||||
: result.candidateEvidenceCount,
|
||||
finalEvidence: finalEvidenceCount,
|
||||
timings: result.timings,
|
||||
contextEvidence: result.contextEvidenceCount,
|
||||
|
||||
@@ -51,6 +51,30 @@ export const resolveSearchResultViewTransition = (
|
||||
result: AiSearchPipelineResult,
|
||||
range: SearchRange
|
||||
): SearchResultViewTransition => {
|
||||
if (result.status === 'understanding_failed') {
|
||||
return {
|
||||
stage: 'insufficient',
|
||||
analysisError: result.error || '我没有完全理解你想怎么查,可以换一种说法。'
|
||||
}
|
||||
}
|
||||
if (result.status === 'contact_not_found') {
|
||||
return {
|
||||
stage: 'insufficient',
|
||||
analysisError: result.error || '我理解你在问这个联系人,但没有在当前通讯录中确认到对应联系人。'
|
||||
}
|
||||
}
|
||||
if (result.status === 'ambiguous_contact') {
|
||||
return {
|
||||
stage: 'insufficient',
|
||||
analysisError: result.error || '找到多个可能的联系人,暂时无法确定你指的是哪一个。'
|
||||
}
|
||||
}
|
||||
if (result.status === 'no_messages') {
|
||||
return {
|
||||
stage: 'insufficient',
|
||||
analysisError: result.error || '已经确认联系人,但当前可读取记录里没有对应聊天消息。'
|
||||
}
|
||||
}
|
||||
if (result.status === 'no_evidence') {
|
||||
return {
|
||||
stage: 'insufficient',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Contact, Message } from '../../../../shared/types'
|
||||
import type { AiSearchTimeRange } from '../../../../shared/ai-search'
|
||||
import { inferAiSearchTimeRange, type AiSearchTimeRange } from '../../../../shared/ai-search'
|
||||
import type {
|
||||
AISearchCacheRecord,
|
||||
EvidenceItem,
|
||||
@@ -315,8 +315,23 @@ export const buildSearchCacheKey = (
|
||||
scope: SearchScope,
|
||||
contactMd5: string,
|
||||
range: SearchRange,
|
||||
query: string
|
||||
): string => JSON.stringify([scope, contactMd5, range, query.trim().toLowerCase()])
|
||||
query: string,
|
||||
resolvedTimeRange?: Pick<AiSearchTimeRange, 'startTime' | 'endTime'>,
|
||||
knowledgeGeneration?: string
|
||||
): string => {
|
||||
const base = [scope, contactMd5, range, query.trim().toLowerCase()]
|
||||
const hasResolvedTime = Boolean(
|
||||
resolvedTimeRange &&
|
||||
(resolvedTimeRange.startTime !== undefined || resolvedTimeRange.endTime !== undefined)
|
||||
)
|
||||
if (!hasResolvedTime && !knowledgeGeneration) return JSON.stringify(base)
|
||||
return JSON.stringify([
|
||||
...base,
|
||||
hasResolvedTime ? resolvedTimeRange?.startTime ?? null : null,
|
||||
hasResolvedTime ? resolvedTimeRange?.endTime ?? null : null,
|
||||
knowledgeGeneration || null
|
||||
])
|
||||
}
|
||||
|
||||
export type CreateSearchRequestContextInput = {
|
||||
query: string
|
||||
@@ -324,6 +339,8 @@ export type CreateSearchRequestContextInput = {
|
||||
range: SearchRange
|
||||
timeRangeOverride?: AiSearchTimeRange
|
||||
activeContactMd5?: string
|
||||
now?: Date
|
||||
knowledgeGeneration?: string
|
||||
retry?: {
|
||||
range: SearchRange
|
||||
timeRangeOverride?: AiSearchTimeRange
|
||||
@@ -335,6 +352,7 @@ export type SearchRequestContext = {
|
||||
effectiveRange: SearchRange
|
||||
effectiveTimeRangeOverride?: AiSearchTimeRange
|
||||
conversationId?: string
|
||||
resolvedTimeRange: AiSearchTimeRange
|
||||
cacheKey: string
|
||||
}
|
||||
|
||||
@@ -344,25 +362,41 @@ export const createSearchRequestContext = ({
|
||||
range,
|
||||
timeRangeOverride,
|
||||
activeContactMd5,
|
||||
now,
|
||||
knowledgeGeneration,
|
||||
retry
|
||||
}: CreateSearchRequestContextInput): SearchRequestContext => {
|
||||
const normalizedQuery = query.trim()
|
||||
const effectiveRange = retry?.range || range
|
||||
const effectiveTimeRangeOverride = retry?.timeRangeOverride || timeRangeOverride
|
||||
const conversationId = scope === 'conversation' ? activeContactMd5 : undefined
|
||||
const resolvedTimeRange = inferAiSearchTimeRange(
|
||||
normalizedQuery,
|
||||
effectiveRange,
|
||||
now || new Date(),
|
||||
effectiveTimeRangeOverride
|
||||
)
|
||||
|
||||
return {
|
||||
normalizedQuery,
|
||||
effectiveRange,
|
||||
effectiveTimeRangeOverride,
|
||||
conversationId,
|
||||
cacheKey: buildSearchCacheKey(scope, conversationId || '', effectiveRange, normalizedQuery)
|
||||
resolvedTimeRange,
|
||||
cacheKey: buildSearchCacheKey(
|
||||
scope,
|
||||
conversationId || '',
|
||||
effectiveRange,
|
||||
normalizedQuery,
|
||||
resolvedTimeRange,
|
||||
knowledgeGeneration
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const parseSearchCacheKey = (
|
||||
key: string
|
||||
): { scope: SearchScope; contactMd5: string; range: SearchRange; query: string } | null => {
|
||||
): { scope: SearchScope; contactMd5: string; range: SearchRange; query: string; startTime?: number; endTime?: number } | null => {
|
||||
try {
|
||||
const parts = JSON.parse(key) as unknown
|
||||
if (
|
||||
@@ -377,7 +411,9 @@ export const parseSearchCacheKey = (
|
||||
scope: parts[0] as SearchScope,
|
||||
contactMd5: typeof parts[1] === 'string' ? parts[1] : '',
|
||||
range: parts[2] as SearchRange,
|
||||
query: parts[3]
|
||||
query: parts[3],
|
||||
startTime: typeof parts[4] === 'number' ? parts[4] : undefined,
|
||||
endTime: typeof parts[5] === 'number' ? parts[5] : undefined
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user