feat: 工具扩展至 20 个,新增 8 个实用工具
新增工具: - get_file_info: 获取文件详细元数据(自动) - tree: 树形目录结构展示(自动) - download_file: URL 下载文件到本地(需确认) - diff_files: 两个文件对比差异(自动) - replace_in_files: glob 批量查找替换(需确认) - read_multiple_files: 批量读取多个文件(自动) - git_status: Git 仓库状态查看(自动) - compress: 创建/解压 zip/tar.gz(需确认)
This commit is contained in:
+34
-2
@@ -350,7 +350,7 @@
|
||||
<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)</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>12 个工具</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>move_file</code> / <code>copy_file</code> / <code>web_fetch</code> / <code>append_file</code> / <code>edit_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>🔧 Tool Calling(AI 自主操作)</h4><ul><li>设置中开启后,AI 可以在对话中<strong>自主调用本地工具</strong>,像一个本地 Agent</li><li><strong>20 个工具</strong>:文件读写、目录管理、搜索替换、网页抓取、下载、Git、压缩/解压等</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><strong>向量语义搜索</strong>:在设置中选择嵌入模型后,支持语义级别记忆检索(更精准)</li><li>未选择嵌入模型时,使用关键词匹配检索记忆</li><li>记忆存储在本地 IndexedDB,不会上传到任何服务器</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>
|
||||
@@ -363,7 +363,7 @@
|
||||
<div class="modal-overlay" id="toolsModal" style="display:none;">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3>🔧 工具面板(12 个)</h3>
|
||||
<h3>🔧 工具面板(20 个)</h3>
|
||||
<button class="icon-btn" id="btnCloseTools">
|
||||
<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"/>
|
||||
@@ -436,6 +436,38 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">ℹ️</span><span class="tool-card-name">get_file_info</span><span class="tool-card-badge auto">自动</span></div>
|
||||
<div class="tool-card-desc">获取文件/目录详细信息:大小、日期、权限</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">🌳</span><span class="tool-card-name">tree</span><span class="tool-card-badge auto">自动</span></div>
|
||||
<div class="tool-card-desc">以树形结构展示目录内容</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">⬇️</span><span class="tool-card-name">download_file</span><span class="tool-card-badge confirm">需确认</span></div>
|
||||
<div class="tool-card-desc">从 URL 下载文件到本地</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">🔀</span><span class="tool-card-name">diff_files</span><span class="tool-card-badge auto">自动</span></div>
|
||||
<div class="tool-card-desc">对比两个文件,显示差异</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">🔄</span><span class="tool-card-name">replace_in_files</span><span class="tool-card-badge confirm">需确认</span></div>
|
||||
<div class="tool-card-desc">按 glob 模式批量查找替换多个文件</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">📚</span><span class="tool-card-name">read_multiple_files</span><span class="tool-card-badge auto">自动</span></div>
|
||||
<div class="tool-card-desc">一次读取多个文件内容</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">🔖</span><span class="tool-card-name">git_status</span><span class="tool-card-badge auto">自动</span></div>
|
||||
<div class="tool-card-desc">查看 Git 仓库状态:分支、修改、暂存</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-card-header"><span class="tool-card-icon">🗜️</span><span class="tool-card-name">compress</span><span class="tool-card-badge confirm">需确认</span></div>
|
||||
<div class="tool-card-desc">创建或解压 zip/tar.gz 归档</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -198,10 +198,134 @@ export const TOOL_DEFINITIONS: ToolDefinition[] = [
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_file_info',
|
||||
description: 'Get detailed file or directory information: size, dates, permissions, type.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['path'],
|
||||
properties: {
|
||||
path: { type: 'string', description: 'File or directory path.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'tree',
|
||||
description: 'Display directory structure as a tree. Shows nested files and folders.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['path'],
|
||||
properties: {
|
||||
path: { type: 'string', description: 'Root directory path.' },
|
||||
max_depth: { type: 'integer', description: 'Max depth. Default: 3.' },
|
||||
include_hidden: { type: 'boolean', description: 'Include hidden files. Default: false.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'download_file',
|
||||
description: 'Download a file from a URL to a local path. Requires user confirmation.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['url', 'destination'],
|
||||
properties: {
|
||||
url: { type: 'string', description: 'URL to download (http/https).' },
|
||||
destination: { type: 'string', description: 'Local path to save the file.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'diff_files',
|
||||
description: 'Compare two files and show differences. Returns a unified diff.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['file1', 'file2'],
|
||||
properties: {
|
||||
file1: { type: 'string', description: 'First file path.' },
|
||||
file2: { type: 'string', description: 'Second file path.' },
|
||||
context_lines: { type: 'integer', description: 'Context lines around changes. Default: 3.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'replace_in_files',
|
||||
description: 'Find and replace text across multiple files matching a glob pattern. Requires user confirmation.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['path', 'glob', 'old_text', 'new_text'],
|
||||
properties: {
|
||||
path: { type: 'string', description: 'Root directory.' },
|
||||
glob: { type: 'string', description: 'File pattern, e.g. "*.ts", "**/*.js".' },
|
||||
old_text: { type: 'string', description: 'Text to find.' },
|
||||
new_text: { type: 'string', description: 'Replacement text.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'read_multiple_files',
|
||||
description: 'Read multiple files at once. Returns contents of all requested files.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['paths'],
|
||||
properties: {
|
||||
paths: { type: 'array', items: { type: 'string' }, description: 'Array of file paths to read.' },
|
||||
max_chars_per_file: { type: 'integer', description: 'Max chars per file. Default: 2000.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'git_status',
|
||||
description: 'Show git repository status: branch, modified/staged/untracked files.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: [],
|
||||
properties: {
|
||||
path: { type: 'string', description: 'Repository path. Defaults to workspace.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'compress',
|
||||
description: 'Create or extract archives (zip/tar.gz). Requires user confirmation for create.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: ['action', 'path'],
|
||||
properties: {
|
||||
action: { type: 'string', enum: ['create', 'extract'], description: 'Create or extract archive.' },
|
||||
path: { type: 'string', description: 'Source path (create) or archive path (extract).' },
|
||||
destination: { type: 'string', description: 'Output archive path (create) or extract dir (extract).' },
|
||||
format: { type: 'string', enum: ['zip', 'tar.gz'], description: 'Archive format. Default: tar.gz.' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const CONFIRM_TOOLS = ['write_file', 'delete_file', 'run_command', 'create_directory', 'move_file', 'copy_file', 'append_file', 'edit_file'];
|
||||
const CONFIRM_TOOLS = ['write_file', 'delete_file', 'run_command', 'create_directory', 'move_file', 'copy_file', 'append_file', 'edit_file', 'download_file', 'replace_in_files', 'compress'];
|
||||
|
||||
export function needsConfirmation(toolName: string): boolean {
|
||||
return CONFIRM_TOOLS.includes(toolName);
|
||||
@@ -211,7 +335,9 @@ let enabledTools: Set<string> = new Set([
|
||||
'read_file', 'list_directory', 'search_files',
|
||||
'write_file', 'create_directory', 'delete_file',
|
||||
'run_command',
|
||||
'move_file', 'copy_file', 'web_fetch', 'append_file', 'edit_file'
|
||||
'move_file', 'copy_file', 'web_fetch', 'append_file', 'edit_file',
|
||||
'get_file_info', 'tree', 'download_file', 'diff_files', 'replace_in_files',
|
||||
'read_multiple_files', 'git_status', 'compress'
|
||||
]);
|
||||
|
||||
export function setToolEnabled(toolName: string, enabled: boolean): void {
|
||||
@@ -289,7 +415,15 @@ export function getToolIcon(name: string): string {
|
||||
copy_file: '📋',
|
||||
web_fetch: '🌐',
|
||||
append_file: '➕',
|
||||
edit_file: '✂️'
|
||||
edit_file: '✂️',
|
||||
get_file_info: 'ℹ️',
|
||||
tree: '🌳',
|
||||
download_file: '⬇️',
|
||||
diff_files: '🔀',
|
||||
replace_in_files: '🔄',
|
||||
read_multiple_files: '📚',
|
||||
git_status: '🔖',
|
||||
compress: '🗜️'
|
||||
};
|
||||
return icons[name] || '🔧';
|
||||
}
|
||||
@@ -307,7 +441,15 @@ export function formatToolName(name: string): string {
|
||||
copy_file: '复制文件',
|
||||
web_fetch: '网页抓取',
|
||||
append_file: '追加文件',
|
||||
edit_file: '编辑文件'
|
||||
edit_file: '编辑文件',
|
||||
get_file_info: '文件信息',
|
||||
tree: '目录树',
|
||||
download_file: '下载文件',
|
||||
diff_files: '文件对比',
|
||||
replace_in_files: '批量替换',
|
||||
read_multiple_files: '批量读取',
|
||||
git_status: 'Git 状态',
|
||||
compress: '压缩/解压'
|
||||
};
|
||||
return names[name] || name;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user