场景:你需要一个本地文件助手
假设这个需求:
我有一个文件夹,里面有几百个 Markdown 文档。我想问 AI:"帮我找出所有关于 TypeScript 的笔记",或者"总结一下上周的工作日志"。
Web 方案的问题:
- 上传文件到服务器?隐私风险。
- 浏览器读文件?权限受限。
桌面 App 的优势:
- 直接访问本地文件系统
- 无需上传,数据不出本地
- 可以调用系统 API
这就是桌面 AI Agent 的典型场景。今天我们用 Tauri + Vercel AI SDK 来实现它。
项目目标
功能需求
- 选择本地文件夹
- 读取文件夹内的文本文件(.md / .txt)
- 将内容发送给 AI
- AI 根据用户问题回答
技术栈
- 前端:React + TypeScript
- 桌面框架:Tauri
- AI 集成:Vercel AI SDK
- LLM:Claude Sonnet 4
架构设计
用户界面(React)
↓
Tauri IPC
↓
Rust 后端
├─ 读取文件系统
└─ 调用 AI API
↓
Claude API
↓
流式响应返回前端
为什么这样设计?
- Tauri IPC: 前端无法直接访问文件系统,通过 Rust 后端桥接
- Rust 后端: 安全、高效处理文件 I/O
- Vercel AI SDK: 简化 AI 调用,自动处理流式响应
Step 1: 创建 Tauri 项目
1.1 初始化项目
# 安装 Tauri CLI
npm install -g @tauri-apps/cli
# 创建项目
npm create tauri-app@latest
# 选择:
# - Template: React + TypeScript
# - Name: file-agent1.2 项目结构
file-agent/
├── src/ # React 前端
│ ├── App.tsx
│ └── main.tsx
├── src-tauri/ # Rust 后端
│ ├── src/
│ │ └── main.rs # 核心逻辑
│ └── Cargo.toml
└── package.json
Step 2: 实现文件读取(Rust 后端)
2.1 添加依赖
编辑 src-tauri/Cargo.toml:
[dependencies]
tauri = { version = '1.5', features = ['dialog-open', 'fs-read-dir'] }
serde = { version = '1.0', features = ['derive'] }
serde_json = '1.0'2.2 创建 Tauri Command
编辑 src-tauri/src/main.rs:
use std::fs;
use std::path::Path;
use tauri::command;
#[command]
fn read_folder(folder_path: String) -> Result<Vec<FileContent>, String> {
let mut files = Vec::new();
// 读取文件夹
let entries = fs::read_dir(&folder_path)
.map_err(|e| e.to_string())?;
for entry in entries {
let entry = entry.map_err(|e| e.to_string())?;
let path = entry.path();
// 只处理 .md 和 .txt 文件
if let Some(ext) = path.extension() {
if ext == 'md' || ext == 'txt' {
let content = fs::read_to_string(&path)
.map_err(|e| e.to_string())?;
files.push(FileContent {
path: path.to_str().unwrap().to_string(),
name: path.file_name().unwrap().to_str().unwrap().to_string(),
content,
});
}
}
}
Ok(files)
}
#[derive(serde::Serialize)]
struct FileContent {
path: String,
name: String,
content: String,
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![read_folder])
.run(tauri::generate_context!())
.expect('error while running tauri application');
}Step 3: 集成 AI SDK(前端)
3.1 安装 Vercel AI SDK
npm install ai
npm install @anthropic-ai/sdk3.2 创建 AI 助手组件
创建 src/FileAgent.tsx:
import { useState } from 'react';
import { useChat } from 'ai/react';
import { invoke } from '@tauri-apps/api/tauri';
interface FileContent {
path: string;
name: string;
content: string;
}
export function FileAgent() {
const [files, setFiles] = useState<FileContent[]>([]);
const [folderPath, setFolderPath] = useState('');
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat', // 本地 API 路由
body: {
files: files.map(f => ({
name: f.name,
content: f.content.slice(0, 1000) // 限制长度
}))
}
});
// 选择文件夹
const selectFolder = async () => {
const selected = await open({
directory: true,
multiple: false,
});
if (selected) {
setFolderPath(selected as string);
// 调用 Rust 后端读取文件
const fileList = await invoke<FileContent[]>('read_folder', {
folderPath: selected
});
setFiles(fileList);
}
};
return (
<div className='container'>
<h1>本地文件助手</h1>
{/* 文件夹选择 */}
<button onClick={selectFolder}>
选择文件夹
</button>
{folderPath && <p>已选择: {folderPath}</p>}
{/* 文件列表 */}
{files.length > 0 && (
<div>
<h3>找到 {files.length} 个文件</h3>
<ul>
{files.map(f => (
<li key={f.path}>{f.name}</li>
))}
</ul>
</div>
)}
{/* 聊天界面 */}
<div className='chat'>
{messages.map(m => (
<div key={m.id} className={`message ${m.role}`}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
</div>
{/* 输入框 */}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder='问我关于这些文件的任何问题...'
disabled={files.length === 0}
/>
<button type='submit'>发送</button>
</form>
</div>
);
}3.3 创建 API 路由
由于 Tauri 是桌面应用,我们需要一个简单的本地 HTTP 服务。
创建 src/api/chat.ts:
import Anthropic from "@anthropic-ai/sdk"
const anthropic = new Anthropic({
apiKey: import.meta.env.VITE_ANTHROPIC_API_KEY,
})
export async function POST(req: Request) {
const { messages, files } = await req.json()
// 构建上下文
const fileContext = files
.map((f: any) => `文件: ${f.name}\n${f.content}`)
.join("\n\n---\n\n")
const systemPrompt = `你是一个本地文件助手。用户有以下文件:\n\n${fileContext}\n\n请根据这些文件回答用户的问题。`
// 调用 Claude
const stream = await anthropic.messages.create({
model: "claude-sonnet-4",
max_tokens: 1024,
messages: [{ role: "system", content: systemPrompt }, ...messages],
stream: true,
})
// 返回流式响应
return new Response(
new ReadableStream({
async start(controller) {
for await (const chunk of stream) {
if (chunk.type === "content_block_delta") {
controller.enqueue(chunk.delta.text)
}
}
controller.close()
},
}),
)
}Step 4: 运行和测试
4.1 设置 API Key
创建 .env:
VITE_ANTHROPIC_API_KEY=sk-ant-xxx4.2 启动开发
# 启动 Tauri 开发模式
npm run tauri dev4.3 测试流程
- 选择文件夹 → 点击"选择文件夹"
- 查看文件列表 → 确认读取成功
- 提问 → "总结一下这些文件的主题"
- 查看回答 → AI 基于文件内容回复
Step 5: 打包发布
5.1 构建应用
npm run tauri build5.2 输出位置
src-tauri/target/release/bundle/
├── dmg/ # macOS
├── msi/ # Windows
└── deb/ # Linux
5.3 分发
- macOS: 双击
.dmg安装 - Windows: 运行
.msi安装 - Linux:
sudo dpkg -i xxx.deb
核心技术点解析
1. Tauri IPC 通信
// 前端调用 Rust
const result = await invoke<T>('command_name', { param: value });
// Rust 定义 Command
#[command]
fn command_name(param: String) -> Result<T, String> {
// ...
}关键:前后端通过 invoke 通信,类型安全。
2. 文件系统访问
Web 限制:
- 浏览器只能访问用户主动选择的文件
- 无法遍历文件夹
Tauri 解决:
- Rust 后端直接调用系统 API
fs::read_dir遍历文件夹- 权限控制在
tauri.conf.json
3. AI 流式响应
const { messages, handleSubmit } = useChat({
api: "/api/chat",
onFinish: (message) => {
console.log("AI 回复完成", message)
},
})Vercel AI SDK 自动处理:
- 流式数据接收
- UI 实时更新
- 错误处理
扩展方向
1. 增强文件处理
- 支持 PDF、Word
- 图片 OCR
- 代码语法高亮
2. 本地向量数据库
npm install chromadb存储文件向量,实现语义搜索。
3. 多模型支持
const model = userChoice === "claude" ? "claude-sonnet-4" : "gpt-4"4. 历史记录
// 存储对话历史到 SQLite
use rusqlite::Connection;
let conn = Connection::open('agent.db')?;
conn.execute(
'INSERT INTO chats (question, answer) VALUES (?1, ?2)',
params![question, answer],
)?;常见问题
Q1: 如何处理大文件?
A: 限制单文件大小,或分块读取:
const MAX_SIZE: usize = 1024 * 1024; // 1MB
if content.len() > MAX_SIZE {
return Err('文件过大'.to_string());
}Q2: 如何保护 API Key?
A: 使用环境变量 + 打包时注入:
// vite.config.ts
export default {
define: {
__API_KEY__: JSON.stringify(process.env.ANTHROPIC_API_KEY),
},
}Q3: 能否支持 Windows/Linux?
A: Tauri 跨平台,一次开发,三端运行。只需:
npm run tauri build -- --target universal-apple-darwin # macOS
npm run tauri build -- --target x86_64-pc-windows-msvc # Windows
npm run tauri build -- --target x86_64-unknown-linux-gnu # Linux行动清单
如果你要开始这个项目:
- 今天 - 搭建 Tauri 基础项目
- 明天 - 实现文件读取功能
- 后天 - 集成 AI SDK
- 第4天 - 完善 UI,打包测试
GitHub 模板:file-agent-template(待创建)
总结
这个项目展示了:
- ✅ Tauri 的文件系统访问能力
- ✅ Rust 与 TypeScript 的协作
- ✅ Vercel AI SDK 的简洁集成
- ✅ 桌面 AI Agent 的完整流程
关键收获:
- 桌面应用 = Web 技能 + 系统能力
- AI SDK 让集成变得简单
- Tauri 性能 > Electron,包体积 < 10MB
这是「AI 时代前端转型」系列的第 6 篇。下一篇我们聊聊转型期的生存策略。