news 2026/10/1 6:53:32

Suna 源码解读:从架构到部署的完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Suna 源码解读:从架构到部署的完整实践

1. Suna 源码解读:从架构到部署的完整实践

Suna 是 Kortix AI 开源的一个通用型 Agent 平台,你可以把它理解成"能自己动手干活的 AI 助手"——不只是聊天,而是能自主规划任务、调用浏览器、执行代码、读写文件、生成报告。它适合谁?想深入理解 Agent 平台设计思路的后端开发者、需要自托管一套自动化任务系统的团队,以及打算基于成熟框架做二次开发的人。我这次把它的源码结构、核心模块和本地跑通流程完整走了一遍,重点拆解三个关键设计:Sandbox 隔离执行、Redis 流式总线、Supabase 数据中心。整篇文章会给出可复制的环境配置、依赖安装命令和启动验证步骤,让你从源码层面掌握它的运行机制,并找到二次开发的入口。需要说明的是,Suna 默认依赖若干海外中间件,国内直连体验会偏慢,所以部署时我会同步给出替换思路,把外部依赖换成自建服务,这样生产环境才跑得动。下面从源码目录开始,一层层往下拆。

1.1 源码目录结构与模块职责

先把仓库拉下来,看清楚每个目录在干什么,后面读代码才不会迷路。

git clone https://github.com/kortix-ai/suna cd suna ls -la

你会看到大致这样的结构:

suna/ ├── backend/ # 后端服务(Python / FastAPI) │ ├── agents/ # Agent 主循环、Prompt、工具集 │ │ └── tools/ # 浏览器、代码、文件、API 等工具实现 │ ├── api/ # FastAPI 路由 │ └── ... ├── frontend/ # 前端应用(Next.js / React / TailwindCSS) ├── docs/ # 文档与架构图 ├── README.md └── docker-compose.yml # 一键起 Supabase + Redis + 后端 + 前端

backend/agents/是整个项目的心脏。Agent 的主循环在这里:接收用户任务,调用 LLM 做规划,解析出要执行的工具,再把工具结果回灌给 LLM,循环直到任务完成。backend/agents/tools/下每个文件对应一类能力,比如浏览器自动化、代码执行、文件系统操作、API 调用。想加自定义工具,基本就是在这个目录里新增一个模块,然后注册到工具列表。

backend/api/是 FastAPI 的路由层,负责对外暴露接口。所有 Agent 执行都从/agent/execute这类端点发起,前端通过 SSE 或 WebSocket 订阅后端推送的流式结果。

frontend/是 Next.js 应用,提供聊天界面、仪表盘、文件管理、浏览器远程查看等交互。它本身不跑 Agent 逻辑,只负责展示和下发指令。

docker-compose.yml是本地跑通的关键,它把 Supabase、Redis、后端、前端编排在一起,一条命令就能起全套。

1.2 三大核心设计:Sandbox、Redis Stream、Supabase

读 Suna 源码,最值得花时间的是这三个抽象,它们决定了平台的隔离性、流式能力和数据组织方式。

Sandbox 隔离执行:每个 Agent 任务会拉起一个独立的容器,代码执行、依赖安装、浏览器操作都在沙箱内完成。这样 A 任务不会污染 B 任务,用户提交的代码也不会碰到宿主机。源码里通过 Daytona SDK 调度容器,简化流程大致是这样:

from daytona_sdk import Daytona daytona = Daytona() sandbox = daytona.create() # 起一个隔离容器 sandbox.exec("pip install pandas") # 在沙箱里装包 result = sandbox.exec("python analysis.py") # 在沙箱里执行 daytona.remove(sandbox) # 任务结束销毁

生产环境里 Daytona 是商业云服务,自部署可以换成自建 Docker host 或 Kubernetes Job,核心思路是把"Agent 执行环境"和"主服务"做物理隔离。

Redis 作为流式响应缓冲:这是 Suna 最值得借鉴的设计。LLM 的流式输出不直接推给前端,而是先写进 Redis Stream,前端再从 Redis 读。写入端和读取端分离:

# 写入端(推理进程) for chunk in llm.stream(messages): redis.xadd(f"agent_run:{run_id}", {"token": chunk}) # 读取端(推送进程 / SSE 端点) last_id = request_last_id or "0" while True: entries = redis.xread({f"agent_run:{run_id}": last_id}, block=1000) for entry in entries: yield entry.token last_id = entry.id

这样做的好处是推理和前端消费完全解耦,前端断线重连后能从上次的 Stream ID 续读,不会丢消息;多个客户端也能同时订阅同一个agent_run。代价是系统复杂度上升,Redis 内存要配MAXLEN裁剪,否则长文本高并发时内存压力会很大。

Supabase 作为数据中心:Supabase 在 Suna 里一身多职——GoTrue 负责邮箱和 OAuth 登录,Postgres 存用户、会话、消息、Agent run、文件元数据,Storage 存上传文档和产出物,Realtime 提供可选的实时订阅。对个人开发者很友好,一个服务搞定认证加数据库加存储。企业生产建议自建 Postgres + MinIO,避免 vendor lock-in。

1.3 整体架构与数据流

把上面三块拼起来,Suna 的架构大致是这样:

┌────────────┐ ┌─────────────┐ ┌────────────────┐ │ Frontend │◄─►│ Backend │◄─►│ LLM API │ │ (Next.js) │SSE│ (FastAPI) │ │ (OpenAI/Claude)│ └────────────┘ └─────┬───────┘ └────────────────┘ │ ┌──────────────┼────────────┬──────────────┐ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Redis │ │Supabase │ │ Daytona │ │ Browser │ │(Stream) │ │(Data/Auth)│ (Sandbox)│ │base/CDP │ └─────────┘ └─────────┘ └─────────┘ └─────────┘

一次任务的完整数据流:前端下发任务 → 后端/agent/execute接收 → Agent 主循环调 LLM 规划 → 需要执行工具时在 Daytona 沙箱里跑 → 每步结果写 Redis Stream → 前端从 Redis 读并渲染。Supabase 全程负责认证、持久化和文件存储。

理解了这条链路,后面部署时遇到报错就能快速定位是哪一环出的问题。

2. TaoToken 前置:模型接入与 API Key 准备

Suna 本身不绑定特定模型,它通过 LLM API 调用外部模型。源码里默认走 OpenAI 或 Anthropic 的接口,但你可以通过 LiteLLM 这类抽象层接国内模型。这里我用 TaoToken 作为模型接入层来演示,因为它兼容 OpenAI 协议,改 Base URL 就能用,省去改源码的麻烦。

2.1 为什么要在 Suna 里配 TaoToken

Suna 的 Agent 主循环对模型的调用量不小——一个"调研加生成报告"的任务往往要 20 到 80 次 LLM 调用,加上多次工具调用。如果模型接口不稳定或延迟高,整个任务会拖得很慢。TaoToken 提供统一的 OpenAI 兼容接口,你可以在不改 Suna 源码的前提下,把模型请求指向它,同时方便切换不同模型做对比。

具体来说,Suna 后端读环境变量里的 LLM 配置,你只要把 Base URL 和 Key 填对,Agent 就能正常跑。下面给出完整配置。

2.2 获取 API Key 与配置入口

先到 TaoToken 控制台创建 API Key。打开 https://taotoken.net/api-keys ,登录后新建一个 Key,复制保存。这个 Key 后面要填进 Suna 的.env文件。

TaoToken 的 API 地址是 https://taotoken.net/api ,兼容 OpenAI 的/v1/chat/completions路径。也就是说,Suna 里凡是填 OpenAI Base URL 的地方,换成这个地址即可。

如果你还没注册,可以先到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 了解下,注册后在控制台就能拿到 Key。

2.3 模型选择建议

Suna 的 Agent 任务对模型的规划能力和工具调用能力要求较高。实测下来,带 function calling 能力的模型跑起来更顺,因为 Agent 需要模型输出结构化的工具调用指令。你可以在 TaoToken 的模型列表里挑一个支持工具调用的模型,把 Model ID 记下来,后面填进配置。

模型对话调试可以到 https://taotoken.net/models 直接试,确认模型能正常返回再往 Suna 里配,省得在项目里排查。

2.4 环境变量准备清单

在改 Suna 配置前,先把需要的值列清楚:

变量名用途取值来源
LLM_API_KEY模型调用密钥TaoToken 控制台
LLM_API_BASE模型接口地址https://taotoken.net/api
LLM_MODEL模型 IDTaoToken 模型列表
REDIS_URL流式总线本地 docker 或自建
SUPABASE_URL数据中心本地 docker 或自建
SUPABASE_KEYSupabase 密钥本地 docker 或自建

把这些准备好,下一步就能直接改配置文件启动。

3. 可复制配置:Suna 本地部署与 settings 片段

这一节给出完整的可复制配置。Suna 用.env管理环境变量,用docker-compose.yml编排服务。我按源码里的路径和字段名来写,你直接照着改就行。

3.1 克隆仓库与初始化环境文件

git clone https://github.com/kortix-ai/suna cd suna cp .env.example .env

.env.example里列出了所有需要的变量,复制成.env后逐项填写。

3.2 填写 .env 配置

打开.env,把模型相关的字段改成 TaoToken 的值。关键片段如下:

# ---- LLM 配置 ---- LLM_API_KEY=sk-你的TaoToken密钥 LLM_API_BASE=https://taotoken.net/api LLM_MODEL=你的模型ID # ---- Redis 流式总线 ---- REDIS_URL=redis://redis:6379/0 # ---- Supabase 数据中心 ---- SUPABASE_URL=http://supabase:8000 SUPABASE_KEY=你的Supabase密钥 # ---- Sandbox 沙箱 ---- DAYTONA_API_KEY=你的Daytona密钥

注意LLM_API_BASE填的是https://taotoken.net/api,不要带/v1,具体路径由 Suna 的调用代码拼接。如果你的模型需要特定路径,可以在源码里调整。

3.3 docker-compose 编排说明

Suna 的docker-compose.yml会把 Supabase、Redis、后端、前端一起起起来。核心服务定义大致如下:

services: redis: image: redis:7-alpine ports: - "6379:6379" supabase: image: supabase/postgres:15 environment: POSTGRES_PASSWORD: yourpassword ports: - "5432:5432" backend: build: ./backend env_file: .env depends_on: - redis - supabase ports: - "8000:8000" frontend: build: ./frontend env_file: .env depends_on: - backend ports: - "3000:3000"

如果你要替换海外中间件,把supabase换成自建 Postgres + MinIO,把 Daytona 换成自建 Docker host,改的就是这一段。

3.4 启动全套服务

docker compose up -d docker compose ps

docker compose ps会列出所有容器状态,确认 redis、supabase、backend、frontend 都是running或healthy。

3.5 后端关键配置片段

Suna 后端读环境变量后,会在启动时初始化 LLM 客户端。源码里大致是这样组织的:

import os from openai import OpenAI client = OpenAI( api_key=os.getenv("LLM_API_KEY"), base_url=os.getenv("LLM_API_BASE"), ) def call_llm(messages, tools=None): resp = client.chat.completions.create( model=os.getenv("LLM_MODEL"), messages=messages, tools=tools, stream=True, ) for chunk in resp: yield chunk

这段代码说明:只要LLM_API_BASE指向 TaoToken,Suna 的模型调用就走 TaoToken,不需要改其他逻辑。

3.6 前端配置片段

前端通过环境变量拿到后端地址,配置在frontend/.env.local:

NEXT_PUBLIC_API_BASE=http://localhost:8000 NEXT_PUBLIC_SUPABASE_URL=http://localhost:8000 NEXT_PUBLIC_SUPABASE_ANON_KEY=你的匿名密钥

改完重启前端容器即可生效。

4. 验证请求与成功结果

配置填好后,先别急着跑复杂任务,用最小请求验证模型链路通不通,再验证 Agent 主循环能不能跑起来。

4.1 验证模型接口连通性

先用 curl 直接打 TaoToken 的接口,确认 Key 和 Base URL 没问题:

curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的TaoToken密钥" \ -H "Content-Type: application/json" \ -d '{ "model": "你的模型ID", "messages": [{"role": "user", "content": "回复 ok"}] }'

如果返回里有choices字段和正常内容,说明模型链路通了。这一步能排除掉大部分 401 和模型不存在的问题。

4.2 验证后端健康状态

curl http://localhost:8000/health

返回{"status":"ok"}之类的响应,说明后端起来了。

4.3 发起一次 Agent 任务

通过后端接口下发一个简单任务:

curl -X POST http://localhost:8000/agent/execute \ -H "Content-Type: application/json" \ -d '{ "task": "计算 1 到 100 的和,并输出结果", "user_id": "test-user" }'

后端会返回一个run_id,你可以用它去订阅流式输出。

4.4 订阅流式结果

前端通过 SSE 订阅,你也可以用 curl 模拟:

curl -N http://localhost:8000/agent/stream?run_id=你的run_id

正常的话,你会看到一段段 token 陆续返回,最后是任务完成的结果。这说明 Redis Stream 的写入和读取都通了。

4.5 成功结果长什么样

一次成功的任务,你会看到类似这样的输出流:

[plan] 我需要计算 1 到 100 的和 [tool] 调用代码执行工具 [code] sum(range(1, 101)) [result] 5050 [answer] 1 到 100 的和是 5050

看到[answer]这一行,说明 Agent 主循环、工具调用、沙箱执行、流式推送整条链路都跑通了。

4.6 前端界面验证

打开浏览器访问http://localhost:3000,登录后新建一个对话,输入同样的任务。如果界面上能看到逐步输出的过程,并且最终给出答案,说明前后端联调成功。

5. 本篇常见错排查

部署 Suna 时最容易卡在几个地方,我按真实报错来对照排查。

5.1 401 Unauthorized

报错长这样:

openai.AuthenticationError: Error code: 401 - {'error': {'message': 'Invalid API key'}}

原因通常是LLM_API_KEY填错,或者 Key 前后带了空格。检查.env里的值,确认没有多余引号和空格。另外确认LLM_API_BASE是https://taotoken.net/api,不要多写/v1导致路径重复。

5.2 local proxy failed / connection refused

报错:

httpx.ConnectError: [Errno 111] Connection refused

这通常是 Redis 或 Supabase 没起来。先docker compose ps看容器状态,如果 redis 是exited,看日志:

docker compose logs redis

常见原因是端口被占用,改docker-compose.yml里的端口映射即可。

5.3 reading choices 报错

报错:

KeyError: 'choices'

这说明模型返回的结构和预期不符。可能是模型 ID 填错,或者该模型不支持 OpenAI 兼容格式。到 https://taotoken.net/models 确认模型 ID,并测试它是否返回标准choices结构。

5.4 OAuth 回调失败

报错:

OAuth callback error: redirect_uri_mismatch

Supabase 的 OAuth 配置里,回调地址要和你实际访问的地址一致。本地开发时把http://localhost:3000/auth/callback加进允许列表。如果你自建了 Supabase,检查 GoTrue 的SITE_URL配置。

5.5 沙箱启动失败

报错:

DaytonaError: failed to create sandbox

Daytona 是海外服务,国内访问可能超时。自部署时把它换成自建 Docker host,改backend/agents/tools/里沙箱调用的实现,把 Daytona SDK 换成dockerPython SDK 即可。

5.6 三件套检查清单

如果你用的是 Claude Code、Cline MCP 或 Codex 这类工具接入,出现认证问题时,按三件套逐项核对:

项目检查点
Base URL是否为 https://taotoken.net/api
API Key是否与控制台一致,无空格
Model ID是否在模型列表中且支持工具调用

三项都对,认证类报错基本能解决。接入文档可以参考 https://taotoken.net/doc ,里面有各工具的配置示例。

5.7 流式输出中断

如果 SSE 连接频繁断开,检查 Redis Stream 是否配了MAXLEN。默认不裁剪时,长任务会把内存吃满,导致 Redis 拒绝写入。在xadd时加上MAXLEN ~ 10000控制单 stream 长度。

6. 从源码到二次开发:扩展入口与落地建议

跑通之后,下一步就是基于 Suna 做二次开发。它的扩展点比较清晰,我按优先级列几个。

6.1 新增自定义工具

backend/agents/tools/是工具目录。新增一个工具,大致是写一个类,实现execute方法,然后注册到工具列表。比如加一个内部 API 调用工具:

class InternalAPITool: name = "internal_api" description = "调用公司内部 API 获取数据" def execute(self, endpoint: str, params: dict): resp = requests.get(f"https://internal.example.com/{endpoint}", params=params) return resp.json()

注册后在 Agent 主循环里就能被模型调用。

6.2 替换模型层

Suna 默认走 OpenAI 兼容接口,如果你想接多个模型做路由,可以在call_llm外面包一层 LiteLLM,根据任务类型选模型。这样简单任务用便宜模型,复杂规划用强模型,成本能降不少。

6.3 替换沙箱实现

把 Daytona 换成自建 Docker host,核心是改沙箱创建和销毁的逻辑。用dockerPython SDK 起容器,挂载工作目录,执行完销毁。这样国内访问没有额外延迟,数据也不出内网。

6.4 多租户与权限

如果面向多团队使用,Supabase 的 RLS(行级安全)可以天然支持多租户隔离。在 Postgres 表上配好策略,每个租户只能看到自己的数据。

6.5 工作流编排

Suna 偏"自由 Agent",强一致的工作流支持较弱。如果你的业务需要严格的多步编排,可以引入 Temporal 或 Prefect,把 Agent 作为其中一个节点,补足这块短板。

6.6 长期编码与 Agent 场景

如果你打算把 Suna 作为长期运行的 Agent 底座,模型调用量会持续增长。这种情况下可以关注 TaoToken 的 Coding Plan,https://taotoken.net/coding-plan ,适合需要稳定模型供给的编码和 Agent 场景。控制台在 https://taotoken.net/console ,可以管理 Key 和用量。

6.7 部署到生产前的检查

上线前把这几项过一遍:沙箱隔离是否到位、Redis 内存策略是否配好、Supabase 是否换成自建、日志审计是否开启、敏感数据是否加密。这几项做完,Suna 就能从 demo 变成能扛生产任务的系统。

整个流程走下来,Suna 的工程价值在于三个抽象:Sandbox 负责隔离执行,Redis Stream 负责解耦流式,Supabase 负责一体化数据。把这三块换成自建基础设施,再配上稳定的模型接入,就能落地成企业级 Agent 平台。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 6:53:01

AI网关治理RAG落地难题:知识路由、模型路由与成本观测实践

1. RAG项目真正卡脖子的地方:不是检索算法,是治理问题我先说个可能有点扎心的结论:绝大多数RAG项目做了一半就卡住,不是因为Embedding选得不好、Chunk切得不对,也不是因为没上GraphRAG这类新玩法,而是因为整…

作者头像 李华
网站建设 2026/10/1 6:52:49

MCP 协议支持哪两种模式?Local Mode 与 Remote Mode 的 stdio 配置实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 6:52:02

隔离开关状态识别:50张VOC+YOLO数据集训练YOLOv8全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 6:51:54

OpenClaw会话自动清除解决方案:把settings改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 6:51:17

风景园林论文最难的不是画图:把“场地踏勘“讲成一份笔记

风景园林专业的论文,最容易在评审环节被打回的一句评语是:"你好像没去过那块地。"风景园林是一门强现场性的学科,研究对象是具体的场地——一段园路、一片林地、一个村庄、一条河流、一个小广场。论文里出现的每一个判断、每一张图…

作者头像 李华