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:
@@ -203,6 +203,22 @@ export async function initDatabase(): Promise<SQL.Database> {
|
||||
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_traces_session ON traces(session_id, created_at);
|
||||
|
||||
-- v4.2 技能表(自动从成功工具调用链提取)
|
||||
CREATE TABLE IF NOT EXISTS skills (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
trigger_keywords TEXT,
|
||||
tool_chain TEXT NOT NULL,
|
||||
success_count INTEGER DEFAULT 0,
|
||||
fail_count INTEGER DEFAULT 0,
|
||||
avg_duration_ms INTEGER DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
last_used_at INTEGER
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_skills_updated ON skills(updated_at DESC);
|
||||
`);
|
||||
|
||||
// 尝试创建 FTS5 全文搜索(可选,sql.js 默认 WASM 可能不包含 FTS5)
|
||||
@@ -286,6 +302,21 @@ export interface ToolCallRow {
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
// v4.2
|
||||
export interface SkillRow {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
trigger_keywords: string | null;
|
||||
tool_chain: string;
|
||||
success_count: number;
|
||||
fail_count: number;
|
||||
avg_duration_ms: number;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
last_used_at: number | null;
|
||||
}
|
||||
|
||||
export interface TraceRow {
|
||||
id: string;
|
||||
session_id: string;
|
||||
@@ -487,6 +518,65 @@ export function getTracesBySession(sessionId: string): TraceRow[] {
|
||||
return queryAll(getDb(), 'SELECT * FROM traces WHERE session_id = ? ORDER BY step_index ASC', [sessionId]) as unknown as TraceRow[];
|
||||
}
|
||||
|
||||
// ─── Skills CRUD (v4.2) ───
|
||||
|
||||
export function saveSkill(skill: SkillRow): string {
|
||||
runExec(getDb(), `INSERT OR REPLACE INTO skills (id, name, description, trigger_keywords, tool_chain, success_count, fail_count, avg_duration_ms, created_at, updated_at, last_used_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[skill.id, skill.name, skill.description, skill.trigger_keywords, skill.tool_chain,
|
||||
skill.success_count, skill.fail_count, skill.avg_duration_ms,
|
||||
skill.created_at, skill.updated_at, skill.last_used_at]
|
||||
);
|
||||
persist();
|
||||
return skill.id;
|
||||
}
|
||||
|
||||
export function getSkill(id: string): SkillRow | null {
|
||||
return queryOne(getDb(), 'SELECT * FROM skills WHERE id = ?', [id]) as unknown as SkillRow | null;
|
||||
}
|
||||
|
||||
export function getAllSkills(): SkillRow[] {
|
||||
return queryAll(getDb(), 'SELECT * FROM skills ORDER BY success_count DESC, updated_at DESC') as unknown as SkillRow[];
|
||||
}
|
||||
|
||||
export function deleteSkill(id: string): void {
|
||||
runExec(getDb(), 'DELETE FROM skills WHERE id = ?', [id]);
|
||||
persist();
|
||||
}
|
||||
|
||||
export function clearAllSkills(): void {
|
||||
getDb().run('DELETE FROM skills');
|
||||
persist();
|
||||
}
|
||||
|
||||
export function searchSkills(keywords: string, limit = 5): SkillRow[] {
|
||||
const likeQ = `%${keywords.replace(/[^\w\u4e00-\u9fff]/g, '%')}%`;
|
||||
return queryAll(getDb(), `
|
||||
SELECT * FROM skills
|
||||
WHERE trigger_keywords LIKE ? OR name LIKE ? OR description LIKE ?
|
||||
ORDER BY success_count DESC, updated_at DESC
|
||||
LIMIT ?
|
||||
`, [likeQ, likeQ, likeQ, limit]) as unknown as SkillRow[];
|
||||
}
|
||||
|
||||
export function incrementSkillUsage(id: string, success: boolean, durationMs: number): void {
|
||||
const d = getDb();
|
||||
const skill = queryOne(d, 'SELECT success_count, fail_count, avg_duration_ms FROM skills WHERE id = ?', [id]) as any;
|
||||
if (!skill) return;
|
||||
|
||||
const newSuccess = success ? skill.success_count + 1 : skill.success_count;
|
||||
const newFail = success ? skill.fail_count : skill.fail_count + 1;
|
||||
const totalUses = newSuccess + newFail;
|
||||
// 指数移动平均
|
||||
const alpha = 0.3;
|
||||
const newAvg = Math.round(alpha * durationMs + (1 - alpha) * (skill.avg_duration_ms || durationMs));
|
||||
|
||||
runExec(d, `UPDATE skills SET success_count = ?, fail_count = ?, avg_duration_ms = ?, updated_at = ?, last_used_at = ? WHERE id = ?`,
|
||||
[newSuccess, newFail, newAvg, Date.now(), Date.now(), id]
|
||||
);
|
||||
persist();
|
||||
}
|
||||
|
||||
// ─── Export/Import ───
|
||||
|
||||
export interface ExportData {
|
||||
|
||||
Reference in New Issue
Block a user