- 移除设置面板中的系统提示词开关和文本框 - 移除 input-area 和 agent-engine 中的系统提示词注入逻辑 - 移除 state/KEYS 中 SYSTEM_PROMPT 和 SYSTEM_PROMPT_ENABLED - 移除 types.d.ts 中对应的 StateKey 类型 - 更新 README.md 设置表和帮助文档 - 记忆系统已完全覆盖系统提示词的功能,且更智能:自动提取、跨会话持久化、按相关性动态注入
469 lines
30 KiB
HTML
469 lines
30 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<meta name="theme-color" content="#202020">
|
||
<meta name="description" content="Metona Ollama Desktop - TypeScript + Electron AI 聊天客户端">
|
||
<link rel="icon" href="/assets/icons/llama.ico" type="image/x-icon">
|
||
<title>Metona Ollama</title>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
|
||
<!-- ═══════════════ 日志面板(左侧)═══════════════ -->
|
||
<aside class="log-panel" id="logPanel">
|
||
<div class="log-panel-header">
|
||
<span class="log-panel-title">📋 执行日志</span>
|
||
</div>
|
||
<div class="log-panel-body" id="logPanelBody"></div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 主内容区 ═══════════════ -->
|
||
<div class="main-wrap with-workspace" id="mainWrap">
|
||
|
||
<!-- ═══════════════ 顶部导航 ═══════════════ -->
|
||
<header class="app-header">
|
||
<div class="header-left">
|
||
<span class="logo">🦙</span>
|
||
<span class="app-title">Metona Ollama</span>
|
||
<span class="app-version">v3.2.6</span>
|
||
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||
</svg>
|
||
</button>
|
||
<span class="total-tokens" id="totalTokens" title="当前会话总 token 消耗"><svg class="token-icon" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/><path d="M5.2 5h5.6v1.5H9.2v6.2H6.8V6.5H5.2V5z" fill="currentColor"/></svg> <span id="totalTokensValue">0</span></span>
|
||
</div>
|
||
<div class="header-right">
|
||
<div class="conn-status pending" id="connStatus" title="连接状态">
|
||
<span class="status-dot"></span>
|
||
<span class="status-label">未连接</span>
|
||
</div>
|
||
<button class="icon-btn" id="btnNewChat" title="新建会话">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnKB" title="知识库">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||
<line x1="8" y1="7" x2="16" y2="7"/>
|
||
<line x1="8" y1="11" x2="14" y2="11"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnMemory" title="Agent 记忆">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnHistory" title="历史记录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnSettings" title="设置">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ═══════════════ 模型选择栏 ═══════════════ -->
|
||
<div class="model-bar">
|
||
<div class="model-bar-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||
<path d="M9 9h6M9 12h6M9 15h4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="model-select-wrap">
|
||
<select class="model-select" id="modelSelect">
|
||
<option value="">正在加载模型...</option>
|
||
</select>
|
||
<span class="model-select-arrow">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
||
</span>
|
||
</div>
|
||
<div class="model-badges" id="modelBadges">
|
||
<span class="model-badge think-badge" id="badgeThink" style="display:none;">🧠 Think</span>
|
||
<span class="model-badge vision-badge" id="badgeVision" style="display:none;">👁️ Vision</span>
|
||
<span class="model-badge tools-badge" id="badgeTools" style="display:none;">🔧 Tools</span>
|
||
<span class="model-badge rag-badge" id="badgeRAG" style="display:none;">📚 RAG</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 聊天区域 ═══════════════ -->
|
||
<main class="chat-area" id="chatArea">
|
||
<div class="empty-state" id="emptyState">
|
||
<div class="empty-icon">
|
||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="100" cy="100" r="80" stroke="rgba(0,245,212,0.2)" stroke-width="2"/>
|
||
<circle cx="100" cy="100" r="60" stroke="rgba(123,47,247,0.2)" stroke-width="2"/>
|
||
<path d="M70 90c0-16.57 13.43-30 30-30s30 13.43 30 30" stroke="rgba(0,245,212,0.5)" stroke-width="2" stroke-linecap="round"/>
|
||
<circle cx="85" cy="100" r="5" fill="rgba(0,245,212,0.6)"/>
|
||
<circle cx="115" cy="100" r="5" fill="rgba(0,245,212,0.6)"/>
|
||
<path d="M88 120c4 4 20 4 24 0" stroke="rgba(0,245,212,0.4)" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
<h2>开始对话</h2>
|
||
<p>选择一个模型,输入消息开始聊天</p>
|
||
</div>
|
||
<div class="messages" id="messagesContainer" style="display:none;"></div>
|
||
<button class="scroll-to-bottom" id="scrollToBottom" style="display:none;" title="回到最新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="6 9 12 15 18 9"/>
|
||
</svg>
|
||
<span>回到底部</span>
|
||
</button>
|
||
</main>
|
||
|
||
<!-- ═══════════════ 历史只读提示栏 ═══════════════ -->
|
||
<div class="history-view-bar" id="historyBar" style="display:none;">
|
||
<span>📖 正在查看历史记录(只读模式)</span>
|
||
<button class="btn btn-sm btn-outline" id="btnExitHistory">返回新会话</button>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 输入区域 ═══════════════ -->
|
||
<div class="input-area" id="inputArea">
|
||
<div class="image-preview" id="imagePreview" style="display:none;"></div>
|
||
<div class="file-preview" id="filePreview" style="display:none;"></div>
|
||
<div class="input-row">
|
||
<button class="icon-btn attach-btn" id="btnAttachImg" title="上传图片">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<polyline points="21 15 16 10 5 21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn attach-btn" id="btnAttachFile" title="上传文件(文本/代码)">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||
<polyline points="10 9 9 9 8 9"/>
|
||
</svg>
|
||
</button>
|
||
<textarea class="chat-input" id="chatInput" placeholder="输入消息..." rows="1"></textarea>
|
||
<label class="think-toggle" id="thinkToggleWrap" title="Think 模式未可用">
|
||
<input type="checkbox" id="toggleThink" disabled>
|
||
<span class="think-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</span>
|
||
</label>
|
||
<button class="send-btn" id="btnSend">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- /.main-wrap -->
|
||
|
||
<!-- ═══════════════ 工作空间面板(右侧,常驻显示,flex 子元素)═══════════════ -->
|
||
<aside class="workspace-panel" id="workspacePanel">
|
||
<div class="ws-header">
|
||
<span class="ws-title">🖥️ 工作空间</span>
|
||
<div class="ws-tabs">
|
||
<button class="ws-header-tab active" id="wsTabTerminal">💻 命令行</button>
|
||
<button class="ws-header-tab" id="wsTabFiles">📁 文件</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 终端 Tab -->
|
||
<div class="ws-content" id="wsTerminalContent">
|
||
<div class="ws-term-output" id="wsTermOutput"></div>
|
||
<div class="ws-term-toolbar">
|
||
<span class="ws-term-hint" id="wsTermHint">等待 AI 执行命令...</span>
|
||
<div class="ws-term-toolbar-btns">
|
||
<button class="ws-toolbar-btn ws-stop-btn" id="wsStopBtn" title="终止当前命令" disabled>
|
||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||
<rect x="6" y="6" width="12" height="12" rx="1"/>
|
||
</svg>
|
||
</button>
|
||
<button class="ws-toolbar-btn" id="wsClearBtn" title="清空">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"/>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件 Tab -->
|
||
<div class="ws-content" id="wsFilesContent" style="display:none;">
|
||
<div class="ws-file-toolbar">
|
||
<button class="ws-toolbar-btn" id="wsFileUpBtn" title="上级目录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="15 18 9 12 15 6"/>
|
||
</svg>
|
||
</button>
|
||
<span class="ws-file-path" id="wsFilePath">~</span>
|
||
<button class="ws-toolbar-btn" id="wsFileRefreshBtn" title="刷新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="23 4 23 10 17 10"/>
|
||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-file-list" id="wsFileList"></div>
|
||
<div class="ws-preview" id="wsPreview" style="display:none;">
|
||
<div class="ws-preview-header">
|
||
<span class="ws-preview-title" id="wsPreviewTitle"></span>
|
||
<button class="icon-btn" id="wsPreviewClose">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-preview-content" id="wsPreviewContent"></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 设置模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="settingsModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>⚙️ 设置</h3>
|
||
<button class="icon-btn" id="btnCloseSettings">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-group">
|
||
<label class="setting-label">Ollama 服务地址</label>
|
||
<input class="setting-input" id="inputServerUrl" type="url" placeholder="http://127.0.0.1:11434">
|
||
<div class="connection-info" id="connInfo">
|
||
<span class="status-badge" id="connBadge">检测中...</span>
|
||
<span class="server-version" id="serverVersion"></span>
|
||
</div>
|
||
<div class="cors-hint" id="corsHint" style="display:none;">
|
||
<p>⚠️ 连接失败,可能是 CORS 问题。请在启动 Ollama 时设置环境变量:</p>
|
||
<code>OLLAMA_ORIGINS="*" ollama serve</code>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">上下文长度(num_ctx)</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputNumCtx" type="number" min="512" max="131072" step="512" value="24576" style="margin-bottom:0;">
|
||
<span class="text-muted" style="white-space:nowrap;">tokens</span>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">常见值:2048 / 4096 / 8192 / 32768。值越大上下文越长,显存占用越高。</p>
|
||
<label class="setting-label" style="margin-top:12px;">温度(Temperature):<span id="tempValue">0.7</span></label>
|
||
<input type="range" id="inputTemperature" class="temp-slider" min="0" max="2" step="0.1" value="0.7">
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">低值更精确稳定,高值更有创意多样(0 = 确定性,2 = 最大随机)</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">运行中的模型</label>
|
||
<div class="running-models" id="runningModels">
|
||
<p class="text-muted">无运行中的模型</p>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">
|
||
🔧 工具调用(Tool Calling)
|
||
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
|
||
<input type="checkbox" id="toggleToolCalling">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:8px;">启用后,AI 可以在对话中主动调用本地工具(读写文件、搜索、创建目录等)。需要模型支持 Tool Calling。</p>
|
||
<div class="tool-settings" id="toolSettings">
|
||
<div class="tool-list">
|
||
<div class="tool-item"><span>📄 读取文件</span><span class="text-muted">自动</span></div>
|
||
<div class="tool-item"><span>✏️ 写入文件</span><span class="text-muted">需确认</span></div>
|
||
<div class="tool-item"><span>📁 列出目录</span><span class="text-muted">自动</span></div>
|
||
<div class="tool-item"><span>🔍 搜索文件</span><span class="text-muted">自动</span></div>
|
||
<div class="tool-item"><span>📂 创建目录</span><span class="text-muted">需确认</span></div>
|
||
<div class="tool-item"><span>🗑️ 删除文件</span><span class="text-muted">需确认</span></div>
|
||
<div class="tool-item">
|
||
<span>💻 执行命令</span>
|
||
<label class="toggle-label" style="margin:0;">
|
||
<input type="checkbox" id="toggleRunCommand">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<p class="text-muted" style="font-size:11px;margin-top:8px;">⚠️ 高风险操作(写入/删除/命令执行)会弹出确认对话框。系统关键路径(/etc, /sys, ~/.ssh 等)已自动屏蔽。</p>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🖥️ 工作空间目录</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputWorkspaceDir" type="text" placeholder="默认: 应用数据目录/workspace" style="margin-bottom:0;flex:1;" readonly>
|
||
<button class="btn btn-sm btn-outline" id="btnBrowseWorkspace">📂 浏览</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">工作空间用于存放 AI 执行命令时的工作文件。命令行默认在此目录执行。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">
|
||
🧠 Agent 记忆系统
|
||
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
|
||
<input type="checkbox" id="toggleMemoryEnabled" checked>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</label>
|
||
<p class="text-muted" style="font-size:11px;">启用后,AI 会自动从对话中提取关键信息(事实、偏好、规则)并跨会话记住。发送消息时自动检索相关记忆注入上下文。</p>
|
||
<div class="tool-list" style="margin-top:8px;">
|
||
<div class="tool-item"><span>📌 事实</span><span class="text-muted">项目信息、身份背景</span></div>
|
||
<div class="tool-item"><span>⚙️ 偏好</span><span class="text-muted">语言风格、技术栈偏好</span></div>
|
||
<div class="tool-item"><span>📏 规则</span><span class="text-muted">编码规范、输出格式要求</span></div>
|
||
<div class="tool-item"><span>📝 事件</span><span class="text-muted">完成的任务、结论</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">显存管理</label>
|
||
<button class="btn btn-danger" id="btnReleaseVRAM">释放显存(卸载模型)</button>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">数据管理</label>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||
<button class="btn btn-outline" id="btnExportAll">📦 导出全部会话</button>
|
||
<button class="btn btn-outline" id="btnImportSessions">📥 导入会话</button>
|
||
<button class="btn btn-danger" id="btnClearAllHistory">清空所有历史</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:6px;font-size:11px;">导出格式为 JSON,可备份后在其他设备导入恢复。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 历史记录模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="historyModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>🕐 历史记录</h3>
|
||
<button class="icon-btn" id="btnCloseHistory">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="history-search-wrap">
|
||
<input class="history-search-input" id="historySearchInput" type="text" placeholder="搜索会话标题或内容...">
|
||
<button class="history-search-clear" id="historySearchClear" style="display:none;">✕</button>
|
||
</div>
|
||
<div class="history-list" id="historyList"></div>
|
||
<div class="history-pagination" id="historyPagination"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 帮助模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="helpModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>📖 使用帮助</h3>
|
||
<button class="icon-btn" id="btnCloseHelp">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="help-section"><h4>🚀 快速开始</h4><ol><li>确保 Ollama 已启动(默认 <code>http://127.0.0.1:11434</code>,可在设置中修改)</li><li>顶部模型栏选择一个模型,右侧会显示模型能力徽章(🧠 Think / 👁️ Vision / 🔧 Tools / 📚 RAG)</li><li>输入消息,按 <kbd>Enter</kbd> 发送,<kbd>Shift+Enter</kbd> 换行</li></ol></div>
|
||
<div class="help-section"><h4>💬 聊天功能</h4><ul><li><strong>流式回复</strong> — 实时打字效果,随时点 ■ 停止</li><li><strong>Think 推理</strong> — 设置中开启,让模型展示深度思考过程(需模型支持,如 Qwen3)</li><li><strong>多模态</strong> — 上传图片,模型需支持 Vision 能力</li><li><strong>文件分析</strong> — 支持 50+ 种文本/代码格式,单文件 ≤500KB,内容以代码块发送给模型</li><li><strong>上下文长度</strong> — 设置中调整 <code>num_ctx</code>,越大记忆越长,消耗显存越多</li></ul></div>
|
||
<div class="help-section"><h4>🔧 Tool Calling(AI 自主操作)</h4><ul><li>设置中开启后,AI 可以在对话中<strong>自主调用本地工具</strong>,像一个本地 Agent</li><li><strong>7 个工具</strong>:<code>read_file</code> / <code>write_file</code> / <code>list_directory</code> / <code>search_files</code> / <code>create_directory</code> / <code>delete_file</code> / <code>run_command</code></li><li>写入/删除/命令执行等高风险操作会弹出<strong>确认对话框</strong>,你可以批准或拒绝</li><li>危险命令(<code>rm -rf</code>、<code>mkfs</code>、反弹 shell 等)和系统路径(<code>/etc</code>、<code>~/.ssh</code> 等)被自动拦截</li><li>工具调用以<strong>可视化卡片</strong>展示状态(pending → running → success/error)</li><li>最多自动循环 <strong>10 轮</strong>工具调用,也可随时中断</li><li>⚠️ 需要模型支持 Tool Calling(推荐 Qwen3、Llama 3.1+、Mistral)</li></ul></div>
|
||
<div class="help-section"><h4>🧠 Agent 记忆系统</h4><ul><li>设置中开启后,AI 从对话中<strong>自动提取关键信息</strong>(事实/偏好/规则/事件),跨会话持续积累</li><li>对话满 <strong>6 条消息</strong>后自动触发记忆提取</li><li>新对话时自动检索相关记忆注入上下文,让 AI "记住"你</li><li>点击顶部 🧠 按钮打开记忆面板:搜索、筛选、编辑、删除</li><li>记忆存储在本地 IndexedDB,不会上传到任何服务器</li></ul></div>
|
||
<div class="help-section"><h4>📚 知识库 (RAG)</h4><ul><li>点击顶部 📚 按钮打开知识库管理</li><li>创建集合 → 选择嵌入模型(推荐 <code>nomic-embed-text</code>)</li><li>上传文档,自动分块并生成向量索引</li><li>开启 RAG 检索后,聊天时自动检索相关文档增强回答</li></ul></div>
|
||
<div class="help-section"><h4>🖥️ 布局说明</h4><ul><li><strong>左侧面板</strong> — 执行日志,实时显示应用运行日志(连接、模型加载、工具调用等)</li><li><strong>中间区域</strong> — 聊天消息,顶部 Header + 模型栏,底部输入框</li><li><strong>右侧面板</strong> — 工作空间(常驻显示),包含:<ul><li><strong>💻 命令行 Tab</strong> — 终端界面,实时流式输出,支持长时间运行(无超时),多终端 Tab</li><li><strong>📁 文件 Tab</strong> — 浏览工作空间目录,点击文件预览内容</li></ul></li><li>工作空间面板宽度可通过拖拽左边缘调整(300–700px)</li><li>工作空间目录可在设置中修改</li></ul></div>
|
||
<div class="help-section"><h4>🕐 历史记录</h4><ul><li>所有会话自动保存到本地 IndexedDB</li><li>点击顶部 🕐 按钮查看、搜索、恢复历史会话</li><li>支持导出 Markdown / HTML / TXT / <code>.metona</code> 加密备份</li></ul></div>
|
||
<div class="help-section"><h4>⚡ 快捷键</h4><table class="help-shortcuts"><tr><td><kbd>Enter</kbd></td><td>发送消息</td></tr><tr><td><kbd>Shift + Enter</kbd></td><td>换行</td></tr><tr><td><kbd>Ctrl + N</kbd></td><td>新建会话</td></tr><tr><td><kbd>Ctrl + K</kbd></td><td>知识库管理</td></tr><tr><td><kbd>Ctrl + M</kbd></td><td>记忆面板</td></tr><tr><td><kbd>Esc</kbd></td><td>关闭弹窗</td></tr></table></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 知识库管理模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="kbModal" style="display:none;">
|
||
<div class="modal modal-lg">
|
||
<div class="modal-header">
|
||
<h3>📚 知识库 (RAG)</h3>
|
||
<button class="icon-btn" id="btnCloseKB">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="kb-layout">
|
||
<div class="kb-sidebar">
|
||
<div class="kb-new-collection">
|
||
<input class="setting-input" id="inputKBName" type="text" placeholder="新建知识库名称...">
|
||
<button class="btn btn-sm btn-primary" id="btnNewCollection">创建</button>
|
||
</div>
|
||
<div class="kb-collection-list" id="kbCollectionList">
|
||
<p class="text-muted" style="padding:16px;text-align:center;">暂无知识库</p>
|
||
</div>
|
||
</div>
|
||
<div class="kb-main">
|
||
<div class="kb-toolbar">
|
||
<button class="btn btn-sm btn-outline" id="btnUploadDoc">📄 上传文档</button>
|
||
<span id="kbProgress" style="display:none;" class="kb-progress">
|
||
<span class="spinner"></span>
|
||
<span id="kbProgressText">处理中...</span>
|
||
</span>
|
||
<select class="kb-embed-select" id="selectEmbedModel">
|
||
<option value="">嵌入模型...</option>
|
||
</select>
|
||
<div class="kb-rag-toggle">
|
||
<label class="setting-label" style="margin:0;font-size:12px;white-space:nowrap;">RAG</label>
|
||
<label class="toggle-label" style="margin:0;">
|
||
<input type="checkbox" id="toggleRAG">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="kb-doc-list" id="kbDocList">
|
||
<p class="text-muted" style="padding:16px;text-align:center;">← 选择一个知识库集合</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<!-- ═══════════════ 图片预览 Lightbox ═══════════════ -->
|
||
<div class="lightbox-overlay" id="lightbox" style="display:none;">
|
||
<button class="lightbox-close" id="lightboxClose">✕</button>
|
||
<img class="lightbox-img" id="lightboxImg" src="" alt="预览">
|
||
</div>
|
||
|
||
<!-- ═══════════════ 工具调用确认对话框 ═══════════════ -->
|
||
<div class="modal-overlay" id="toolConfirmModal" style="display:none;">
|
||
<div class="modal" style="max-width:520px;">
|
||
<div class="modal-header">
|
||
<h3 id="toolConfirmTitle">⚠️ 确认操作</h3>
|
||
</div>
|
||
<div class="modal-body" id="toolConfirmBody">
|
||
<p>AI 请求执行一个需要确认的操作。</p>
|
||
</div>
|
||
<div class="modal-actions" style="padding:12px 20px 16px;">
|
||
<div style="flex:1;"></div>
|
||
<button class="btn btn-outline" id="toolCancelBtn">❌ 取消</button>
|
||
<button class="btn btn-primary" id="toolConfirmBtn">✅ 确认执行</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ Toast 通知容器 ═══════════════ -->
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
|
||
<!-- ═══════════════ 应用入口 ════════════════ -->
|
||
<script type="module" src="./main.ts"></script>
|
||
</body>
|
||
</html>
|