feat: v0.4.0 四阶段迭代 — 安全加固 + 工程基线 + 架构重构 + 双 Provider 扩展
P0 安全修复: - API Key 加密存储(safeStorage 密钥链,版本化前缀,历史明文平滑兼容) - 间接提示注入防护(SecurityScanHook 工具结果深扫描,网络工具脱敏/本地工具警示分级) - error:report IPC 断链修复(渲染进程错误上报落 electron-log + 审计) - abort 信号贯通工具层(run_command/dev-tools 子进程随会话中断终止) - run_command 沙箱加固(cd 系统目录/敏感文件读取拦截 + chcp 前缀剥离防解析退化) - .env 真实生效(dotenv 回退加载,应用内配置优先) P1 工程基础: - ESLint 9 flat config + 全部 34 条存量 warnings 清零(零容忍基线) - 测试基线 118 用例 11 文件(token/文件防护/权限/沙箱/注入/命令/引擎/注册表/审计链/摘要分层) - test:electron 双模式(ELECTRON_RUN_AS_NODE 跑 Electron ABI,SQLite 套件全执行) - SessionRecorder 多会话隔离 + 9 种 TRACE 事件补全(含最终轮 iteration_end) - Provider 故障转移(重试耗尽/不可重试一次性切换 fallback + 前端通知) - MCP 真就绪(等待全部连接完成再广播 tools:ready) - SLO/HealthChecker 真实接入(60s 巡检 + 托盘状态) - CONFIG_DEFAULTS 单一来源(消除 SEED 双源漂移) P2 架构升级: - handlers.ts 1940 行拆分为 13 个 IPC 域模块(防重入注册 + 多窗口广播) - AgentEngineManager 每会话独立引擎(LRU 30 + adapter 工厂隔离 abort 信号) - TaskOrchestrator EngineProvider 改造 + abortByParent 联动中断 SubAgent - 会话摘要分层上下文(session_summaries 滚动摘要 + 截断游标清理防因果污染) - 消息编辑重发/重新生成(truncateAfter IPC + store 动作 + UI) - Markdown 导出 / WebSearch 并行抓取(并发 3)/ 记忆 TF 缓存 / 版本构建期注入 P3 能力扩展: - OpenAI Adapter(o 系列推理模型 reasoning_effort/max_completion_tokens) - Anthropic Adapter(原生 Messages API:tool_use 块/角色合并/thinking budget/图片 base64/SSE 事件机) - 设置页/Onboarding 六 Provider 全链路接入
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Context Builder — 上下文构建器
|
||||
*
|
||||
* 负责组装 MetonaContext:System Prompt + 会话历史 + 检索记忆 + 工具列表。
|
||||
* 负责 System Prompt 组装:SOUL.md(角色)+ MEMORY.md(记忆)+ 内置安全准则。
|
||||
* 采用静态区 + 动态区分区策略,利用 LLM 缓存减少 Token 消耗。
|
||||
*
|
||||
* System Prompt 构建规则(按优先级):
|
||||
@@ -10,26 +10,15 @@
|
||||
* 3. 内置安全准则 → 尾部锚定
|
||||
*
|
||||
* v0.3.14: 移除 AGENTS.md 和 USERS.md 的读取(不再注入到 System Prompt)
|
||||
* P1-12: 移除从未被调用的 build()/MetonaContext 组装路径(死代码),
|
||||
* 实际上下文组装由 IPC 层(buildSystemPrompt)+ Engine(messages)完成
|
||||
*
|
||||
* @see docs/MetonaAI-Desktop 架构与交互设计.html — 磁盘文件
|
||||
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章
|
||||
*/
|
||||
|
||||
import log from 'electron-log';
|
||||
import type { MetonaContext, MetonaMemoryItem, MetonaMessage, MetonaSystemPrompt, MetonaToolDef } from '../types';
|
||||
import type { WorkspaceFiles } from '../../services/workspace.service';
|
||||
import { estimateStringTokens } from '../utils/token-estimator';
|
||||
|
||||
interface ContextBuildParams {
|
||||
userInput: string;
|
||||
sessionId: string;
|
||||
availableTools: MetonaToolDef[];
|
||||
history?: MetonaMessage[];
|
||||
memories?: MetonaMemoryItem[];
|
||||
workspaceFiles?: WorkspaceFiles;
|
||||
workspacePath?: string;
|
||||
contextWindow?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文构建器
|
||||
@@ -37,7 +26,7 @@ interface ContextBuildParams {
|
||||
export class ContextBuilder {
|
||||
/**
|
||||
* v0.3.18 修复: 标记上次 build 是否使用了兜底身份(SOUL.md 为空或不存在)
|
||||
* 供 handlers.ts 读取后决定是否向前端发送 toast 提示
|
||||
* 供 handlers 读取后决定是否向前端发送 toast 提示
|
||||
*/
|
||||
private lastUsedFallbackRole = false;
|
||||
/**
|
||||
@@ -59,41 +48,6 @@ export class ContextBuilder {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的 MetonaContext
|
||||
*/
|
||||
async build(params: ContextBuildParams): Promise<MetonaContext> {
|
||||
const {
|
||||
userInput,
|
||||
sessionId,
|
||||
availableTools,
|
||||
history = [],
|
||||
memories = [],
|
||||
workspaceFiles,
|
||||
workspacePath,
|
||||
contextWindow = 128_000,
|
||||
} = params;
|
||||
|
||||
const systemPrompt = this.buildSystemPrompt(workspaceFiles, workspacePath);
|
||||
const estimatedTokens = this.estimateTokens(history, memories, availableTools, userInput, systemPrompt);
|
||||
|
||||
return {
|
||||
id: `ctx_${Date.now()}`,
|
||||
sessionId,
|
||||
systemPrompt,
|
||||
history,
|
||||
relevantMemories: memories,
|
||||
currentTask: {
|
||||
userInput,
|
||||
iteration: 0,
|
||||
},
|
||||
availableTools,
|
||||
estimatedTokens,
|
||||
usageRatio: estimatedTokens / contextWindow,
|
||||
needsCompression: estimatedTokens > contextWindow * 0.8,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 System Prompt
|
||||
*
|
||||
@@ -104,7 +58,12 @@ export class ContextBuilder {
|
||||
*
|
||||
* v0.3.14: 移除 AGENTS.md 和 USERS.md 的读取,SOUL.md 仅做角色定义
|
||||
*/
|
||||
buildSystemPrompt(workspaceFiles?: WorkspaceFiles, workspacePath?: string): MetonaSystemPrompt {
|
||||
buildSystemPrompt(workspaceFiles?: WorkspaceFiles, workspacePath?: string): {
|
||||
roleDefinition: string;
|
||||
outputConstraints: string;
|
||||
safetyGuidelines: string;
|
||||
dynamicReminders?: string;
|
||||
} {
|
||||
// ===== 静态区:角色定义(SOUL.md)=====
|
||||
const roleDefinition = this.buildRoleDefinition(workspaceFiles?.soul);
|
||||
|
||||
@@ -177,7 +136,7 @@ export class ContextBuilder {
|
||||
this.fallbackRoleNotified = false;
|
||||
parts.push(soulContent);
|
||||
} else {
|
||||
// v0.3.18 修复: 降级时打 WARN 日志 + 设置标志,供 handlers.ts 读取后发 toast
|
||||
// v0.3.18 修复: 降级时打 WARN 日志 + 设置标志,供 IPC 层读取后发 toast
|
||||
this.lastUsedFallbackRole = true;
|
||||
log.warn('[ContextBuilder] SOUL.md is missing or empty, falling back to default Metona identity');
|
||||
// 兜底身份定义(Metona 灵魂定义)
|
||||
@@ -285,55 +244,4 @@ For multi-step complex tasks (3+ steps), proactively use \`task_manager\` to bre
|
||||
|
||||
return contentLines.join('\n').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 估算
|
||||
* 使用智能字符估算:中文 1.0 token/字,ASCII 0.25 token/字,其他 1 token/字
|
||||
* v0.3.18: CJK 系数从 1.5 调整为 1.0,更贴近 BPE 实际值
|
||||
* @see electron/harness/utils/token-estimator.ts
|
||||
*/
|
||||
private estimateTokens(
|
||||
history: MetonaMessage[],
|
||||
memories: MetonaMemoryItem[],
|
||||
tools: MetonaToolDef[],
|
||||
userInput: string,
|
||||
systemPrompt: MetonaSystemPrompt,
|
||||
): number {
|
||||
let total = 0;
|
||||
|
||||
// System Prompt
|
||||
total += estimateStringTokens(systemPrompt.roleDefinition ?? '');
|
||||
total += estimateStringTokens(systemPrompt.outputConstraints ?? '');
|
||||
total += estimateStringTokens(systemPrompt.safetyGuidelines ?? '');
|
||||
total += estimateStringTokens(systemPrompt.dynamicReminders ?? '');
|
||||
|
||||
// 历史消息(每条加 4 token 结构性开销)
|
||||
for (const msg of history) {
|
||||
total += estimateStringTokens(msg.content) + 4;
|
||||
}
|
||||
|
||||
// 记忆
|
||||
for (const mem of memories) total += estimateStringTokens(mem.content);
|
||||
|
||||
// 工具定义
|
||||
// #31 修复: 之前仅累加 tool.name + tool.description,忽略 tool.parameters(JSON Schema),
|
||||
// 而 parameters schema 通常占工具 token 的 70%+,导致估算严重偏低、压缩阈值判断错误
|
||||
for (const tool of tools) {
|
||||
total += estimateStringTokens(tool.name);
|
||||
total += estimateStringTokens(tool.description);
|
||||
// 审查修复: 用 try-catch 包裹 JSON.stringify,防止循环引用等异常导致估算中断
|
||||
if (tool.parameters) {
|
||||
try {
|
||||
total += estimateStringTokens(JSON.stringify(tool.parameters));
|
||||
} catch {
|
||||
total += estimateStringTokens(String(tool.parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用户输入
|
||||
total += estimateStringTokens(userInput);
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user