feat: v4.2 智能进化 — 技能自动生成 + 用户画像 + 人格模板

新增功能:

1. 技能自动生成系统 (Skill Loop)
   - 新增 skills 表:id/name/description/trigger_keywords/tool_chain/success_count/fail_count
   - skill-manager.ts: 从成功工具调用链自动提取可复用技能
   - 关键词匹配 + 成功率加权的技能检索
   - 匹配到的技能自动注入 system prompt(含工具链步骤和成功率)
   - Agent Loop 结束后自动提取,开始前自动匹配注入
   - DB/IPC/Preload 全链路支持

2. 用户画像模型
   - 自动从对话中推断技术栈(14 种技术关键词检测)
   - 画像存储在 settings.user_profile
   - 自动注入 system prompt(技术栈/角色/风格)
   - 随使用不断积累完善

3. 人格模板切换(6 种预设)
   - 🤖 通用助手(默认,温度跟随设置)
   - 💻 编程助手(温度 0.3,注重代码质量)
   - ✍️ 写作助手(温度 0.8,注重文采流畅)
   - 📊 数据分析(温度 0.2,注重精确逻辑)
   - 🌐 翻译助手(温度 0.4,注重信达雅)
   - 🔍 代码审查(温度 0.3,注重安全性能)
   - 设置面板 3×2 网格按钮,点击切换 + 自动调整温度

修改文件:9 个已有 + 1 个新建(skill-manager.ts)
This commit is contained in:
thzxx
2026-04-18 09:22:25 +08:00
parent d8f89ad097
commit f1625b4353
10 changed files with 623 additions and 4 deletions
+99 -4
View File
@@ -12,6 +12,8 @@ import {
needsConfirmation
} from './tool-registry.js';
import { searchMemories, buildMemoryContext, markMemoryUsed, isMemoryEnabled } from './memory-manager.js';
import { extractSkillsFromToolRecords, matchSkills, buildSkillContext } from './skill-manager.js';
import { PERSONALITY_SYSTEM_PROMPTS } from '../components/settings-modal.js';
import { showToast } from '../components/toast.js';
import { logInfo, logWarn, logError, logToolStart, logToolResult, logThink, logAgentLoop, logModelResponse } from './log-service.js';
import { getWorkspaceDirPath } from '../components/workspace-panel.js';
@@ -136,7 +138,53 @@ function parseToolCallsFromText(content: string): ToolCall[] {
return calls;
}
/** 工具调用缓存:key = toolName + JSON(args),用于避免重复调用 */
/** v4.2: 从对话中自动推断用户画像 */
async function inferUserProfile(userMsg: string, assistantMsg: string): Promise<void> {
const bridge = (window as any).metonaDesktop;
if (!bridge?.db?.getSetting) return;
const profile: Record<string, unknown> = await bridge.db.getSetting('user_profile', {}) || {};
// 技术栈检测
const techPatterns: Record<string, RegExp> = {
'Python': /\b(python|pip|py|django|flask|fastapi|pandas|numpy)\b/i,
'TypeScript': /\b(typescript|ts|tsx|deno)\b/i,
'JavaScript': /\b(javascript|js|jsx|node\.?js|npm|yarn|pnpm)\b/i,
'Go': /\b(golang|go\s+(run|build|mod)|\.go\b)/i,
'Rust': /\b(rust|cargo|rustc|\.rs\b)/i,
'Java': /\b(java|maven|gradle|spring|\.java\b)/i,
'React': /\b(react|next\.?js|jsx|tsx|vite)\b/i,
'Vue': /\b(vue|nuxt|vuex|pinia)\b/i,
'Docker': /\b(docker|dockerfile|docker-compose|container)\b/i,
'Kubernetes': /\b(k8s|kubernetes|kubectl|helm)\b/i,
'Linux': /\b(linux|ubuntu|debian|centos|bash|shell|terminal)\b/i,
'Git': /\b(git|github|gitee|gitlab|commit|push|pull|branch|merge)\b/i,
'SQL': /\b(sql|sqlite|mysql|postgres|database|查询)\b/i,
'AI/ML': /\b(machine learning|深度学习|神经网络|模型训练|ollama|llm|transformer)\b/i,
};
const combinedText = userMsg + ' ' + assistantMsg;
const currentStack: string[] = (profile.tech_stack as string[]) || [];
let changed = false;
for (const [tech, pattern] of Object.entries(techPatterns)) {
if (pattern.test(combinedText) && !currentStack.includes(tech)) {
currentStack.push(tech);
changed = true;
}
}
// 限制技术栈数量
if (currentStack.length > 15) {
currentStack.splice(0, currentStack.length - 15);
changed = true;
}
if (changed) {
profile.tech_stack = currentStack;
await bridge.db.saveSetting('user_profile', profile);
}
}
const toolResultCache = new Map<string, ToolResult>();
/** 生成工具调用缓存 key */
@@ -342,10 +390,45 @@ export async function runAgentLoop(
文件操作工具(read_file、write_file 等)的相对路径基于此目录解析。`);
}
// v4.2 注入匹配的技能上下文
if (useTools && userContent) {
const matchedSkills = await matchSkills(userContent, 3);
if (matchedSkills.length > 0) {
const skillContext = buildSkillContext(matchedSkills);
if (skillContext) {
systemPromptParts.push(skillContext);
logInfo(`技能匹配: ${matchedSkills.length} 个技能已注入`, matchedSkills.map(s => s.name).join(', '));
}
}
}
// v4.2 注入用户画像
const db = state.get<any>(KEYS.DB);
if (db) {
try {
const userProfile = await db.getSetting('user_profile', null);
if (userProfile && typeof userProfile === 'object') {
const parts: string[] = [];
if (userProfile.tech_stack?.length) parts.push(`技术栈: ${userProfile.tech_stack.join(', ')}`);
if (userProfile.role) parts.push(`角色: ${userProfile.role}`);
if (userProfile.style) parts.push(`代码风格: ${userProfile.style}`);
if (parts.length > 0) {
systemPromptParts.push(`【用户画像】\n${parts.join('\n')}`);
}
}
} catch { /* 不阻塞 */ }
}
// 组合 system prompt
const fullSystemPrompt = systemPromptParts.length > 0
? systemPromptParts.join('\n\n') + '\n\n' + AGENT_SYSTEM_PROMPT + '\n\n' + TOOL_USAGE_GUIDE
: AGENT_SYSTEM_PROMPT + '\n\n' + TOOL_USAGE_GUIDE;
const personality = state.get<string>('personality', 'default');
const personalityPrompt = PERSONALITY_SYSTEM_PROMPTS[personality] || '';
const fullSystemPrompt = [
...systemPromptParts,
AGENT_SYSTEM_PROMPT,
...(personalityPrompt ? [personalityPrompt] : []),
TOOL_USAGE_GUIDE
].join('\n\n');
messages.push({ role: 'system', content: fullSystemPrompt });
@@ -538,6 +621,18 @@ export async function runAgentLoop(
} catch { /* 不阻塞 */ }
}
// v4.2 自动提取技能
if (allToolRecords.length >= 2) {
try {
await extractSkillsFromToolRecords(allToolRecords, userContent, currentSession?.title || '');
} catch { /* 不阻塞 */ }
}
// v4.2 自动推断用户画像
try {
await inferUserProfile(userContent, content);
} catch { /* 不阻塞 */ }
callbacks.onDone(content || '(模型未返回内容)', allToolRecords.length > 0 ? allToolRecords : undefined, makeStats());
return;
}