feat(suggest): 改造 createCompleteHandler 并行化 + generateLLMSuggestions 增强
- createCompleteHandler: 记忆提取+上下文拉取 Promise.all 并行 - generateLLMSuggestions: 新增 SuggestionContext 参数,构建增强 user message - llmSuggestViaSaaS: 删除 2s 人为延迟(并行化后不再需要) - 变量重命名 context→conversationContext 避免与 SuggestionContext 冲突
This commit is contained in:
@@ -36,6 +36,7 @@ import { useMessageStore } from './messageStore';
|
||||
import { useArtifactStore } from './artifactStore';
|
||||
import { llmSuggest } from '../../lib/llm-service';
|
||||
import { detectNameSuggestion, detectAgentNameSuggestion } from '../../lib/cold-start-mapper';
|
||||
import { fetchSuggestionContext, type SuggestionContext } from '../../lib/suggestion-context';
|
||||
|
||||
const log = createLogger('StreamStore');
|
||||
|
||||
@@ -399,35 +400,43 @@ function createCompleteHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// Async memory extraction (independent — failures don't block name detection)
|
||||
// Parallel: memory extraction + intelligence context fetch
|
||||
const filtered = msgs
|
||||
.filter(m => m.role === 'user' || m.role === 'assistant')
|
||||
.map(m => ({ role: m.role, content: m.content }));
|
||||
const convId = useConversationStore.getState().currentConversationId;
|
||||
getMemoryExtractor().extractFromConversation(filtered, agentId, convId ?? undefined)
|
||||
.catch(err => log.warn('Memory extraction failed:', err));
|
||||
const lastUserContent = typeof lastContent === 'string' ? lastContent : '';
|
||||
|
||||
intelligenceClient.reflection.recordConversation().catch(err => {
|
||||
log.warn('Recording conversation failed:', err);
|
||||
});
|
||||
intelligenceClient.reflection.shouldReflect().then(shouldReflect => {
|
||||
if (shouldReflect) {
|
||||
intelligenceClient.reflection.reflect(agentId, []).catch(err => {
|
||||
log.warn('Reflection failed:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
const suggestionContextPromise = fetchSuggestionContext(agentId, lastUserContent);
|
||||
|
||||
// Follow-up suggestions (async LLM call with keyword fallback)
|
||||
const latestMsgs = chat.getMessages() || [];
|
||||
const conversationMessages = latestMsgs
|
||||
.filter(m => m.role === 'user' || m.role === 'assistant')
|
||||
.filter(m => !m.streaming)
|
||||
.map(m => ({ role: m.role, content: m.content }));
|
||||
// Fire-and-forget background tasks
|
||||
Promise.all([
|
||||
getMemoryExtractor().extractFromConversation(filtered, agentId, convId ?? undefined)
|
||||
.catch(err => log.warn('Memory extraction failed:', err)),
|
||||
intelligenceClient.reflection.recordConversation()
|
||||
.catch(err => log.warn('Recording conversation failed:', err)),
|
||||
suggestionContextPromise,
|
||||
]).then(([, , context]) => {
|
||||
// Conditional reflection (after context is ready)
|
||||
intelligenceClient.reflection.shouldReflect().then(shouldReflect => {
|
||||
if (shouldReflect) {
|
||||
intelligenceClient.reflection.reflect(agentId, []).catch(err => {
|
||||
log.warn('Reflection failed:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
generateLLMSuggestions(conversationMessages, set).catch(err => {
|
||||
log.warn('Suggestion generation error:', err);
|
||||
set({ suggestionsLoading: false });
|
||||
// Follow-up suggestions with enriched context
|
||||
const latestMsgs = chat.getMessages() || [];
|
||||
const conversationMessages = latestMsgs
|
||||
.filter(m => m.role === 'user' || m.role === 'assistant')
|
||||
.filter(m => !m.streaming)
|
||||
.map(m => ({ role: m.role, content: m.content }));
|
||||
|
||||
generateLLMSuggestions(conversationMessages, set, context).catch(err => {
|
||||
log.warn('Suggestion generation error:', err);
|
||||
set({ suggestionsLoading: false });
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -559,15 +568,32 @@ function parseSuggestionResponse(raw: string): string[] {
|
||||
async function generateLLMSuggestions(
|
||||
messages: Array<{ role: string; content: string }>,
|
||||
set: (partial: Partial<StreamState>) => void,
|
||||
context?: SuggestionContext,
|
||||
): Promise<void> {
|
||||
set({ suggestionsLoading: true });
|
||||
|
||||
try {
|
||||
const recentMessages = messages.slice(-6);
|
||||
const context = recentMessages
|
||||
const conversationContext = recentMessages
|
||||
.map(m => `${m.role === 'user' ? '用户' : '助手'}: ${m.content}`)
|
||||
.join('\n\n');
|
||||
|
||||
// Build dynamic user message with intelligence context
|
||||
const ctx = context ?? { userProfile: '', painPoints: '', experiences: '', skillMatch: '' };
|
||||
const hasContext = ctx.userProfile || ctx.painPoints || ctx.experiences || ctx.skillMatch;
|
||||
let userMessage: string;
|
||||
if (hasContext) {
|
||||
const sections: string[] = ['以下是用户的背景信息,请在生成建议时参考:\n'];
|
||||
if (ctx.userProfile) sections.push(`## 用户画像\n${ctx.userProfile}`);
|
||||
if (ctx.painPoints) sections.push(`## 活跃痛点\n${ctx.painPoints}`);
|
||||
if (ctx.experiences) sections.push(`## 相关经验\n${ctx.experiences}`);
|
||||
if (ctx.skillMatch) sections.push(`## 可用技能\n${ctx.skillMatch}`);
|
||||
sections.push(`\n最近对话:\n${conversationContext}`);
|
||||
userMessage = sections.join('\n\n');
|
||||
} else {
|
||||
userMessage = `以下是对话中最近的消息:\n\n${conversationContext}\n\n请生成 3 个后续问题。`;
|
||||
}
|
||||
|
||||
const connectionMode = typeof localStorage !== 'undefined'
|
||||
? localStorage.getItem('zclaw-connection-mode')
|
||||
: null;
|
||||
@@ -575,9 +601,9 @@ async function generateLLMSuggestions(
|
||||
let raw: string;
|
||||
|
||||
if (connectionMode === 'saas') {
|
||||
raw = await llmSuggestViaSaaS(context);
|
||||
raw = await llmSuggestViaSaaS(userMessage);
|
||||
} else {
|
||||
raw = await llmSuggest(context);
|
||||
raw = await llmSuggest(userMessage);
|
||||
}
|
||||
|
||||
const suggestions = parseSuggestionResponse(raw);
|
||||
@@ -601,7 +627,7 @@ async function generateLLMSuggestions(
|
||||
* with non-streaming requests. Collects the full response from SSE deltas,
|
||||
* then parses the suggestion JSON from the accumulated text.
|
||||
*/
|
||||
async function llmSuggestViaSaaS(context: string): Promise<string> {
|
||||
async function llmSuggestViaSaaS(userMessage: string): Promise<string> {
|
||||
const { saasClient } = await import('../../lib/saas-client');
|
||||
const { useConversationStore } = await import('./conversationStore');
|
||||
const { useSaaSStore } = await import('../saasStore');
|
||||
@@ -611,9 +637,6 @@ async function llmSuggestViaSaaS(context: string): Promise<string> {
|
||||
const model = currentModel || (availableModels.length > 0 ? availableModels[0]?.id : undefined);
|
||||
if (!model) throw new Error('No model available for suggestions');
|
||||
|
||||
// Delay to avoid concurrent relay requests with memory extraction
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 60000);
|
||||
|
||||
@@ -623,7 +646,7 @@ async function llmSuggestViaSaaS(context: string): Promise<string> {
|
||||
model,
|
||||
messages: [
|
||||
{ role: 'system', content: LLM_PROMPTS_SYSTEM },
|
||||
{ role: 'user', content: `以下是对话中最近的消息:\n\n${context}\n\n请生成 3 个后续问题。` },
|
||||
{ role: 'user', content: userMessage },
|
||||
],
|
||||
max_tokens: 500,
|
||||
temperature: 0.7,
|
||||
|
||||
Reference in New Issue
Block a user