docs(website): 重构文档为六部分结构
- 重组侧边栏导航为六个模块:入门指南、核心功能、API参考、部署配置、客户端、扩展开发 - 移动核心功能文档到 core/ 目录 (checkpoint, edit-modes, session) - 新增部署配置文档 (configuration, local, production) - 新增客户端文档 (web, desktop, cli) - 新增扩展开发文档 (custom-tools, hooks, mcp-server)
This commit is contained in:
@@ -7,7 +7,7 @@ const { currentPath } = Astro.props;
|
||||
|
||||
const navigation = [
|
||||
{
|
||||
title: '开始',
|
||||
title: '入门指南',
|
||||
items: [
|
||||
{ title: '概览', href: '/docs' },
|
||||
{ title: '快速开始', href: '/docs/quickstart' },
|
||||
@@ -19,9 +19,9 @@ const navigation = [
|
||||
items: [
|
||||
{ title: 'Agent 系统', href: '/docs/core/agent' },
|
||||
{ title: '工具系统', href: '/docs/core/tools' },
|
||||
{ title: '编辑模式', href: '/docs/features/edit-modes' },
|
||||
{ title: 'Checkpoint 系统', href: '/docs/features/checkpoint' },
|
||||
{ title: '会话管理', href: '/docs/features/chat-history' },
|
||||
{ title: '编辑模式', href: '/docs/core/edit-modes' },
|
||||
{ title: 'Checkpoint', href: '/docs/core/checkpoint' },
|
||||
{ title: '会话管理', href: '/docs/core/session' },
|
||||
{ title: 'MCP 集成', href: '/docs/core/mcp' },
|
||||
],
|
||||
},
|
||||
@@ -34,21 +34,27 @@ const navigation = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '更多功能',
|
||||
title: '部署与配置',
|
||||
items: [
|
||||
{ title: '多模型支持', href: '/docs/features/multi-model' },
|
||||
{ title: '仓库地图', href: '/docs/features/repo-map' },
|
||||
{ title: '浏览器 GUI', href: '/docs/features/browser-gui' },
|
||||
{ title: '配置管理', href: '/docs/features/configuration' },
|
||||
{ title: '配置管理', href: '/docs/deploy/configuration' },
|
||||
{ title: '本地部署', href: '/docs/deploy/local' },
|
||||
{ title: '生产部署', href: '/docs/deploy/production' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '规划中',
|
||||
title: '客户端',
|
||||
items: [
|
||||
{ title: 'Linting 集成', href: '/docs/features/linting' },
|
||||
{ title: '测试集成', href: '/docs/features/testing' },
|
||||
{ title: 'Watch 模式', href: '/docs/features/watch-mode' },
|
||||
{ title: '语音输入', href: '/docs/features/voice-input' },
|
||||
{ title: 'Web 前端', href: '/docs/clients/web' },
|
||||
{ title: '桌面应用', href: '/docs/clients/desktop' },
|
||||
{ title: 'CLI 命令', href: '/docs/clients/cli' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '扩展开发',
|
||||
items: [
|
||||
{ title: '自定义工具', href: '/docs/extend/custom-tools' },
|
||||
{ title: 'Hooks 系统', href: '/docs/extend/hooks' },
|
||||
{ title: 'MCP 服务器', href: '/docs/extend/mcp-server' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="CLI 命令" description="AI Terminal Assistant 命令行工具使用指南">
|
||||
<h1>CLI 命令</h1>
|
||||
|
||||
<p>
|
||||
CLI 提供命令行界面,支持启动服务器和连接到远程服务器进行交互式对话。
|
||||
</p>
|
||||
|
||||
<h2>安装</h2>
|
||||
|
||||
<pre><code class="language-bash"># 全局安装
|
||||
npm install -g @ai-assistant/cli
|
||||
|
||||
# 或使用 pnpm
|
||||
pnpm add -g @ai-assistant/cli
|
||||
|
||||
# 验证安装
|
||||
ai-assistant --version</code></pre>
|
||||
|
||||
<h2>命令概览</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>命令</th>
|
||||
<th>说明</th>
|
||||
<th>示例</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>serve</code></td>
|
||||
<td>启动 HTTP 服务器</td>
|
||||
<td><code>ai-assistant serve</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>attach</code></td>
|
||||
<td>连接到服务器</td>
|
||||
<td><code>ai-assistant attach http://localhost:3000</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--help</code></td>
|
||||
<td>显示帮助信息</td>
|
||||
<td><code>ai-assistant --help</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--version</code></td>
|
||||
<td>显示版本号</td>
|
||||
<td><code>ai-assistant --version</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>serve 命令</h2>
|
||||
|
||||
<p>启动 AI Assistant HTTP 服务器,提供 REST API 和 WebSocket 接口。</p>
|
||||
|
||||
<pre><code class="language-bash">ai-assistant serve [选项]</code></pre>
|
||||
|
||||
<h3>选项</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>选项</th>
|
||||
<th>说明</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>-p, --port <port></code></td>
|
||||
<td>服务器端口</td>
|
||||
<td>3000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-h, --host <host></code></td>
|
||||
<td>监听地址</td>
|
||||
<td>localhost</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-d, --dir <path></code></td>
|
||||
<td>工作目录</td>
|
||||
<td>当前目录</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--auth</code></td>
|
||||
<td>启用认证</td>
|
||||
<td>非 localhost 时自动启用</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--token <token></code></td>
|
||||
<td>指定认证 Token</td>
|
||||
<td>自动生成</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>示例</h3>
|
||||
|
||||
<pre><code class="language-bash"># 基本启动
|
||||
ai-assistant serve
|
||||
|
||||
# 指定端口和目录
|
||||
ai-assistant serve --port 8080 --dir /path/to/project
|
||||
|
||||
# 远程访问模式
|
||||
ai-assistant serve --host 0.0.0.0 --port 3000
|
||||
|
||||
# 指定认证 Token
|
||||
ai-assistant serve --auth --token my-secret-token</code></pre>
|
||||
|
||||
<h3>输出示例</h3>
|
||||
|
||||
<pre><code class="language-text">🚀 AI Terminal Assistant Server
|
||||
|
||||
Local: http://localhost:3000
|
||||
Network: http://192.168.1.100:3000
|
||||
|
||||
Auth: Enabled
|
||||
Token: abc123...xyz789
|
||||
|
||||
Working Directory: /Users/dev/project
|
||||
|
||||
Press Ctrl+C to stop</code></pre>
|
||||
|
||||
<h2>attach 命令</h2>
|
||||
|
||||
<p>连接到运行中的 AI Assistant 服务器,进行交互式对话。</p>
|
||||
|
||||
<pre><code class="language-bash">ai-assistant attach <url> [选项]</code></pre>
|
||||
|
||||
<h3>选项</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>选项</th>
|
||||
<th>说明</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>-t, --token <token></code></td>
|
||||
<td>认证 Token</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-s, --session <id></code></td>
|
||||
<td>指定会话 ID</td>
|
||||
<td>自动创建</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>示例</h3>
|
||||
|
||||
<pre><code class="language-bash"># 连接本地服务器
|
||||
ai-assistant attach http://localhost:3000
|
||||
|
||||
# 连接远程服务器(带认证)
|
||||
ai-assistant attach https://ai.example.com --token your-token
|
||||
|
||||
# 恢复已有会话
|
||||
ai-assistant attach http://localhost:3000 --session session-123</code></pre>
|
||||
|
||||
<h3>交互式界面</h3>
|
||||
|
||||
<pre><code class="language-text">✓ Connected to http://localhost:3000
|
||||
Session: session-abc123
|
||||
|
||||
You: 帮我分析这个项目的结构
|
||||
|
||||
AI: 我来分析这个项目的结构...
|
||||
|
||||
[调用工具: list_directory]
|
||||
路径: /Users/dev/project
|
||||
|
||||
项目结构如下:
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ └── utils/
|
||||
├── package.json
|
||||
└── README.md
|
||||
|
||||
这是一个 TypeScript 项目...
|
||||
|
||||
You: _</code></pre>
|
||||
|
||||
<h2>交互式命令</h2>
|
||||
|
||||
<p>在 attach 模式下,支持以下交互式命令:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>命令</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/exit</code> 或 <code>/quit</code></td>
|
||||
<td>退出交互</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/clear</code></td>
|
||||
<td>清空当前会话历史</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/new</code></td>
|
||||
<td>创建新会话</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/sessions</code></td>
|
||||
<td>列出所有会话</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/switch <id></code></td>
|
||||
<td>切换到指定会话</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/status</code></td>
|
||||
<td>显示连接状态</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/help</code></td>
|
||||
<td>显示帮助信息</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>环境变量</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>变量</th>
|
||||
<th>说明</th>
|
||||
<th>命令</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>ANTHROPIC_API_KEY</code></td>
|
||||
<td>Anthropic API Key</td>
|
||||
<td>serve</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AI_ASSISTANT_TOKEN</code></td>
|
||||
<td>默认认证 Token</td>
|
||||
<td>attach</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AI_ASSISTANT_URL</code></td>
|
||||
<td>默认服务器地址</td>
|
||||
<td>attach</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre><code class="language-bash"># 设置环境变量
|
||||
export ANTHROPIC_API_KEY=sk-ant-...
|
||||
export AI_ASSISTANT_TOKEN=my-token
|
||||
export AI_ASSISTANT_URL=http://localhost:3000
|
||||
|
||||
# 使用环境变量(无需每次指定)
|
||||
ai-assistant attach</code></pre>
|
||||
|
||||
<h2>配置文件</h2>
|
||||
|
||||
<p>可以在 <code>~/.ai-assistant/config.json</code> 中配置默认选项:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"defaultServer": "http://localhost:3000",
|
||||
"defaultToken": "your-token",
|
||||
"theme": "dark",
|
||||
"historySize": 1000,
|
||||
"autoConnect": true
|
||||
}</code></pre>
|
||||
|
||||
<h2>开发模式运行</h2>
|
||||
|
||||
<p>在项目开发中直接运行:</p>
|
||||
|
||||
<pre><code class="language-bash"># 使用 bun 运行
|
||||
cd packages/cli
|
||||
bun run src/index.ts serve --port 3000
|
||||
|
||||
# 或使用 tsx
|
||||
npx tsx src/index.ts attach http://localhost:3000</code></pre>
|
||||
|
||||
<h2>退出码</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>退出码</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>0</td><td>正常退出</td></tr>
|
||||
<tr><td>1</td><td>一般错误</td></tr>
|
||||
<tr><td>2</td><td>连接失败</td></tr>
|
||||
<tr><td>3</td><td>认证失败</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/deploy/local" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">本地部署</h3>
|
||||
<p class="text-sm text-gray-400">本地开发环境搭建</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/api/rest" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">REST API</h3>
|
||||
<p class="text-sm text-gray-400">API 端点文档</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,287 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="桌面应用" description="AI Terminal Assistant 桌面客户端 (Tauri)">
|
||||
<h1>桌面应用</h1>
|
||||
|
||||
<p class="text-yellow-400 font-medium">🚧 开发中 - 基础功能已实现</p>
|
||||
|
||||
<p>
|
||||
桌面应用基于 Tauri 构建,提供原生桌面体验,支持 Windows、macOS 和 Linux。
|
||||
复用 Web 前端代码,结合 Rust 后端提供更好的性能和系统集成。
|
||||
</p>
|
||||
|
||||
<h2>功能特性</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>跨平台</strong> - 支持 Windows、macOS、Linux</li>
|
||||
<li><strong>原生性能</strong> - Rust 后端,低内存占用</li>
|
||||
<li><strong>系统集成</strong> - 系统托盘、快捷键、通知</li>
|
||||
<li><strong>离线支持</strong> - 本地数据存储</li>
|
||||
<li><strong>自动更新</strong> - 内置更新机制</li>
|
||||
</ul>
|
||||
|
||||
<h2>系统要求</h2>
|
||||
|
||||
<h3>开发环境</h3>
|
||||
|
||||
<ul>
|
||||
<li>Node.js 18+</li>
|
||||
<li>Rust 1.70+</li>
|
||||
<li>pnpm 8+</li>
|
||||
</ul>
|
||||
|
||||
<h3>平台依赖</h3>
|
||||
|
||||
<pre><code class="language-bash"># macOS
|
||||
xcode-select --install
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
||||
|
||||
# Windows
|
||||
# 需要安装 Visual Studio C++ Build Tools</code></pre>
|
||||
|
||||
<h2>快速开始</h2>
|
||||
|
||||
<h3>开发模式</h3>
|
||||
|
||||
<pre><code class="language-bash"># 进入 desktop 包目录
|
||||
cd packages/desktop
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
# 启动开发模式
|
||||
pnpm tauri dev</code></pre>
|
||||
|
||||
<h3>构建发布版本</h3>
|
||||
|
||||
<pre><code class="language-bash"># 构建当前平台
|
||||
pnpm tauri build
|
||||
|
||||
# 输出目录:
|
||||
# - macOS: src-tauri/target/release/bundle/dmg/
|
||||
# - Windows: src-tauri/target/release/bundle/msi/
|
||||
# - Linux: src-tauri/target/release/bundle/deb/</code></pre>
|
||||
|
||||
<h2>项目结构</h2>
|
||||
|
||||
<pre><code class="language-text">packages/desktop/
|
||||
├── src/ # 前端代码 (复用 web)
|
||||
├── src-tauri/ # Tauri/Rust 后端
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs # 应用入口
|
||||
│ │ ├── commands.rs # Tauri 命令
|
||||
│ │ ├── tray.rs # 系统托盘
|
||||
│ │ └── window.rs # 窗口管理
|
||||
│ ├── icons/ # 应用图标
|
||||
│ ├── tauri.conf.json # Tauri 配置
|
||||
│ └── Cargo.toml # Rust 依赖
|
||||
├── index.html
|
||||
└── vite.config.ts</code></pre>
|
||||
|
||||
<h2>Tauri 配置</h2>
|
||||
|
||||
<pre><code class="language-json">// src-tauri/tauri.conf.json
|
||||
{
|
||||
"productName": "AI Terminal Assistant",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.ai-assistant.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"devPath": "http://localhost:5173",
|
||||
"distDir": "../dist"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "AI Terminal Assistant",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>Tauri 命令</h2>
|
||||
|
||||
<p>在 Rust 中定义可从前端调用的命令:</p>
|
||||
|
||||
<pre><code class="language-rust">// src-tauri/src/commands.rs
|
||||
use tauri::command;
|
||||
|
||||
#[command]
|
||||
pub fn get_system_info() -> Result<SystemInfo, String> {
|
||||
Ok(SystemInfo {
|
||||
os: std::env::consts::OS.to_string(),
|
||||
arch: std::env::consts::ARCH.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn open_project(path: String) -> Result<(), String> {
|
||||
// 打开项目目录
|
||||
std::env::set_current_dir(&path)
|
||||
.map_err(|e| e.to_string())
|
||||
}</code></pre>
|
||||
|
||||
<p>在前端调用:</p>
|
||||
|
||||
<pre><code class="language-typescript">import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
// 调用 Rust 命令
|
||||
const info = await invoke('get_system_info');
|
||||
console.log('系统信息:', info);
|
||||
|
||||
// 打开项目
|
||||
await invoke('open_project', { path: '/path/to/project' });</code></pre>
|
||||
|
||||
<h2>系统托盘</h2>
|
||||
|
||||
<pre><code class="language-rust">// src-tauri/src/tray.rs
|
||||
use tauri::{
|
||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||
Manager,
|
||||
};
|
||||
|
||||
pub fn create_tray(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tray = TrayIconBuilder::new()
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
.tooltip("AI Terminal Assistant")
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
if let TrayIconEvent::Click {
|
||||
button: MouseButton::Left,
|
||||
button_state: MouseButtonState::Up,
|
||||
..
|
||||
} = event
|
||||
{
|
||||
let app = tray.app_handle();
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(app)?;
|
||||
|
||||
Ok(())
|
||||
}</code></pre>
|
||||
|
||||
<h2>快捷键</h2>
|
||||
|
||||
<pre><code class="language-rust">use tauri::Manager;
|
||||
|
||||
fn setup_shortcuts(app: &tauri::App) {
|
||||
use tauri_plugin_global_shortcut::GlobalShortcutExt;
|
||||
|
||||
app.handle().plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_shortcuts(["ctrl+shift+a"])?
|
||||
.with_handler(|app, shortcut, event| {
|
||||
if shortcut.matches(Modifiers::CONTROL | Modifiers::SHIFT, Code::KeyA) {
|
||||
// 显示/隐藏窗口
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if window.is_visible().unwrap_or(false) {
|
||||
let _ = window.hide();
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
)?;
|
||||
}</code></pre>
|
||||
|
||||
<h2>文件系统访问</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { open, save } from '@tauri-apps/plugin-dialog';
|
||||
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
|
||||
// 打开文件选择对话框
|
||||
const selected = await open({
|
||||
multiple: false,
|
||||
filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
|
||||
});
|
||||
|
||||
if (selected) {
|
||||
const content = await readTextFile(selected);
|
||||
console.log('文件内容:', content);
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
const savePath = await save({
|
||||
filters: [{ name: 'Markdown', extensions: ['md'] }]
|
||||
});
|
||||
|
||||
if (savePath) {
|
||||
await writeTextFile(savePath, '# Hello World');
|
||||
}</code></pre>
|
||||
|
||||
<h2>自动更新</h2>
|
||||
|
||||
<pre><code class="language-json">// tauri.conf.json
|
||||
{
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": [
|
||||
"https://releases.example.com/{{target}}/{{arch}}/{{current_version}}"
|
||||
],
|
||||
"pubkey": "YOUR_PUBLIC_KEY"
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<pre><code class="language-typescript">import { check } from '@tauri-apps/plugin-updater';
|
||||
|
||||
async function checkForUpdates() {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
console.log(`发现新版本: ${update.version}`);
|
||||
await update.downloadAndInstall();
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>开发注意事项</h2>
|
||||
|
||||
<ul>
|
||||
<li>Tauri 构建较慢,开发时建议使用 <code>pnpm tauri dev</code> 热重载</li>
|
||||
<li>前端代码变更会自动重载,Rust 代码需要重新编译</li>
|
||||
<li>跨平台测试建议使用 CI/CD 自动化</li>
|
||||
<li>敏感信息应使用 Tauri 的安全存储 API</li>
|
||||
</ul>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/clients/web" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">Web 前端</h3>
|
||||
<p class="text-sm text-gray-400">React Web 客户端</p>
|
||||
</a>
|
||||
|
||||
<a href="https://tauri.app/v1/guides/" target="_blank" rel="noopener" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">Tauri 官方文档 ↗</h3>
|
||||
<p class="text-sm text-gray-400">Tauri 框架文档</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,253 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="Web 前端" description="AI Terminal Assistant Web 客户端使用指南">
|
||||
<h1>Web 前端</h1>
|
||||
|
||||
<p>
|
||||
Web 前端是基于 React + Vite 构建的现代化 Web 应用,提供完整的 AI 对话交互界面。
|
||||
</p>
|
||||
|
||||
<h2>功能特性</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>实时对话</strong> - WebSocket 流式响应</li>
|
||||
<li><strong>工具可视化</strong> - 实时显示工具调用和结果</li>
|
||||
<li><strong>权限管理</strong> - 交互式权限审批</li>
|
||||
<li><strong>代码高亮</strong> - 语法高亮和 Diff 显示</li>
|
||||
<li><strong>会话管理</strong> - 多会话切换</li>
|
||||
<li><strong>响应式设计</strong> - 适配桌面和移动设备</li>
|
||||
</ul>
|
||||
|
||||
<h2>快速开始</h2>
|
||||
|
||||
<h3>开发模式</h3>
|
||||
|
||||
<pre><code class="language-bash"># 进入 web 包目录
|
||||
cd packages/web
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
# 启动开发服务器
|
||||
pnpm dev
|
||||
|
||||
# 访问 http://localhost:5173</code></pre>
|
||||
|
||||
<h3>生产构建</h3>
|
||||
|
||||
<pre><code class="language-bash"># 构建生产版本
|
||||
pnpm build
|
||||
|
||||
# 预览构建结果
|
||||
pnpm preview
|
||||
|
||||
# 输出目录: dist/</code></pre>
|
||||
|
||||
<h2>环境配置</h2>
|
||||
|
||||
<p>在 <code>.env</code> 或 <code>.env.local</code> 中配置:</p>
|
||||
|
||||
<pre><code class="language-bash"># API 服务器地址
|
||||
VITE_API_URL=http://localhost:3000
|
||||
|
||||
# WebSocket 地址(可选,默认从 API_URL 推导)
|
||||
VITE_WS_URL=ws://localhost:3000
|
||||
|
||||
# 认证 Token(远程连接时需要)
|
||||
VITE_AUTH_TOKEN=your-auth-token</code></pre>
|
||||
|
||||
<h2>项目结构</h2>
|
||||
|
||||
<pre><code class="language-text">packages/web/
|
||||
├── src/
|
||||
│ ├── components/ # React 组件
|
||||
│ │ ├── Chat/ # 聊天相关组件
|
||||
│ │ ├── Message/ # 消息显示组件
|
||||
│ │ ├── Tool/ # 工具调用显示
|
||||
│ │ └── Layout/ # 布局组件
|
||||
│ ├── hooks/ # 自定义 Hooks
|
||||
│ │ ├── useWebSocket.ts
|
||||
│ │ ├── useSession.ts
|
||||
│ │ └── usePermission.ts
|
||||
│ ├── services/ # API 服务
|
||||
│ │ ├── api.ts # REST API 客户端
|
||||
│ │ └── websocket.ts # WebSocket 客户端
|
||||
│ ├── stores/ # 状态管理
|
||||
│ ├── types/ # TypeScript 类型
|
||||
│ ├── App.tsx # 应用入口
|
||||
│ └── main.tsx # 渲染入口
|
||||
├── public/ # 静态资源
|
||||
├── index.html # HTML 模板
|
||||
└── vite.config.ts # Vite 配置</code></pre>
|
||||
|
||||
<h2>核心组件</h2>
|
||||
|
||||
<h3>ChatContainer</h3>
|
||||
|
||||
<p>主聊天容器,管理消息列表和输入:</p>
|
||||
|
||||
<pre><code class="language-tsx">import { ChatContainer } from '@/components/Chat';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ChatContainer
|
||||
sessionId="session-123"
|
||||
onMessage={(msg) => console.log(msg)}
|
||||
/>
|
||||
);
|
||||
}</code></pre>
|
||||
|
||||
<h3>MessageList</h3>
|
||||
|
||||
<p>消息列表组件,支持多种消息类型:</p>
|
||||
|
||||
<pre><code class="language-tsx">import { MessageList } from '@/components/Message';
|
||||
|
||||
<MessageList
|
||||
messages={messages}
|
||||
onToolApprove={handleToolApprove}
|
||||
onToolReject={handleToolReject}
|
||||
/></code></pre>
|
||||
|
||||
<h3>ToolCallDisplay</h3>
|
||||
|
||||
<p>工具调用展示组件:</p>
|
||||
|
||||
<pre><code class="language-tsx">import { ToolCallDisplay } from '@/components/Tool';
|
||||
|
||||
<ToolCallDisplay
|
||||
tool={{
|
||||
name: 'read_file',
|
||||
params: { path: '/src/index.ts' },
|
||||
status: 'completed',
|
||||
result: '文件内容...'
|
||||
}}
|
||||
/></code></pre>
|
||||
|
||||
<h2>WebSocket 集成</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { useWebSocket } from '@/hooks/useWebSocket';
|
||||
|
||||
function ChatComponent() {
|
||||
const {
|
||||
connected,
|
||||
send,
|
||||
lastMessage,
|
||||
disconnect
|
||||
} = useWebSocket(sessionId);
|
||||
|
||||
const handleSend = (content: string) => {
|
||||
send({
|
||||
type: 'message',
|
||||
payload: { content }
|
||||
});
|
||||
};
|
||||
|
||||
// 处理接收的消息
|
||||
useEffect(() => {
|
||||
if (lastMessage) {
|
||||
switch (lastMessage.type) {
|
||||
case 'chunk':
|
||||
appendContent(lastMessage.payload.content);
|
||||
break;
|
||||
case 'tool_call':
|
||||
showToolCall(lastMessage.payload);
|
||||
break;
|
||||
case 'done':
|
||||
finishMessage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [lastMessage]);
|
||||
}</code></pre>
|
||||
|
||||
<h2>状态管理</h2>
|
||||
|
||||
<p>使用 Zustand 进行状态管理:</p>
|
||||
|
||||
<pre><code class="language-typescript">import { create } from 'zustand';
|
||||
|
||||
interface ChatStore {
|
||||
messages: Message[];
|
||||
currentSession: string | null;
|
||||
addMessage: (msg: Message) => void;
|
||||
setSession: (id: string) => void;
|
||||
}
|
||||
|
||||
export const useChatStore = create<ChatStore>((set) => ({
|
||||
messages: [],
|
||||
currentSession: null,
|
||||
addMessage: (msg) => set((state) => ({
|
||||
messages: [...state.messages, msg]
|
||||
})),
|
||||
setSession: (id) => set({ currentSession: id })
|
||||
}));</code></pre>
|
||||
|
||||
<h2>主题定制</h2>
|
||||
|
||||
<p>使用 Tailwind CSS 自定义主题:</p>
|
||||
|
||||
<pre><code class="language-javascript">// tailwind.config.js
|
||||
export default {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f0f9ff',
|
||||
// ...
|
||||
900: '#0c4a6e',
|
||||
},
|
||||
background: '#0a0a0a',
|
||||
surface: '#1a1a1a',
|
||||
},
|
||||
fontFamily: {
|
||||
mono: ['JetBrains Mono', 'monospace'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};</code></pre>
|
||||
|
||||
<h2>部署</h2>
|
||||
|
||||
<h3>静态部署</h3>
|
||||
|
||||
<pre><code class="language-bash"># 构建
|
||||
VITE_API_URL=https://api.example.com pnpm build
|
||||
|
||||
# 部署 dist/ 目录到任何静态托管服务
|
||||
# - Vercel
|
||||
# - Netlify
|
||||
# - Cloudflare Pages
|
||||
# - Nginx</code></pre>
|
||||
|
||||
<h3>Docker 部署</h3>
|
||||
|
||||
<pre><code class="language-dockerfile"># Dockerfile.web
|
||||
FROM node:20-alpine as builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN npm install -g pnpm
|
||||
RUN pnpm install
|
||||
RUN pnpm --filter @ai-assistant/web build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/packages/web/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80</code></pre>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/api/websocket" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">WebSocket API</h3>
|
||||
<p class="text-sm text-gray-400">实时通信协议</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/clients/desktop" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">桌面应用</h3>
|
||||
<p class="text-sm text-gray-400">Tauri 桌面客户端</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="配置管理" description="AI Terminal Assistant 配置管理指南">
|
||||
<h1>配置管理</h1>
|
||||
|
||||
<p>
|
||||
AI Terminal Assistant 支持多层配置系统,可以通过环境变量、配置文件等方式进行配置。
|
||||
</p>
|
||||
|
||||
<h2>环境变量</h2>
|
||||
|
||||
<h3>必需配置</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>变量名</th>
|
||||
<th>说明</th>
|
||||
<th>示例</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>ANTHROPIC_API_KEY</code></td>
|
||||
<td>Anthropic API 密钥</td>
|
||||
<td><code>sk-ant-...</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>可选配置</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>变量名</th>
|
||||
<th>说明</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>OPENAI_API_KEY</code></td>
|
||||
<td>OpenAI API 密钥</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEEPSEEK_API_KEY</code></td>
|
||||
<td>DeepSeek API 密钥</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>TAVILY_API_KEY</code></td>
|
||||
<td>Tavily 搜索 API 密钥</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AI_MODEL</code></td>
|
||||
<td>默认模型</td>
|
||||
<td><code>claude-sonnet-4-20250514</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AI_MAX_TOKENS</code></td>
|
||||
<td>最大输出 tokens</td>
|
||||
<td><code>4096</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AI_TEMPERATURE</code></td>
|
||||
<td>温度参数</td>
|
||||
<td><code>0.7</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>配置文件</h2>
|
||||
|
||||
<h3>.env 文件</h3>
|
||||
|
||||
<pre><code class="language-bash"># .env 文件示例
|
||||
ANTHROPIC_API_KEY=sk-ant-your-key-here
|
||||
OPENAI_API_KEY=sk-your-openai-key
|
||||
TAVILY_API_KEY=tvly-your-tavily-key
|
||||
|
||||
# 模型配置
|
||||
AI_MODEL=claude-sonnet-4-20250514
|
||||
AI_MAX_TOKENS=8192
|
||||
AI_TEMPERATURE=0.7</code></pre>
|
||||
|
||||
<h3>项目配置文件</h3>
|
||||
|
||||
<p>在项目根目录创建 <code>.ai-assistant/config.json</code>:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"maxTokens": 4096,
|
||||
"temperature": 0.7,
|
||||
"systemPrompt": "你是一个专业的编程助手",
|
||||
"tools": {
|
||||
"enabled": ["read_file", "write_file", "bash", "grep"],
|
||||
"disabled": ["web_search"]
|
||||
},
|
||||
"permissions": {
|
||||
"allowedPaths": ["/project"],
|
||||
"deniedPaths": ["/project/secrets"],
|
||||
"autoApprove": ["read_file", "grep"]
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>MCP 服务器配置</h2>
|
||||
|
||||
<p>在项目根目录创建 <code>mcp.json</code>:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"],
|
||||
"env": {}
|
||||
},
|
||||
"github": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-github"],
|
||||
"env": {
|
||||
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>配置优先级</h2>
|
||||
|
||||
<p>配置按以下优先级加载(高 → 低):</p>
|
||||
|
||||
<ol>
|
||||
<li>命令行参数</li>
|
||||
<li>环境变量</li>
|
||||
<li>项目配置文件 (<code>.ai-assistant/config.json</code>)</li>
|
||||
<li>用户配置文件 (<code>~/.config/ai-assistant/config.json</code>)</li>
|
||||
<li>默认值</li>
|
||||
</ol>
|
||||
|
||||
<h2>服务器配置</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>环境变量</th>
|
||||
<th>说明</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>--port</code></td>
|
||||
<td><code>PORT</code></td>
|
||||
<td>服务器端口</td>
|
||||
<td><code>3000</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--host</code></td>
|
||||
<td><code>HOST</code></td>
|
||||
<td>绑定地址</td>
|
||||
<td><code>127.0.0.1</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--auth</code></td>
|
||||
<td><code>AUTH_ENABLED</code></td>
|
||||
<td>启用认证</td>
|
||||
<td>远程模式自动启用</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--token</code></td>
|
||||
<td><code>AUTH_TOKEN</code></td>
|
||||
<td>认证 Token</td>
|
||||
<td>自动生成</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>权限配置</h2>
|
||||
|
||||
<pre><code class="language-typescript">interface PermissionConfig {
|
||||
// 允许访问的路径
|
||||
allowedPaths: string[];
|
||||
|
||||
// 禁止访问的路径
|
||||
deniedPaths: string[];
|
||||
|
||||
// 自动批准的工具
|
||||
autoApprove: string[];
|
||||
|
||||
// 禁用的工具
|
||||
disabledTools: string[];
|
||||
|
||||
// 允许执行的 Shell 命令模式
|
||||
shellPatterns: string[];
|
||||
}</code></pre>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/deploy/local" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">本地部署</h3>
|
||||
<p class="text-sm text-gray-400">开发环境搭建指南</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/deploy/production" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">生产部署</h3>
|
||||
<p class="text-sm text-gray-400">服务器和容器部署</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="本地部署" description="AI Terminal Assistant 本地开发环境搭建">
|
||||
<h1>本地部署</h1>
|
||||
|
||||
<p>
|
||||
本指南介绍如何在本地搭建 AI Terminal Assistant 开发环境。
|
||||
</p>
|
||||
|
||||
<h2>系统要求</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>Node.js</strong> 18.0 或更高版本</li>
|
||||
<li><strong>Bun</strong> 1.0 或更高版本(推荐)</li>
|
||||
<li><strong>pnpm</strong> 8.0 或更高版本</li>
|
||||
<li><strong>Git</strong> 2.0 或更高版本</li>
|
||||
</ul>
|
||||
|
||||
<h3>可选依赖</h3>
|
||||
|
||||
<ul>
|
||||
<li><strong>Rust</strong> - 构建桌面应用需要</li>
|
||||
<li><strong>Python 3</strong> - 部分 MCP 服务器需要</li>
|
||||
</ul>
|
||||
|
||||
<h2>安装步骤</h2>
|
||||
|
||||
<h3>1. 克隆仓库</h3>
|
||||
|
||||
<pre><code class="language-bash">git clone https://github.com/your-username/ai-terminal-assistant.git
|
||||
cd ai-terminal-assistant</code></pre>
|
||||
|
||||
<h3>2. 安装依赖</h3>
|
||||
|
||||
<pre><code class="language-bash"># 使用 pnpm 安装所有依赖
|
||||
pnpm install</code></pre>
|
||||
|
||||
<h3>3. 配置环境变量</h3>
|
||||
|
||||
<pre><code class="language-bash"># 复制环境变量模板
|
||||
cp .env.example .env
|
||||
|
||||
# 编辑 .env 文件,添加 API 密钥
|
||||
# ANTHROPIC_API_KEY=your-api-key</code></pre>
|
||||
|
||||
<h3>4. 构建项目</h3>
|
||||
|
||||
<pre><code class="language-bash"># 构建核心包
|
||||
pnpm --filter @ai-assistant/core build
|
||||
|
||||
# 构建服务器
|
||||
pnpm --filter @ai-assistant/server build
|
||||
|
||||
# 或一次性构建所有包
|
||||
pnpm build</code></pre>
|
||||
|
||||
<h2>启动开发服务器</h2>
|
||||
|
||||
<h3>方式一:分开启动</h3>
|
||||
|
||||
<pre><code class="language-bash"># 终端 1:启动 HTTP 服务器
|
||||
pnpm server:dev
|
||||
|
||||
# 终端 2:启动 Web 前端
|
||||
cd packages/web && pnpm dev</code></pre>
|
||||
|
||||
<p>然后访问 <code>http://localhost:5173</code>。</p>
|
||||
|
||||
<h3>方式二:使用 CLI</h3>
|
||||
|
||||
<pre><code class="language-bash"># 启动服务器
|
||||
cd packages/cli
|
||||
bun run src/index.ts serve --port 3000</code></pre>
|
||||
|
||||
<h2>开发命令</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>命令</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>pnpm build</code></td>
|
||||
<td>构建所有包</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pnpm test</code></td>
|
||||
<td>运行所有测试</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pnpm --filter @ai-assistant/core build</code></td>
|
||||
<td>只构建 core 包</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pnpm --filter @ai-assistant/core test:watch</code></td>
|
||||
<td>监视模式运行 core 测试</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pnpm server:dev</code></td>
|
||||
<td>开发模式启动服务器</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>项目结构</h2>
|
||||
|
||||
<pre><code class="language-text">ai-terminal-assistant/
|
||||
├── packages/
|
||||
│ ├── core/ # 核心引擎
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── core/ # Agent 系统
|
||||
│ │ │ ├── tools/ # 工具注册
|
||||
│ │ │ ├── editors/ # 编辑器
|
||||
│ │ │ ├── checkpoint/ # 检查点
|
||||
│ │ │ ├── mcp/ # MCP 客户端
|
||||
│ │ │ └── lsp/ # LSP 集成
|
||||
│ │ └── package.json
|
||||
│ │
|
||||
│ ├── server/ # HTTP 服务器
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── routes/ # API 路由
|
||||
│ │ │ ├── ws.ts # WebSocket
|
||||
│ │ │ └── sse.ts # SSE
|
||||
│ │ └── package.json
|
||||
│ │
|
||||
│ ├── cli/ # 命令行工具
|
||||
│ ├── web/ # React 前端
|
||||
│ └── desktop/ # Tauri 桌面应用
|
||||
│
|
||||
├── docs/ # 设计文档
|
||||
├── pnpm-workspace.yaml
|
||||
└── package.json</code></pre>
|
||||
|
||||
<h2>调试技巧</h2>
|
||||
|
||||
<h3>查看 Agent 日志</h3>
|
||||
|
||||
<pre><code class="language-bash"># 启用详细日志
|
||||
DEBUG=ai-assistant:* pnpm server:dev</code></pre>
|
||||
|
||||
<h3>测试单个工具</h3>
|
||||
|
||||
<pre><code class="language-typescript">import { toolRegistry } from '@ai-assistant/core/tools';
|
||||
|
||||
// 获取工具
|
||||
const readFile = toolRegistry.get('read_file');
|
||||
|
||||
// 执行测试
|
||||
const result = await readFile.execute({
|
||||
path: '/path/to/file.ts'
|
||||
});
|
||||
|
||||
console.log(result);</code></pre>
|
||||
|
||||
<h3>VS Code 调试配置</h3>
|
||||
|
||||
<pre><code class="language-json">// .vscode/launch.json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Server",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "bun",
|
||||
"runtimeArgs": ["run"],
|
||||
"program": "${workspaceFolder}/packages/server/src/bin/server.ts",
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
|
||||
<h2>常见问题</h2>
|
||||
|
||||
<h3>依赖安装失败</h3>
|
||||
|
||||
<pre><code class="language-bash"># 清理缓存重新安装
|
||||
rm -rf node_modules pnpm-lock.yaml
|
||||
pnpm store prune
|
||||
pnpm install</code></pre>
|
||||
|
||||
<h3>端口被占用</h3>
|
||||
|
||||
<pre><code class="language-bash"># 查找占用端口的进程
|
||||
lsof -i :3000
|
||||
|
||||
# 使用其他端口
|
||||
pnpm server:dev -- --port 3001</code></pre>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/deploy/configuration" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">配置管理</h3>
|
||||
<p class="text-sm text-gray-400">环境变量和配置文件</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/deploy/production" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">生产部署</h3>
|
||||
<p class="text-sm text-gray-400">Docker 和服务器部署</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,271 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="生产部署" description="AI Terminal Assistant 生产环境部署指南">
|
||||
<h1>生产部署</h1>
|
||||
|
||||
<p>
|
||||
本指南介绍如何将 AI Terminal Assistant 部署到生产环境。
|
||||
</p>
|
||||
|
||||
<h2>部署方式</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>Docker</strong> - 容器化部署(推荐)</li>
|
||||
<li><strong>直接部署</strong> - 服务器直接运行</li>
|
||||
<li><strong>Vercel/Cloudflare</strong> - Web 前端静态部署</li>
|
||||
</ul>
|
||||
|
||||
<h2>Docker 部署</h2>
|
||||
|
||||
<h3>Dockerfile</h3>
|
||||
|
||||
<pre><code class="language-dockerfile"># Dockerfile
|
||||
FROM oven/bun:1 as builder
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
||||
COPY packages ./packages
|
||||
|
||||
RUN npm install -g pnpm
|
||||
RUN pnpm install --frozen-lockfile
|
||||
RUN pnpm build
|
||||
|
||||
# 生产镜像
|
||||
FROM oven/bun:1-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/packages/server/dist ./dist
|
||||
COPY --from=builder /app/packages/core/dist ./core
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["bun", "run", "dist/bin/server.js"]</code></pre>
|
||||
|
||||
<h3>Docker Compose</h3>
|
||||
|
||||
<pre><code class="language-yaml"># docker-compose.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
ai-assistant:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||
- HOST=0.0.0.0
|
||||
- PORT=3000
|
||||
- AUTH_ENABLED=true
|
||||
- AUTH_TOKEN=${AUTH_TOKEN}
|
||||
volumes:
|
||||
- ./projects:/projects:rw
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.web
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- ai-assistant
|
||||
restart: unless-stopped</code></pre>
|
||||
|
||||
<h3>运行容器</h3>
|
||||
|
||||
<pre><code class="language-bash"># 构建镜像
|
||||
docker build -t ai-assistant .
|
||||
|
||||
# 运行容器
|
||||
docker run -d \
|
||||
--name ai-assistant \
|
||||
-p 3000:3000 \
|
||||
-e ANTHROPIC_API_KEY=your-key \
|
||||
-e AUTH_TOKEN=your-secret-token \
|
||||
-v /path/to/projects:/projects \
|
||||
ai-assistant
|
||||
|
||||
# 使用 Docker Compose
|
||||
docker-compose up -d</code></pre>
|
||||
|
||||
<h2>直接部署</h2>
|
||||
|
||||
<h3>系统服务 (systemd)</h3>
|
||||
|
||||
<pre><code class="language-ini"># /etc/systemd/system/ai-assistant.service
|
||||
[Unit]
|
||||
Description=AI Terminal Assistant
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=ai-assistant
|
||||
WorkingDirectory=/opt/ai-assistant
|
||||
ExecStart=/usr/local/bin/bun run dist/bin/server.js
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=3000
|
||||
Environment=HOST=0.0.0.0
|
||||
EnvironmentFile=/opt/ai-assistant/.env
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target</code></pre>
|
||||
|
||||
<pre><code class="language-bash"># 启用并启动服务
|
||||
sudo systemctl enable ai-assistant
|
||||
sudo systemctl start ai-assistant
|
||||
|
||||
# 查看状态
|
||||
sudo systemctl status ai-assistant
|
||||
|
||||
# 查看日志
|
||||
sudo journalctl -u ai-assistant -f</code></pre>
|
||||
|
||||
<h3>PM2 部署</h3>
|
||||
|
||||
<pre><code class="language-javascript">// ecosystem.config.js
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'ai-assistant',
|
||||
script: 'dist/bin/server.js',
|
||||
interpreter: 'bun',
|
||||
cwd: '/opt/ai-assistant',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3000,
|
||||
HOST: '0.0.0.0'
|
||||
},
|
||||
env_file: '.env',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G'
|
||||
}]
|
||||
};</code></pre>
|
||||
|
||||
<pre><code class="language-bash"># 启动
|
||||
pm2 start ecosystem.config.js
|
||||
|
||||
# 保存并开机启动
|
||||
pm2 save
|
||||
pm2 startup</code></pre>
|
||||
|
||||
<h2>Nginx 反向代理</h2>
|
||||
|
||||
<pre><code class="language-nginx"># /etc/nginx/sites-available/ai-assistant
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
# 重定向到 HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
||||
|
||||
# API 和 WebSocket
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket 超时
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
}
|
||||
|
||||
# 静态文件
|
||||
location / {
|
||||
root /var/www/ai-assistant;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>Web 前端静态部署</h2>
|
||||
|
||||
<h3>构建静态文件</h3>
|
||||
|
||||
<pre><code class="language-bash"># 设置 API 地址
|
||||
echo "VITE_API_URL=https://api.your-domain.com" > packages/web/.env.production
|
||||
|
||||
# 构建
|
||||
cd packages/web && pnpm build
|
||||
|
||||
# 输出在 dist/ 目录</code></pre>
|
||||
|
||||
<h3>Vercel 部署</h3>
|
||||
|
||||
<pre><code class="language-json">// vercel.json
|
||||
{
|
||||
"buildCommand": "cd packages/web && pnpm build",
|
||||
"outputDirectory": "packages/web/dist",
|
||||
"framework": "vite",
|
||||
"rewrites": [
|
||||
{ "source": "/(.*)", "destination": "/index.html" }
|
||||
]
|
||||
}</code></pre>
|
||||
|
||||
<h2>安全配置</h2>
|
||||
|
||||
<h3>HTTPS 证书</h3>
|
||||
|
||||
<pre><code class="language-bash"># 使用 Let's Encrypt
|
||||
sudo certbot --nginx -d your-domain.com</code></pre>
|
||||
|
||||
<h3>防火墙</h3>
|
||||
|
||||
<pre><code class="language-bash"># UFW
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw enable</code></pre>
|
||||
|
||||
<h3>认证配置</h3>
|
||||
|
||||
<pre><code class="language-bash"># 生成强 Token
|
||||
openssl rand -hex 32
|
||||
|
||||
# 设置环境变量
|
||||
export AUTH_TOKEN=your-generated-token</code></pre>
|
||||
|
||||
<h2>监控与日志</h2>
|
||||
|
||||
<pre><code class="language-bash"># 健康检查
|
||||
curl https://your-domain.com/health
|
||||
|
||||
# 查看容器日志
|
||||
docker logs -f ai-assistant
|
||||
|
||||
# 查看系统服务日志
|
||||
journalctl -u ai-assistant -f</code></pre>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/deploy/configuration" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">配置管理</h3>
|
||||
<p class="text-sm text-gray-400">环境变量和配置文件</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/api/rest" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">REST API</h3>
|
||||
<p class="text-sm text-gray-400">API 端点文档</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,300 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="自定义工具" description="为 AI Terminal Assistant 开发自定义工具">
|
||||
<h1>自定义工具</h1>
|
||||
|
||||
<p>
|
||||
工具是 AI Agent 与外部世界交互的方式。你可以创建自定义工具来扩展 AI 的能力,
|
||||
例如访问数据库、调用 API、执行特定业务逻辑等。
|
||||
</p>
|
||||
|
||||
<h2>工具结构</h2>
|
||||
|
||||
<p>每个工具需要定义以下属性:</p>
|
||||
|
||||
<pre><code class="language-typescript">interface Tool {
|
||||
name: string; // 工具名称(唯一标识)
|
||||
description: string; // 工具描述(AI 用于判断何时使用)
|
||||
parameters: ZodSchema; // 参数 Schema(Zod 定义)
|
||||
execute: (params: T, context: ToolContext) => Promise<ToolResult>;
|
||||
}</code></pre>
|
||||
|
||||
<h2>基本示例</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { z } from 'zod';
|
||||
import { toolRegistry } from '@ai-assistant/core';
|
||||
|
||||
// 定义参数 Schema
|
||||
const weatherParams = z.object({
|
||||
city: z.string().describe('城市名称'),
|
||||
units: z.enum(['celsius', 'fahrenheit']).default('celsius')
|
||||
});
|
||||
|
||||
// 注册工具
|
||||
toolRegistry.register({
|
||||
name: 'get_weather',
|
||||
description: '获取指定城市的天气信息',
|
||||
parameters: weatherParams,
|
||||
execute: async (params) => {
|
||||
const { city, units } = params;
|
||||
|
||||
// 调用天气 API
|
||||
const response = await fetch(
|
||||
`https://api.weather.com/v1/current?city=${city}&units=${units}`
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: `${city} 当前温度: ${data.temperature}°${units === 'celsius' ? 'C' : 'F'}`
|
||||
};
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>工具上下文</h2>
|
||||
|
||||
<p><code>ToolContext</code> 提供执行环境信息:</p>
|
||||
|
||||
<pre><code class="language-typescript">interface ToolContext {
|
||||
workDir: string; // 当前工作目录
|
||||
sessionId: string; // 当前会话 ID
|
||||
abortSignal?: AbortSignal; // 取消信号
|
||||
agent: Agent; // Agent 实例
|
||||
}
|
||||
|
||||
toolRegistry.register({
|
||||
name: 'list_project_files',
|
||||
description: '列出项目中的文件',
|
||||
parameters: z.object({
|
||||
pattern: z.string().optional()
|
||||
}),
|
||||
execute: async (params, context) => {
|
||||
const { workDir } = context;
|
||||
// 使用 workDir 作为基础路径
|
||||
const files = await glob(params.pattern || '**/*', {
|
||||
cwd: workDir,
|
||||
ignore: ['node_modules/**']
|
||||
});
|
||||
return { success: true, result: files.join('\n') };
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>错误处理</h2>
|
||||
|
||||
<pre><code class="language-typescript">toolRegistry.register({
|
||||
name: 'read_database',
|
||||
description: '从数据库读取数据',
|
||||
parameters: z.object({
|
||||
query: z.string()
|
||||
}),
|
||||
execute: async (params) => {
|
||||
try {
|
||||
const result = await db.query(params.query);
|
||||
return {
|
||||
success: true,
|
||||
result: JSON.stringify(result, null, 2)
|
||||
};
|
||||
} catch (error) {
|
||||
// 返回错误信息,AI 可以根据错误调整
|
||||
return {
|
||||
success: false,
|
||||
error: `数据库查询失败: ${error.message}`
|
||||
};
|
||||
}
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>异步和流式结果</h2>
|
||||
|
||||
<pre><code class="language-typescript">toolRegistry.register({
|
||||
name: 'long_running_task',
|
||||
description: '执行长时间运行的任务',
|
||||
parameters: z.object({
|
||||
taskId: z.string()
|
||||
}),
|
||||
execute: async (params, context) => {
|
||||
const { abortSignal } = context;
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
// 检查是否被取消
|
||||
if (abortSignal?.aborted) {
|
||||
return { success: false, error: '任务已取消' };
|
||||
}
|
||||
|
||||
await processStep(i);
|
||||
}
|
||||
|
||||
return { success: true, result: '任务完成' };
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>权限控制</h2>
|
||||
|
||||
<p>可以为工具设置权限级别:</p>
|
||||
|
||||
<pre><code class="language-typescript">toolRegistry.register({
|
||||
name: 'delete_file',
|
||||
description: '删除文件',
|
||||
parameters: z.object({
|
||||
path: z.string()
|
||||
}),
|
||||
// 需要用户确认
|
||||
requiresConfirmation: true,
|
||||
// 权限级别
|
||||
permissionLevel: 'dangerous',
|
||||
execute: async (params, context) => {
|
||||
await fs.unlink(path.join(context.workDir, params.path));
|
||||
return { success: true, result: `已删除: ${params.path}` };
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>工具分组</h2>
|
||||
|
||||
<pre><code class="language-typescript">// 创建工具组
|
||||
const databaseTools = createToolGroup('database', {
|
||||
description: '数据库操作工具',
|
||||
tools: [
|
||||
{
|
||||
name: 'query',
|
||||
description: '执行 SQL 查询',
|
||||
parameters: z.object({ sql: z.string() }),
|
||||
execute: async (params) => { /* ... */ }
|
||||
},
|
||||
{
|
||||
name: 'insert',
|
||||
description: '插入数据',
|
||||
parameters: z.object({
|
||||
table: z.string(),
|
||||
data: z.record(z.any())
|
||||
}),
|
||||
execute: async (params) => { /* ... */ }
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 注册工具组
|
||||
toolRegistry.registerGroup(databaseTools);
|
||||
|
||||
// AI 会看到: database_query, database_insert</code></pre>
|
||||
|
||||
<h2>完整示例:GitHub 工具</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { z } from 'zod';
|
||||
import { toolRegistry } from '@ai-assistant/core';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN
|
||||
});
|
||||
|
||||
// 创建 Issue
|
||||
toolRegistry.register({
|
||||
name: 'github_create_issue',
|
||||
description: '在 GitHub 仓库中创建 Issue',
|
||||
parameters: z.object({
|
||||
owner: z.string().describe('仓库所有者'),
|
||||
repo: z.string().describe('仓库名称'),
|
||||
title: z.string().describe('Issue 标题'),
|
||||
body: z.string().describe('Issue 内容'),
|
||||
labels: z.array(z.string()).optional().describe('标签列表')
|
||||
}),
|
||||
execute: async (params) => {
|
||||
const { owner, repo, title, body, labels } = params;
|
||||
|
||||
const { data } = await octokit.issues.create({
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
labels
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: `Issue 创建成功: ${data.html_url}`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 列出 PR
|
||||
toolRegistry.register({
|
||||
name: 'github_list_prs',
|
||||
description: '列出 GitHub 仓库的 Pull Requests',
|
||||
parameters: z.object({
|
||||
owner: z.string(),
|
||||
repo: z.string(),
|
||||
state: z.enum(['open', 'closed', 'all']).default('open')
|
||||
}),
|
||||
execute: async (params) => {
|
||||
const { owner, repo, state } = params;
|
||||
|
||||
const { data } = await octokit.pulls.list({
|
||||
owner,
|
||||
repo,
|
||||
state
|
||||
});
|
||||
|
||||
const prs = data.map(pr => `#${pr.number}: ${pr.title}`).join('\n');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: prs || '没有找到 PR'
|
||||
};
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>测试工具</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { describe, it, expect } from 'vitest';
|
||||
import { toolRegistry } from '@ai-assistant/core';
|
||||
|
||||
describe('get_weather tool', () => {
|
||||
it('should return weather for valid city', async () => {
|
||||
const tool = toolRegistry.get('get_weather');
|
||||
const result = await tool.execute(
|
||||
{ city: 'Beijing', units: 'celsius' },
|
||||
{ workDir: '/tmp', sessionId: 'test' }
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.result).toContain('Beijing');
|
||||
});
|
||||
|
||||
it('should handle invalid city', async () => {
|
||||
const tool = toolRegistry.get('get_weather');
|
||||
const result = await tool.execute(
|
||||
{ city: 'InvalidCity123', units: 'celsius' },
|
||||
{ workDir: '/tmp', sessionId: 'test' }
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});</code></pre>
|
||||
|
||||
<h2>最佳实践</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>描述清晰</strong> - AI 根据描述判断何时使用工具</li>
|
||||
<li><strong>参数验证</strong> - 使用 Zod 进行严格的参数验证</li>
|
||||
<li><strong>错误处理</strong> - 返回有意义的错误信息</li>
|
||||
<li><strong>幂等性</strong> - 尽可能让工具可重复执行</li>
|
||||
<li><strong>权限控制</strong> - 危险操作需要确认</li>
|
||||
<li><strong>超时处理</strong> - 长时间操作支持取消</li>
|
||||
</ul>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/core/tools" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">工具系统</h3>
|
||||
<p class="text-sm text-gray-400">内置工具详解</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/extend/mcp-server" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">MCP 服务器</h3>
|
||||
<p class="text-sm text-gray-400">通过 MCP 协议扩展工具</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,364 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="Hooks 系统" description="AI Terminal Assistant Hooks 扩展机制">
|
||||
<h1>Hooks 系统</h1>
|
||||
|
||||
<p>
|
||||
Hooks 允许你在 Agent 执行的关键节点注入自定义逻辑,
|
||||
用于日志记录、监控、修改行为等场景。
|
||||
</p>
|
||||
|
||||
<h2>Hook 类型</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hook</th>
|
||||
<th>触发时机</th>
|
||||
<th>用途</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>beforeChat</code></td>
|
||||
<td>发送消息前</td>
|
||||
<td>修改消息、验证输入</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>afterChat</code></td>
|
||||
<td>收到响应后</td>
|
||||
<td>日志记录、响应处理</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>beforeToolCall</code></td>
|
||||
<td>工具调用前</td>
|
||||
<td>参数验证、权限检查</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>afterToolCall</code></td>
|
||||
<td>工具执行后</td>
|
||||
<td>结果处理、审计日志</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>onError</code></td>
|
||||
<td>发生错误时</td>
|
||||
<td>错误处理、告警</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>onStream</code></td>
|
||||
<td>流式数据块</td>
|
||||
<td>实时处理、进度显示</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>注册 Hook</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { Agent } from '@ai-assistant/core';
|
||||
|
||||
const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: {
|
||||
beforeChat: async (context) => {
|
||||
console.log('即将发送消息:', context.message);
|
||||
// 可以修改消息
|
||||
return {
|
||||
...context,
|
||||
message: context.message + '\n请用简洁的方式回答。'
|
||||
};
|
||||
},
|
||||
|
||||
afterChat: async (context) => {
|
||||
console.log('收到响应:', context.response);
|
||||
// 记录到数据库
|
||||
await db.logs.insert({
|
||||
sessionId: context.sessionId,
|
||||
message: context.message,
|
||||
response: context.response,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>工具 Hooks</h2>
|
||||
|
||||
<pre><code class="language-typescript">const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: {
|
||||
beforeToolCall: async (context) => {
|
||||
const { tool, params } = context;
|
||||
|
||||
// 权限检查
|
||||
if (tool.name === 'execute_bash' && !isAllowedCommand(params.command)) {
|
||||
throw new Error('命令不被允许');
|
||||
}
|
||||
|
||||
// 记录工具调用
|
||||
console.log(`调用工具: ${tool.name}`, params);
|
||||
|
||||
return context;
|
||||
},
|
||||
|
||||
afterToolCall: async (context) => {
|
||||
const { tool, params, result, duration } = context;
|
||||
|
||||
// 审计日志
|
||||
await auditLog.record({
|
||||
tool: tool.name,
|
||||
params,
|
||||
result: result.success ? 'success' : 'failure',
|
||||
duration,
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
// 可以修改结果
|
||||
if (tool.name === 'read_file' && result.success) {
|
||||
// 过滤敏感信息
|
||||
return {
|
||||
...context,
|
||||
result: {
|
||||
...result,
|
||||
result: filterSensitiveData(result.result)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>错误处理 Hook</h2>
|
||||
|
||||
<pre><code class="language-typescript">const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: {
|
||||
onError: async (context) => {
|
||||
const { error, phase, tool } = context;
|
||||
|
||||
// 发送告警
|
||||
await alertService.send({
|
||||
level: 'error',
|
||||
message: error.message,
|
||||
context: {
|
||||
phase, // 'chat' | 'tool' | 'stream'
|
||||
tool: tool?.name,
|
||||
sessionId: context.sessionId
|
||||
}
|
||||
});
|
||||
|
||||
// 可以选择恢复或重新抛出
|
||||
if (error.code === 'RATE_LIMIT') {
|
||||
// 等待后重试
|
||||
await sleep(5000);
|
||||
return { retry: true };
|
||||
}
|
||||
|
||||
// 重新抛出错误
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>流式处理 Hook</h2>
|
||||
|
||||
<pre><code class="language-typescript">const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: {
|
||||
onStream: async (context) => {
|
||||
const { chunk, type, accumulated } = context;
|
||||
|
||||
if (type === 'text') {
|
||||
// 实时显示
|
||||
process.stdout.write(chunk);
|
||||
} else if (type === 'tool_call') {
|
||||
// 显示工具调用进度
|
||||
console.log(`\n[调用工具: ${chunk.name}]`);
|
||||
}
|
||||
|
||||
// 进度回调
|
||||
if (context.onProgress) {
|
||||
context.onProgress({
|
||||
type,
|
||||
content: chunk,
|
||||
total: accumulated.length
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});</code></pre>
|
||||
|
||||
<h2>Hook 链</h2>
|
||||
|
||||
<p>可以注册多个同类型的 Hook,按顺序执行:</p>
|
||||
|
||||
<pre><code class="language-typescript">import { createHookChain } from '@ai-assistant/core';
|
||||
|
||||
const hooks = createHookChain();
|
||||
|
||||
// 添加日志 Hook
|
||||
hooks.add('beforeChat', async (ctx) => {
|
||||
console.log('[Logger] 消息:', ctx.message);
|
||||
return ctx;
|
||||
});
|
||||
|
||||
// 添加验证 Hook
|
||||
hooks.add('beforeChat', async (ctx) => {
|
||||
if (ctx.message.length > 10000) {
|
||||
throw new Error('消息过长');
|
||||
}
|
||||
return ctx;
|
||||
});
|
||||
|
||||
// 添加修改 Hook
|
||||
hooks.add('beforeChat', async (ctx) => {
|
||||
return {
|
||||
...ctx,
|
||||
message: ctx.message.trim()
|
||||
};
|
||||
});
|
||||
|
||||
const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: hooks.toObject()
|
||||
});</code></pre>
|
||||
|
||||
<h2>条件 Hook</h2>
|
||||
|
||||
<pre><code class="language-typescript">import { createConditionalHook } from '@ai-assistant/core';
|
||||
|
||||
// 只在特定条件下执行的 Hook
|
||||
const debugHook = createConditionalHook(
|
||||
// 条件函数
|
||||
(ctx) => process.env.DEBUG === 'true',
|
||||
// Hook 实现
|
||||
{
|
||||
beforeToolCall: async (ctx) => {
|
||||
console.log('[DEBUG] 工具调用:', ctx.tool.name, ctx.params);
|
||||
return ctx;
|
||||
},
|
||||
afterToolCall: async (ctx) => {
|
||||
console.log('[DEBUG] 工具结果:', ctx.result);
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const agent = new Agent({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
hooks: debugHook
|
||||
});</code></pre>
|
||||
|
||||
<h2>内置 Hooks</h2>
|
||||
|
||||
<h3>日志 Hook</h3>
|
||||
|
||||
<pre><code class="language-typescript">import { createLoggerHook } from '@ai-assistant/core/hooks';
|
||||
|
||||
const loggerHook = createLoggerHook({
|
||||
level: 'info',
|
||||
output: 'file',
|
||||
path: './logs/agent.log',
|
||||
format: 'json'
|
||||
});
|
||||
|
||||
const agent = new Agent({
|
||||
hooks: loggerHook
|
||||
});</code></pre>
|
||||
|
||||
<h3>速率限制 Hook</h3>
|
||||
|
||||
<pre><code class="language-typescript">import { createRateLimitHook } from '@ai-assistant/core/hooks';
|
||||
|
||||
const rateLimitHook = createRateLimitHook({
|
||||
maxRequests: 10,
|
||||
windowMs: 60000, // 1 分钟
|
||||
onLimit: async () => {
|
||||
console.log('达到速率限制,等待中...');
|
||||
}
|
||||
});
|
||||
|
||||
const agent = new Agent({
|
||||
hooks: rateLimitHook
|
||||
});</code></pre>
|
||||
|
||||
<h3>缓存 Hook</h3>
|
||||
|
||||
<pre><code class="language-typescript">import { createCacheHook } from '@ai-assistant/core/hooks';
|
||||
|
||||
const cacheHook = createCacheHook({
|
||||
ttl: 3600, // 1 小时
|
||||
storage: 'redis',
|
||||
// 只缓存特定工具的结果
|
||||
tools: ['read_file', 'search_code']
|
||||
});
|
||||
|
||||
const agent = new Agent({
|
||||
hooks: cacheHook
|
||||
});</code></pre>
|
||||
|
||||
<h2>Hook 上下文类型</h2>
|
||||
|
||||
<pre><code class="language-typescript">interface BeforeChatContext {
|
||||
message: string | ContentBlock[];
|
||||
sessionId: string;
|
||||
history: Message[];
|
||||
}
|
||||
|
||||
interface AfterChatContext extends BeforeChatContext {
|
||||
response: string;
|
||||
toolCalls: ToolCall[];
|
||||
usage: TokenUsage;
|
||||
}
|
||||
|
||||
interface BeforeToolCallContext {
|
||||
tool: Tool;
|
||||
params: Record<string, any>;
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
interface AfterToolCallContext extends BeforeToolCallContext {
|
||||
result: ToolResult;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
interface ErrorContext {
|
||||
error: Error;
|
||||
phase: 'chat' | 'tool' | 'stream';
|
||||
tool?: Tool;
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
interface StreamContext {
|
||||
chunk: string | ToolCallChunk;
|
||||
type: 'text' | 'tool_call' | 'tool_result';
|
||||
accumulated: string;
|
||||
}</code></pre>
|
||||
|
||||
<h2>最佳实践</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>保持轻量</strong> - Hook 应该快速执行,避免阻塞</li>
|
||||
<li><strong>错误处理</strong> - Hook 内的错误应该被妥善处理</li>
|
||||
<li><strong>不可变性</strong> - 返回新对象而非修改原对象</li>
|
||||
<li><strong>类型安全</strong> - 使用 TypeScript 确保类型正确</li>
|
||||
<li><strong>单一职责</strong> - 每个 Hook 只做一件事</li>
|
||||
</ul>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/core/agent" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">Agent 系统</h3>
|
||||
<p class="text-sm text-gray-400">了解 Agent 核心架构</p>
|
||||
</a>
|
||||
|
||||
<a href="/docs/extend/custom-tools" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">自定义工具</h3>
|
||||
<p class="text-sm text-gray-400">创建自定义工具</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
@@ -0,0 +1,423 @@
|
||||
---
|
||||
import DocsLayout from '../../../layouts/DocsLayout.astro';
|
||||
---
|
||||
|
||||
<DocsLayout title="MCP 服务器开发" description="开发自定义 Model Context Protocol 服务器">
|
||||
<h1>MCP 服务器开发</h1>
|
||||
|
||||
<p>
|
||||
Model Context Protocol (MCP) 是一个开放协议,允许你创建服务器来扩展 AI 的能力。
|
||||
通过开发 MCP 服务器,可以让 AI 访问自定义数据源、执行特定操作、使用预定义提示等。
|
||||
</p>
|
||||
|
||||
<h2>MCP 服务器能力</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>Tools (工具)</strong> - 可供 AI 调用的函数</li>
|
||||
<li><strong>Resources (资源)</strong> - 可读取的数据源</li>
|
||||
<li><strong>Prompts (提示)</strong> - 预定义的提示模板</li>
|
||||
</ul>
|
||||
|
||||
<h2>快速开始</h2>
|
||||
|
||||
<h3>项目初始化</h3>
|
||||
|
||||
<pre><code class="language-bash"># 创建项目目录
|
||||
mkdir my-mcp-server && cd my-mcp-server
|
||||
|
||||
# 初始化 npm 项目
|
||||
npm init -y
|
||||
|
||||
# 安装依赖
|
||||
npm install @modelcontextprotocol/sdk zod
|
||||
|
||||
# 安装开发依赖
|
||||
npm install -D typescript @types/node tsx</code></pre>
|
||||
|
||||
<h3>TypeScript 配置</h3>
|
||||
|
||||
<pre><code class="language-json">// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}</code></pre>
|
||||
|
||||
<h2>基本服务器结构</h2>
|
||||
|
||||
<pre><code class="language-typescript">// src/index.ts
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
// 创建服务器实例
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'my-mcp-server',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
resources: {},
|
||||
prompts: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// 列出可用工具
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: 'hello',
|
||||
description: '返回问候语',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: '要问候的名字',
|
||||
},
|
||||
},
|
||||
required: ['name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 处理工具调用
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (request.params.name === 'hello') {
|
||||
const name = request.params.arguments?.name as string;
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `你好,${name}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
throw new Error(`未知工具: ${request.params.name}`);
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('MCP 服务器已启动');
|
||||
}
|
||||
|
||||
main().catch(console.error);</code></pre>
|
||||
|
||||
<h2>添加资源</h2>
|
||||
|
||||
<pre><code class="language-typescript">import {
|
||||
ListResourcesRequestSchema,
|
||||
ReadResourceRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
// 列出资源
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
||||
resources: [
|
||||
{
|
||||
uri: 'config://app/settings',
|
||||
name: '应用配置',
|
||||
description: '当前应用配置',
|
||||
mimeType: 'application/json',
|
||||
},
|
||||
{
|
||||
uri: 'db://users/list',
|
||||
name: '用户列表',
|
||||
description: '所有用户数据',
|
||||
mimeType: 'application/json',
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 读取资源
|
||||
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
||||
const { uri } = request.params;
|
||||
|
||||
if (uri === 'config://app/settings') {
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri,
|
||||
mimeType: 'application/json',
|
||||
text: JSON.stringify({
|
||||
theme: 'dark',
|
||||
language: 'zh-CN',
|
||||
version: '1.0.0',
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (uri === 'db://users/list') {
|
||||
const users = await db.users.findAll();
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri,
|
||||
mimeType: 'application/json',
|
||||
text: JSON.stringify(users),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`未知资源: ${uri}`);
|
||||
});</code></pre>
|
||||
|
||||
<h2>添加提示模板</h2>
|
||||
|
||||
<pre><code class="language-typescript">import {
|
||||
ListPromptsRequestSchema,
|
||||
GetPromptRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
// 列出提示
|
||||
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
||||
prompts: [
|
||||
{
|
||||
name: 'code-review',
|
||||
description: '代码审查提示',
|
||||
arguments: [
|
||||
{
|
||||
name: 'file',
|
||||
description: '要审查的文件路径',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'focus',
|
||||
description: '审查重点(安全/性能/可读性)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 获取提示
|
||||
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
if (name === 'code-review') {
|
||||
const file = args?.file as string;
|
||||
const focus = (args?.focus as string) || '整体质量';
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `请审查文件 ${file},重点关注: ${focus}
|
||||
|
||||
请按以下格式提供审查意见:
|
||||
1. 总体评价
|
||||
2. 优点
|
||||
3. 需要改进的地方
|
||||
4. 具体建议`,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`未知提示: ${name}`);
|
||||
});</code></pre>
|
||||
|
||||
<h2>完整示例:数据库 MCP 服务器</h2>
|
||||
|
||||
<pre><code class="language-typescript">// src/db-server.ts
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
const db = new Database('data.db');
|
||||
|
||||
const server = new Server(
|
||||
{ name: 'sqlite-server', version: '1.0.0' },
|
||||
{ capabilities: { tools: {} } }
|
||||
);
|
||||
|
||||
// 工具列表
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: 'query',
|
||||
description: '执行 SQL 查询',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sql: { type: 'string', description: 'SQL 查询语句' },
|
||||
},
|
||||
required: ['sql'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'execute',
|
||||
description: '执行 SQL 命令(INSERT/UPDATE/DELETE)',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sql: { type: 'string', description: 'SQL 命令' },
|
||||
params: {
|
||||
type: 'array',
|
||||
items: {},
|
||||
description: '参数列表',
|
||||
},
|
||||
},
|
||||
required: ['sql'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'schema',
|
||||
description: '获取数据库表结构',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
table: { type: 'string', description: '表名(可选)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 工具调用
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
switch (name) {
|
||||
case 'query': {
|
||||
const sql = args?.sql as string;
|
||||
// 安全检查:只允许 SELECT
|
||||
if (!sql.trim().toLowerCase().startsWith('select')) {
|
||||
throw new Error('query 只允许 SELECT 语句');
|
||||
}
|
||||
const rows = db.prepare(sql).all();
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(rows, null, 2) }],
|
||||
};
|
||||
}
|
||||
|
||||
case 'execute': {
|
||||
const sql = args?.sql as string;
|
||||
const params = (args?.params as any[]) || [];
|
||||
const result = db.prepare(sql).run(...params);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `执行成功: 影响 ${result.changes} 行`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'schema': {
|
||||
const table = args?.table as string | undefined;
|
||||
let sql = `SELECT name, sql FROM sqlite_master WHERE type='table'`;
|
||||
if (table) {
|
||||
sql += ` AND name = '${table}'`;
|
||||
}
|
||||
const schemas = db.prepare(sql).all();
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(schemas, null, 2) }],
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`未知工具: ${name}`);
|
||||
}
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
server.connect(transport);</code></pre>
|
||||
|
||||
<h2>在 AI Assistant 中使用</h2>
|
||||
|
||||
<pre><code class="language-json">// mcp.json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-server": {
|
||||
"command": "node",
|
||||
"args": ["./dist/index.js"],
|
||||
"env": {}
|
||||
},
|
||||
"sqlite": {
|
||||
"command": "node",
|
||||
"args": ["./my-mcp-server/dist/db-server.js"],
|
||||
"env": {
|
||||
"DB_PATH": "/path/to/database.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>调试与测试</h2>
|
||||
|
||||
<pre><code class="language-bash"># 直接运行测试
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js
|
||||
|
||||
# 使用 MCP Inspector
|
||||
npx @modelcontextprotocol/inspector node dist/index.js</code></pre>
|
||||
|
||||
<h2>发布服务器</h2>
|
||||
|
||||
<pre><code class="language-json">// package.json
|
||||
{
|
||||
"name": "my-mcp-server",
|
||||
"version": "1.0.0",
|
||||
"bin": {
|
||||
"my-mcp-server": "./dist/index.js"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepublishOnly": "npm run build"
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<pre><code class="language-bash"># 发布到 npm
|
||||
npm publish
|
||||
|
||||
# 用户可以这样使用
|
||||
npx my-mcp-server</code></pre>
|
||||
|
||||
<h2>最佳实践</h2>
|
||||
|
||||
<ul>
|
||||
<li><strong>输入验证</strong> - 始终验证用户输入</li>
|
||||
<li><strong>错误处理</strong> - 返回有意义的错误信息</li>
|
||||
<li><strong>安全性</strong> - 防止 SQL 注入等攻击</li>
|
||||
<li><strong>文档</strong> - 为工具和资源提供清晰描述</li>
|
||||
<li><strong>版本管理</strong> - 遵循语义化版本</li>
|
||||
</ul>
|
||||
|
||||
<h2>相关文档</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 not-prose mt-6">
|
||||
<a href="/docs/core/mcp" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">MCP 集成</h3>
|
||||
<p class="text-sm text-gray-400">了解 MCP 客户端集成</p>
|
||||
</a>
|
||||
|
||||
<a href="https://modelcontextprotocol.io" target="_blank" rel="noopener" class="block rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
|
||||
<h3 class="mb-2 text-lg font-semibold text-white">MCP 官方文档 ↗</h3>
|
||||
<p class="text-sm text-gray-400">Model Context Protocol 规范</p>
|
||||
</a>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
Reference in New Issue
Block a user