Suna 是一款开源的通用 AI Agent 平台,知识库(Knowledge Base)模块是其核心功能之一——让 AI Agent 拥有"记忆"和"专业知识"。本文将从设计理念、系统架构、数据模型、后端实现、前端交互到缓存优化,全面剖析 Suna 知识库模块的工程实践。
一、知识库模块的定位与价值
1.1 为什么 Agent 需要知识库?
大语言模型(LLM)虽然拥有广泛的通用知识,但面对用户特定领域的专业资料、私有文档、项目上下文时却力不从心。知识库模块的核心价值在于:
- 持久化专业知识:用户可以上传 PDF、DOCX、文本文件等各种格式的文档,为 Agent 注入领域知识
- 精准上下文注入:通过 LLM 生成的摘要,在 Agent 执行时动态注入最相关的知识背景
- 语义搜索能力:Agent 可以通过自然语言查询从知识库中检索信息,而非简单的关键词匹配
- 多 Agent 知识共享:同一知识库的文件可以分配给不同 Agent,实现知识复用
1.2 Suna 知识库的设计哲学
Suna 的知识库模块采用了 “摘要注入 + 按需检索” 的双层架构,而非传统 RAG 系统中常见的纯向量检索方案:
传统 RAG: 文档 → 分块 → 向量化 → 向量数据库 → 检索 → 注入 PromptSuna KB: 文档 → LLM 生成摘要 → 摘要注入 System Prompt(轻量级、始终可用) ↘ 按需同步到沙箱 → kb-fusion 语义搜索(深度检索)
这种设计的优势在于:
- 低延迟:摘要直接注入 System Prompt,无需每次对话都进行向量检索
- 高质量:LLM 生成的摘要比机械分块更好地保留了文档的核心信息
- 灵活可控:Agent 可以按需决定是否深入检索原始文档内容
二、系统架构全景
2.1 整体架构图
┌─────────────────────────────────────────────────────────────────────┐│ 前端层 (Next.js) ││ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ ││ │ KB Manager │ │ Agent Config │ │ KbToolView │ ││ │ (文件夹/文件树)│ │ (知识分配) │ │ (对话中 KB 操作可视化) │ ││ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────────┘ ││ │ │ │ │├─────────┼─────────────────┼──────────────────────┼──────────────────┤│ │ REST API 层 │ ││ ▼ ▼ ▼ ││ ┌──────────────────────────────────────────────────────────────┐ ││ │ FastAPI Router (/knowledge-base) │ ││ │ • 文件夹 CRUD • 文件上传/删除/移动 │ ││ │ • Agent 分配 • 文件内容获取 │ ││ └──────────────────────┬───────────────────────────────────────┘ ││ │ │├─────────────────────────┼───────────────────────────────────────────┤│ 核心服务层 ││ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ ││ │FileProcessor │ │PromptManager │ │ SandboxKbTool │ ││ │ • 内容抽取 │ │ • 摘要注入 │ │ • kb-fusion 语义搜索 │ ││ │ • LLM 摘要 │ │ • 上下文组装 │ │ • 文件同步到沙箱 │ ││ │ • 格式转换 │ │ • 缓存优化 │ │ • CRUD 操作 │ ││ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────────┘ ││ │ │ │ │├─────────┼─────────────────┼──────────────────────┼──────────────────┤│ │ 存储与缓存层 │ ││ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────────┴───────────────┐ ││ │ S3 (文件) │ │Redis (缓存) │ │ Supabase/PostgreSQL │ ││ │ file-uploads│ │ KB Context │ │ • knowledge_base_folders │ ││ │ │ │ TTL=300s │ │ • knowledge_base_entries │ ││ │ │ │ │ │ • agent_assignments │ ││ └──────────────┘ └──────────────┘ └──────────────────────────┘ │└─────────────────────────────────────────────────────────────────────┘
2.2 核心文件清单
| 层级 | 文件路径 | 职责 |
|---|---|---|
| 后端 API | backend/core/knowledge_base/api.py | RESTful API 路由,文件夹/文件/分配管理 |
| 文件处理 | backend/core/knowledge_base/file_processor.py | 文件内容抽取、LLM 摘要生成 |
| 验证 | backend/core/knowledge_base/validation.py | 文件名/文件夹名跨平台验证与消毒 |
| 沙箱工具 | backend/core/tools/sb_kb_tool.py | Agent 运行时的 KB 操作工具 |
| Prompt 集成 | backend/core/agents/runner/prompt_manager.py | KB 摘要注入 System Prompt |
| 缓存 | backend/core/cache/runtime_cache.py | Redis 缓存 KB 上下文 |
| Embedding | backend/core/memory/embedding_service.py | 嵌入向量服务(多 Provider) |
| 数据库 | backend/supabase/migrations/20250916000000_new_knowledge_base.sql | 数据库表结构与函数 |
| 前端管理 | apps/frontend/src/components/knowledge-base/knowledge-base-manager.tsx | KB 主管理界面 |
| 前端工具视图 | apps/frontend/src/components/thread/tool-views/KbToolView.tsx | 对话中 KB 操作可视化 |
三、数据模型设计
3.1 数据库表结构
Suna 的知识库采用三表设计,层次清晰:
-- 1. 文件夹表:用户级别的知识组织单元CREATETABLE knowledge_base_folders ( folder_id UUID PRIMARY KEYDEFAULT gen_random_uuid(), account_id UUIDNOTNULLREFERENCES basejump.accounts(id) ONDELETECASCADE, name VARCHAR(255) NOTNULL, description TEXT, created_at TIMESTAMPTZ DEFAULTNOW(), updated_at TIMESTAMPTZ DEFAULTNOW(), CONSTRAINT kb_folders_name_not_empty CHECK (LENGTH(TRIM(name)) > 0));-- 2. 文件条目表:知识库中的每一个文件CREATETABLE knowledge_base_entries ( entry_id UUID PRIMARY KEYDEFAULT gen_random_uuid(), folder_id UUIDNOTNULLREFERENCES knowledge_base_folders(folder_id) ONDELETECASCADE, account_id UUIDNOTNULLREFERENCES basejump.accounts(id) ONDELETECASCADE, filename VARCHAR(255) NOTNULL, file_path TEXTNOTNULL, -- S3 存储路径 file_size BIGINTNOTNULL, mime_type VARCHAR(255), summary TEXTNOTNULL, -- LLM 生成的摘要 usage_context VARCHAR(100) DEFAULT'always', -- always | on_request | contextual is_active BOOLEANDEFAULTTRUE, created_at TIMESTAMPTZ DEFAULTNOW(), updated_at TIMESTAMPTZ DEFAULTNOW());-- 3. Agent 分配表:多对多关联 Agent 和文件CREATETABLE agent_knowledge_entry_assignments ( assignment_id UUID PRIMARY KEYDEFAULT gen_random_uuid(), agent_id UUIDNOTNULLREFERENCES agents(agent_id) ONDELETECASCADE, entry_id UUIDNOTNULLREFERENCES knowledge_base_entries(entry_id) ONDELETECASCADE, account_id UUIDNOTNULLREFERENCES basejump.accounts(id) ONDELETECASCADE, enabled BOOLEANDEFAULTTRUE, assigned_at TIMESTAMPTZ DEFAULTNOW(), UNIQUE(agent_id, entry_id));
3.2 关键数据库函数
系统定义了一个核心的 PostgreSQL 函数,用于高效聚合 Agent 的知识库上下文:
CREATE ORREPLACEFUNCTION get_agent_knowledge_base_context( p_agent_id UUID, p_max_tokens INTEGERDEFAULT4000)RETURNSTEXTSECURITY DEFINERLANGUAGE plpgsqlAS $$DECLARE context_text TEXT := ''; entry_record RECORD; current_length INTEGER := 0; estimated_tokens INTEGER;BEGIN FOR entry_record IN SELECT kbe.filename, kbe.summary, kbf.name as folder_name FROM knowledge_base_entries kbe JOIN knowledge_base_folders kbf ON kbe.folder_id = kbf.folder_id JOIN agent_knowledge_entry_assignments akea ON kbe.entry_id = akea.entry_id WHERE akea.agent_id = p_agent_id AND akea.enabled = TRUE AND kbe.is_active = TRUE AND kbe.usage_context IN ('always', 'contextual') ORDERBY kbe.created_at DESC LOOP -- Token 估算:约 4 字符 = 1 token estimated_tokens := (current_length + LENGTH(entry_record.filename) + LENGTH(entry_record.summary) + 50) / 4; IF estimated_tokens > p_max_tokens THEN EXIT; ENDIF; context_text := context_text || E'\n\n## ' || entry_record.folder_name || '/' || entry_record.filename || E'\n' || entry_record.summary; current_length := LENGTH(context_text); ENDLOOP; RETURN CASE WHEN context_text = '' THEN NULL ELSE E'# KNOWLEDGE BASE\n\nThe following files are available:' || context_text END;END;$$;
这个函数的设计要点:
- Token 限制:通过
p_max_tokens参数控制注入上下文的总长度,默认 4000 token - 粗略估算:使用 4 字符 ≈ 1 token 的经验公式进行快速估算
- 优先级排序:按创建时间倒序,优先注入最新的文件摘要
- 过滤机制:只聚合
enabled=TRUE、is_active=TRUE、且usage_context为always或contextual的条目
3.3 安全模型
数据安全通过 PostgreSQL 的行级安全策略(RLS)保障:
ALTER TABLE knowledge_base_folders ENABLEROWLEVELSECURITY;ALTERTABLE knowledge_base_entries ENABLEROWLEVELSECURITY;ALTERTABLE agent_knowledge_entry_assignments ENABLEROWLEVELSECURITY;-- 基于账户的访问控制CREATEPOLICY kb_folders_account_access ON knowledge_base_folders FORALLUSING (basejump.has_role_on_account(account_id) = true);
每个用户只能访问自己账户下的知识库内容,通过 basejump.has_role_on_account() 函数进行鉴权。
四、后端核心实现
4.1 文件处理管线 (FileProcessor)
文件处理是知识库的核心环节,FileProcessor 类承担了从原始文件到可检索知识的全部转换工作。
4.1.1 处理流程
用户上传文件 │ ▼文件类型检测 │ 支持: .txt, .pdf, .docx, .json, .xml, .csv, .md, .yml, .log 等 │ 智能检测: 对未知扩展名的文件尝试文本编码检测 ▼文件内容抽取 (_extract_content) │ PDF → PyPDF2 逐页提取 │ DOCX → python-docx 逐段提取 │ Text → chardet 编码检测 + 解码 ▼智能分块 (_smart_chunk_content) │ 如果内容超出 LLM 上下文限制 │ 策略: 前半部分 + 重要中间段落 + 末尾部分 ▼LLM 摘要生成 (_generate_summary) │ 模型链: gemini-2.5-flash-lite → gpt-5-mini → 本地 fallback ▼数据库写入 + S3 存储
4.1.2 快速处理模式
Suna 采用了先上传后摘要的异步处理策略:
async def process_file_fast(self, account_id, folder_id, file_content, filename, mime_type, background_tasks): """快速文件处理——立即上传文件,后台生成摘要""" # 1. 文件类型检测(同步,极快) is_text_based = ( mime_type.startswith('text/') or mime_type in ['application/json', 'application/xml'] or self._is_likely_text_file(file_content) ) # 2. 立即上传到 S3(同步,用户等待此步骤) s3_path = f"knowledge-base/{folder_id}/{entry_id}/{sanitized_filename}" await client.storage.from_('file-uploads').upload(s3_path, file_content) # 3. 写入数据库,摘要暂用占位符 entry_data = { 'entry_id': entry_id, 'summary': 'Processing...', # 占位摘要 'is_active': True } await client.table('knowledge_base_entries').insert(entry_data).execute() # 4. 后台任务异步生成摘要(不阻塞用户) background_tasks.add_task( self._generate_and_update_summary, entry_id, file_content, filename, mime_type ) return {'success': True, 'entry_id': entry_id, 'processing': True}
这种设计使得用户上传文件后能立即看到文件出现在列表中(显示 “Processing…” 状态),而耗时的摘要生成在后台异步完成,体验非常流畅。
4.1.3 智能内容分块策略
当文件内容超出 LLM 的上下文窗口时,Suna 使用了一种精心设计的分块策略:
def _smart_chunk_content(self, content: str, max_chars: int) -> str: """智能分块:保留文档中最重要的部分""" if len(content) <= max_chars: return content quarter = max_chars // 4 # 始终保留开头(通常包含最重要的上下文) beginning = content[:quarter * 2] # 前 50% # 保留结尾(通常包含总结/结论) ending = content[-quarter:] # 后 25% # 从中间部分提取重要段落 # 剩余 25% middle_content = content[len(beginning):len(content) - quarter] important_lines = [ ] for line in middle_content.split('\n'): is_important = ( line.strip().isupper() or # 大写标题 line.startswith(('•', '-', '*')) or# 列表项 any(kw in line.lower() for kw in # 关键词 ['summary', 'conclusion', 'important', 'key', 'main']) or len(line.strip()) < 100 # 短行通常更重要 ) if is_important: important_lines.append(line) returnf"{beginning}\n\n[KEY SECTIONS]\n{middle_section}\n\n[ENDING]\n{ending}"
4.1.4 LLM 摘要生成与模型降级
摘要生成采用了多模型级联降级的策略,确保高可用性:
async def _generate_summary(self, content: str, filename: str) -> str: # 模型优先级链:轻量快速 → 通用备选 models = [ ("openrouter/google/gemini-2.5-flash-lite", 1_000_000), # 100万 token 上下文 ("gpt-5-mini", 400_000) # 40万 token 上下文 ] for model_name, context_limit in models: try: prompt = f"""Analyze this file and create a concise summary.File: {filename}Content: {processed_content}Generate a 2-3 sentence summary that captures:1. What this file contains2. Key information or purpose 3. When this knowledge would be usefulKeep it under 200 words and make it actionable for context injection.""" response = await make_llm_api_call( messages=[{"role": "user", "content": prompt}], model_name=model_name, temperature=0.1, # 低温度确保摘要稳定 max_tokens=300, stream=False ) if summary: return summary except Exception: continue# 降级到下一个模型 # 所有模型失败时的本地 fallback return self._create_fallback_summary(content, filename)
Prompt 中的 "actionable for context injection" 这个要求非常关键——它引导 LLM 不是简单地做文档摘要,而是生成对 AI Agent 有实际指导意义的描述。
4.2 API 路由设计
API 路由定义在 api.py 中,挂载在 /knowledge-base 前缀下:
| 方法 | 路径 | 功能 | 说明 |
|---|---|---|---|
| GET | /folders | 获取文件夹列表 | 按创建时间倒序,附带条目计数 |
| POST | /folders | 创建文件夹 | 自动去重命名(类似 macOS) |
| PUT | /folders/{id} | 更新文件夹 | 支持部分更新 |
| DELETE | /folders/{id} | 删除文件夹 | 级联删除文件 + S3 清理 + 缓存失效 |
| POST | /folders/{id}/upload | 上传文件 | 快速模式,后台生成摘要 |
| GET | /folders/{id}/entries | 获取文件列表 | 按文件夹过滤 |
| DELETE | /entries/{id} | 删除文件 | S3 + DB + 缓存三重清理 |
| PATCH | /entries/{id} | 更新摘要 | 手动编辑 LLM 生成的摘要 |
| GET | /entries/{id}/content | 获取文件内容 | 支持 PDF/DOCX 即时解析 |
| PUT | /entries/{id}/move | 移动文件 | 跨文件夹移动,含 S3 路径更新 |
| GET | /agents/{id}/assignments | 获取 Agent 分配 | 返回 entry_id → enabled 映射 |
| POST | /agents/{id}/assignments | 更新 Agent 分配 | 批量更新分配关系 |
4.2.1 文件大小限制机制
系统对用户的总文件大小实施了 50MB 的限制:
MAX_TOTAL_FILE_SIZE = 50 * 1024 * 1024# 50MBasyncdef check_total_file_size_limit(account_id: str, new_file_size: int): """检查新文件是否会超出总大小限制""" result = await client.table('knowledge_base_entries').select( 'file_size' ).eq('account_id', account_id).eq('is_active', True).execute() current_total_size = sum(entry['file_size'] for entry in result.data) if current_total_size + new_file_size > MAX_TOTAL_FILE_SIZE: raise HTTPException( status_code=413, detail=f"File size limit exceeded. Current: {current_mb:.1f}MB, " f"New: {new_mb:.1f}MB, Limit: {limit_mb}MB" )
4.2.2 文件名验证与消毒
FileNameValidator 类提供了跨平台的文件名安全保障:
class FileNameValidator: # Windows 非法字符 ILLEGAL_CHARS = r'[<>:"/\|?*\x00-\x1f]' # Windows 保留名称 RESERVED_NAMES = {'CON', 'PRN', 'AUX', 'NUL', 'COM1', ...} @classmethod def sanitize_name(cls, name: str) -> str: """消毒文件名""" name = re.sub(cls.ILLEGAL_CHARS, '', name) # 移除非法字符 name = name.strip('. ') # 去除首尾点/空格 name = unicodedata.normalize('NFKC', name) # Unicode 标准化 if base_name in cls.RESERVED_NAMES: # 保留名加前缀 name = f"_{name}" return name @classmethod def generate_unique_name(cls, base_name, existing_names, item_type="file"): """macOS 风格的自动去重命名: file.txt → file 2.txt → file 3.txt""" if base_name.lower() notin [n.lower() for n in existing_names]: return base_name counter = 2 whileTrue: new_name = f"{name_part} {counter}{ext}" if new_name.lower() notin [n.lower() for n in existing_names]: return new_name counter += 1
4.3 缓存层优化
知识库上下文的获取是一个涉及多表 JOIN 的数据库操作。为了避免每次 Agent 执行都触发这个昂贵的查询,Suna 设计了精细的 Redis 缓存策略:
KB_CONTEXT_TTL = 300 # 5 分钟缓存asyncdef get_cached_kb_context(agent_id: str) -> Optional[str]: """从 Redis 获取缓存的 KB 上下文""" cached = await redis_service.get(f"kb_context:{agent_id}") if cached isnotNone: return cached.decode() if isinstance(cached, bytes) else cached returnNoneasyncdef set_cached_kb_context(agent_id: str, context: str) -> None: """缓存 KB 上下文到 Redis""" await redis_service.set(f"kb_context:{agent_id}", context, ex=KB_CONTEXT_TTL)asyncdef invalidate_kb_context_cache(agent_id: str) -> None: """主动失效 KB 上下文缓存""" await redis_service.delete(f"kb_context:{agent_id}")
缓存失效的触发点非常全面,覆盖了所有可能导致知识库上下文变化的操作:
| 触发操作 | 失效范围 |
|---|---|
| 删除文件夹 | 该文件夹下所有文件关联的所有 Agent |
| 删除文件 | 该文件关联的所有 Agent |
| 更新文件摘要 | 该文件关联的所有 Agent |
| 更新 Agent 分配 | 被修改的 Agent |
| 后台摘要生成完成 | 该文件关联的所有 Agent |
此外还有缓存预热机制,在用户登录时提前加载 KB 上下文:
async def warm_kb_context_for_agent(agent_id: str, client) -> None: """预热 Agent 的 KB 上下文缓存""" cached = await get_cached_kb_context(agent_id) if cached isnotNone: return# 已有缓存,跳过 # 快速检查是否有 KB 条目 entry_count = await threads_repo.get_kb_entry_count(agent_id) if entry_count == 0: await set_cached_kb_context(agent_id, "") # 缓存空结果避免重复查询 return # 调用数据库函数获取上下文 kb_result = await client.rpc("get_agent_knowledge_base_context", {"p_agent_id": agent_id}).execute() if kb_result.data and kb_result.data.strip(): await set_cached_kb_context(agent_id, kb_result.data)
五、Prompt 注入机制
5.1 摘要注入 System Prompt
PromptManager._fetch_knowledge_base() 是知识库与 Agent 运行时的关键连接点。它采用了三级优化策略:
┌─ 命中缓存 → 直接返回(毫秒级) │KB 上下文获取 ──────┼─ 缓存未命中 → 快速 EXISTS 检查 │ │ │ ├─ 无条目 → 缓存空结果 → 返回 null │ │ │ └─ 有条目 → RPC 聚合查询 → 缓存 → 注入 │ └─ 超时保护(2 秒硬限制)
注入到 System Prompt 的内容格式如下:
=== YOUR KNOWLEDGE BASE ===You have a knowledge base with files assigned to you. Below are SUMMARIES:# KNOWLEDGE BASE## ProjectDocs/architecture.pdfThis document describes the system architecture...## Research/market-analysis.docx A comprehensive market analysis covering...=== END KNOWLEDGE BASE ===HOW TO ACCESS FULL FILE CONTENT:1. global_kb_list_contents - List all your KB files2. global_kb_sync - Download KB files to /workspace/downloads/global-knowledge/3. After sync, read files at /workspace/downloads/global-knowledge/[FolderName]/[filename]4. semantic_search - Search across all files with natural language queries
注意这里不仅注入了摘要内容,还明确告诉了 Agent 如何在需要时获取完整文件内容,这是引导 Agent 行为的关键 Prompt 工程技巧。
5.2 超时保护
KB 获取设置了 2 秒的硬超时限制,确保不会因为数据库慢查询影响 Agent 响应速度:
kb_task = PromptManager._with_timeout( PromptManager._fetch_knowledge_base(agent_config, client), 2.0, # 2 秒超时 "KB fetch" # 超时日志标签)
六、Agent 运行时集成:SandboxKbTool
6.1 设计理念
SandboxKbTool 是 Agent 在对话过程中操作知识库的工具类。它实现了 Agent 与知识库的深度交互,而不仅仅是被动接收摘要注入。
这个工具注册了以下能力:
@tool_metadata( display_name="Knowledge Base", description="Store and retrieve information from your personal knowledge library", icon="Brain", color="bg-yellow-100 dark:bg-yellow-800/50", weight=200, visible=True)class SandboxKbTool(SandboxToolsBase): ...
6.2 核心工具函数
| 函数 | 功能 | 说明 |
|---|---|---|
semantic_search | 语义搜索 | 使用 kb-fusion 在已索引文件中搜索 |
global_kb_sync | 文件同步 | 从 S3 下载 KB 文件到沙箱 |
global_kb_list_contents | 列出内容 | 显示所有文件夹和文件及其启用状态 |
global_kb_enable_all | 全部启用 | 一键启用所有 KB 文件 |
global_kb_enable_item | 单项启用 | 启用/禁用特定文件 |
global_kb_create_folder | 创建文件夹 | Agent 主动创建知识组织 |
global_kb_upload_file | 上传文件 | 从沙箱上传文件到 KB |
global_kb_delete_item | 删除项目 | 删除文件或文件夹 |
ls_kb | 列出索引 | 查看 kb-fusion 已索引的文件 |
cleanup_kb | 清理维护 | 清理过期数据 |
6.3 文件同步机制
文件同步是将云端知识库文件下载到 Agent 沙箱环境的过程,这是 Agent 能直接读取文件内容的前提:
async def global_kb_sync(self) -> ToolResult: """同步 KB 文件到 /workspace/downloads/global-knowledge/""" # 1. 查询 Agent 被分配且启用的所有文件 result = await client.from_("agent_knowledge_entry_assignments").select(""" entry_id, enabled, knowledge_base_entries ( entry_id, filename, file_path, file_size, mime_type, knowledge_base_folders (name) ) """).eq("agent_id", agent_id).eq("enabled", True).execute() # 2. 创建沙箱目录结构 kb_dir = f"{self.workspace_path}/downloads/global-knowledge" await self.sandbox.fs.create_folder(kb_dir, "755") # 3. 逐文件同步:S3 下载 → 沙箱上传 for assignment in result.data: entry = assignment['knowledge_base_entries'] folder_name = entry['knowledge_base_folders']['name'] # 从 S3 下载(带重试) file_response = await self._download_with_retry(client, entry['file_path']) # 上传到沙箱(带重试和验证) file_destination = f"{kb_dir}/{safe_folder_name}/{safe_filename}" await self._upload_file_with_retry(file_response, file_destination) # 4. 生成 README.md 索引文件 # 5. 最终验证:确认所有文件都已正确写入
整个同步过程包含了精细的错误处理:
- 下载重试:最多 3 次重试,指数退避
- 上传重试:最多 3 次重试,每次上传后进行大小校验
- 文件名安全化:自动处理特殊字符、emoji 等
- 最终验证:用
find命令检查所有预期文件是否到位
6.4 语义搜索
语义搜索依赖 kb-fusion 外部工具(一个 Rust 编写的高性能搜索引擎):
async def semantic_search(self, queries=None, query=None, path=None): # 1. 确保 kb-fusion 已安装(自动下载安装) await self._ensure_kb_initialized() # 2. 自动同步 KB 文件(如果还没同步) await self._auto_sync_kb_if_needed() # 3. 构建搜索命令 search_command = f'kb search "{search_path}" {query_args} -k 18 --json' # 4. 在沙箱中执行搜索(需要 OPENAI_API_KEY 做 embedding) result = await self._execute_kb_command(search_command) # 5. 解析并返回结果 results_data = json.loads(result["output"]) total_hits = sum(len(qr.get("hits", [ ])) for qr in results_data)
kb-fusion 工具的自动管理非常优雅——首次使用时自动从 GitHub Releases 下载安装,并在后续调用中检查版本是否需要更新:
async def _ensure_kb_initialized(self) -> dict: # 检查版本是否匹配 check_result = await self._execute_kb_command("kb -v 2>&1") if f"kb-fusion {self.kb_version}" in check_result["output"]: return {"success": True, "already_installed": True} # 需要安装或更新 await self.sandbox.process.exec( f"curl -L -f {self.kb_download_url} -o /tmp/kb && " f"chmod +x /tmp/kb && mv /tmp/kb /usr/local/bin/kb" )
七、Embedding 服务
虽然知识库模块的核心搜索依赖 kb-fusion,但 Suna 也提供了通用的 Embedding 服务用于记忆系统和语义搜索:
class EmbeddingService: """支持多 Provider 的嵌入向量服务""" def _create_provider(self) -> EmbeddingProvider: if provider_name == "openai": return OpenAIEmbeddingProvider(model="text-embedding-3-small") elif provider_name in ("voyage", "voyageai"): return VoyageAIEmbeddingProvider(model="voyage-2") elif provider_name == "local": return LocalEmbeddingProvider(model="all-MiniLM-L6-v2")
三种 Provider 的对比:
| Provider | 模型 | 维度 | 特点 |
|---|---|---|---|
| OpenAI | text-embedding-3-small | 1536 | 默认选项,平衡成本和质量 |
| VoyageAI | voyage-2 | — | 高质量替代方案 |
| Local | all-MiniLM-L6-v2 | 384 | 完全本地化,无需 API |
八、前端交互设计
8.1 KnowledgeBaseManager 组件
主管理界面是一个功能丰富的文件管理器,支持:
- 树形文件浏览器:文件夹 + 文件的层级展示
- 拖拽操作:使用
@dnd-kit/core实现文件在文件夹间的拖拽移动 - 文件上传:支持拖拽上传、多文件批量上传、上传进度显示
- 内联编辑:文件夹重命名、文件摘要编辑
- Agent 分配模式:在 Agent 配置页面以复选框形式管理文件分配
interface KnowledgeBaseManagerProps { agentId?: string; // Agent 上下文 agentName?: string; showHeader?: boolean; enableAssignments?: boolean; // 启用 Agent 分配模式 maxHeight?: string;}
组件使用了精心设计的 TreeItem 数据结构来统一管理文件夹和文件:
interface TreeItem { id: string; type: 'folder' | 'file'; name: string; parentId?: string; data?: Folder | Entry; children?: TreeItem[ ]; expanded?: boolean;}
8.2 KbToolView:对话中的可视化
当 Agent 在对话中调用 KB 工具时,KbToolView 组件提供了丰富的可视化展示:
- 搜索结果:显示查询文本和匹配片段
- 同步状态:显示同步文件数、文件夹结构、失败项
- 列表内容:以卡片形式展示文件夹和文件统计
- 操作反馈:创建、上传、删除、启用等操作的状态展示
组件通过解析工具函数名来判断操作类型:
const parseKbTool = (functionName: string, args: Record<string, any>): KbOperation => { if (functionName.includes('global_kb')) { const type = functionName.includes('sync') ? 'sync' : functionName.includes('create') ? 'create' : functionName.includes('upload') ? 'upload' : ... return { type, scope: 'global', data: args }; } // ...};
8.3 React Query 数据管理
前端使用 React Query 管理 KB 数据的获取和缓存:
// hooks/knowledge-base/use-folders.tsexportconst useKnowledgeFolders = () => { const [folders, setFolders] = useState<Folder[ ]>([ ]); const [loading, setLoading] = useState(true); const fetchFolders = async () => { const response = await backendApi.get<Folder[ ]>('/knowledge-base/folders'); setFolders(response.data || [ ]); }; useEffect(() => { fetchFolders(); }, [ ]); return { folders, loading, refetch: fetchFolders };};
九、典型工作流程
9.1 用户上传文件到与 Agent 使用
用户操作 后端处理 Agent 运行时──────── ──────── ──────────1. 打开 KB 管理页面2. 创建文件夹 "项目资料" ──→ 写入 knowledge_base_folders3. 上传 proposal.pdf ──→ S3 存储 + DB 写入 ──→ [后台] LLM 生成摘要 ──→ 更新 DB summary 字段 ──→ 失效关联 Agent 缓存4. 进入 Agent 配置页面5. 勾选 proposal.pdf ──→ 写入 agent_assignments ──→ 失效该 Agent KB 缓存6. 与 Agent 对话 ──→ ──→ 获取 KB 上下文(缓存/DB) "帮我总结项目提案" 注入摘要到 System Prompt ──→ Agent 发现需要详细内容 ──→ 调用 global_kb_sync ──→ S3 → 沙箱文件系统 ──→ 读取完整文件内容 ──→ 生成详细回复
9.2 Agent 主动管理知识库
Agent 不仅能读取知识库,还能主动进行管理操作:
用户: "把这次分析的结果保存到知识库里"Agent 执行: 1. global_kb_create_folder("分析报告") → 创建文件夹 2. 将分析结果写入沙箱文件 3. global_kb_upload_file( → 上传到 KB sandbox_file_path="/workspace/analysis.md", folder_name="分析报告" )
十、架构亮点总结
10.1 摘要注入 vs 纯 RAG
| 特性 | Suna 方案 | 传统纯 RAG |
|---|---|---|
| 首次响应延迟 | 低(摘要已在 System Prompt) | 高(需要 embedding + 检索) |
| 知识全面性 | Agent 始终知道所有文件的存在 | 只知道检索命中的片段 |
| 深度检索 | 按需通过 kb-fusion 进行 | 每次都触发 |
| 成本 | 摘要生成一次性消耗 | 每次对话都有 embedding 成本 |
10.2 异步处理模式
文件上传采用"先返回后处理"的模式:
- 用户等待时间:仅 S3 上传 + DB 写入(通常 < 1 秒)
- 后台处理:文件解析 + LLM 摘要(通常 5-30 秒)
- 体验效果:文件立即出现在列表中,摘要稍后自动更新
10.3 多层缓存策略
请求到达 │ ├─ Redis 缓存命中(TTL=300s)──→ 直接返回 │ ├─ 快速 EXISTS 检查 ──→ 无条目 → 缓存空结果 │ └─ RPC 聚合查询 → 缓存结果 → 返回 │ └─ 超时保护(2 秒硬限制)
10.4 完善的失效机制
每一个可能导致 KB 上下文变化的写操作都会触发对应的缓存失效,确保 Agent 始终获取到最新的知识库状态。失效是精确到 Agent 级别的——只失效受影响的 Agent 缓存,而非全局清空。
10.5 安全与隔离
- RLS:PostgreSQL 行级安全策略确保用户只能访问自己的知识库
- 沙箱隔离:KB 文件同步到 Agent 的独立沙箱环境
- 文件名消毒:跨平台的文件名安全处理,防止路径遍历等攻击
- 大小限制:每用户 50MB 的总存储限制
十一、未来演进方向
从代码中可以发现一些正在准备或预留的功能:
- Git 仓库克隆:前端已有 “Git Clone” Tab UI(目前 disabled),预示着将支持直接克隆 Git 仓库作为知识源
- 使用上下文分级:
usage_context字段支持always、on_request、contextual三种模式,为更智能的知识注入策略做了预留 - 知识库设置页面:前端有独立的知识库设置入口(目前显示 “Coming Soon”)
- 更多文件格式:文件处理器的架构设计支持方便地扩展新格式
结语
Suna 的知识库模块是一个设计精良的工程系统。它没有追求"大而全"的 RAG 方案,而是用 “摘要注入 + 按需检索” 的务实设计,在保证低延迟和高可用性的同时,为 Agent 提供了灵活的知识访问能力。从异步文件处理、多模型摘要降级、Redis 缓存优化到沙箱文件同步,每个环节都体现了对生产环境可靠性的深思熟虑。
对于正在构建 AI Agent 平台的开发者来说,Suna 的知识库设计提供了一个很好的参考:不必从一开始就构建复杂的向量数据库管线,LLM 驱动的摘要 + 按需深度检索可能是一个更务实的起步策略。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

3270



被折叠的 条评论
为什么被折叠?



