Last Updated: 2026-01-11 Purpose: 汇总所有功能规划和实施方案,提供清晰的开发路线
- 聊天功能规划 -
docs/CHAT_FEATURE_PLAN.md - API 层优化 -
docs/API_LAYER_OPTIMIZATION.md - Letta Code 功能分析 -
docs/LETTA_CODE_FEATURES.md - Letta 后端分析 -
docs/LETTA_BACKEND_ANALYSIS.md - 远程开发方案对比 -
docs/REMOTE_DEV_COMPARISON.md - 本文档 - 总体路线图
构建一个移动优先的 Letta Web UI,支持:
- ✅ 移动端远程开发(iOS/Android)
- ✅ Agent 管理
- ✅ 聊天交互
- ✅ 工具调用批准
- ✅ 记忆管理
Framework: Flutter 3.38.5
Language: Dart 3.10.4
State Management: Riverpod 3.0.3
Navigation: go_router 16.x
Target:
- Mobile: iOS, Android (primary)
- Web: CanvasKit renderer (secondary)
API: Letta Backend (REST + SSE)┌─────────────────────────────────────────┐
│ Klui (Flutter) │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ 移动端 │ │ 桌面端 │ │
│ │ (iOS/Android) │ │ (Web/Desktop) │ │
│ └──────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ UI Layer (features/) │ │
│ │ - Agent screens │ │
│ │ - Provider screens │ │
│ │ - Chat screens │ │
│ │ - Settings screens │ │
│ └──────────────┬──────────────────┘ │
│ │ (Riverpod providers) │
│ ┌──────────────▼──────────────────┐ │
│ │ Provider Layer (core/providers/)│ │
│ │ - agent_providers.dart │ │
│ │ - provider_providers.dart │ │
│ │ - model_providers.dart │ │
│ │ - chat_providers.dart │ │
│ └──────────────┬──────────────────┘ │
│ │ (API helpers) │
│ ┌──────────────▼──────────────────┐ │
│ │ API Layer (core/utils/) │ │
│ │ - api_client.dart │ │
│ │ - api_helper.dart │ │
│ └──────────────┬──────────────────┘ │
│ │ HTTP/SSE │
└─────────────────┼─────────────────────┘
│
▼
┌─────────────────────┐
│ Letta Backend │
│ - Agent management │
│ - Memory system │
│ - Tool execution │
│ - SSE streaming │
└─────────────────────┘
关键原则 (来自 CLAUDE.md):
- ✅ UI MUST use
ref.watch(provider)orref.read(provider.future) - ✅ Providers MUST use
ApiHelper.parseList/parseSingle/parseEmpty - ✅ Providers MUST handle errors via
ApiException - ❌ UI MUST NOT import
api_client.dartdirectly - ❌ Providers MUST NOT contain UI logic
- 项目初始化
- Riverpod 状态管理
- go_router 路由配置
- API 封装 (ApiClient, ApiHelper)
- 基础模型 (Agent, Provider, LLMModel, EmbeddingModel)
- CRUD Providers (Agent, Provider, Model)
- 列表页面
- 详情页面
目标: 实现基本的聊天交互功能
详见: docs/CHAT_FEATURE_PLAN.md
// lib/core/models/chat_message.dart
enum MessageType {
user, // 用户消息
assistant, // Assistant 回复
toolCall, // 工具调用请求
toolReturn, // 工具执行结果
reasoning, // 推理过程(Claude Opus)
error, // 错误消息
}
class ChatMessage {
final String id;
final MessageType type;
final String content;
final Map<String, dynamic>? metadata;
// Tool call 特有字段
final String? toolName;
final Map<String, dynamic>? toolInput;
// Tool return 特有字段
final String? toolCallId;
final bool? isToolError;
}// lib/core/providers/chat_providers.dart
@riverpod
class ChatNotifier extends _$ChatNotifier {
Future<void> sendMessage(String agentId, String content) async {
// 1. 添加用户消息到列表
// 2. 调用 Letta API 发送消息
// 3. 处理 SSE 流式响应
// 4. 更新消息状态
}
Stream<ChatMessage> streamMessages(String agentId, String message) async* {
// SSE 流式处理
}
Future<void> approveToolCall(String runId, String toolCallId, Map<String, dynamic> output) async {
// 批准工具调用
}
Future<void> rejectToolCall(String runId, String toolCallId) async {
// 拒绝工具调用
}
}// lib/features/chat/screens/chat_screen.dart
class ChatScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final messages = ref.watch(chatMessagesProvider(agentId));
final isStreaming = ref.watch(isStreamingProvider(agentId));
return Column(
children: [
// 消息列表
Expanded(
child: ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
return MessageBubble(messages[index]);
},
),
),
// 工具批准对话框
if (pendingApproval != null)
ToolApprovalDialog(
approval: pendingApproval,
onApprove: (output) => ref.read(chatNotifierProvider.notifier).approveToolCall(...),
onReject: () => ref.read(chatNotifierProvider.notifier).rejectToolCall(...),
),
// 输入框
MessageInput(
onSend: (message) => ref.read(chatNotifierProvider.notifier).sendMessage(...),
enabled: !isStreaming,
),
],
);
}
}// lib/core/utils/sse_parser.dart
class LettaSSEParser {
static Stream<ChatMessage> parse(Stream<String> sseStream) async* {
await for (final chunk in sseStream) {
// 解析 SSE 格式
// data: {"type":"assistant_message", ...}
final lines = chunk.split('\n');
for (final line in lines) {
if (line.startsWith('data: ')) {
final data = jsonDecode(line.substring(6));
yield _parseMessage(data);
}
}
}
}
static ChatMessage _parseMessage(Map<String, dynamic> data) {
// 根据 data['type'] 解析不同类型的消息
// - assistant_message
// - tool_call_message
// - tool_return_message
// - reasoning_message (Claude Opus)
}
}Letta 后端消息类型:
// Assistant 消息
{
"type": "assistant_message",
"content": "Hello!",
"id": "msg_..."
}
// Tool Call 消息
{
"type": "tool_call_message",
"tool": "web_search",
"tool_input": { "query": "..." },
"id": "tool_..."
}
// Tool Return 消息
{
"type": "tool_return_message",
"tool_call_id": "tool_...",
"output": "..."
}
// Reasoning 消息(Claude Opus)
{
"type": "reasoning_message",
"content": "Thinking..."
}预计工作量: 3-5 天
目标: 实现工具调用的权限控制
// lib/core/models/tool_approval.dart
class ToolApprovalRequest {
final String runId;
final String toolCallId;
final String toolName;
final Map<String, dynamic> toolInput;
final String? description;
final bool isDangerous;
}
class ToolApprovalDecision {
final bool approved;
final Map<String, dynamic>? modifiedOutput;
final String? reason;
}// lib/core/utils/permission_analyzer.dart
class PermissionAnalyzer {
static ToolApprovalDecision analyze(
String toolName,
Map<String, dynamic> toolInput,
) {
// 根据 Letta Code 的权限规则分析
// - 读取文件: 允许
// - 写入文件: 询问
// - 执行命令: 拒绝(除非是 safe_commands)
// - web_search: 允许
}
}Letta Code 的权限规则(参考):
// 安全工具(无需批准)
const SAFE_TOOLS = ['memory', 'conversation_search', 'web_search', 'fetch_webpage'];
// 危险工具(总是拒绝)
const DANGEROUS_TOOLS = ['shell', 'run_shell_command'];
// 文件操作工具(需要批准)
const FILE_TOOLS = ['read', 'write', 'edit', 'multi_edit'];// lib/features/chat/components/tool_approval_dialog.dart
class ToolApprovalDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('工具执行批准'),
content: Column(
children: [
Text('工具: ${approval.toolName}'),
Text('参数: ${approval.toolInput}'),
if (approval.isDangerous)
Text('⚠️ 此操作可能有风险', style: TextStyle(color: Colors.red)),
],
),
actions: [
TextButton(
onPressed: onReject,
child: Text('拒绝'),
),
ElevatedButton(
onPressed: onApprove,
child: Text('批准'),
),
],
);
}
}预计工作量: 2-3 天
目标: 支持通过 Letta 后端执行代码和文件操作
详见: docs/REMOTE_DEV_COMPARISON.md
架构决策: 直接使用 Letta 后端(不使用 Letta Code 包装)
// 示例:让 Agent 执行代码
final response = await agent.sendMessage('''
使用 run_code 工具执行以下操作:
1. 列出当前目录文件
2. 读取 package.json
3. 运行 npm test
返回结果。
''');Letta 后端的 run_code 工具:
def run_code(code: str, language: Literal["python", "js", "ts", "r", "java"]) -> str:
"""
Run code in a sandbox. Supports Python, Javascript, Typescript, R, and Java.
"""
# 在沙箱中执行代码
# 返回 stdout, stderr, error traces// 临时方案:通过 run_code 执行文件操作
final response = await agent.sendMessage('''
使用 Python 的 run_code 工具执行:
```python
import os
# 列出文件
files = os.listdir('.')
print(files)
# 读取文件
with open('src/main.ts', 'r') as f:
content = f.read()
print(content[:500]) # 打印前 500 字符''');
#### 3.3 文件操作(阶段 2:等待 Letta 后端实现或添加自定义工具)
**方案 A**: 等待 Letta 后端实现文件工具
- `open_files` - 接口已定义,未实现
- `grep_files` - 接口已定义,未实现
**方案 B**: 添加自定义工具
```dart
// 通过 Letta API 添加工具
POST /v1/tools
{
"name": "server_read_file",
"description": "Read a file from server workspace",
"source_code": '''
async def server_read_file(path: str) -> str:
"""Read file from server workspace directory"""
import os
if not os.path.exists(path):
return f"Error: File not found: {path}"
with open(path, 'r') as f:
return f.read()
''',
"tool_type": "python"
}
// lib/features/chat/screens/file_browser_screen.dart
class FileBrowserScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final files = ref.watch(serverFilesProvider);
return ListView.builder(
itemCount: files.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(files[index].name),
trailing: Icon(Icons.insert_drive_file),
onTap: () {
// 打开文件预览
ref.read(chatNotifierProvider.notifier).sendMessage('''
请使用 run_code 读取文件:${files[index].path}
返回前 1000 行内容。
''');
},
);
},
);
}
}预计工作量: 3-5 天
目标: 查看和管理 Agent 的记忆块
// lib/features/agents/screens/agent_memory_screen.dart
class AgentMemoryScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final blocks = ref.watch(agentBlocksProvider(agentId));
return ListView.builder(
itemCount: blocks.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(blocks[index].label),
subtitle: Text(blocks[index].description ?? ''),
trailing: Text('${blocks[index].value.length}/${blocks[index].limit ?? '∞'}'),
onTap: () {
// 显示记忆块详情
Navigator.push(context, MaterialPageRoute(
builder: (_) => MemoryDetailScreen(block: blocks[index]),
));
},
);
},
);
}
}// lib/features/agents/screens/memory_detail_screen.dart
class MemoryDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(block.label)),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('描述:', style: Theme.of(context).textTheme.subtitle1),
Text(block.description ?? '无'),
SizedBox(height: 16),
Text('内容:', style: Theme.of(context).textTheme.subtitle1),
Text(block.value),
],
),
),
);
}
}预计工作量: 2-3 天
- 触摸友好的按钮大小
- 滑动手势支持
- 响应式布局
- Dark mode
- 消息列表虚拟化
- 图片懒加载
- 缓存策略
预计工作量: 2-3 天
| 阶段 | 任务 | 状态 | 预计工作量 |
|---|---|---|---|
| Phase 0 | 基础设施 | ✅ 完成 | - |
| Phase 1 | 聊天功能 | 🚧 进行中 | 3-5 天 |
| Phase 2 | 工具批准 | ⏸️ 待开始 | 2-3 天 |
| Phase 3 | 远程开发 | ⏸️ 待开始 | 3-5 天 |
| Phase 4 | 记忆管理 | ⏸️ 待开始 | 2-3 天 |
| Phase 5 | UI 优化 | ⏸️ 待开始 | 2-3 天 |
总预计工作量: 12-19 天
- ✅ Agent CRUD 功能
- ✅ Provider CRUD 功能
- 🚧 聊天 UI + SSE 流式响应
- ⏸️ 工具批准系统
- ⏸️ 远程开发能力(run_code)
- ⏸️ 记忆查看 UI
- ⏸️ 移动端优化
- ⏸️ 完整的记忆管理(创建/编辑/删除)
- ⏸️ 文件浏览器 UI
- ⏸️ 桌面端适配
docs/CHAT_FEATURE_PLAN.md- 聊天功能详细规划docs/API_LAYER_OPTIMIZATION.md- API 层优化记录docs/LETTA_CODE_FEATURES.md- Letta Code 功能分析docs/LETTA_BACKEND_ANALYSIS.md- Letta 后端工具分析docs/REMOTE_DEV_COMPARISON.md- 远程开发方案对比
CLAUDE.md- 项目开发指南(必读!)README.md- 项目介绍pubspec.yaml- 依赖配置
- Letta Code:
../letta-code/- CLI 参考实现 - Letta Backend:
../letta/- 后端服务器 - Happy:
../happy/- 移动端参考实现
- ✅ Agent 管理
- ✅ Provider 管理
- 🚧 基本聊天功能
- 🚧 工具批准
- ⏸️ 代码执行
- ⏸️ 文件操作(通过 run_code)
- ⏸️ 记忆查看
- ⏸️ 完整记忆管理
- ⏸️ 文件浏览器
- ⏸️ UI 优化
下一步: 开始实现 Phase 1 - 聊天功能!