feat(suggest): 改造 createCompleteHandler 并行化 + generateLLMSuggestions 增强

- createCompleteHandler: 记忆提取+上下文拉取 Promise.all 并行
- generateLLMSuggestions: 新增 SuggestionContext 参数,构建增强 user message
- llmSuggestViaSaaS: 删除 2s 人为延迟(并行化后不再需要)
- 变量重命名 context→conversationContext 避免与 SuggestionContext 冲突
This commit is contained in:
iven
2026-04-23 17:57:17 +08:00
parent 980a8135fa
commit 87110ffdff

View File

@@ -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,